From 0c75467c9e145731526e11eafeee1a1c620cab71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=84=95=E6=99=A8HChen?= Date: Sat, 23 Dec 2023 16:44:33 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E9=99=A4=E5=B8=B8=E7=94=A8=E8=AF=AD?= =?UTF-8?q?=E5=89=AA=E8=B4=B4=E6=9D=BF=E6=9D=A1=E6=95=B0=E5=92=8C=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E9=99=90=E5=88=B6=E5=8A=9F=E8=83=BD=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E4=B8=8A=E6=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + README_en-US.md | 1 + README_pt-BR.md | 1 + .../hyperceiler/module/app/Various.java | 48 ++- .../module/hook/various/ClipboardList.java | 311 ++++++++++-------- app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- app/src/main/res/xml/prefs_about_use.xml | 7 + 8 files changed, 234 insertions(+), 139 deletions(-) diff --git a/README.md b/README.md index 97eb11d3d7..cc753203a2 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,7 @@ Android 11 ~ 14 的 MIUI 12.5 ~ 14/Xiaomi HyperOS1.0 - [「CorePatch」 by LSPosed](https://github.com/LSPosed/CorePatch) - [「CustoMIUIzer」 by MonwF](https://github.com/MonwF/customiuizer) - [「CustoMIUIzerMod」 by liyafe1997](https://github.com/liyafe1997/CustoMIUIzerMod) +- [「ClipboardList」 by 焕晨HChen](https://github.com/HChenX/ClipboardList) - [「DexKit」 by LuckyPray](https://github.com/LuckyPray/DexKit) - [「Disable app link verify」 by tehcneko](https://github.com/Xposed-Modules-Repo/io.github.tehcneko.applinkverify) - [「DisableFlagSecure」 by LSPosed](https://github.com/LSPosed/DisableFlagSecure) diff --git a/README_en-US.md b/README_en-US.md index c18f5db578..a96a4edbd7 100644 --- a/README_en-US.md +++ b/README_en-US.md @@ -121,6 +121,7 @@ You can provide translations for the HyperCeiler project [here](https://crwd.in/ - [「CorePatch」 by LSPosed](https://github.com/LSPosed/CorePatch) - [「CustoMIUIzer」 by MonwF](https://github.com/MonwF/customiuizer) - [「CustoMIUIzerMod」 by liyafe1997](https://github.com/liyafe1997/CustoMIUIzerMod) +- [「ClipboardList」 by 焕晨HChen](https://github.com/HChenX/ClipboardList) - [「DexKit」 by LuckyPray](https://github.com/LuckyPray/DexKit) - [「Disable app link verify」 by tehcneko](https://github.com/Xposed-Modules-Repo/io.github.tehcneko.applinkverify) - [「DisableFlagSecure」 by LSPosed](https://github.com/LSPosed/DisableFlagSecure) diff --git a/README_pt-BR.md b/README_pt-BR.md index dfcbc637c3..611eabb24d 100644 --- a/README_pt-BR.md +++ b/README_pt-BR.md @@ -119,6 +119,7 @@ Você pode fornecer traduções para o projeto [aqui](https://crwd.in/cemiuiler) - [「CorePatch」 por LSPosed](https://github.com/LSPosed/CorePatch) - [「CustoMIUIzer」 por MonwF](https://github.com/MonwF/customiuizer) - [「CustoMIUIzerMod」 por liyafe1997](https://github.com/liyafe1997/CustoMIUIzerMod) +- [「ClipboardList」 by 焕晨HChen](https://github.com/HChenX/ClipboardList) - [「DexKit」 por LuckyPray](https://github.com/LuckyPray/DexKit) - [「Disable app link verify」 por tehcneko](https://github.com/Xposed-Modules-Repo/io.github.tehcneko.applinkverify) - [「DisableFlagSecure」 por LSPosed](https://github.com/LSPosed/DisableFlagSecure) diff --git a/app/src/main/java/com/sevtinge/hyperceiler/module/app/Various.java b/app/src/main/java/com/sevtinge/hyperceiler/module/app/Various.java index 94ebf1dff3..28e4bb9f13 100644 --- a/app/src/main/java/com/sevtinge/hyperceiler/module/app/Various.java +++ b/app/src/main/java/com/sevtinge/hyperceiler/module/app/Various.java @@ -1,22 +1,35 @@ package com.sevtinge.hyperceiler.module.app; +import android.content.Context; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; + import com.sevtinge.hyperceiler.module.base.BaseModule; import com.sevtinge.hyperceiler.module.hook.various.ClipboardList; import com.sevtinge.hyperceiler.module.hook.various.CollapseMiuiTitle; import com.sevtinge.hyperceiler.module.hook.various.DialogCustom; import com.sevtinge.hyperceiler.module.hook.various.MiuiAppNoOverScroll; import com.sevtinge.hyperceiler.module.hook.various.UnlockIme; +import com.sevtinge.hyperceiler.utils.XposedUtils; +import com.sevtinge.hyperceiler.utils.log.XposedLogUtils; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import java.util.List; public class Various extends BaseModule { Class mHelpers; String mPackageName; boolean isMiuiApps; + public static List mAppsUsingInputMethod = new ArrayList<>(); + @Override public void handleLoadPackage() { + if (mAppsUsingInputMethod.isEmpty()) { + mAppsUsingInputMethod = getAppsUsingInputMethod(XposedUtils.findContext()); + } mPackageName = mLoadPackageParam.packageName; isMiuiApps = mPackageName.startsWith("com.miui") || mPackageName.startsWith("com.xiaomi") || miuiDialogCustomApps.contains(mPackageName); @@ -25,14 +38,45 @@ public void handleLoadPackage() { initHook(new CollapseMiuiTitle(), isCollapseMiuiTitleApps()); - initHook(new UnlockIme(), mPrefsMap.getBoolean("various_unlock_ime")); + initHook(new UnlockIme(), mPrefsMap.getBoolean("various_unlock_ime") && isInputMethod(mPackageName)); - initHook(new ClipboardList(), mPrefsMap.getBoolean("various_phrase_clipboardlist")); + initHook(new ClipboardList(), mPrefsMap.getBoolean("various_phrase_clipboardlist") && isInputMethod(mPackageName)); // initHook(new NoBrightness(), isPay(mPackageName)); } + private List getAppsUsingInputMethod(Context context) { + try { + if (context == null) { + XposedLogUtils.logE("getAppsUsingInputMethod", "context is null"); + return new ArrayList<>(); + } + List pkgName = new ArrayList<>(); + InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + List enabledInputMethods = inputMethodManager.getEnabledInputMethodList(); + for (InputMethodInfo inputMethodInfo : enabledInputMethods) { + pkgName.add(inputMethodInfo.getServiceInfo().packageName); + } + return pkgName; + } catch (Throwable e) { + XposedLogUtils.logE("getAppsUsingInputMethod", "have e: " + e); + return new ArrayList<>(); + } + } + + private boolean isInputMethod(String pkgName) { + if (mAppsUsingInputMethod.isEmpty()) { + return false; + } + for (String inputMethod : mAppsUsingInputMethod) { + if (inputMethod.equals(pkgName)) { + return true; + } + } + return false; + } + private boolean isPay(String param) { return mPrefsMap.getBoolean("various_nobrightness") && checkPay(param); } diff --git a/app/src/main/java/com/sevtinge/hyperceiler/module/hook/various/ClipboardList.java b/app/src/main/java/com/sevtinge/hyperceiler/module/hook/various/ClipboardList.java index 4510e0ebd3..d815b26691 100644 --- a/app/src/main/java/com/sevtinge/hyperceiler/module/hook/various/ClipboardList.java +++ b/app/src/main/java/com/sevtinge/hyperceiler/module/hook/various/ClipboardList.java @@ -1,5 +1,6 @@ package com.sevtinge.hyperceiler.module.hook.various; +import android.annotation.SuppressLint; import android.content.Context; import android.net.Uri; import android.os.Bundle; @@ -14,6 +15,7 @@ import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; @@ -22,15 +24,23 @@ import de.robv.android.xposed.XposedHelpers; public class ClipboardList extends BaseHook { - public ArrayList lastArray = null; + public static ArrayList lastArray = new ArrayList<>(); + + public static String lastFilePath; public ArrayList mClipboardList; + public static String filePath; + @Override - public void init() throws NoSuchMethodException { + public void init() { findAndHookMethod("android.inputmethodservice.InputMethodModuleManager", "loadDex", ClassLoader.class, String.class, new MethodHook() { + @SuppressLint("SdCardPath") @Override - protected void after(MethodHookParam param) throws Throwable { + protected void after(MethodHookParam param) { + // logE(TAG, "get class: " + param.args[0]); + filePath = "/data/user/0/" + lpparam.packageName + "/files/array_list.dat"; + lastFilePath = "/data/user/0/" + lpparam.packageName + "/files/last_list.dat"; getNoExpiredData((ClassLoader) param.args[0]); getView((ClassLoader) param.args[0]); clearArray((ClassLoader) param.args[0]); @@ -46,7 +56,7 @@ public void getClipboardData(ClassLoader classLoader) { "getClipboardData", Context.class, new MethodHook() { @Override - protected void before(MethodHookParam param) throws Throwable { + protected void before(MethodHookParam param) { Context context = (Context) param.args[0]; Bundle call = context.getContentResolver().call(Uri.parse("content://com.miui.input.provider"), "getClipboardList", (String) null, new Bundle()); @@ -62,88 +72,97 @@ public void getNoExpiredData(ClassLoader classLoader) { findAndHookMethod("com.miui.inputmethod.InputMethodUtil", classLoader, "getNoExpiredData", Context.class, String.class, long.class, new MethodHook() { @Override - protected void before(MethodHookParam param) throws Throwable { + protected void before(MethodHookParam param) { ArrayList mArray = new ArrayList<>(); - /*获取原始list数据内容*/ - ArrayList jsonToBean = jsonToBean((String) param.args[1], classLoader); - /*防止null引发的未知后果*/ - if (jsonToBean == null) { - logE(TAG, "jsonToBean is null"); - param.setResult(new ArrayList<>()); - return; - } - if (jsonToBean.size() == 0) { - /*防止在数据为空时误删数据库数据*/ - // resetFile(); - lastArray = null; - if (!checkFile()) { - mArray = jsonToLIst(classLoader); - if (!mArray.isEmpty()) { - param.setResult(mArray); - return; + try { + checkFile(filePath); + checkFile(lastFilePath); + /*获取原始list数据内容*/ + ArrayList jsonToBean = jsonToBean((String) param.args[1], classLoader); + if (jsonToBean.size() == 0) { + /*防止在数据为空时误删数据库数据*/ + // resetFile(); + lastArray = new ArrayList<>(); + if (!isEmptyFile(filePath)) { + mArray = jsonToLIst(filePath, classLoader); + if (!mArray.isEmpty()) { + param.setResult(mArray); + return; + } } + param.setResult(new ArrayList<>()); + logD(TAG + ": get saved clipboard list size is 0."); + return; } - param.setResult(new ArrayList<>()); - logD(TAG + ": get saved clipboard list size is 0."); - return; - } - /*文件不为空说明有数据*/ - if (!checkFile()) { - mArray = jsonToLIst(classLoader); - if (lastArray != null) { - /*虽说不太可能为null但还是检查一下*/ - if (mArray == null) { - logE(TAG, "mArray is null it's bad"); - param.setResult(new ArrayList<>()); - return; - } - Object oneLast = getContent(lastArray, 0); - /*防止在只复制一个元素时重复开关界面引发的null*/ - if (jsonToBean.size() < 2) { - if (!getContent(jsonToBean, 0).equals(getContent(mArray, 0))) { - addOrHw(mArray, getContent(jsonToBean, 0), jsonToBean); + if (isEmptyFile(lastFilePath)) { + lastArray = jsonToLIst(lastFilePath, classLoader); + } + /*文件不为空说明有数据*/ + if (!isEmptyFile(filePath)) { + /*数据库数据*/ + mArray = jsonToLIst(filePath, classLoader); + if (!lastArray.isEmpty()) { + /*虽说不太可能为空但还是检查一下*/ + if (mArray.isEmpty()) { + logE(TAG, "mArray is empty it's bad"); + param.setResult(new ArrayList<>()); + return; } - param.setResult(mArray); - return; - } - /*读取第一第二个数据判断操作*/ - Object oneArray = getContent(jsonToBean, 0); - Object twoArray = getContent(jsonToBean, 1); - if (oneArray == null || twoArray == null || oneLast == null) { - logE(TAG, "oneArray or twoArray or oneLast is null"); - param.setResult(new ArrayList<>()); - return; - } - if (!oneArray.equals(oneLast) && twoArray.equals(oneLast)) { - /*第一个不同第二个相同说明可能换位或新增*/ - mArray = addOrHw(mArray, oneArray, jsonToBean); - } else if (!oneArray.equals(oneLast) && !twoArray.equals(oneLast)) { - /*两个不同为新增*/ - int have = 0; - for (int i = 0; i < jsonToBean.size(); i++) { - ++have; - if (getContent(jsonToBean, i).equals(oneLast)) { - break; + Object oneLast = getContent(lastArray, 0); + /*防止在只复制一个元素时重复开关界面引发的未知问题*/ + if (jsonToBean.size() < 2) { + if (!getContent(jsonToBean, 0).equals(getContent(mArray, 0))) { + mArray = addOrHw(mArray, getContent(jsonToBean, 0), jsonToBean); } + param.setResult(mArray); + return; } - for (int i = 0; i < have - 1; i++) { - mArray.add(i, jsonToBean.get(i)); + /*读取第一第二个数据判断操作*/ + Object oneArray = getContent(jsonToBean, 0); + Object twoArray = getContent(jsonToBean, 1); + if (!oneArray.equals(oneLast) && twoArray.equals(oneLast)) { + /*第一个不同第二个相同说明可能换位或新增*/ + mArray = addOrHw(mArray, oneArray, jsonToBean); + } else if (!oneArray.equals(oneLast) && !twoArray.equals(oneLast)) { + /*两个不同为新增*/ + int have = -1; + for (int i = 0; i < jsonToBean.size(); i++) { + have++; + if (getContent(jsonToBean, i).equals(oneLast)) { + break; + } + } + for (int i = 0; i < have; i++) { + mArray.add(i, jsonToBean.get(i)); + } } + /*else if (jsonToBean.hashCode() != lastArray.hashCode()) { + *//*很极端的情况,应该不会发生*//* + mArray.addAll(0, jsonToBean); + }*/ } - - } - /*置旧*/ - lastArray = jsonToBean; - /*清空文件写入*/ - if (resetFile()) { - writeFile(listToJson(mArray)); + /*置旧*/ + lastArray = jsonToBean; + if (resetFile(lastFilePath)) { + writeFile(lastFilePath, listToJson(lastArray)); + } + /*清空文件写入*/ + if (resetFile(filePath)) { + writeFile(filePath, listToJson(mArray)); + } + param.setResult(mArray); + } else { + /*置旧*/ + lastArray = jsonToBean; + if (resetFile(lastFilePath)) { + writeFile(lastFilePath, listToJson(lastArray)); + } + writeFile(filePath, listToJson(jsonToBean)); + param.setResult(jsonToBean); } + } catch (Throwable throwable) { + logE(TAG, "getContent is null: " + throwable); param.setResult(mArray); - } else { - /*置旧*/ - lastArray = jsonToBean; - writeFile(listToJson(jsonToBean)); - param.setResult(jsonToBean); } } } @@ -156,7 +175,7 @@ public void getView(ClassLoader classLoader) { "getView", int.class, View.class, ViewGroup.class, new MethodHook() { @Override - protected void after(MethodHookParam param) throws Throwable { + protected void after(MethodHookParam param) { mClipboardList = (ArrayList) XposedHelpers.getObjectField(param.thisObject, "mClipboardList"); } @@ -166,13 +185,21 @@ protected void after(MethodHookParam param) throws Throwable { hookAllMethods("com.miui.inputmethod.InputMethodUtil", classLoader, "setClipboardModelList", new MethodHook() { @Override - protected void after(MethodHookParam param) throws Throwable { + protected void after(MethodHookParam param) { ArrayList arrayList = (ArrayList) param.args[1]; if (arrayList.isEmpty()) { - lastArray = null; - } else lastArray = arrayList; - if (resetFile()) { - writeFile(listToJson(arrayList)); + lastArray = new ArrayList<>(); + if (resetFile(lastFilePath)) { + writeFile(lastFilePath, new JSONArray()); + } + } else { + lastArray = arrayList; + if (resetFile(lastFilePath)) { + writeFile(lastFilePath, listToJson(arrayList)); + } + } + if (resetFile(filePath)) { + writeFile(filePath, listToJson(arrayList)); } } } @@ -187,19 +214,25 @@ public void clearArray(ClassLoader classLoader) { @Override protected void before(MethodHookParam param) { /*点击清除全部按键,清理所有数据*/ - lastArray = null; + lastArray = new ArrayList<>(); } } ); } - public ArrayList addOrHw(ArrayList mArray, Object oneArray, ArrayList jsonToBean) { + /*添加或换位*/ + public ArrayList addOrHw(ArrayList mArray, Object oneArray, ArrayList jsonToBean) throws Throwable { + if (oneArray == null) { + logE(TAG, "oneArray is null, mArray: " + mArray + " jsonToBean: " + jsonToBean); + return mArray; + } boolean needAdd = false; boolean needHw = false; - int run = 0; + int run = -1; for (int i = 0; i < mArray.size(); i++) { run++; needAdd = true; + /*如果数据库存在重复数据*/ if (oneArray.equals(getContent(mArray, i))) { needHw = true; needAdd = false; @@ -207,88 +240,93 @@ public ArrayList addOrHw(ArrayList mArray, Object oneArray, ArrayList jsonToB } } if (needHw) { - mArray.add(0, mArray.get(run - 1)); - mArray.remove(run); + mArray.add(0, mArray.get(run)); + mArray.remove(run + 1); } if (needAdd) mArray.add(0, jsonToBean.get(0)); return mArray; } - public void writeFile(JSONArray jsonArray) { + public void checkFile(String path) { + File file = new File(path); + File parentDir = file.getParentFile(); + if (parentDir == null) { + logE(TAG, "parentDir is null: " + path); + } + if (parentDir != null && !parentDir.exists()) { + if (parentDir.mkdirs()) { + logI(TAG, "mkdirs: " + parentDir); + } else { + logE(TAG, "mkdirs: " + parentDir); + } + } + if (!file.exists()) { + try { + if (file.createNewFile()) { + writeFile(path, new JSONArray()); + logI(TAG, "createNewFile: " + file); + } else { + logE(TAG, "createNewFile: " + file); + } + } catch (IOException e) { + logE(TAG, "createNewFile: " + e); + } + } + } + + public void writeFile(String path, JSONArray jsonArray) { if (jsonArray == null) { logE(TAG, "write json is null"); return; } try (BufferedWriter writer = new BufferedWriter(new - FileWriter("/data/user/0/" + - lpparam.packageName + "/files/array_list.dat"))) { + FileWriter(path, false))) { writer.write(jsonArray.toString()); } catch (IOException e) { logE(TAG, "writeFile: " + e); } } - public boolean checkFile() { - try (BufferedReader reader = new BufferedReader(new - FileReader("/data/user/0/" + - lpparam.packageName + "/files/array_list.dat"))) { - StringBuilder builder = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - builder.append(line); - } - String jsonString = builder.toString(); - if (jsonString.equals("[]") || jsonString.isEmpty()) { - resetFile(); - return true; - } - return false; - } catch (IOException e) { - logE(TAG, "readFile: " + e); - return true; - } + public boolean isEmptyFile(String path) { + JSONArray jsonArray = readFile(path); + return jsonArray.length() == 0; } - public JSONArray readFile() { + public JSONArray readFile(String path) { try (BufferedReader reader = new BufferedReader(new - FileReader("/data/user/0/" + - lpparam.packageName + "/files/array_list.dat"))) { + FileReader(path))) { StringBuilder builder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { builder.append(line); } String jsonString = builder.toString(); + if ("".equals(jsonString)) { + jsonString = "[]"; + } JSONArray jsonArray = new JSONArray(jsonString); return jsonArray; } catch (IOException | JSONException e) { logE(TAG, "readFile: " + e); } - return null; + return new JSONArray(); } - public boolean resetFile() { + public boolean resetFile(String path) { // 清空文件内容 - try (BufferedWriter writer = new BufferedWriter(new - FileWriter("/data/user/0/" + - lpparam.packageName + "/files/array_list.dat", false))) { - writer.write(""); - return true; - } catch (IOException e) { - logE(TAG, "writeFile: " + e); - return false; - } + writeFile(path, new JSONArray()); + return isEmptyFile(path); } - public ArrayList jsonToLIst(ClassLoader classLoader) { + /*JSON到ArrayList*/ + public ArrayList jsonToLIst(String path, ClassLoader classLoader) { try { JSONArray mJson; ArrayList mArray = new ArrayList<>(); - mJson = readFile(); - if (mJson == null) { - logE(TAG, "jsonToLIst readFile is null"); - return null; + mJson = readFile(path); + if (mJson.length() == 0) { + return new ArrayList<>(); } for (int i = 0; i < mJson.length(); i++) { JSONObject jSONObject = mJson.getJSONObject(i); @@ -300,12 +338,13 @@ public ArrayList jsonToLIst(ClassLoader classLoader) { } } return mArray; - } catch (JSONException e) { + } catch (Throwable e) { logE(TAG, "jsonToLIst: " + e); } - return null; + return new ArrayList<>(); } + /*String到ArrayList,中转JSON*/ public ArrayList jsonToBean(String str, ClassLoader classLoader) { ArrayList arrayList = new ArrayList<>(); try { @@ -319,12 +358,13 @@ public ArrayList jsonToBean(String str, ClassLoader classLoader) { "fromJSONObject", jSONObject)); } } - } catch (JSONException e2) { + } catch (Throwable e2) { logE(TAG, "jsonToBean,parse JSON error: " + e2); } return arrayList; } + /*ArrayList到JSON*/ public JSONArray listToJson(ArrayList arrayList) { try { JSONArray jSONArray = new JSONArray(); @@ -335,15 +375,16 @@ public JSONArray listToJson(ArrayList arrayList) { } catch (Throwable throwable) { logE(TAG, "listToJson: " + throwable); } - return null; + return new JSONArray(); } - public String getContent(ArrayList arrayList, int num) { + /*获取剪贴板内容*/ + public String getContent(ArrayList arrayList, int num) throws Throwable { try { return (String) XposedHelpers.callMethod(arrayList.get(num), "getContent"); } catch (Throwable throwable) { - logE(TAG, "getContent: " + throwable); + logE(TAG, "getContent array: " + arrayList + " num: " + num + " e: " + throwable); + throw new Throwable("callMethod getContent error: " + throwable); } - return null; } } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 357158efee..9ae1b71733 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1369,7 +1369,7 @@ 禁用越界回弹 关闭访问设备日志确认 - 解除常用语剪贴板数量和时间限制 + 解除常用语剪贴板条数和时间限制 开启后勾选输入法作用域并重启 解锁输入法全面屏优化 开启后请在 Xposed 管理器中勾选对应输入法作用域 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e1b6a3bf97..5062a7b910 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1336,7 +1336,7 @@ Disable out-of-bounds bounce Dismiss access device logs request dialog - Remove the number of phrases clipboard and time limits + Remove the clipboard and time limits for phrases After it is enabled, select the input method scope and restart it Unlock IME After opening, please check the corresponding input method scope in the Xposed manager diff --git a/app/src/main/res/xml/prefs_about_use.xml b/app/src/main/res/xml/prefs_about_use.xml index 3883bba075..9fa10d882c 100644 --- a/app/src/main/res/xml/prefs_about_use.xml +++ b/app/src/main/res/xml/prefs_about_use.xml @@ -72,6 +72,13 @@ android:action="android.intent.action.VIEW" android:data="https://github.com/liyafe1997/CustoMIUIzerMod" /> + + +