Skip to content

Commit 6af09fb

Browse files
committed
fix: 解除剪贴板常用语限制导致输入法异常卡顿的问题
Closes: #1550
1 parent 87708e4 commit 6af09fb

7 files changed

Lines changed: 401 additions & 209 deletions

File tree

library/libhook/src/main/java/com/sevtinge/hyperceiler/libhook/app/Others/VariousThirdApps.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,11 @@
2626
import com.sevtinge.hyperceiler.common.log.XposedLog;
2727
import com.sevtinge.hyperceiler.common.utils.PrefsBridge;
2828
import com.sevtinge.hyperceiler.libhook.base.BaseLoad;
29-
import com.sevtinge.hyperceiler.libhook.rules.phrase.NewUnPhraseLimit;
3029
import com.sevtinge.hyperceiler.libhook.rules.various.MusicHooks;
3130
import com.sevtinge.hyperceiler.libhook.rules.various.clipboard.BaiduClipboard;
3231
import com.sevtinge.hyperceiler.libhook.rules.various.clipboard.ClearClipboard;
33-
import com.sevtinge.hyperceiler.libhook.rules.various.clipboard.ClipboardLimit;
34-
import com.sevtinge.hyperceiler.libhook.rules.various.clipboard.LoadInputMethodDex;
32+
import com.sevtinge.hyperceiler.libhook.rules.various.clipboard.ClipboardUnlock;
33+
import com.sevtinge.hyperceiler.libhook.rules.various.clipboard.InputMethodDexHelper;
3534
import com.sevtinge.hyperceiler.libhook.rules.various.clipboard.SoGouClipboard;
3635
import com.sevtinge.hyperceiler.libhook.rules.various.clipboard.UnlockIme;
3736
import com.sevtinge.hyperceiler.libhook.utils.hookapi.tool.AppsTool;
@@ -52,20 +51,22 @@ public void onPackageLoaded() {
5251
mAppsUsingInputMethod = getAppsUsingInputMethod(AppsTool.findContext(AppsTool.FlAG_ONLY_ANDROID));
5352
}
5453
mPackageName = getPackageName();
55-
if (PrefsBridge.getBoolean("various_phrase_clipboardlist")) {
56-
if (isInputMethod(mPackageName)) {
57-
initHook(new LoadInputMethodDex());
58-
initHook(new ClipboardLimit());
59-
}
54+
boolean isIme = isInputMethod(mPackageName);
55+
boolean needClipboard = PrefsBridge.getBoolean("various_phrase_clipboardlist");
56+
boolean needUnlockIme = PrefsBridge.getBoolean("various_unlock_ime");
57+
boolean needClearClipboard = PrefsBridge.getBoolean("add_clipboard_clear");
58+
59+
if (isIme && (needClipboard || needUnlockIme || needClearClipboard)) {
60+
InputMethodDexHelper.init();
6061
}
61-
initHook(new UnlockIme(), PrefsBridge.getBoolean("various_unlock_ime") && isInputMethod(mPackageName));
62-
initHook(new NewUnPhraseLimit(), PrefsBridge.getBoolean("various_phrase_clipboardlist") && isInputMethod(mPackageName));
62+
63+
initHook(new ClipboardUnlock(), needClipboard && isIme);
64+
initHook(new UnlockIme(), needUnlockIme && isIme);
6365
initHook(new SoGouClipboard(), PrefsBridge.getBoolean("sogou_xiaomi_clipboard") &&
6466
("com.sohu.inputmethod.sogou.xiaomi".equals(mPackageName) || "com.sohu.inputmethod.sogou".equals(mPackageName)));
6567
initHook(new BaiduClipboard(), PrefsBridge.getBoolean("sogou_xiaomi_clipboard") &&
6668
("com.baidu.input".equals(mPackageName) || "com.baidu.input_mi".equals(mPackageName)));
67-
//initHook(new ClipboardList(), PrefsBridge.getBoolean("various_phrase_clipboardlist") && isInputMethod(mPackageName));
68-
initHook(new ClearClipboard(), PrefsBridge.getBoolean("add_clipboard_clear") && isInputMethod(mPackageName));
69+
initHook(new ClearClipboard(), needClearClipboard && isIme);
6970

7071
// 焦点歌词(音乐软件相关)
7172
initHook(MusicHooks.INSTANCE, PrefsBridge.getBoolean("system_ui_statusbar_music_switch") && PrefsBridge.getBoolean("system_ui_statusbar_music_show_app"));

library/libhook/src/main/java/com/sevtinge/hyperceiler/libhook/rules/phrase/NewUnPhraseLimit.java

Lines changed: 108 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import com.sevtinge.hyperceiler.libhook.base.BaseHook;
3333
import com.sevtinge.hyperceiler.libhook.callback.IMethodHook;
3434
import com.sevtinge.hyperceiler.libhook.utils.hookapi.dexkit.IDexKit;
35-
import com.sevtinge.hyperceiler.libhook.utils.hookapi.tool.EzxHelpUtils;
3635

3736
import org.json.JSONArray;
3837
import org.luckypray.dexkit.DexKitBridge;
@@ -48,7 +47,6 @@
4847
import java.lang.reflect.Method;
4948
import java.util.ArrayList;
5049
import java.util.List;
51-
import java.util.Objects;
5250

5351
import io.github.kyuubiran.ezxhelper.xposed.common.HookParam;
5452

@@ -64,133 +62,126 @@ protected boolean useDexKit() {
6462

6563
@Override
6664
protected boolean initDexKit() {
67-
if (Objects.equals(getPackageName(), "com.miui.phrase")) {
68-
mBuildClipboardJsonMethod = requiredMember("BuildClipboardJson", new IDexKit() {
69-
@Override
70-
public BaseData dexkit(DexKitBridge bridge) throws ReflectiveOperationException {
71-
MethodData methodData = bridge.findMethod(FindMethod.create()
72-
.matcher(MethodMatcher.create()
73-
.usingStrings("get savedList size :")
74-
)).singleOrNull();
75-
return methodData;
76-
}
77-
});
78-
mPhraseMethod = requiredMember("phrase$1", new IDexKit() {
79-
@Override
80-
public BaseData dexkit(DexKitBridge bridge) throws ReflectiveOperationException {
81-
MethodData methodData = bridge.findMethod(FindMethod.create()
82-
.matcher(MethodMatcher.create()
83-
.declaredClass(ClassMatcher.create()
84-
.usingStrings("phrase_list")
85-
)
86-
.usingStrings("layout_inflater")
87-
)).singleOrNull();
88-
return methodData;
89-
}
90-
});
91-
mPhraseEditTextField = requiredMember("phrase$2", new IDexKit() {
92-
@Override
93-
public BaseData dexkit(DexKitBridge bridge) throws ReflectiveOperationException {
94-
return bridge.findField(FindField.create()
95-
.matcher(FieldMatcher.create()
96-
.declaredClass(ClassMatcher.create()
97-
.usingStrings("phrase_list")
98-
)
99-
.type(EditText.class)
65+
mBuildClipboardJsonMethod = requiredMember("BuildClipboardJson", new IDexKit() {
66+
@Override
67+
public BaseData dexkit(DexKitBridge bridge) throws ReflectiveOperationException {
68+
MethodData methodData = bridge.findMethod(FindMethod.create()
69+
.matcher(MethodMatcher.create()
70+
.usingStrings("get savedList size :")
71+
)).singleOrNull();
72+
return methodData;
73+
}
74+
});
75+
mPhraseMethod = requiredMember("phrase$1", new IDexKit() {
76+
@Override
77+
public BaseData dexkit(DexKitBridge bridge) throws ReflectiveOperationException {
78+
MethodData methodData = bridge.findMethod(FindMethod.create()
79+
.matcher(MethodMatcher.create()
80+
.declaredClass(ClassMatcher.create()
81+
.usingStrings("phrase_list")
10082
)
101-
).single();
102-
}
103-
});
104-
}
83+
.usingStrings("layout_inflater")
84+
)).singleOrNull();
85+
return methodData;
86+
}
87+
});
88+
mPhraseEditTextField = requiredMember("phrase$2", new IDexKit() {
89+
@Override
90+
public BaseData dexkit(DexKitBridge bridge) throws ReflectiveOperationException {
91+
return bridge.findField(FindField.create()
92+
.matcher(FieldMatcher.create()
93+
.declaredClass(ClassMatcher.create()
94+
.usingStrings("phrase_list")
95+
)
96+
.type(EditText.class)
97+
)
98+
).single();
99+
}
100+
});
105101
return true;
106102
}
107103

108104
@Override
109105
public void init() {
110-
try {
111-
findAndHookMethod("android.inputmethodservice.InputMethodModuleManager", "loadDex", ClassLoader.class, String.class, new IMethodHook() {
112-
@Override
113-
public void after(HookParam param) {
114-
ClassLoader classLoader = (ClassLoader) param.getArgs()[0];
115-
setStaticField(findClass("com.miui.inputmethod.MiuiClipboardManager", classLoader), "MAX_CLIP_CONTENT_SIZE", Integer.MAX_VALUE);
116-
EzxHelpUtils.findAndHookMethod("com.miui.inputmethod.MiuiClipboardManager", classLoader, "init", new IMethodHook() {
117-
@Override
118-
public void before(HookParam param) {
119-
setStaticField(findClass("com.miui.inputmethod.MiuiClipboardManager", classLoader), "MAX_CLIP_CONTENT_SIZE", Integer.MAX_VALUE);
120-
}
121-
122-
@Override
123-
public void after(HookParam param) throws Exception {
124-
setStaticField(findClass("com.miui.inputmethod.MiuiClipboardManager", classLoader), "MAX_CLIP_CONTENT_SIZE", Integer.MAX_VALUE);
125-
}
126-
}
127-
);
128-
}
106+
hookBuildClipboardJson();
107+
hookPhraseListLimit();
108+
hookPhraseEditCharLimit();
109+
}
110+
111+
/**
112+
* Hook BuildClipboardJson 方法,移除 20 条剪贴板条目限制。
113+
* <p>
114+
* 原始方法会将超过 20 条的旧条目删除,这里重新实现为不限制条数,
115+
* 但保留过期清理逻辑以避免剪贴板 JSON 无限膨胀。
116+
*/
117+
private void hookBuildClipboardJson() {
118+
hookMethod(mBuildClipboardJsonMethod, new IMethodHook() {
119+
@Override
120+
public void before(HookParam param) {
121+
Object newModel = param.getArgs()[2];
122+
String oldJson = (String) param.getArgs()[3];
123+
124+
JSONArray jsonArray = new JSONArray();
125+
ArrayList<Object> list = new ArrayList<>();
126+
127+
if (newModel != null) {
128+
list.add(newModel);
129129
}
130-
);
131-
} catch (Exception ignore) {}
132-
if (Objects.equals(getPackageName(), "com.miui.phrase")) {
133-
hookMethod(mBuildClipboardJsonMethod, new IMethodHook() {
134-
@Override
135-
public void before(HookParam param) {
136-
Object newModel = param.getArgs()[2];
137-
String oldJson = (String) param.getArgs()[3];
138-
Class<?> mgrCls = null;
139-
140-
JSONArray jsonArray = new JSONArray();
141-
ArrayList<Object> list = new ArrayList<>();
142-
143-
if (newModel != null) {
144-
list.add(newModel);
145-
}
146-
147-
if (!TextUtils.isEmpty(oldJson)) {
148-
mgrCls = findClass("com.miui.inputmethod.MiuiClipboardManager", getClassLoader());
149-
List<?> oldList = (List<?>) callStaticMethod(mgrCls, "jsonToBeanList", oldJson);
150-
list.addAll(oldList);
151-
}
152-
153-
for (int i = 0; i < list.size(); i++) {
154-
Object model = list.get(i);
155-
Object jsonObj = callMethod(model, "toJSONObject");
156-
jsonArray.put(jsonObj);
157-
}
158-
159-
param.setResult(jsonArray.toString());
130+
131+
if (!TextUtils.isEmpty(oldJson)) {
132+
Class<?> mgrCls = findClass("com.miui.inputmethod.MiuiClipboardManager", getClassLoader());
133+
List<?> oldList = (List<?>) callStaticMethod(mgrCls, "jsonToBeanList", oldJson);
134+
list.addAll(oldList);
160135
}
161-
});
162136

163-
// 解除 20 条限制
164-
Class<?> InputMethodUtil = findClass("com.miui.inputmethod.InputMethodUtil");
165-
setStaticField(InputMethodUtil, "sPhraseListSize", 0);
166-
findAndHookMethod(InputMethodUtil, "queryPhrase", Context.class, new IMethodHook() {
167-
@Override
168-
public void after(HookParam param) {
169-
setStaticField(InputMethodUtil, "sPhraseListSize", 0);
137+
for (int i = 0; i < list.size(); i++) {
138+
Object model = list.get(i);
139+
Object jsonObj = callMethod(model, "toJSONObject");
140+
jsonArray.put(jsonObj);
170141
}
171-
});
172142

173-
Class<?> AddPhraseActivity = findClass("com.miui.phrase.AddPhraseActivity");
174-
findAndHookMethod("com.miui.phrase.PhraseEditActivity", "onClick", View.class, new IMethodHook() {
143+
param.setResult(jsonArray.toString());
144+
}
145+
});
146+
}
147+
148+
/**
149+
* 解除常用语 20 条数量限制
150+
*/
151+
private void hookPhraseListLimit() {
152+
Class<?> InputMethodUtil = findClass("com.miui.inputmethod.InputMethodUtil");
153+
setStaticField(InputMethodUtil, "sPhraseListSize", 0);
154+
findAndHookMethod(InputMethodUtil, "queryPhrase", Context.class, new IMethodHook() {
155+
@Override
156+
public void after(HookParam param) {
157+
setStaticField(InputMethodUtil, "sPhraseListSize", 0);
158+
}
159+
});
160+
161+
Class<?> AddPhraseActivity = findClass("com.miui.phrase.AddPhraseActivity");
162+
findAndHookMethod("com.miui.phrase.PhraseEditActivity", "onClick", View.class, new IMethodHook() {
163+
@Override
164+
public void before(HookParam param) {
165+
Activity activity = (Activity) param.getThisObject();
166+
Intent intent = new Intent(activity, AddPhraseActivity);
167+
intent.setAction("com.miui.intent.action.PHRASE_ADD");
168+
activity.startActivityForResult(intent, 0);
169+
param.setResult(null);
170+
}
171+
});
172+
}
173+
174+
/**
175+
* 解除常用语字数限制
176+
*/
177+
private void hookPhraseEditCharLimit() {
178+
hookMethod(mPhraseMethod, new IMethodHook() {
175179
@Override
176-
public void before(HookParam param) {
177-
Activity activity = (Activity) param.getThisObject();
178-
Intent intent = new Intent(activity, AddPhraseActivity);
179-
intent.setAction("com.miui.intent.action.PHRASE_ADD");
180-
activity.startActivityForResult(intent, 0);
181-
param.setResult(null);
182-
}
183-
});
184-
185-
// 解除字数限制
186-
hookMethod(mPhraseMethod, new IMethodHook() {
187-
@Override
188-
public void after(HookParam param) {
189-
EditText editText = (EditText) getObjectField(param.getThisObject(), mPhraseEditTextField.getName());
190-
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(Integer.MAX_VALUE)});
191-
}
180+
public void after(HookParam param) {
181+
EditText editText = (EditText) getObjectField(param.getThisObject(), mPhraseEditTextField.getName());
182+
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(Integer.MAX_VALUE)});
192183
}
193-
);
194-
}
184+
}
185+
);
195186
}
196187
}

library/libhook/src/main/java/com/sevtinge/hyperceiler/libhook/rules/various/clipboard/ClearClipboard.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,9 @@ import io.github.kyuubiran.ezxhelper.xposed.dsl.HookFactory.`-Static`.createAfte
3939
class ClearClipboard : BaseHook() {
4040
override fun init() {
4141
if (isMoreSmallVersion(200, 2f)) return
42-
MethodFinder.fromClass("android.inputmethodservice.InputMethodModuleManager")
43-
.filterByName("loadDex")
44-
.filterByParamTypes(ClassLoader::class.java, String::class.java)
45-
.first().createAfterHook {
46-
createHook(it.args[0] as ClassLoader)
47-
}
42+
InputMethodDexHelper.addListener { classLoader ->
43+
createHook(classLoader)
44+
}
4845
}
4946

5047
private fun createHook(classLoader: ClassLoader) {

0 commit comments

Comments
 (0)