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 連続で通話がかかってこないとかからないように + 随時キャッシュを削除してクラッシュを予防