From d04737e9f90d2b9882870ac899e4e7a3fdfe7b81 Mon Sep 17 00:00:00 2001 From: CLOUDERHEM Date: Wed, 31 Jan 2024 16:06:05 +0800 Subject: [PATCH] =?UTF-8?q?feature:=20=E6=96=B0=E5=A2=9E=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E9=A6=96=E9=A1=B5=E9=A1=B6=E6=A0=8FTab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/shatyuka/zhiliao/Hooks.java | 4 +- .../zhiliao/hooks/CustomTabFilter.java | 103 ++++++++++++++++++ .../zhiliao/hooks/ZhihuPreference.java | 23 +++- app/src/main/res/drawable/ic_filter.xml | 5 + app/src/main/res/xml/preferences_zhihu.xml | 6 + 5 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/com/shatyuka/zhiliao/hooks/CustomTabFilter.java create mode 100644 app/src/main/res/drawable/ic_filter.xml diff --git a/app/src/main/java/com/shatyuka/zhiliao/Hooks.java b/app/src/main/java/com/shatyuka/zhiliao/Hooks.java index 9f05d9f..2af4e4d 100644 --- a/app/src/main/java/com/shatyuka/zhiliao/Hooks.java +++ b/app/src/main/java/com/shatyuka/zhiliao/Hooks.java @@ -9,6 +9,7 @@ import com.shatyuka.zhiliao.hooks.ColorMode; import com.shatyuka.zhiliao.hooks.CommentAd; import com.shatyuka.zhiliao.hooks.CustomFilter; +import com.shatyuka.zhiliao.hooks.CustomTabFilter; import com.shatyuka.zhiliao.hooks.ExternLink; import com.shatyuka.zhiliao.hooks.FeedAd; import com.shatyuka.zhiliao.hooks.FeedTopHotBanner; @@ -63,7 +64,8 @@ public class Hooks { new Cleaner(), new FeedTopHotBanner(), new HeadZoneBanner(), - new MineHybridView() + new MineHybridView(), + new CustomTabFilter(), }; public static void init(final ClassLoader classLoader) { diff --git a/app/src/main/java/com/shatyuka/zhiliao/hooks/CustomTabFilter.java b/app/src/main/java/com/shatyuka/zhiliao/hooks/CustomTabFilter.java new file mode 100644 index 0000000..7d8f6ea --- /dev/null +++ b/app/src/main/java/com/shatyuka/zhiliao/hooks/CustomTabFilter.java @@ -0,0 +1,103 @@ +package com.shatyuka.zhiliao.hooks; + +import com.shatyuka.zhiliao.Helper; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XposedBridge; + + +public class CustomTabFilter implements IHook { + + static Class mainPageFragment; + + static Field customTabInfoList; + + static Field tabType; + + @Override + public String getName() { + return "自定义首页顶栏Tab"; + } + + @Override + public void init(ClassLoader classLoader) throws Throwable { + mainPageFragment = classLoader.loadClass("com.zhihu.android.app.feed.explore.view.MainPageFragment"); + + customTabInfoList = Arrays.stream(mainPageFragment.getDeclaredFields()) + .filter(field -> field.getType() == List.class).findFirst().get(); + customTabInfoList.setAccessible(true); + + tabType = classLoader.loadClass("com.zhihu.android.api.model.CustomTabInfo").getDeclaredField("tab_type"); + tabType.setAccessible(true); + } + + @Override + public void hook() throws Throwable { + if (Helper.prefs.getBoolean("switch_mainswitch", false)) { + XposedBridge.hookAllMethods(mainPageFragment, "onCreate", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + + List tabList = (List) customTabInfoList.get(param.thisObject); + + String tabFilter = Helper.prefs.getString("edit_tabfilter", ""); + if (tabFilter.isEmpty()) { + Helper.prefs.edit().putString("edit_tabfilter", encodePattern(tabList)).apply(); + } else { + List postProcessTabList = postProcessTabList(tabList, decodePattern(tabFilter)); + customTabInfoList.set(param.thisObject, postProcessTabList); + } + } + + }); + } + } + + private String encodePattern(List tabList) { + if (tabList == null || tabList.isEmpty()) { + return null; + } + return tabList.stream().map(o -> { + try { + return (String) tabType.get(o); + } catch (IllegalAccessException e) { + return null; + } + }).filter(s -> s != null && !s.isEmpty()) + .collect(Collectors.joining("|")); + } + + private List decodePattern(String pattern) { + return Arrays.stream(pattern.split("\\|")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + } + + private List postProcessTabList(List tabList, List typeFilterList) { + if (tabList == null || tabList.isEmpty()) { + return tabList; + } + + Map typeTabMap = tabList.stream() + .collect(Collectors.toMap(tab -> { + try { + return (String) tabType.get(tab); + } catch (IllegalAccessException ignore) { + return null; + } + }, tab -> tab)); + + return typeFilterList.stream() + .map(typeTabMap::get) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } +} diff --git a/app/src/main/java/com/shatyuka/zhiliao/hooks/ZhihuPreference.java b/app/src/main/java/com/shatyuka/zhiliao/hooks/ZhihuPreference.java index 171ec8b..1459e27 100644 --- a/app/src/main/java/com/shatyuka/zhiliao/hooks/ZhihuPreference.java +++ b/app/src/main/java/com/shatyuka/zhiliao/hooks/ZhihuPreference.java @@ -401,6 +401,7 @@ protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { Object switch_autoclean = findPreference.invoke(thisObject, "switch_autoclean"); Object switch_feedtophot = findPreference.invoke(thisObject, "switch_feedtophot"); Object switch_minehybrid = findPreference.invoke(thisObject, "switch_minehybrid"); + Object edit_tabfilter = findPreference.invoke(thisObject, "edit_tabfilter"); setOnPreferenceChangeListener.invoke(findPreference.invoke(thisObject, "accept_eula"), thisObject); setOnPreferenceClickListener.invoke(switch_externlink, thisObject); @@ -430,6 +431,7 @@ protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { setOnPreferenceClickListener.invoke(preference_donate, thisObject); setOnPreferenceClickListener.invoke(switch_feedtophot, thisObject); setOnPreferenceClickListener.invoke(switch_minehybrid, thisObject); + setOnPreferenceChangeListener.invoke(edit_tabfilter, thisObject); String real_version = null; try { @@ -517,6 +519,7 @@ protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { setIcon.invoke(preference_donate, Helper.modRes.getDrawable(R.drawable.ic_monetization)); setIcon.invoke(findPreference.invoke(thisObject, "switch_feedtophot"), Helper.modRes.getDrawable(R.drawable.ic_whatshot)); setIcon.invoke(findPreference.invoke(thisObject, "switch_minehybrid"), Helper.modRes.getDrawable(R.drawable.ic_viewcard)); + setIcon.invoke(findPreference.invoke(thisObject, "edit_tabfilter"), Helper.modRes.getDrawable(R.drawable.ic_filter)); if (Helper.prefs.getBoolean("accept_eula", false)) { Object category_eula = findPreference.invoke(thisObject, "category_eula"); @@ -620,12 +623,22 @@ protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { XposedBridge.hookMethod(Helper.getMethodByParameterTypes(DebugFragment, Preference, Object.class), new XC_MethodReplacement() { @Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { - if ((boolean) param.args[1]) { - Object switch_main = findPreference.invoke(param.thisObject, "switch_mainswitch"); - setChecked.invoke(switch_main, true); - Object category_eula = findPreference.invoke(param.thisObject, "category_eula"); - setVisible.invoke(category_eula, false); + if (param.args[0].getClass() == SwitchPreference) { + if (param.args[1] instanceof Boolean) { + if ((boolean) param.args[1]) { + Object switch_main = findPreference.invoke(param.thisObject, "switch_mainswitch"); + setChecked.invoke(switch_main, true); + Object category_eula = findPreference.invoke(param.thisObject, "category_eula"); + setVisible.invoke(category_eula, false); + } + } + } else if (param.args[0].getClass() == EditTextPreference) { + String key = (String) getKey.invoke(param.args[0]); + if ("edit_tabfilter".equals(key)) { + Helper.toast("重启知乎生效", Toast.LENGTH_SHORT); + } } + return true; } }); diff --git a/app/src/main/res/drawable/ic_filter.xml b/app/src/main/res/drawable/ic_filter.xml new file mode 100644 index 0000000..4679e51 --- /dev/null +++ b/app/src/main/res/drawable/ic_filter.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/xml/preferences_zhihu.xml b/app/src/main/res/xml/preferences_zhihu.xml index 92e7903..96cd0dd 100644 --- a/app/src/main/res/xml/preferences_zhihu.xml +++ b/app/src/main/res/xml/preferences_zhihu.xml @@ -229,6 +229,12 @@ android:key="switch_minehybrid" android:title="隐藏混合卡片" android:summary="隐藏「我的」底部混合卡片"/> +