mirror of
https://github.com/areteruhiro/LIME-beta-hiro.git
synced 2025-02-05 21:11:39 +09:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # app/build.gradle # app/src/main/java/io/github/hiro/lime/LimeOptions.java # app/src/main/java/io/github/hiro/lime/Main.java # app/src/main/java/io/github/hiro/lime/hooks/Constants.java # app/src/main/java/io/github/hiro/lime/hooks/ReadChecker.java # app/src/main/java/io/github/hiro/lime/hooks/test.java
This commit is contained in:
commit
91a0b1cbbb
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -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
|
||||
|
@ -54,7 +54,6 @@ Macro SAMPLE<br>
|
||||
|
||||
## 概要
|
||||
|
||||
β版です。自己責任でお願いします。
|
||||
|
||||
このアプリで追加されている機能は、いずれ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) では不具合が発生する可能性があります。
|
||||
|
63
app/src/main/java/io/github/hiro/lime/hooks/CallVolume.java
Normal file
63
app/src/main/java/io/github/hiro/lime/hooks/CallVolume.java
Normal file
@ -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];
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
58
app/src/main/java/io/github/hiro/lime/hooks/CrashGuard.java
Normal file
58
app/src/main/java/io/github/hiro/lime/hooks/CrashGuard.java
Normal file
@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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<String, String> contentMetadata = (Map<String, String>) 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
419
app/src/main/java/io/github/hiro/lime/hooks/Test.java
Normal file
419
app/src/main/java/io/github/hiro/lime/hooks/Test.java
Normal file
@ -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<String> 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<String> 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");
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -95,5 +95,6 @@
|
||||
<string name="ReadChecker">送信したメッセージの既読者の確認</string>
|
||||
<string name="switch_KeepUnreadLSpatch">LsPatch用「未読のまま閲覧」スイッチを表示</string>
|
||||
<string name="SleepCall">連続で通話がかかってこないとかからないように</string>
|
||||
<string name="CrashGuard">随時キャッシュを削除してクラッシュを予防</string>
|
||||
|
||||
</resources>
|
||||
|
@ -96,4 +96,5 @@
|
||||
<string name="switch_KeepUnreadLSpatch">LsPatch用「未読のまま閲覧」スイッチを表示</string>
|
||||
<string name="AutomaticBackup">定期的にバックアップ\n</string>
|
||||
<string name="SleepCall">連続で通話がかかってこないとかからないように</string>
|
||||
<string name="CrashGuard">随時キャッシュを削除してクラッシュを予防</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user