diff --git a/app/src/main/java/me/firesun/wechat/enhancement/Main.java b/app/src/main/java/me/firesun/wechat/enhancement/Main.java index fa94970..fb4a976 100644 --- a/app/src/main/java/me/firesun/wechat/enhancement/Main.java +++ b/app/src/main/java/me/firesun/wechat/enhancement/Main.java @@ -17,6 +17,7 @@ import me.firesun.wechat.enhancement.plugin.IPlugin; import me.firesun.wechat.enhancement.plugin.Limits; import me.firesun.wechat.enhancement.plugin.LuckMoney; +import me.firesun.wechat.enhancement.plugin.SecretFriend; import me.firesun.wechat.enhancement.util.HookParams; import me.firesun.wechat.enhancement.util.SearchClasses; @@ -33,6 +34,7 @@ public class Main implements IXposedHookLoadPackage { new HideModule(), new LuckMoney(), new Limits(), + new SecretFriend() }; @Override diff --git a/app/src/main/java/me/firesun/wechat/enhancement/PreferencesUtils.java b/app/src/main/java/me/firesun/wechat/enhancement/PreferencesUtils.java index 5b16c00..6818c05 100644 --- a/app/src/main/java/me/firesun/wechat/enhancement/PreferencesUtils.java +++ b/app/src/main/java/me/firesun/wechat/enhancement/PreferencesUtils.java @@ -81,6 +81,13 @@ public static boolean isBreakLimit() { return getInstance().getBoolean("is_break_limit", false); } + public static boolean isEnableSecretFriend() { + return getInstance().getBoolean("is_secret_friend", false); + } + + public static String secretFriendIds() { + return getInstance().getString("secret_friend_ids", "").replace(",", ","); + } } diff --git a/app/src/main/java/me/firesun/wechat/enhancement/plugin/SecretFriend.java b/app/src/main/java/me/firesun/wechat/enhancement/plugin/SecretFriend.java new file mode 100644 index 0000000..92e9417 --- /dev/null +++ b/app/src/main/java/me/firesun/wechat/enhancement/plugin/SecretFriend.java @@ -0,0 +1,114 @@ +package me.firesun.wechat.enhancement.plugin; + +import android.app.Activity; +import android.content.ClipboardManager; +import android.content.Context; +import android.os.Bundle; +import android.widget.BaseAdapter; +import android.widget.Toast; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XposedHelpers; +import de.robv.android.xposed.callbacks.XC_LoadPackage; +import me.firesun.wechat.enhancement.PreferencesUtils; +import me.firesun.wechat.enhancement.util.HookParams; + +import static android.widget.Toast.LENGTH_LONG; +import static me.firesun.wechat.enhancement.util.ReflectionUtil.log; + +public class SecretFriend implements IPlugin { + + //暂时只支持隐藏单个id,多id需要建立对照表 + private int secretItemIndex = -1; + private Class conversationWithCacheAdapterClass; + + @Override + public void hook(XC_LoadPackage.LoadPackageParam lpparam) { + try { + log("SecretFriend hook " + HookParams.getInstance().ConversationWithCacheAdapterClassName); + conversationWithCacheAdapterClass = XposedHelpers.findClass(HookParams.getInstance().ConversationWithCacheAdapterClassName, lpparam.classLoader); + } catch (Error | Exception e) { + } + + XposedHelpers.findAndHookMethod(HookParams.getInstance().ChattingUIClassName, lpparam.classLoader, "onCreate", Bundle.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) { + try { + if (PreferencesUtils.isEnableSecretFriend()) { + Activity activity = (Activity) param.thisObject; + ClipboardManager cmb = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE); + String wechatId = activity.getIntent().getStringExtra("Chat_User"); + log(wechatId); + cmb.setText(wechatId); + Toast.makeText(activity, "微信ID:" + wechatId + "已复制到剪切板", LENGTH_LONG).show(); + } + } catch (Error | Exception e) { + } + } + }); + +// XposedBridge.hookAllConstructors(HookParams.getInstance().ConversationWithCacheAdapterClass, new XC_MethodHook() { +// @Override +// protected void afterHookedMethod(MethodHookParam param) throws Throwable { +// if (PreferencesUtils.isEnableSecretFriend()) { +// //BaseAdapter adapter = (BaseAdapter) param.thisObject; +// secretItemIndex = -1; +// } +// } +// }); + + XposedHelpers.findAndHookMethod(conversationWithCacheAdapterClass.getSuperclass(), + HookParams.getInstance().MMBaseAdapter_getItemInternal, int.class, new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + if (PreferencesUtils.isEnableSecretFriend() && secretItemIndex > -1) { + BaseAdapter adapter = (BaseAdapter) param.thisObject; + if (adapter.getClass().equals(conversationWithCacheAdapterClass)) { + int index = (int) param.args[0]; + if (index >= secretItemIndex) { + param.args[0] = index + 1; + } + } + } + } + }); + + XposedHelpers.findAndHookMethod(conversationWithCacheAdapterClass.getSuperclass(),"getCount", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + if (PreferencesUtils.isEnableSecretFriend()) { + log("getCount: " + param.thisObject.getClass().getName()); + if (param.thisObject.getClass().equals(conversationWithCacheAdapterClass)) { + if (secretItemIndex > -1) { + int size = (int) param.getResult(); + log("count: " + size); + if (size > 0) { + param.setResult(size - 1); + } + } + } + } + } + }); + + // Hook notifyDataSetChanged() of base adapters + XposedHelpers.findAndHookMethod(BaseAdapter.class, "notifyDataSetChanged", new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + if (PreferencesUtils.isEnableSecretFriend()) { + BaseAdapter adapter = (BaseAdapter) param.thisObject; + if (adapter.getClass().equals(conversationWithCacheAdapterClass)) { + secretItemIndex = -1; + for (int i = 0; i < adapter.getCount(); i++) { + String username = (String) XposedHelpers.getObjectField(adapter.getItem(i), "field_username"); + if (username.equals(PreferencesUtils.secretFriendIds())) { + secretItemIndex = i; + break; + } + } + } + } + } + }); + } +} diff --git a/app/src/main/java/me/firesun/wechat/enhancement/util/HookParams.java b/app/src/main/java/me/firesun/wechat/enhancement/util/HookParams.java index 0a065be..1fad12d 100644 --- a/app/src/main/java/me/firesun/wechat/enhancement/util/HookParams.java +++ b/app/src/main/java/me/firesun/wechat/enhancement/util/HookParams.java @@ -4,7 +4,7 @@ public class HookParams { public static final String SAVE_WECHAT_ENHANCEMENT_CONFIG = "wechat.intent.action.SAVE_WECHAT_ENHANCEMENT_CONFIG"; public static final String WECHAT_ENHANCEMENT_CONFIG_NAME = "wechat_enhancement_config"; public static final String WECHAT_PACKAGE_NAME = "com.tencent.mm"; - public static final int VERSION_CODE = 46; //大版本变动时候才需要修改 + public static final int VERSION_CODE = 47; //大版本变动时候才需要修改 public String SQLiteDatabaseClassName = "com.tencent.wcdb.database.SQLiteDatabase"; public String SQLiteDatabaseUpdateMethod = "updateWithOnConflict"; @@ -40,6 +40,13 @@ public class HookParams { public String versionName; public int versionCode; + public String ChattingUIClassName = "com.tencent.mm.ui.chatting.ChattingUI"; +// public Class ConversationWithCacheAdapterClass; + public String ConversationWithCacheAdapterClassName; +// public Class MMBaseAdapterClass; + public String MMBaseAdapterClassName; + public String MMBaseAdapter_getItemInternal; + private static HookParams instance = null; private HookParams() { diff --git a/app/src/main/java/me/firesun/wechat/enhancement/util/ReflectionUtil.java b/app/src/main/java/me/firesun/wechat/enhancement/util/ReflectionUtil.java index 22eafd2..836b4c3 100644 --- a/app/src/main/java/me/firesun/wechat/enhancement/util/ReflectionUtil.java +++ b/app/src/main/java/me/firesun/wechat/enhancement/util/ReflectionUtil.java @@ -292,6 +292,28 @@ public final Classes filterByMethod(Class returnType, String methodName, Class.. return new Classes(arrayList); } + public final Classes filterByMethod(String methodName, Class... parameterTypes) { + List arrayList = new ArrayList(); + for (Object next : this.classes) { + Method method = ReflectionUtil.findMethodExactIfExists((Class) next, methodName, (Class[]) Arrays.copyOf(parameterTypes, parameterTypes.length)); + if (method != null) { + arrayList.add(next); + } + } + + return new Classes(arrayList); + } + + public final Classes filterBySuper(Class superClass) { + List arrayList = new ArrayList(); + for (Class next : this.classes) { + if (next.getSuperclass() == superClass) { + arrayList.add(next); + } + } + return new Classes(arrayList); + } + public final Class firstOrNull() { if (this.classes.isEmpty()) return null; @@ -303,5 +325,6 @@ else if (this.classes.size() > 1) { } return this.classes.get(0); } + } } diff --git a/app/src/main/java/me/firesun/wechat/enhancement/util/SearchClasses.java b/app/src/main/java/me/firesun/wechat/enhancement/util/SearchClasses.java index 9d18d58..c2e20a0 100644 --- a/app/src/main/java/me/firesun/wechat/enhancement/util/SearchClasses.java +++ b/app/src/main/java/me/firesun/wechat/enhancement/util/SearchClasses.java @@ -11,6 +11,7 @@ import org.json.JSONObject; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -200,6 +201,35 @@ public static void generateConfig(String wechatApk, ClassLoader classLoader, Str } catch (Error | Exception e) { log("Search Photo Limits Classes Failed!"); } + + //SecretFriend + try { + Class conversationWithCacheAdapterClass = ReflectionUtil.findClassesFromPackage(classLoader, wxClasses, "com.tencent.mm.ui.conversation", 0) + .filterByMethod("clearCache") + .firstOrNull(); + hp.ConversationWithCacheAdapterClassName = conversationWithCacheAdapterClass.getName(); + log("ConversationWithCacheAdapterClassName: " + hp.ConversationWithCacheAdapterClassName); + log(conversationWithCacheAdapterClass.getSuperclass().getName()); + +// Class MMBaseAdapterClass = ReflectionUtil.findClassesFromPackage(classLoader, wxClasses, "com.tencent.mm.ui", 0) +// .filterBySuper(BaseAdapter.class) +// .filterByField("TAG", "java.lang.String") +// .firstOrNull(); + Class MMBaseAdapterClass = conversationWithCacheAdapterClass.getSuperclass(); + hp.MMBaseAdapterClassName = MMBaseAdapterClass.getName(); + log("MMBaseAdapterClassName: " + hp.MMBaseAdapterClassName); + for (Method m: MMBaseAdapterClass.getDeclaredMethods()) { + if (m.getParameterTypes().length == 1 && m.getParameterTypes()[0] == int.class + && !"getItem".equals(m.getName()) && !"getItemId".equals(m.getName())) { + hp.MMBaseAdapter_getItemInternal = m.getName(); + log(hp.MMBaseAdapter_getItemInternal); + break; + } + } + } catch (Error | Exception e) { + log("Search ConversationWithCacheAdapter Classes Failed! " + e); + //throw e; + } } private static int getVersionNum(String version) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a830cee..a34725a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -36,4 +36,7 @@ 生成配置成功 生成配置失败 正在生成配置… + 私密好友 + 启用私密好友 + 私密好友清单 diff --git a/app/src/main/res/xml/pref_setting.xml b/app/src/main/res/xml/pref_setting.xml index a163f6e..2cf1a62 100644 --- a/app/src/main/res/xml/pref_setting.xml +++ b/app/src/main/res/xml/pref_setting.xml @@ -102,6 +102,16 @@ + + + +