mirror of
https://github.com/areteruhiro/LIME-beta-hiro.git
synced 2025-02-06 05:21:37 +09:00
既読者機能の修正
This commit is contained in:
parent
87f45c7961
commit
64a87bbce8
@ -42,6 +42,9 @@ public class LimeOptions {
|
||||
public Option removeAllServices = new Option("remove_Services", R.string.RemoveService, false);
|
||||
public Option calltone = new Option("calltone", R.string.calltone, false);
|
||||
public Option ReadChecker = new Option("ReadChecker", R.string.ReadChecker, false);
|
||||
public Option MySendMessage = new Option("MySendMessage", R.string.MySendMessage, false);
|
||||
|
||||
|
||||
public Option RemoveNotification = new Option("RemoveNotification", R.string.removeNotification, false);
|
||||
public Option DarkColor = new Option("DarkColor", R.string.DarkColor, false);
|
||||
public Option NoMuteMessage = new Option("NoMuteMessage", R.string.NoMuteMessage, false);
|
||||
@ -69,7 +72,7 @@ public class LimeOptions {
|
||||
preventUnsendMessage,
|
||||
sendMuteMessage,
|
||||
Archived,
|
||||
ReadChecker,
|
||||
ReadChecker,MySendMessage,
|
||||
removeKeepUnread,
|
||||
KeepUnreadLSpatch,
|
||||
blockTracking,
|
||||
@ -83,4 +86,5 @@ public class LimeOptions {
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import io.github.hiro.lime.hooks.RemoveIcons;
|
||||
import io.github.hiro.lime.hooks.RemoveNotification;
|
||||
import io.github.hiro.lime.hooks.RemoveReplyMute;
|
||||
import io.github.hiro.lime.hooks.RemoveVoiceRecord;
|
||||
import io.github.hiro.lime.hooks.Resend;
|
||||
import io.github.hiro.lime.hooks.Ringtone;
|
||||
import io.github.hiro.lime.hooks.SendMuteMessage;
|
||||
import io.github.hiro.lime.hooks.SpoofAndroidId;
|
||||
@ -45,6 +46,7 @@ import io.github.hiro.lime.hooks.UnsentRec;
|
||||
import io.github.hiro.lime.hooks.Archived;
|
||||
import io.github.hiro.lime.hooks.ReadChecker;
|
||||
import io.github.hiro.lime.hooks.DarkColor;
|
||||
import io.github.hiro.lime.hooks.test;
|
||||
|
||||
|
||||
public class Main implements IXposedHookLoadPackage, IXposedHookInitPackageResources, IXposedHookZygoteInit {
|
||||
@ -86,7 +88,7 @@ public class Main implements IXposedHookLoadPackage, IXposedHookInitPackageResou
|
||||
new RemoveNotification(),
|
||||
new Disabled_Group_notification(),
|
||||
new PhotoAddNotification(),
|
||||
new RemoveVoiceRecord()
|
||||
new RemoveVoiceRecord(),
|
||||
};
|
||||
|
||||
public void handleLoadPackage(@NonNull XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
||||
|
@ -1,11 +1,14 @@
|
||||
|
||||
package io.github.hiro.lime.hooks;
|
||||
|
||||
|
||||
import android.app.AndroidAppHelper;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -18,18 +21,20 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||
import io.github.hiro.lime.LimeOptions;
|
||||
|
||||
|
||||
public class KeepUnread implements IHook {
|
||||
static boolean keepUnread = true;
|
||||
static boolean keepUnread = false;
|
||||
|
||||
|
||||
@Override
|
||||
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
||||
if (limeOptions.removeKeepUnread.checked) return;
|
||||
|
||||
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"com.linecorp.line.chatlist.view.fragment.ChatListFragment",
|
||||
loadPackageParam.classLoader,
|
||||
@ -41,28 +46,38 @@ public class KeepUnread implements IHook {
|
||||
View rootView = (View) param.getResult();
|
||||
Context appContext = rootView.getContext();
|
||||
|
||||
|
||||
Context moduleContext = AndroidAppHelper.currentApplication().createPackageContext(
|
||||
"io.github.hiro.lime", Context.CONTEXT_IGNORE_SECURITY);
|
||||
|
||||
|
||||
RelativeLayout layout = new RelativeLayout(appContext);
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
|
||||
layout.setLayoutParams(layoutParams);
|
||||
|
||||
|
||||
keepUnread = readStateFromFile(appContext);
|
||||
ImageView imageView = new ImageView(appContext);
|
||||
updateSwitchImage(imageView, keepUnread, moduleContext);
|
||||
|
||||
DisplayMetrics displayMetrics = appContext.getResources().getDisplayMetrics();
|
||||
int screenWidth = displayMetrics.widthPixels;
|
||||
int screenHeight = displayMetrics.heightPixels;
|
||||
|
||||
int horizontalMargin = (int) (screenWidth * 0.5);
|
||||
int verticalMargin = (int) (screenHeight * 0.015);
|
||||
Resources resources = appContext.getResources();
|
||||
Configuration configuration = resources.getConfiguration();
|
||||
int smallestWidthDp = configuration.smallestScreenWidthDp;
|
||||
|
||||
|
||||
float density = resources.getDisplayMetrics().density;
|
||||
|
||||
|
||||
int horizontalMarginPx = (int) ((smallestWidthDp * 0.5) * density);
|
||||
int verticalMarginPx = (int) (15 * density);
|
||||
|
||||
|
||||
RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
|
||||
imageParams.setMargins(horizontalMargin, verticalMargin, 0, 0); // 動的に計算されたマージンを設定
|
||||
imageParams.setMargins(horizontalMarginPx, verticalMarginPx, 0, 0);
|
||||
|
||||
|
||||
imageView.setOnClickListener(v -> {
|
||||
keepUnread = !keepUnread;
|
||||
@ -70,23 +85,40 @@ public class KeepUnread implements IHook {
|
||||
saveStateToFile(appContext, keepUnread);
|
||||
});
|
||||
|
||||
|
||||
layout.addView(imageView, imageParams);
|
||||
|
||||
|
||||
if (rootView instanceof ViewGroup) {
|
||||
ViewGroup rootViewGroup = (ViewGroup) rootView;
|
||||
if (rootViewGroup.getChildCount() > 0 && rootViewGroup.getChildAt(0) instanceof ListView) {
|
||||
ListView listView = (ListView) rootViewGroup.getChildAt(0);
|
||||
listView.addFooterView(layout);
|
||||
} else {
|
||||
|
||||
|
||||
boolean added = false;
|
||||
for (int i = 0; i < rootViewGroup.getChildCount(); i++) {
|
||||
View child = rootViewGroup.getChildAt(i);
|
||||
if (child instanceof ListView) {
|
||||
ListView listView = (ListView) child;
|
||||
listView.addFooterView(layout);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!added) {
|
||||
rootViewGroup.addView(layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updateSwitchImage(ImageView imageView, boolean isOn, Context moduleContext) {
|
||||
|
||||
|
||||
String imageName = isOn ? "switch_on" : "switch_off";
|
||||
int imageResource = moduleContext.getResources().getIdentifier(imageName, "drawable", "io.github.hiro.lime");
|
||||
|
||||
|
||||
if (imageResource != 0) {
|
||||
Drawable drawable = moduleContext.getResources().getDrawable(imageResource, null);
|
||||
if (drawable != null) {
|
||||
@ -96,12 +128,14 @@ public class KeepUnread implements IHook {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Drawable scaleDrawable(Drawable drawable, int width, int height) {
|
||||
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
|
||||
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
|
||||
return new BitmapDrawable(scaledBitmap);
|
||||
}
|
||||
|
||||
|
||||
private void saveStateToFile(Context context, boolean state) {
|
||||
String filename = "keep_unread_state.txt";
|
||||
try (FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE)) {
|
||||
@ -110,6 +144,7 @@ public class KeepUnread implements IHook {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean readStateFromFile(Context context) {
|
||||
String filename = "keep_unread_state.txt";
|
||||
try (FileInputStream fis = context.openFileInput(filename)) {
|
||||
@ -126,6 +161,7 @@ public class KeepUnread implements IHook {
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
XposedHelpers.findAndHookMethod(
|
||||
loadPackageParam.classLoader.loadClass(Constants.MARK_AS_READ_HOOK.className),
|
||||
Constants.MARK_AS_READ_HOOK.methodName,
|
||||
@ -138,19 +174,6 @@ public class KeepUnread implements IHook {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
XposedBridge.hookAllMethods(
|
||||
loadPackageParam.classLoader.loadClass(Constants.RESPONSE_HOOK.className),
|
||||
Constants.RESPONSE_HOOK.methodName,
|
||||
new XC_MethodHook() {
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
if (param.args[0] != null && param.args[0].toString().equals("sendChatChecked")) {
|
||||
param.setResult(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
package io.github.hiro.lime.hooks;
|
||||
|
||||
|
||||
|
||||
|
||||
import static io.github.hiro.lime.Main.limeOptions;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AndroidAppHelper;
|
||||
@ -49,6 +53,8 @@ public class ReadChecker implements IHook {
|
||||
private SQLiteDatabase db4 = null;
|
||||
private boolean shouldHookOnCreate = false;
|
||||
private String currentGroupId = null;
|
||||
|
||||
|
||||
@Override
|
||||
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
||||
if (!limeOptions.ReadChecker.checked) return;
|
||||
@ -57,6 +63,7 @@ public class ReadChecker implements IHook {
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
Application appContext = (Application) param.thisObject;
|
||||
|
||||
|
||||
if (appContext == null) {
|
||||
return;
|
||||
}
|
||||
@ -68,29 +75,36 @@ public class ReadChecker implements IHook {
|
||||
SQLiteDatabase.OpenParams dbParams1 = builder1.build();
|
||||
|
||||
|
||||
|
||||
|
||||
SQLiteDatabase.OpenParams.Builder builder2 = new SQLiteDatabase.OpenParams.Builder();
|
||||
builder2.addOpenFlags(SQLiteDatabase.OPEN_READWRITE);
|
||||
SQLiteDatabase.OpenParams dbParams2 = builder2.build();
|
||||
|
||||
|
||||
|
||||
|
||||
db3 = SQLiteDatabase.openDatabase(dbFile3, dbParams1);
|
||||
db4 = SQLiteDatabase.openDatabase(dbFile4, dbParams2);
|
||||
|
||||
|
||||
Context moduleContext = AndroidAppHelper.currentApplication().createPackageContext(
|
||||
"io.github.hiro.lime", Context.CONTEXT_IGNORE_SECURITY);
|
||||
initializeLimeDatabase(appContext);
|
||||
catchNotification(loadPackageParam, db3, db4, appContext,moduleContext);
|
||||
catchNotification(loadPackageParam, db3, db4, appContext, moduleContext);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
Class<?> chatHistoryRequestClass = XposedHelpers.findClass("com.linecorp.line.chat.request.ChatHistoryRequest", loadPackageParam.classLoader);
|
||||
XposedHelpers.findAndHookMethod(chatHistoryRequestClass, "getChatId", new XC_MethodHook() {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
String chatId = (String) param.getResult();
|
||||
//XposedBridge.log(chatId);
|
||||
////XposedBridge.log(chatId);
|
||||
if (isGroupExists(chatId)) {
|
||||
shouldHookOnCreate = true;
|
||||
currentGroupId = chatId;
|
||||
@ -102,10 +116,13 @@ public class ReadChecker implements IHook {
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
Class<?> chatHistoryActivityClass = XposedHelpers.findClass("jp.naver.line.android.activity.chathistory.ChatHistoryActivity", loadPackageParam.classLoader);
|
||||
XposedHelpers.findAndHookMethod(chatHistoryActivityClass, "onCreate", Bundle.class, new XC_MethodHook() {
|
||||
Context moduleContext;
|
||||
|
||||
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
if (moduleContext == null) {
|
||||
@ -113,22 +130,25 @@ public class ReadChecker implements IHook {
|
||||
Context systemContext = (Context) XposedHelpers.callMethod(param.thisObject, "getApplicationContext");
|
||||
moduleContext = systemContext.createPackageContext("io.github.hiro.lime", Context.CONTEXT_IGNORE_SECURITY);
|
||||
} catch (Exception e) {
|
||||
XposedBridge.log("Failed to get module context: " + e.getMessage());
|
||||
//XposedBridge.log("Failed to get module context: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
if (moduleContext == null) {
|
||||
XposedBridge.log("Module context is null. Skipping hook.");
|
||||
//XposedBridge.log("Module context is null. Skipping hook.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (shouldHookOnCreate && currentGroupId != null) {
|
||||
if (!isNoGroup(currentGroupId)) {
|
||||
Activity activity = (Activity) param.thisObject;
|
||||
|
||||
|
||||
addButton(activity, moduleContext);
|
||||
}
|
||||
}
|
||||
@ -136,11 +156,14 @@ public class ReadChecker implements IHook {
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private boolean isGroupExists(String groupId) {
|
||||
if (limeDatabase == null) {
|
||||
// //XposedBridge.log("Database is not initialized.");
|
||||
// ////XposedBridge.log("Database is not initialized.");
|
||||
return false;
|
||||
}
|
||||
String query = "SELECT 1 FROM group_messages WHERE group_id = ?";
|
||||
@ -150,9 +173,10 @@ public class ReadChecker implements IHook {
|
||||
return exists;
|
||||
}
|
||||
|
||||
|
||||
private boolean isNoGroup(String groupId) {
|
||||
if (limeDatabase == null) {
|
||||
// //XposedBridge.log("Database is not initialized.");
|
||||
// ////XposedBridge.log("Database is not initialized.");
|
||||
return true;
|
||||
}
|
||||
String query = "SELECT group_name FROM group_messages WHERE group_id = ?";
|
||||
@ -163,17 +187,21 @@ public class ReadChecker implements IHook {
|
||||
noGroup = groupName == null || groupName.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
cursor.close();
|
||||
return noGroup;
|
||||
}
|
||||
|
||||
private void addButton(Activity activity,Context moduleContext) {
|
||||
|
||||
private void addButton(Activity activity, Context moduleContext) {
|
||||
Button button = new Button(activity);
|
||||
button.setText("R");
|
||||
button.setBackgroundColor(Color.BLACK);
|
||||
|
||||
|
||||
button.setTextColor(Color.WHITE);
|
||||
|
||||
|
||||
FrameLayout.LayoutParams frameParams = new FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT
|
||||
@ -182,37 +210,46 @@ public class ReadChecker implements IHook {
|
||||
frameParams.topMargin = 150;
|
||||
button.setLayoutParams(frameParams);
|
||||
|
||||
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (currentGroupId != null) {
|
||||
showDataForGroupId(activity, currentGroupId,moduleContext);
|
||||
showDataForGroupId(activity, currentGroupId, moduleContext);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
ViewGroup layout = activity.findViewById(android.R.id.content);
|
||||
layout.addView(button);
|
||||
}
|
||||
|
||||
private void showDataForGroupId(Activity activity, String groupId,Context moduleContext) {
|
||||
|
||||
private void showDataForGroupId(Activity activity, String groupId, Context moduleContext) {
|
||||
|
||||
|
||||
if (limeDatabase == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
String query = "SELECT server_id, content, created_time FROM group_messages WHERE group_id=? ORDER BY created_time ASC";
|
||||
Cursor cursor = limeDatabase.rawQuery(query, new String[]{groupId});
|
||||
|
||||
|
||||
Map<String, DataItem> dataItemMap = new HashMap<>();
|
||||
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
String serverId = cursor.getString(0);
|
||||
String content = cursor.getString(1);
|
||||
String createdTime = cursor.getString(2);
|
||||
|
||||
|
||||
List<String> user_nameList = getuser_namesForServerId(serverId);
|
||||
|
||||
|
||||
if (dataItemMap.containsKey(serverId)) {
|
||||
DataItem existingItem = dataItemMap.get(serverId);
|
||||
existingItem.user_names.addAll(user_nameList);
|
||||
@ -224,16 +261,26 @@ public class ReadChecker implements IHook {
|
||||
}
|
||||
cursor.close();
|
||||
|
||||
|
||||
List<DataItem> sortedDataItems = new ArrayList<>(dataItemMap.values());
|
||||
Collections.sort(sortedDataItems, Comparator.comparing(item -> item.createdTime));
|
||||
|
||||
|
||||
StringBuilder resultBuilder = new StringBuilder();
|
||||
for (DataItem item : sortedDataItems) {
|
||||
resultBuilder.append("Content: ").append(item.content != null ? item.content : "Media").append("\n");
|
||||
resultBuilder.append("Created Time: ").append(item.createdTime).append("\n");
|
||||
|
||||
|
||||
if (!item.user_names.isEmpty()) {
|
||||
resultBuilder.append(moduleContext.getResources().getString(R.string.Reader)+" (").append(item.user_names.size()).append("):\n");
|
||||
|
||||
|
||||
int newlineCount = 0;
|
||||
for (String user_name : item.user_names) {
|
||||
newlineCount += countNewlines(user_name);
|
||||
}
|
||||
resultBuilder.append(moduleContext.getResources().getString(R.string.Reader))
|
||||
.append(" (").append(item.user_names.size() + newlineCount).append("):\n");
|
||||
for (String user_name : item.user_names) {
|
||||
resultBuilder.append("- ").append(user_name).append("\n");
|
||||
}
|
||||
@ -243,71 +290,102 @@ public class ReadChecker implements IHook {
|
||||
resultBuilder.append("\n");
|
||||
}
|
||||
|
||||
|
||||
TextView textView = new TextView(activity);
|
||||
textView.setText(resultBuilder.toString());
|
||||
textView.setPadding(20, 20, 20, 20);
|
||||
|
||||
|
||||
ScrollView scrollView = new ScrollView(activity);
|
||||
scrollView.addView(textView);
|
||||
|
||||
|
||||
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setTitle("READ Data");
|
||||
builder.setView(scrollView);
|
||||
|
||||
|
||||
builder.setPositiveButton("OK", null);
|
||||
|
||||
|
||||
|
||||
|
||||
builder.setNegativeButton(moduleContext.getResources().getString(R.string.Delete), (dialog, which) -> {
|
||||
|
||||
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(moduleContext.getResources().getString(R.string.check))
|
||||
.setMessage(moduleContext.getResources().getString(R.string.really_delete))
|
||||
.setPositiveButton(moduleContext.getResources().getString(R.string.yes), (confirmDialog, confirmWhich) -> deleteGroupData(groupId,activity,moduleContext))
|
||||
.setPositiveButton(moduleContext.getResources().getString(R.string.yes), (confirmDialog, confirmWhich) -> deleteGroupData(groupId, activity, moduleContext))
|
||||
.setNegativeButton(moduleContext.getResources().getString(R.string.no), null)
|
||||
.show();
|
||||
});
|
||||
|
||||
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
scrollView.post(() -> scrollView.fullScroll(View.FOCUS_DOWN));
|
||||
}
|
||||
|
||||
private void deleteGroupData(String groupId,Activity activity,Context moduleContext) {
|
||||
|
||||
private void deleteGroupData(String groupId, Activity activity, Context moduleContext) {
|
||||
if (limeDatabase == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
String deleteQuery = "DELETE FROM group_messages WHERE group_id=?";
|
||||
limeDatabase.execSQL(deleteQuery, new String[]{groupId});
|
||||
Toast.makeText(activity, moduleContext.getResources().getString(R.string.Reader_Data_Delete_Success), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
|
||||
private List<String> getuser_namesForServerId(String serverId) {
|
||||
if (limeDatabase == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// user_name のすべてのエントリを取得する
|
||||
String query = "SELECT user_name FROM group_messages WHERE server_id=? ORDER BY created_time ASC";
|
||||
String query = "SELECT user_name FROM group_messages WHERE server_id=?";
|
||||
Cursor cursor = limeDatabase.rawQuery(query, new String[]{serverId});
|
||||
List<String> userNames = new ArrayList<>();
|
||||
List<String> user_names = new ArrayList<>();
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
String userNameStr = cursor.getString(0);
|
||||
if (userNameStr != null) {
|
||||
// user_nameをそのままリストに追加
|
||||
userNames.add(userNameStr);
|
||||
|
||||
|
||||
String[] names = userNameStr.split("\n");
|
||||
Collections.addAll(user_names, names);
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
return userNames;
|
||||
return user_names;
|
||||
}
|
||||
|
||||
|
||||
private int countNewlines(String text) {
|
||||
if (text == null || text.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
for (char c : text.toCharArray()) {
|
||||
if (c == '\n') {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
private static class DataItem {
|
||||
String serverId;
|
||||
String content;
|
||||
String createdTime;
|
||||
Set<String> user_names;
|
||||
|
||||
|
||||
DataItem(String serverId, String content, String createdTime) {
|
||||
this.serverId = serverId;
|
||||
this.content = content;
|
||||
@ -315,7 +393,9 @@ public class ReadChecker implements IHook {
|
||||
this.user_names = new HashSet<>();
|
||||
}
|
||||
}
|
||||
private void catchNotification(XC_LoadPackage.LoadPackageParam loadPackageParam, SQLiteDatabase db3, SQLiteDatabase db4, Context appContext,Context moduleContext) {
|
||||
|
||||
|
||||
private void catchNotification(XC_LoadPackage.LoadPackageParam loadPackageParam, SQLiteDatabase db3, SQLiteDatabase db4, Context appContext, Context moduleContext) {
|
||||
try {
|
||||
XposedBridge.hookAllMethods(
|
||||
loadPackageParam.classLoader.loadClass(Constants.NOTIFICATION_READ_HOOK.className),
|
||||
@ -324,21 +404,22 @@ public class ReadChecker implements IHook {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
String paramValue = param.args[0].toString();
|
||||
//XposedBridge.log(paramValue);
|
||||
if (appContext == null) {
|
||||
//XposedBridge.log("appContext is null!");
|
||||
//
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Context moduleContext;
|
||||
try {
|
||||
moduleContext = appContext.createPackageContext(
|
||||
"io.github.hiro.lime", Context.CONTEXT_IGNORE_SECURITY);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// //XposedBridge.log("Failed to create package context: " + e.getMessage());
|
||||
////XposedBridge.log("Failed to create package context: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (paramValue != null && paramValue.contains("type:NOTIFIED_READ_MESSAGE")) {
|
||||
List<String> messages = extractMessages(paramValue);
|
||||
for (String message : messages) {
|
||||
@ -353,21 +434,26 @@ public class ReadChecker implements IHook {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<String> extractMessages(String paramValue) {
|
||||
List<String> messages = new ArrayList<>();
|
||||
Pattern pattern = Pattern.compile("type:NOTIFIED_READ_MESSAGE.*?(?=type:|$)");
|
||||
Matcher matcher = pattern.matcher(paramValue);
|
||||
|
||||
|
||||
while (matcher.find()) {
|
||||
messages.add(matcher.group().trim());
|
||||
}
|
||||
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
|
||||
private void fetchDataAndSave(SQLiteDatabase db3, SQLiteDatabase db4, String paramValue, Context context, Context moduleContext) {
|
||||
File dbFile = new File(context.getFilesDir(), "data_log.txt");
|
||||
|
||||
|
||||
try {
|
||||
String serverId = extractServerId(paramValue, context);
|
||||
String checkedUser = extractCheckedUser(paramValue);
|
||||
@ -375,6 +461,7 @@ public class ReadChecker implements IHook {
|
||||
writeToFile(dbFile, "Missing parameters: serverId=" + serverId + ", checkedUser=" + checkedUser);
|
||||
return;
|
||||
}
|
||||
String SendUser = queryDatabase(db3, "SELECT from_mid FROM chat_history WHERE server_id=?", serverId);
|
||||
String groupId = queryDatabase(db3, "SELECT chat_id FROM chat_history WHERE server_id=?", serverId);
|
||||
String groupName = queryDatabase(db3, "SELECT name FROM groups WHERE id=?", groupId);
|
||||
String content = queryDatabase(db3, "SELECT content FROM chat_history WHERE server_id=?", serverId);
|
||||
@ -400,15 +487,17 @@ public class ReadChecker implements IHook {
|
||||
}
|
||||
}
|
||||
String finalContent = (content != null && !content.isEmpty()) ? content : (!mediaDescription.isEmpty() ? mediaDescription : "No content:" + serverId);
|
||||
saveData(groupId, serverId, checkedUser, groupName, finalContent, user_name, timeFormatted, context);
|
||||
// markPreviousMessagesAsRead(groupId, checkedUser, timeEpochStr, context);
|
||||
saveData(SendUser, groupId, serverId, checkedUser, groupName, finalContent, user_name, timeFormatted, context);
|
||||
// markPreviousMessagesAsRead(groupId, checkedUser, timeEpochStr, context);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void markPreviousMessagesAsRead(String groupId, String checkedUser, String timeEpochStr, Context context) {
|
||||
initializeLimeDatabase(context);
|
||||
|
||||
|
||||
try {
|
||||
String query = "SELECT server_id, content, created_time, user_name FROM group_messages " +
|
||||
"WHERE group_id=? AND created_time<? AND user_name NOT LIKE ?";
|
||||
@ -423,10 +512,12 @@ public class ReadChecker implements IHook {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("user_name", updatedUserName);
|
||||
|
||||
|
||||
limeDatabase.update("group_messages", values, "group_id=? AND server_id=?",
|
||||
new String[]{groupId, previousServerId});
|
||||
|
||||
// //XposedBridge.log("Marked as read in lime_data.db: Group_id: " + groupId + ", Server_id: " + previousServerId + ", Updated user_name: " + updatedUserName);
|
||||
|
||||
// ////XposedBridge.log("Marked as read in lime_data.db: Group_id: " + groupId + ", Server_id: " + previousServerId + ", Updated user_name: " + updatedUserName);
|
||||
}
|
||||
cursor.close();
|
||||
} catch (Exception e) {
|
||||
@ -434,6 +525,7 @@ public class ReadChecker implements IHook {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void writeToFile(File file, String text) {
|
||||
try (FileWriter writer = new FileWriter(file, true)) {
|
||||
writer.write(text + "\n");
|
||||
@ -441,6 +533,7 @@ public class ReadChecker implements IHook {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String formatMessageTime(String timeEpochStr) {
|
||||
if (timeEpochStr == null) return null;
|
||||
long timeEpoch = Long.parseLong(timeEpochStr);
|
||||
@ -448,43 +541,55 @@ public class ReadChecker implements IHook {
|
||||
return sdf.format(new Date(timeEpoch));
|
||||
}
|
||||
|
||||
|
||||
private String extractCheckedUser(String paramValue) {
|
||||
Pattern pattern = Pattern.compile("param2:([a-zA-Z0-9]+)");
|
||||
Matcher matcher = pattern.matcher(paramValue);
|
||||
|
||||
|
||||
return matcher.find() ? matcher.group(1) : null;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private String extractServerId(String paramValue, Context context) {
|
||||
Pattern pattern = Pattern.compile("param3:([0-9]+)");
|
||||
Matcher matcher = pattern.matcher(paramValue);
|
||||
|
||||
//XposedBridge.log(paramValue);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
|
||||
|
||||
} else {
|
||||
saveParamToFile(paramValue, context);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void saveParamToFile(String paramValue, Context context) {
|
||||
try {
|
||||
File logFile = new File(context.getFilesDir(), "missing_param_values.txt");
|
||||
|
||||
|
||||
if (!logFile.exists()) {
|
||||
logFile.createNewFile();
|
||||
}
|
||||
|
||||
|
||||
FileWriter writer = new FileWriter(logFile, true);
|
||||
writer.append("Missing serverId in paramValue:").append(paramValue).append("\n");
|
||||
writer.close();
|
||||
} catch (IOException ignored) {
|
||||
// //XposedBridge.log("Error writing paramValue to file: " + e.getMessage());
|
||||
// ////XposedBridge.log("Error writing paramValue to file: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String queryDatabase(SQLiteDatabase db, String query, String... selectionArgs) {
|
||||
if (db == null) {
|
||||
// //XposedBridge.log("Database is not initialized.");
|
||||
// ////XposedBridge.log("Database is not initialized.");
|
||||
return null;
|
||||
}
|
||||
Cursor cursor = db.rawQuery(query, selectionArgs);
|
||||
@ -496,21 +601,24 @@ public class ReadChecker implements IHook {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private void initializeLimeDatabase(Context context) {
|
||||
|
||||
|
||||
File oldDbFile = new File(context.getFilesDir(), "lime_data.db");
|
||||
if (oldDbFile.exists()) {
|
||||
boolean deleted = oldDbFile.delete();
|
||||
if (deleted) {
|
||||
//XposedBridge.log("Old database file lime_data.db deleted.");
|
||||
////XposedBridge.log("Old database file lime_data.db deleted.");
|
||||
} else {
|
||||
//XposedBridge.log("Failed to delete old database file lime_data.db.");
|
||||
////XposedBridge.log("Failed to delete old database file lime_data.db.");
|
||||
}
|
||||
}
|
||||
// 新しいデータベースファイルの初期化
|
||||
File dbFile = new File(context.getFilesDir(), "lime_checked_data.db");
|
||||
limeDatabase = SQLiteDatabase.openOrCreateDatabase(dbFile, null);
|
||||
|
||||
|
||||
String createGroupTable = "CREATE TABLE IF NOT EXISTS group_messages (" +
|
||||
"group_id TEXT NOT NULL, " +
|
||||
"server_id TEXT NOT NULL, " +
|
||||
@ -522,12 +630,16 @@ public class ReadChecker implements IHook {
|
||||
"PRIMARY KEY(group_id, server_id, checked_user)" +
|
||||
");";
|
||||
|
||||
|
||||
limeDatabase.execSQL(createGroupTable);
|
||||
// //XposedBridge.log("Database initialized and group_messages table created.");
|
||||
// ////XposedBridge.log("Database initialized and group_messages table created.");
|
||||
}
|
||||
private void saveData(String groupId, String serverId, String checkedUser, String groupName, String content, String user_name, String createdTime, Context context) {
|
||||
|
||||
|
||||
private void saveData( String SendUser, String groupId, String serverId, String checkedUser, String groupName, String content, String user_name, String createdTime, Context context) {
|
||||
if (groupName == null) {
|
||||
Log.w("saveData", "group_name is null. Skipping save operation.");
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
Cursor cursor = null;
|
||||
@ -539,22 +651,23 @@ public class ReadChecker implements IHook {
|
||||
String existingUserName = cursor.getString(1);
|
||||
String currentTime = getCurrentTime();
|
||||
|
||||
|
||||
if (count > 0) {
|
||||
if (!existingUserName.contains(user_name)) {
|
||||
String updatedUserName = existingUserName + (existingUserName.isEmpty() ? "" : "\n") + "-" + user_name + " [" + currentTime + "]";
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("user_name", updatedUserName);
|
||||
limeDatabase.update("group_messages", values, "server_id=? AND checked_user=?", new String[]{serverId, checkedUser});
|
||||
// //XposedBridge.log("User name updated for server_id: " + serverId + ", checked_user: " + checkedUser);
|
||||
// ////XposedBridge.log("User name updated for server_id: " + serverId + ", checked_user: " + checkedUser);
|
||||
}
|
||||
} else {
|
||||
insertNewRecord(groupId, serverId, checkedUser, groupName, content, "-" + user_name + " [" + currentTime + "]", createdTime);
|
||||
insertNewRecord(SendUser, groupId, serverId, checkedUser, groupName, content, "-" + user_name + " [" + currentTime + "]", createdTime);
|
||||
}
|
||||
updateOtherRecordsUserNames(groupId, user_name, currentTime);
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("saveData", "Error during data existence check or update:", e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
@ -562,23 +675,27 @@ public class ReadChecker implements IHook {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updateOtherRecordsUserNames(String groupId, String user_name, String currentTime) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
String selectOtherQuery = "SELECT server_id, user_name FROM group_messages WHERE group_id=? AND user_name NOT LIKE ?";
|
||||
cursor = limeDatabase.rawQuery(selectOtherQuery, new String[]{groupId, "%-" + user_name + "%"});
|
||||
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
String serverId = cursor.getString(cursor.getColumnIndexOrThrow("server_id"));
|
||||
String existingUserName = cursor.getString(cursor.getColumnIndexOrThrow("user_name"));
|
||||
|
||||
|
||||
if (!existingUserName.contains(user_name)) {
|
||||
String updatedUserName = existingUserName + (existingUserName.isEmpty() ? "" : "\n") + "-" + user_name + " [" + currentTime + "]";
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("user_name", updatedUserName);
|
||||
|
||||
|
||||
limeDatabase.update("group_messages", values, "group_id=? AND server_id=?", new String[]{groupId, serverId});
|
||||
// //XposedBridge.log("Updated user_name for other records in group_id: " + groupId + ", server_id: " + serverId);
|
||||
// ////XposedBridge.log("Updated user_name for other records in group_id: " + groupId + ", server_id: " + serverId);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@ -590,18 +707,32 @@ public class ReadChecker implements IHook {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getCurrentTime() {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
||||
return sdf.format(new Date());
|
||||
}
|
||||
|
||||
private void insertNewRecord(String groupId, String serverId, String checkedUser, String groupName, String content, String user_name, String createdTime) {
|
||||
try {
|
||||
String insertQuery = "INSERT INTO group_messages(group_id, server_id, checked_user, group_name, content, user_name, created_time)" +
|
||||
"VALUES(?, ?, ?, ?, ?, ?, ?);";
|
||||
|
||||
private void insertNewRecord(String SendUser, String groupId, String serverId, String checkedUser, String groupName, String content, String user_name, String createdTime) {
|
||||
String insertQuery = "INSERT INTO group_messages(group_id, server_id, checked_user, group_name, content, user_name, created_time)" +
|
||||
" VALUES(?, ?, ?, ?, ?, ?, ?);";
|
||||
if (!limeOptions.MySendMessage.checked) {
|
||||
limeDatabase.execSQL(insertQuery, new Object[]{groupId, serverId, checkedUser, groupName, content, user_name, createdTime});
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
if (SendUser == null) {
|
||||
try {
|
||||
limeDatabase.beginTransaction();
|
||||
limeDatabase.execSQL(insertQuery, new Object[]{groupId, serverId, checkedUser, groupName, content, user_name, createdTime});
|
||||
limeDatabase.setTransactionSuccessful();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
limeDatabase.endTransaction();
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 10 KiB |
Binary file not shown.
Before Width: | Height: | Size: 451 KiB After Width: | Height: | Size: 20 KiB |
@ -131,4 +131,8 @@
|
||||
<string name="AutomaticBackup">定期的にバックアップ</string>
|
||||
<string name="set_id">Set ID</string>
|
||||
<string name="RemoveVoiceRecord">音声ボタンを無効化</string>
|
||||
<string name="MySendMessage">(既読機能)自分以外のメッセージを保存しない</string>
|
||||
|
||||
|
||||
|
||||
</resources>
|
||||
|
@ -125,4 +125,6 @@
|
||||
<string name="AutomaticBackup">將您訂閱的群組的通知靜音</string>
|
||||
<string name="RemoveVoiceRecord">禁用音訊按鈕</string>
|
||||
|
||||
<string name="MySendMessage">(讀取功能)請勿儲存您自己以外的信息</string>
|
||||
|
||||
</resources>
|
||||
|
@ -134,4 +134,9 @@
|
||||
<string name="Talk_Auto_Back_up_Error">Error occurred during automatic backup</string>
|
||||
<string name="Talk_Picture_Back_up_Success">The talk picture folder was successfully backed up</string>
|
||||
<string name="RemoveVoiceRecord">Enable audio button</string>
|
||||
|
||||
<string name="MySendMessage">(Read function) Do not save messages other than your own</string>
|
||||
|
||||
|
||||
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user