diff --git a/README.md b/README.md index 4acb4e3..3d1e494 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,8 @@ v1.13版本开始,默认隐藏App在桌面的图标。隐藏以后,打开模 8.0.40 (2420) 2023-07-20 8.0.40 (2420) 2023-07-20 8.0.41 (2441) 2023-09-06 +8.0.42 (2460) 2023-09-22 +8.0.43 (2480) 2023-11-06 **PS.** - 仅支持上述版本,所有其他版本号以及32位版本未经测试,预计百分之九十九不可用 @@ -123,6 +125,14 @@ SHA1: BCCA3CCACE5F40184A42FEFB06190C7279024985 8.0.41(2441):[https://dldir1.qq.com/weixin/android/weixin8041android2441_arm64_1.apk](https://dldir1.qq.com/weixin/android/weixin8041android2441_arm64_1.apk) SHA1: 51D3E1C9594723FE8A69B68780C4B561964C7718 +8.0.42(2460):[https://dldir1.qq.com/weixin/android/weixin8042android2460_arm64.apk](https://dldir1.qq.com/weixin/android/weixin8042android2460_arm64.apk) +SHA1: 227E395C67A2C0B0BCC750E1A3C52F642B433441 + +8.0.43(2480):[https://dldir1.qq.com/weixin/android/weixin8043android2480_0x28002b35_arm64.apk](https://dldir1.qq.com/weixin/android/weixin8043android2480_0x28002b35_arm64.apk) +SHA1: C46C85AF05130EDABCDBA8D487A5373ECE4AE6D0 + + + 推荐适配的最后两个版本,因为其他版本,作者自己不再使用 **问题4:是否支持Google Play版本?** diff --git a/app/build.gradle b/app/build.gradle index 7729751..459ed25 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -67,8 +67,8 @@ android { applicationId "com.lu.wxmask" minSdk 24 targetSdk 34 - versionCode 21 - versionName "1.20-bug" + versionCode 22 + versionName "1.21-bug" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/lu/wxmask/App.java b/app/src/main/java/com/lu/wxmask/App.java new file mode 100644 index 0000000..cb76695 --- /dev/null +++ b/app/src/main/java/com/lu/wxmask/App.java @@ -0,0 +1,44 @@ +package com.lu.wxmask; + +import android.app.Application; + +import androidx.lifecycle.ViewModelStore; +import androidx.lifecycle.ViewModelStoreOwner; + +import com.lu.wxmask.ui.JsonMenuManager; + +import org.jetbrains.annotations.NotNull; + +public final class App extends Application implements ViewModelStoreOwner { + public static final Companion Companion = new Companion(); + public static App instance; + + public void onCreate() { + super.onCreate(); + Companion.setInstance(this); + JsonMenuManager.Companion.updateMenuListFromRemote(this); + } + + @Override + public ViewModelStore getViewModelStore() { + return new ViewModelStore(); + } + + public static final class Companion { + private Companion() { + } + + public final App getInstance() { + App app = instance; + if (app != null) { + return app; + } + return null; + } + + public final void setInstance(@NotNull App app) { + instance = app; + } + + } +} diff --git a/app/src/main/java/com/lu/wxmask/App.kt b/app/src/main/java/com/lu/wxmask/App.kt deleted file mode 100644 index e567ed4..0000000 --- a/app/src/main/java/com/lu/wxmask/App.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lu.wxmask - -import android.app.Application -import androidx.lifecycle.ViewModelStore -import androidx.lifecycle.ViewModelStoreOwner -import com.lu.wxmask.ui.JsonMenuManager - -class App : Application(), ViewModelStoreOwner { - override fun onCreate() { - super.onCreate() - instance = this - JsonMenuManager.updateMenuListFromRemote(this) - } - companion object{ - lateinit var instance: App - } - - override fun getViewModelStore(): ViewModelStore { - return ViewModelStore() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lu/wxmask/Constrant.kt b/app/src/main/java/com/lu/wxmask/Constrant.kt index 2447488..c9dd267 100644 --- a/app/src/main/java/com/lu/wxmask/Constrant.kt +++ b/app/src/main/java/com/lu/wxmask/Constrant.kt @@ -2,6 +2,8 @@ package com.lu.wxmask class Constrant { companion object { + const val ONE_YEAR_MILLS = 1000L * 60 * 60 * 24 * 365L + //intent key, 标记来源是 Mask App const val KEY_INTENT_FROM_MASK = "KEY_INTENT_FROM_MASK" @@ -33,6 +35,7 @@ class Constrant { const val WX_CODE_8_0_32 = 2300 const val WX_CODE_8_0_33 = 2320 const val WX_CODE_8_0_34 = 2340 + //不知道为毛8.0.35找到一个和版本号与8.0.34重复的版本 //const val WX_CODE_8_0_35 = 2340 const val WX_CODE_8_0_35 = 2360 @@ -40,6 +43,8 @@ class Constrant { const val WX_CODE_8_0_38 = 2400 const val WX_CODE_8_0_40 = 2420 const val WX_CODE_8_0_41 = 2441 + const val WX_CODE_8_0_42 = 2460 + const val WX_CODE_8_0_43 = 2480 } } \ No newline at end of file diff --git a/app/src/main/java/com/lu/wxmask/plugin/WXConfigPlugin.java b/app/src/main/java/com/lu/wxmask/plugin/WXConfigPlugin.java index c155e32..457c281 100644 --- a/app/src/main/java/com/lu/wxmask/plugin/WXConfigPlugin.java +++ b/app/src/main/java/com/lu/wxmask/plugin/WXConfigPlugin.java @@ -76,7 +76,7 @@ private void onEntryWechatUI(Activity activity, Intent intent) { boolean isFromMaskPlugin = intent.getBooleanExtra(Constrant.KEY_INTENT_FROM_MASK, false); pluginMode = intent.getIntExtra(Constrant.KEY_INTENT_PLUGIN_MODE, -1); if (!isFromMaskPlugin) { - LogUtil.i("ignore not from mask"); + LogUtil.d("ignore not from mask"); return; } if (!AppVersionUtil.isSupportWechat()) { diff --git a/app/src/main/java/com/lu/wxmask/plugin/part/EmptySingChatHistoryGalleryPluginPart.kt b/app/src/main/java/com/lu/wxmask/plugin/part/EmptySingChatHistoryGalleryPluginPart.kt index 600ba29..e6ed65a 100644 --- a/app/src/main/java/com/lu/wxmask/plugin/part/EmptySingChatHistoryGalleryPluginPart.kt +++ b/app/src/main/java/com/lu/wxmask/plugin/part/EmptySingChatHistoryGalleryPluginPart.kt @@ -82,7 +82,7 @@ class EmptySingChatHistoryGalleryPluginPart : IPlugin { val MediaHistoryGalleryUI = "com.tencent.mm.ui.chatting.gallery.MediaHistoryGalleryUI" val methodName = when (AppVersionUtil.getVersionCode()) { in Constrant.WX_CODE_8_0_22..Constrant.WX_CODE_8_0_35 -> "k" - in Constrant.WX_CODE_8_0_35..Constrant.WX_CODE_8_0_41 -> "l" + in Constrant.WX_CODE_8_0_35..Constrant.WX_CODE_8_0_43 -> "l" else -> null } var galleryMethod: Method? = null @@ -145,7 +145,8 @@ class EmptySingChatHistoryGalleryPluginPart : IPlugin { Constrant.WX_CODE_8_0_35 -> "P" Constrant.WX_CODE_8_0_37 -> "Q" Constrant.WX_CODE_8_0_38 -> "R" - in Constrant.WX_CODE_8_0_40..Constrant.WX_CODE_8_0_41 -> "Q" + in Constrant.WX_CODE_8_0_40..Constrant.WX_CODE_8_0_41, Constrant.WX_CODE_8_0_43-> "Q" + in Constrant.WX_CODE_8_0_41 .. Constrant.WX_CODE_8_0_42 -> "R" else -> null } LogUtil.d("setEmptyActionBarTabPageUI method is :", commonHookMethodName) diff --git a/app/src/main/java/com/lu/wxmask/plugin/part/EnterChattingUIPluginPart.kt b/app/src/main/java/com/lu/wxmask/plugin/part/EnterChattingUIPluginPart.kt index 57f7bf5..38ec88d 100644 --- a/app/src/main/java/com/lu/wxmask/plugin/part/EnterChattingUIPluginPart.kt +++ b/app/src/main/java/com/lu/wxmask/plugin/part/EnterChattingUIPluginPart.kt @@ -45,7 +45,7 @@ class EnterChattingUIPluginPart() : IPlugin { "onEnterBegin" ) if (onEnterBeginMethod == null) { - LogUtil.i("onEnterBegin function == null, maybe change") + LogUtil.d("onEnterBegin function == null, maybe change") } else { //8.0.22 LogUtil.d("hook onEnterBegin") @@ -69,9 +69,10 @@ class EnterChattingUIPluginPart() : IPlugin { } Constrant.WX_CODE_8_0_35 -> "J" - Constrant.WX_CODE_8_0_37 -> "K" + Constrant.WX_CODE_8_0_37, Constrant.WX_CODE_8_0_43 -> "K" Constrant.WX_CODE_8_0_38 -> "M" in Constrant.WX_CODE_8_0_40..Constrant.WX_CODE_8_0_41 -> "K" + in Constrant.WX_CODE_8_0_41..Constrant.WX_CODE_8_0_42 -> "M" else -> null } var dispatchMethod: Method? = null @@ -159,7 +160,12 @@ class EnterChattingHookAction( }.getOrNull() if (listView == null) { listView = runCatching { - val mmListViewId = ResUtil.getViewId("b5n") + val mmListViewId = + if (AppVersionUtil.getVersionCode() in Constrant.WX_CODE_8_0_42..Constrant.WX_CODE_8_0_43) { + ResUtil.getViewId("bm6") + } else { + ResUtil.getViewId("b5n") + } XposedHelpers2.callMethod(fragmentObj, "findViewById", mmListViewId) as View }.getOrNull() diff --git a/app/src/main/java/com/lu/wxmask/plugin/part/HideMainUIListPluginPart.kt b/app/src/main/java/com/lu/wxmask/plugin/part/HideMainUIListPluginPart.kt index 8c77f1e..67d7cd3 100644 --- a/app/src/main/java/com/lu/wxmask/plugin/part/HideMainUIListPluginPart.kt +++ b/app/src/main/java/com/lu/wxmask/plugin/part/HideMainUIListPluginPart.kt @@ -35,6 +35,7 @@ class HideMainUIListPluginPart : IPlugin { LogUtil.w("hide mainUI listview fail, try to old function.") handleMainUIChattingListView(context, lpparam) } + } //隐藏指定用户的主页的消息 @@ -215,7 +216,7 @@ class HideMainUIListPluginPart : IPlugin { Constrant.WX_CODE_8_0_22 -> "com.tencent.mm.ui.g" in Constrant.WX_CODE_8_0_32..Constrant.WX_CODE_8_0_34 -> "com.tencent.mm.ui.y" in Constrant.WX_CODE_8_0_35..Constrant.WX_CODE_8_0_38 -> "com.tencent.mm.ui.z" - in Constrant.WX_CODE_8_0_40..Constrant.WX_CODE_8_0_41 -> "com.tencent.mm.ui.b0" + in Constrant.WX_CODE_8_0_40..Constrant.WX_CODE_8_0_43 -> "com.tencent.mm.ui.b0" else -> null } var getItemMethod = if (adapterClazzName != null && getItemMethodName != null) { @@ -276,6 +277,19 @@ class HideMainUIListPluginPart : IPlugin { XposedHelpers2.setObjectField(itemData, "field_unReadCount", 0) XposedHelpers2.setObjectField(itemData, "field_UnReadInvite", 0) XposedHelpers2.setObjectField(itemData, "field_unReadMuteCount", 0) + //文本消息 + XposedHelpers2.setObjectField(itemData, "field_msgType", "1") + +// try { +// var cTime = XposedHelpers2.getObjectField(itemData, "field_conversationTime") +// if (cTime != null) { +// val cTime2 = cTime - Constrant.ONE_YEAR_MILLS +// XposedHelpers2.setObjectField(itemData, "field_flag", cTime2) +// XposedHelpers2.setObjectField(itemData, "field_conversationTime", cTime2) +// } +// } catch (e: Exception) { +// } + } } @@ -284,4 +298,5 @@ class HideMainUIListPluginPart : IPlugin { ) } + } \ No newline at end of file diff --git a/app/src/main/java/com/lu/wxmask/plugin/part/HideSearchListUIPluginPart.kt b/app/src/main/java/com/lu/wxmask/plugin/part/HideSearchListUIPluginPart.kt index 32b19fe..a4c3000 100644 --- a/app/src/main/java/com/lu/wxmask/plugin/part/HideSearchListUIPluginPart.kt +++ b/app/src/main/java/com/lu/wxmask/plugin/part/HideSearchListUIPluginPart.kt @@ -1,9 +1,13 @@ package com.lu.wxmask.plugin.part import android.content.Context +import android.util.LruCache import com.lu.lposed.api2.XC_MethodHook2 import com.lu.lposed.api2.XposedHelpers2 import com.lu.lposed.plugin.IPlugin +import com.lu.lposed.plugin.PluginProviders +import com.lu.magic.util.GsonUtil +import com.lu.magic.util.ReflectUtil import com.lu.magic.util.log.LogUtil import com.lu.wxmask.BuildConfig import com.lu.wxmask.Constrant @@ -12,11 +16,16 @@ import com.lu.wxmask.util.AppVersionUtil import com.lu.wxmask.util.dev.DebugUtil import de.robv.android.xposed.XC_MethodHook import de.robv.android.xposed.callbacks.XC_LoadPackage +import java.lang.reflect.Field +import java.sql.Ref /** * 隐藏搜索列表 */ class HideSearchListUIPluginPart : IPlugin { + private val hideFieldInfoCache = HashMap>() + private val jsonResultLruCache = LruCache(16) + override fun handleHook(context: Context, lpparam: XC_LoadPackage.LoadPackageParam) { handleGlobalSearch(context, lpparam) handleDetailSearch(context, lpparam) @@ -72,7 +81,7 @@ class HideSearchListUIPluginPart : IPlugin { private fun handleDetailSearch(context: Context, lpparam: XC_LoadPackage.LoadPackageParam) { var hookClazzName = when (AppVersionUtil.getVersionCode()) { - in Constrant.WX_CODE_8_0_38.. Constrant.WX_CODE_8_0_41 -> "com.tencent.mm.plugin.fts.ui.x" + in Constrant.WX_CODE_8_0_38..Constrant.WX_CODE_8_0_41 -> "com.tencent.mm.plugin.fts.ui.x" else -> "com.tencent.mm.plugin.fts.ui.y" } //全局搜索详情置空 @@ -83,18 +92,19 @@ class HideSearchListUIPluginPart : IPlugin { Integer.TYPE, object : XC_MethodHook2() { override fun afterHookedMethod(param: MethodHookParam) { - if (needHideUserName(param, param.result)) { + if (needHideUserName2(param, param.result)) { LogUtil.d(param.result) param.result = try { //将命中的用户数据抹除掉 param.result::class.java.newInstance() } catch (e: Throwable) { - LogUtil.w("error new Instance, return null") + LogUtil.d("error new Instance, return null") null } } } + } ) } @@ -103,7 +113,7 @@ class HideSearchListUIPluginPart : IPlugin { // val wxVersionCode = AppVersionUtil.getVersionCode() // 理论上 hook com.tencent.mm.plugin.fts.ui.z#getItem 也是一样的,但是被覆盖重命名了 var hookClazzName = when (AppVersionUtil.getVersionCode()) { - in Constrant.WX_CODE_8_0_38 .. Constrant.WX_CODE_8_0_41 -> "com.tencent.mm.plugin.fts.ui.y" + in Constrant.WX_CODE_8_0_38..Constrant.WX_CODE_8_0_43 -> "com.tencent.mm.plugin.fts.ui.y" else -> "com.tencent.mm.plugin.fts.ui.z" } //全局搜索首页 @@ -222,4 +232,89 @@ class HideSearchListUIPluginPart : IPlugin { } + + private fun needHideUserName2(param: XC_MethodHook.MethodHookParam, itemData: Any?): Boolean { + if (itemData == null) { + return false + } + var clazz: Class<*>? = itemData.javaClass ?: return false + if (hideFieldInfoCache[clazz!!.name] != null) { + for (field in hideFieldInfoCache[clazz.name]!!) { + if (checkFieldNeedHide(itemData, field)) { + LogUtil.d("hide field from cache: ", field.type.name, field.name, field.get(itemData)) + return true + } + } + return false + } + while (clazz != null) { + for (field in clazz.declaredFields) { + field.isAccessible = true + try { + if (checkFieldNeedHide(itemData, field)) { + LogUtil.d("hide field: ", field.type.name, field.name, field.get(itemData)) + return true + } + } catch (e: Exception) { + continue + } + } + clazz = try { + clazz.superclass + } catch (e: Exception) { + break + } + } + return false + } + + private fun checkFieldNeedHide(itemData: Any, field: Field): Boolean { + var fieldValue: Any? = field.get(itemData) ?: return false + var clazzName = field.type.name + if (field.type.isAssignableFrom(Number::class.java) + || field.type.isAssignableFrom(Byte::class.java) + || clazzName.startsWith("android") + ) { + return false + } + + var jsonKey = fieldValue.toString().hashCode().toString() + + var compareText = if (fieldValue is CharSequence) { + fieldValue + } else { + if (jsonResultLruCache[jsonKey] == null){ + GsonUtil.toJson(fieldValue) + }else{ + jsonResultLruCache[jsonKey] + } + } + if (compareText.isBlank()) { + return false + } + jsonResultLruCache.put(jsonKey, compareText) + + for (wxid in PluginProviders.from(WXMaskPlugin::class.java).maskIdList) { + if (wxid == null) { + continue + } + if (compareText.contains(wxid)) { + putField2Cache(itemData::class.java.name, field) + LogUtil.d("hit wxid compareText: ", compareText) + return true + } + } + return false + + } + + private fun putField2Cache(itemClassName: String, field: Field) { + var pool = hideFieldInfoCache[itemClassName] + if (pool == null) { + pool = hashSetOf(field) + hideFieldInfoCache[itemClassName] = pool + } else { + pool.add(field) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lu/wxmask/util/AppVersionUtil.kt b/app/src/main/java/com/lu/wxmask/util/AppVersionUtil.kt index 1257c8f..7500a91 100644 --- a/app/src/main/java/com/lu/wxmask/util/AppVersionUtil.kt +++ b/app/src/main/java/com/lu/wxmask/util/AppVersionUtil.kt @@ -55,7 +55,8 @@ class AppVersionUtil { Constrant.WX_CODE_8_0_37, Constrant.WX_CODE_8_0_38, Constrant.WX_CODE_8_0_40, - Constrant.WX_CODE_8_0_41 -> true + Constrant.WX_CODE_8_0_41, + Constrant.WX_CODE_8_0_42 -> true else -> false } diff --git a/app/src/main/java/com/lu/wxmask/util/EachUtil.java b/app/src/main/java/com/lu/wxmask/util/EachUtil.java new file mode 100644 index 0000000..cd0f8e1 --- /dev/null +++ b/app/src/main/java/com/lu/wxmask/util/EachUtil.java @@ -0,0 +1,30 @@ +package com.lu.wxmask.util; + +import java.lang.reflect.Field; +import java.util.function.Predicate; + +public class EachUtil { + + public static void eachFieldValue(Object obj, Predicate consumer) { + Class clazz = obj.getClass(); + while (clazz != null) { + for (Field field : clazz.getDeclaredFields()) { + field.setAccessible(true); + try { + Object v = field.get(obj); + boolean isBreak = consumer.test(v); + if (isBreak) { + break; + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + try { + clazz = clazz.getSuperclass(); + } catch (Exception e) { + break; + } + } + } +}