diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index c69319e..5b387cc 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -26,13 +26,13 @@ body:
label: 内容の詳細
placeholder: 具体的に記述してください
validations:
- required: true
+ required: false
- type: textarea
attributes:
label: 再現する手順
placeholder: 可能な限り具体的に記述してください
validations:
- required: true
+ required: false
- type: dropdown
attributes:
label: 有効にしている LIME の設定
@@ -65,4 +65,4 @@ body:
label: バージョンの確認
options:
- label: 私は最新版の LIME を使用しており、それに適合するバージョンの LINE アプリを使用しています
- required: true
+ required: false
diff --git a/README.md b/README.md
index e296e58..556a94a 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,6 @@ Macro SAMPLE
## 概要
-β版です。自己責任でお願いします。
このアプリで追加されている機能は、いずれPRするものがおおいですが、機能の追加を優先しているため、修正が必要なものが多く、このような形で公開させていただいております。
@@ -211,7 +210,7 @@ LI**M**E
> - 着信が入るとクラッシュ
> - コインの購入が不可
> - LINE Pay の一部の機能が使用不可
-> - Wear OS (スマートウォッチ)での使用不可
+> - △ Wear OS (スマートウォッチ)での連携
1. [**LSPatch**](https://github.com/LSPosed/LSPatch) をインストール
※フォークで開発されている [**NPatch**](https://github.com/HSSkyBoy/NPatch) では不具合が発生する可能性があります。
diff --git a/app/src/main/java/io/github/hiro/lime/hooks/CallVolume.java b/app/src/main/java/io/github/hiro/lime/hooks/CallVolume.java
new file mode 100644
index 0000000..58142dd
--- /dev/null
+++ b/app/src/main/java/io/github/hiro/lime/hooks/CallVolume.java
@@ -0,0 +1,63 @@
+package io.github.hiro.lime.hooks;
+
+import de.robv.android.xposed.callbacks.XC_LoadPackage;
+import io.github.hiro.lime.LimeOptions;
+
+
+import static io.github.hiro.lime.Main.limeOptions;
+
+import android.app.AndroidAppHelper;
+import android.app.Application;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.media.AudioManager;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+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 CallVolume implements IHook {
+
+ @Override
+ public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
+ XposedHelpers.findAndHookMethod(AudioManager.class, "requestAudioFocus",
+ AudioManager.OnAudioFocusChangeListener.class, int.class, int.class, new XC_MethodHook() {
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
+ AudioManager.OnAudioFocusChangeListener listener = (AudioManager.OnAudioFocusChangeListener) param.args[0];
+ int focusGain = (int) param.args[1];
+ int mode = (int) param.args[2];
+
+ XposedBridge.log("Blocking transient audio focus request.");
+ param.setResult(AudioManager.AUDIOFOCUS_REQUEST_FAILED);
+
+ }
+ });
+
+ XposedHelpers.findAndHookMethod(AudioManager.class, "abandonAudioFocus",
+ AudioManager.OnAudioFocusChangeListener.class, new XC_MethodHook() {
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
+ AudioManager.OnAudioFocusChangeListener listener = (AudioManager.OnAudioFocusChangeListener) param.args[0];
+
+ }
+ });
+
+
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/github/hiro/lime/hooks/CrashGuard.java b/app/src/main/java/io/github/hiro/lime/hooks/CrashGuard.java
new file mode 100644
index 0000000..feb6b4a
--- /dev/null
+++ b/app/src/main/java/io/github/hiro/lime/hooks/CrashGuard.java
@@ -0,0 +1,58 @@
+package io.github.hiro.lime.hooks;
+
+import android.app.Application;
+import android.content.Context;
+import java.io.File;
+import java.lang.reflect.Field;
+
+
+import de.robv.android.xposed.callbacks.XC_LoadPackage;
+import io.github.hiro.lime.LimeOptions;
+import de.robv.android.xposed.XC_MethodHook;
+import de.robv.android.xposed.XposedBridge;
+import de.robv.android.xposed.XposedHelpers;
+
+public class CrashGuard implements IHook {
+
+ @Override
+ public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
+
+ if (!limeOptions.CrashGuard.checked) return;
+ XposedBridge.hookAllMethods(Application.class, "onCreate", new XC_MethodHook() {
+ @Override
+ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
+ Application appContext = (Application) param.thisObject;
+ clearCache(appContext);
+ }
+
+ private void clearCache(Context context) {
+ try {
+ File cacheDir = context.getCacheDir();
+ if (cacheDir != null && cacheDir.isDirectory()) {
+ deleteDir(cacheDir);
+ XposedBridge.log("Cache cleared successfully.");
+ }
+ } catch (Exception e) {
+ XposedBridge.log("Failed to clear cache: " + e.getMessage());
+ }
+ }
+
+ private boolean deleteDir(File dir) {
+ if (dir != null && dir.isDirectory()) {
+ String[] children = dir.list();
+ if (children != null) {
+ for (String child : children) {
+ boolean success = deleteDir(new File(dir, child));
+ if (!success) {
+ return false;
+ }
+ }
+ }
+ }
+ return dir.delete();
+ }
+ });
+
+
+ }
+}
diff --git a/app/src/main/java/io/github/hiro/lime/hooks/OutputRequest.java b/app/src/main/java/io/github/hiro/lime/hooks/OutputRequest.java
index 3e6da29..a306027 100644
--- a/app/src/main/java/io/github/hiro/lime/hooks/OutputRequest.java
+++ b/app/src/main/java/io/github/hiro/lime/hooks/OutputRequest.java
@@ -16,7 +16,7 @@ public class OutputRequest implements IHook {
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
- XposedBridge.log(new Communication(Communication.Type.REQUEST, param.args[0].toString(), param.args[1]).toString());
+ XposedBridge.log(new Communication(Communication.Type.REQUEST, param.args[0].toString(), param.args[1]).toString());
}
}
);
diff --git a/app/src/main/java/io/github/hiro/lime/hooks/PreventMarkAsRead.java b/app/src/main/java/io/github/hiro/lime/hooks/PreventMarkAsRead.java
index cf5b803..fce5532 100644
--- a/app/src/main/java/io/github/hiro/lime/hooks/PreventMarkAsRead.java
+++ b/app/src/main/java/io/github/hiro/lime/hooks/PreventMarkAsRead.java
@@ -2,6 +2,7 @@ package io.github.hiro.lime.hooks;
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;
@@ -10,6 +11,20 @@ public class PreventMarkAsRead implements IHook {
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
if (!limeOptions.preventMarkAsRead.checked) return;
+
+ XposedHelpers.findAndHookMethod(
+ loadPackageParam.classLoader.loadClass(Constants.MARK_AS_READ_HOOK.className),
+ Constants.MARK_AS_READ_HOOK.methodName,
+ new XC_MethodHook() {
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) {
+
+ param.setResult(null);
+
+ }
+ }
+ );
+
XposedBridge.hookAllMethods(
loadPackageParam.classLoader.loadClass(Constants.REQUEST_HOOK.className),
Constants.REQUEST_HOOK.methodName,
@@ -22,5 +37,19 @@ public class PreventMarkAsRead 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);
+ }
+ }
+ }
+ );
+
+
}
}
diff --git a/app/src/main/java/io/github/hiro/lime/hooks/PreventUnsendMessage.java b/app/src/main/java/io/github/hiro/lime/hooks/PreventUnsendMessage.java
index 7615481..dab5e62 100644
--- a/app/src/main/java/io/github/hiro/lime/hooks/PreventUnsendMessage.java
+++ b/app/src/main/java/io/github/hiro/lime/hooks/PreventUnsendMessage.java
@@ -18,29 +18,42 @@ public class PreventUnsendMessage implements IHook {
loadPackageParam.classLoader.loadClass(Constants.RESPONSE_HOOK.className),
Constants.RESPONSE_HOOK.methodName,
new XC_MethodHook() {
- @SuppressWarnings("unchecked")
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
- if (param.args[0].toString().equals("sync")) {
+ if (!"sync".equals(param.args[0].toString())) return;
+
+ try {
Object wrapper = param.args[1].getClass().getDeclaredField("a").get(param.args[1]);
Field operationResponseField = wrapper.getClass().getSuperclass().getDeclaredField("value_");
operationResponseField.setAccessible(true);
Object operationResponse = operationResponseField.get(wrapper);
+ if (operationResponse == null) return;
+
ArrayList> operations = (ArrayList>) operationResponse.getClass().getDeclaredField("a").get(operationResponse);
+ if (operations == null) return;
+
for (Object operation : operations) {
Field typeField = operation.getClass().getDeclaredField("c");
+ typeField.setAccessible(true);
Object type = typeField.get(operation);
- if (type.toString().equals("NOTIFIED_DESTROY_MESSAGE")) {
+
+ if ("NOTIFIED_DESTROY_MESSAGE".equals(type.toString())) {
typeField.set(operation, type.getClass().getMethod("valueOf", String.class).invoke(operation, "DUMMY"));
- } else if (type.toString().equals("RECEIVE_MESSAGE")) {
+ } else if ("RECEIVE_MESSAGE".equals(type.toString())) {
Object message = operation.getClass().getDeclaredField("j").get(operation);
+ if (message == null) continue;
Map contentMetadata = (Map) message.getClass().getDeclaredField("k").get(message);
- contentMetadata.remove("UNSENT");
+ if (contentMetadata != null) {
+ contentMetadata.remove("UNSENT");
+ }
}
}
+ } catch (Exception e) {
+ XposedBridge.log("PreventUnsendMessage: Exception occurred - " + e.getMessage());
}
}
}
);
+
}
}
diff --git a/app/src/main/java/io/github/hiro/lime/hooks/RemoveNotification.java b/app/src/main/java/io/github/hiro/lime/hooks/RemoveNotification.java
index 76515a9..9f65341 100644
--- a/app/src/main/java/io/github/hiro/lime/hooks/RemoveNotification.java
+++ b/app/src/main/java/io/github/hiro/lime/hooks/RemoveNotification.java
@@ -11,31 +11,46 @@ public class RemoveNotification implements IHook {
@Override
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
if (!limeOptions.RemoveNotification.checked) return;
-
XposedBridge.hookAllMethods(
loadPackageParam.classLoader.loadClass(Constants.RESPONSE_HOOK.className),
Constants.RESPONSE_HOOK.methodName,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
- if (param.args[0].toString().equals("sync")) {
+ if (!"sync".equals(param.args[0].toString())) return;
+
+ try {
Object wrapper = param.args[1].getClass().getDeclaredField("a").get(param.args[1]);
+ if (wrapper == null) return;
+
Field operationResponseField = wrapper.getClass().getSuperclass().getDeclaredField("value_");
operationResponseField.setAccessible(true);
Object operationResponse = operationResponseField.get(wrapper);
+ if (operationResponse == null) return;
+
ArrayList> operations = (ArrayList>) operationResponse.getClass().getDeclaredField("a").get(operationResponse);
+ if (operations == null) return;
+
for (int i = operations.size() - 1; i >= 0; i--) {
Object operation = operations.get(i);
Field typeField = operation.getClass().getDeclaredField("c");
+ typeField.setAccessible(true);
Object type = typeField.get(operation);
- if (type.toString().equals("NOTIFIED_UPDATE_PROFILE")) {
+
+
+ if ("NOTIFIED_UPDATE_PROFILE".equals(type.toString())) {
typeField.set(operation, type.getClass().getMethod("valueOf", String.class).invoke(type, "DUMMY"));
}
}
+ } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) {
+ XposedBridge.log("RemoveNotification: Error accessing fields - " + e.getMessage());
+ } catch (Exception e) {
+ XposedBridge.log("RemoveNotification: Unexpected error - " + e.getMessage());
}
}
}
);
+
}
}
diff --git a/app/src/main/java/io/github/hiro/lime/hooks/Test.java b/app/src/main/java/io/github/hiro/lime/hooks/Test.java
new file mode 100644
index 0000000..29c323d
--- /dev/null
+++ b/app/src/main/java/io/github/hiro/lime/hooks/Test.java
@@ -0,0 +1,419 @@
+package io.github.hiro.lime.hooks;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
+
+import de.robv.android.xposed.XC_MethodHook;
+import de.robv.android.xposed.XposedBridge;
+import de.robv.android.xposed.callbacks.XC_LoadPackage;
+import dalvik.system.DexFile;
+import io.github.hiro.lime.LimeOptions;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+
+public class Test implements IHook {
+ private boolean isButtonAdded = false; // ボタンが追加されたかどうかを追跡するフラグ
+ @Override
+ public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
+ String packageName = loadPackageParam.packageName;
+
+ XposedBridge.log("Hooking package: " + packageName);
+ hookOnViewAdded(loadPackageParam.classLoader);
+ hookAllClassesInPackage(loadPackageParam.classLoader, loadPackageParam);
+ hookFragmentOnCreateView(loadPackageParam.classLoader);
+ //hookChatHistoryActivity(loadPackageParam.classLoader); // ChatHistoryActivityのフック
+ //hookLongClickListeners(loadPackageParam.classLoader); // 長押しリスナーのフック
+
+
+
+ }
+
+
+ private void hookAllClassesInPackage(ClassLoader classLoader, XC_LoadPackage.LoadPackageParam loadPackageParam) {
+ try {
+ String apkPath = loadPackageParam.appInfo.sourceDir;
+ if (apkPath == null) {
+ XposedBridge.log("Could not get APK path.");
+ return;
+ }
+
+ DexFile dexFile = new DexFile(new File(apkPath));
+ Enumeration classNames = dexFile.entries();
+ while (classNames.hasMoreElements()) {
+ String className = classNames.nextElement();
+
+ // 指定されたパッケージで始まるクラスのみをフック
+ // if (className.startsWith("com.linecorp.line") || className.startsWith("jp.naver.line.android")) {
+ try {
+ Class> clazz = Class.forName(className, false, classLoader);
+ hookAllMethods(clazz);
+ } catch (ClassNotFoundException e) {
+ XposedBridge.log("Class not found: " + className);
+ } catch (Throwable e) {
+ XposedBridge.log("Error loading class " + className + ": " + e.getMessage());
+ }
+ // }
+ }
+ } catch (Throwable e) {
+ XposedBridge.log("Error while hooking classes: " + e.getMessage());
+ }
+ }
+
+ private void hookAllClasses(ClassLoader classLoader, XC_LoadPackage.LoadPackageParam loadPackageParam) {
+ try {
+ String apkPath = loadPackageParam.appInfo.sourceDir;
+ if (apkPath == null) {
+ XposedBridge.log("Could not get APK path.");
+ return;
+ }
+
+ DexFile dexFile = new DexFile(new File(apkPath));
+ Enumeration classNames = dexFile.entries();
+ while (classNames.hasMoreElements()) {
+ String className = classNames.nextElement();
+ try {
+ Class> clazz = Class.forName(className, false, classLoader);
+ hookAllMethods(clazz);
+ } catch (ClassNotFoundException e) {
+ XposedBridge.log("Class not found: " + className);
+ } catch (Throwable e) {
+ XposedBridge.log("Error loading class " + className + ": " + e.getMessage());
+ }
+ }
+ } catch (Throwable e) {
+ XposedBridge.log("Error while hooking classes: " + e.getMessage());
+ }
+ }
+
+
+ private void hookFragmentOnCreateView(ClassLoader classLoader) {
+ try {
+ Class> fragmentClass = Class.forName("androidx.fragment.app.Fragment", false, classLoader);
+ Method onCreateViewMethod = fragmentClass.getDeclaredMethod("onCreateView", LayoutInflater.class, ViewGroup.class, Bundle.class);
+
+ XposedBridge.hookMethod(onCreateViewMethod, new XC_MethodHook() {
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
+ XposedBridge.log("Before calling: " + fragmentClass.getName() + ".onCreateView");
+ }
+
+ @Override
+ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
+ ViewGroup rootView = (ViewGroup) param.getResult(); // ルートのViewGroupを取得
+ Context context = rootView.getContext();
+
+ // IDによるビューの取得
+ int messageContainerId = getIdByName(context, "message_context_menu_content_container");
+ View messageContainer = rootView.findViewById(messageContainerId);
+
+ if (messageContainer != null) {
+ XposedBridge.log("messageContainer found: " + messageContainer.toString());
+ } else {
+ XposedBridge.log("messageContainer not found");
+ }
+
+ // 新しいビューの追加を監視
+ rootView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ XposedBridge.log("Child view added: " + child.toString());
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ XposedBridge.log("Child view removed: " + child.toString());
+ }
+ });
+ }
+ });
+ } catch (ClassNotFoundException e) {
+ XposedBridge.log("Class not found: androidx.fragment.app.Fragment");
+ } catch (NoSuchMethodException e) {
+ XposedBridge.log("Method not found: onCreateView in Fragment");
+ } catch (Throwable e) {
+ XposedBridge.log("Error hooking onCreateView: " + e.getMessage());
+ }
+ }
+
+
+ private void hookOnViewAdded(ClassLoader classLoader) {
+ try {
+ Class> constraintLayoutClass = Class.forName("androidx.constraintlayout.widget.ConstraintLayout", false, classLoader);
+ Method onViewAddedMethod = constraintLayoutClass.getDeclaredMethod("onViewAdded", View.class);
+
+ XposedBridge.hookMethod(onViewAddedMethod, new XC_MethodHook() {
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
+ View addedView = (View) param.args[0];
+ XposedBridge.log(addedView.toString());
+ }
+
+ private boolean isButtonAdded = false; // ボタンが追加されたかどうかを追跡するフラグ
+
+ @Override
+ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
+ View addedView = (View) param.args[0];
+ XposedBridge.log("Called: " + constraintLayoutClass.getName() + ".onViewAdded");
+
+ // 追加されたビューの ID を取得
+ int addedViewId = addedView.getId();
+
+ // 親ビューを取得
+ ViewGroup parent = (ViewGroup) param.thisObject;
+
+ // 追加されたビューのリソース名を取得
+ String resourceName = parent.getContext().getResources().getResourceEntryName(addedViewId);
+
+ // リソース名が chat_ui_message_context_menu_row_container の場合
+ if ("chat_ui_message_context_menu_row_container".equals(resourceName) && !isButtonAdded) {
+ // ボタンを作成
+
+
+ }
+
+
+ }
+
+
+ private void createAndAddButton(ConstraintLayout parent, View referenceView) {
+ // ボタンを作成
+ Button newButton = new Button(parent.getContext());
+ newButton.setText("新しいボタン");
+
+ // ボタンの ID を設定
+ newButton.setId(View.generateViewId());
+
+ // ボタンのレイアウトパラメータを設定
+ ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(
+ ConstraintLayout.LayoutParams.WRAP_CONTENT,
+ ConstraintLayout.LayoutParams.WRAP_CONTENT
+ );
+
+ // ボタンを親ビューに追加
+ parent.addView(newButton, params);
+
+ // 制約を設定
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(parent);
+
+ // ボタンに対する制約を設定
+ constraintSet.connect(newButton.getId(), ConstraintSet.TOP, referenceView.getId(), ConstraintSet.BOTTOM); // 上に参照ビューを設定
+ constraintSet.connect(newButton.getId(), ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START); // 左端に設定
+ constraintSet.connect(newButton.getId(), ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END); // 右端に設定
+ constraintSet.setHorizontalBias(newButton.getId(), 0.5f); // 中央に配置
+
+ // 制約を適用
+ constraintSet.applyTo(parent);
+ }
+
+ private void createAndAddButton(ViewGroup parent) {
+ // ボタンを作成
+ Button newButton = new Button(parent.getContext());
+ newButton.setText("新しいボタン"); // ボタンのテキストを設定
+
+ // ボタンのレイアウトパラメータを設定
+ ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(
+ ConstraintLayout.LayoutParams.WRAP_CONTENT,
+ ConstraintLayout.LayoutParams.WRAP_CONTENT
+ );
+
+ // 親の一番右に追加するために、適切な位置を設定
+ params.startToEnd = parent.getChildAt(parent.getChildCount() - 1).getId(); // 最後のビューの右側に配置
+ params.topToTop = ConstraintLayout.LayoutParams.PARENT_ID; // 上部を親の上部に固定
+
+ newButton.setLayoutParams(params);
+
+ // 親ビューにボタンを追加
+ parent.addView(newButton);
+ }
+
+
+ });
+ } catch (ClassNotFoundException e) {
+ XposedBridge.log("Class not found: androidx.constraintlayout.widget.ConstraintLayout");
+ } catch (NoSuchMethodException e) {
+ XposedBridge.log("Method not found: onViewAdded in ConstraintLayout");
+ } catch (Throwable e) {
+ XposedBridge.log("Error hooking onViewAdded: " + e.getMessage());
+ }
+ }
+
+
+
+ private int getIdByName(Context context, String resourceName) {
+ return context.getResources().getIdentifier(resourceName, "id", context.getPackageName());
+ }
+
+ private void hookAllMethods(Class> clazz) {
+ // クラス内のすべてのメソッドを取得
+ Method[] methods = clazz.getDeclaredMethods();
+
+ for (Method method : methods) {
+ // 抽象メソッドをスキップ
+ if (java.lang.reflect.Modifier.isAbstract(method.getModifiers())) {
+ continue;
+ }
+
+ // 対象メソッドが特定のビュー関連メソッドであるか確認
+ if (!"invokeSuspend".equals(method.getName()) &&
+ !"setOnLongClickListener".equals(method.getName()) &&
+ !"setOnTouchListener".equals(method.getName()) &&
+ !"setVisibility".equals(method.getName()) &&
+ !"setAlpha".equals(method.getName()) &&
+ !"setEnabled".equals(method.getName()) &&
+ !"setFocusable".equals(method.getName()) &&
+ !"setOnClickListener".equals(method.getName()) &&
+ !"setBackgroundColor".equals(method.getName()) &&
+ !"setPadding".equals(method.getName()) &&
+ !"setLayoutParams".equals(method.getName()) &&
+ !"requestLayout".equals(method.getName()) &&
+ !"invalidate".equals(method.getName()) &&
+ !"setText".equals(method.getName()) && // 新しく追加されたメソッド
+ !"setTextColor".equals(method.getName()) && // 新しく追加されたメソッド
+ !"setHint".equals(method.getName()) && // 新しく追加されたメソッド
+ !"setHintTextColor".equals(method.getName()) && // 新しく追加されたメソッド
+ !"onStart".equals(method.getName()) &&
+ !"setCompoundDrawables".equals(method.getName()) &&
+ !"getActivity".equals(method.getName()) && // PendingIntent method
+ !"onViewAdded".equals(method.getName()) && // PendingIntent method
+ !"setState".equals(method.getName())) { // PendingIntent method
+ continue;
+ }
+
+ method.setAccessible(true); // アクセス可能に設定
+
+ try {
+ // メソッドをフックする
+ XposedBridge.hookMethod(method, new XC_MethodHook() {
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
+ StringBuilder argsString = new StringBuilder("Args: ");
+
+ // 引数が複数の場合、すべてを追加
+ for (int i = 0; i < param.args.length; i++) {
+ Object arg = param.args[i];
+ argsString.append("Arg[").append(i).append("]: ")
+ .append(arg != null ? arg.toString() : "null")
+ .append(", ");
+ }
+
+// メソッドに応じたログ出力
+ if ("invokeSuspend".equals(method.getName())) {
+ XposedBridge.log("Before calling invokeSuspend in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setVisibility".equals(method.getName())) {
+ XposedBridge.log("Before calling setVisibility in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setAlpha".equals(method.getName())) {
+ XposedBridge.log("Before calling setAlpha in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setEnabled".equals(method.getName())) {
+ XposedBridge.log("Before calling setEnabled in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setFocusable".equals(method.getName())) {
+ XposedBridge.log("Before calling setFocusable in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setOnClickListener".equals(method.getName())) {
+ XposedBridge.log("Before calling setOnClickListener in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setBackgroundColor".equals(method.getName())) {
+ XposedBridge.log("Before calling setBackgroundColor in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setPadding".equals(method.getName())) {
+ XposedBridge.log("Before calling setPadding in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setLayoutParams".equals(method.getName())) {
+ XposedBridge.log("Before calling setLayoutParams in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("requestLayout".equals(method.getName())) {
+ XposedBridge.log("Before calling requestLayout in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("invalidate".equals(method.getName())) {
+ XposedBridge.log("Before calling invalidate in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setText".equals(method.getName())) {
+ XposedBridge.log("Before calling setText in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setTextColor".equals(method.getName())) {
+ XposedBridge.log("Before calling setTextColor in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setHint".equals(method.getName())) {
+ XposedBridge.log("Before calling setHint in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setHintTextColor".equals(method.getName())) {
+ XposedBridge.log("Before calling setHintTextColor in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setCompoundDrawables".equals(method.getName())) {
+ XposedBridge.log("Before calling setCompoundDrawables in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("onStart".equals(method.getName())) {
+ XposedBridge.log("Before calling onStart in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("getActivity".equals(method.getName())) {
+ XposedBridge.log("Before calling getActivity in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("onViewAdded".equals(method.getName())) {
+ XposedBridge.log("Before calling onViewAdded in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("getService".equals(method.getName())) {
+ XposedBridge.log("Before calling getService in class: " + clazz.getName() + " with args: " + argsString);
+ } else if ("setState".equals(method.getName())) {
+ XposedBridge.log("Before setState invoke in class: " + clazz.getName() + " with args: " + argsString);
+ }
+ }
+
+ @Override
+ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
+ Object result = param.getResult();
+ if ("invokeSuspend".equals(method.getName())) {
+ XposedBridge.log("Before calling invokeSuspend in class: " + clazz.getName() + (result != null ? result.toString() : "null"));
+ } else if ("setVisibility".equals(method.getName())) {
+ XposedBridge.log("After calling setVisibility in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setAlpha".equals(method.getName())) {
+ XposedBridge.log("After calling setAlpha in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setEnabled".equals(method.getName())) {
+ XposedBridge.log("After calling setEnabled in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setFocusable".equals(method.getName())) {
+ XposedBridge.log("After calling setFocusable in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setOnClickListener".equals(method.getName())) {
+ XposedBridge.log("After calling setOnClickListener in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setBackgroundColor".equals(method.getName())) {
+ XposedBridge.log("After calling setBackgroundColor in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setPadding".equals(method.getName())) {
+ XposedBridge.log("After calling setPadding in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setLayoutParams".equals(method.getName())) {
+ XposedBridge.log("After calling setLayoutParams in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("requestLayout".equals(method.getName())) {
+ XposedBridge.log("After calling requestLayout in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("invalidate".equals(method.getName())) {
+ XposedBridge.log("After calling invalidate in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setText".equals(method.getName())) {
+ XposedBridge.log("After calling setText in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setTextColor".equals(method.getName())) {
+ XposedBridge.log("After calling setTextColor in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setHint".equals(method.getName())) {
+ XposedBridge.log("After calling setHint in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setHintTextColor".equals(method.getName())) {
+ XposedBridge.log("After calling setHintTextColor in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setCompoundDrawables".equals(method.getName())) {
+ XposedBridge.log("After calling setCompoundDrawables in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("onStart".equals(method.getName())) {
+ XposedBridge.log("Before calling onStart in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("getActivity".equals(method.getName())) {
+ XposedBridge.log("After calling getActivity in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("onViewAdded".equals(method.getName())) {
+ XposedBridge.log("After calling onViewAdded in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("getService".equals(method.getName())) {
+ XposedBridge.log("After calling getService in class: " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ } else if ("setState".equals(method.getName())) {
+ XposedBridge.log("setState " + clazz.getName() + " with result: " + (result != null ? result.toString() : "null"));
+ }
+ }
+ });
+ } catch (IllegalArgumentException e) {
+ XposedBridge.log("Error hooking method " + method.getName() + " in class " + clazz.getName() + " : " + e.getMessage());
+ } catch (Throwable e) {
+ XposedBridge.log("Unexpected error hooking method " + method.getName() + " in class " + clazz.getName() + " : " + Log.getStackTraceString(e));
+ }
+ }
+ }
+
+ private boolean isViewCreationMethod(Method method) {
+ // View作成に関連するメソッドを検出
+ String methodName = method.getName().toLowerCase();
+
+ return methodName.contains("inflate") || methodName.contains("new") || methodName.contains("create");
+ }
+}
diff --git a/app/src/main/java/io/github/hiro/lime/hooks/UnsentRec.java b/app/src/main/java/io/github/hiro/lime/hooks/UnsentRec.java
index 89f283b..fd65316 100644
--- a/app/src/main/java/io/github/hiro/lime/hooks/UnsentRec.java
+++ b/app/src/main/java/io/github/hiro/lime/hooks/UnsentRec.java
@@ -45,8 +45,6 @@ public class UnsentRec implements IHook {
public static final String Main_file = "UNSENT_REC.txt";
public static final String Main_backup = "BackUpFile.txt";
- SQLiteDatabase db1 = null;
- SQLiteDatabase db2 = null;
@Override
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 96ad593..73bafef 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -95,5 +95,6 @@
送信したメッセージの既読者の確認
LsPatch用「未読のまま閲覧」スイッチを表示
連続で通話がかかってこないとかからないように
+ 随時キャッシュを削除してクラッシュを予防
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e71aad8..229201f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -96,4 +96,5 @@
LsPatch用「未読のまま閲覧」スイッチを表示
定期的にバックアップ\n
連続で通話がかかってこないとかからないように
+ 随時キャッシュを削除してクラッシュを予防