diff --git a/app/src/main/java/gm/tieba/tabswitch/XposedInit.java b/app/src/main/java/gm/tieba/tabswitch/XposedInit.java index f97f235d..76d1ec4f 100644 --- a/app/src/main/java/gm/tieba/tabswitch/XposedInit.java +++ b/app/src/main/java/gm/tieba/tabswitch/XposedInit.java @@ -33,6 +33,7 @@ import gm.tieba.tabswitch.hooker.add.HistoryCache; import gm.tieba.tabswitch.hooker.add.Ripple; import gm.tieba.tabswitch.hooker.add.SaveImages; +import gm.tieba.tabswitch.hooker.add.SelectClipboard; import gm.tieba.tabswitch.hooker.auto.AgreeNum; import gm.tieba.tabswitch.hooker.auto.AutoSign; import gm.tieba.tabswitch.hooker.auto.FrsTab; @@ -155,7 +156,8 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable { new FoldTopCardView(), new MsgCenterTab(), new NotificationDetect(), - new PurgeVideo() + new PurgeVideo(), + new SelectClipboard() ); final var matchers = new ArrayList(hookers.size() + 2); matchers.add(new TbDialog()); diff --git a/app/src/main/java/gm/tieba/tabswitch/hooker/TSPreference.java b/app/src/main/java/gm/tieba/tabswitch/hooker/TSPreference.java index e7ec790a..fe3239a1 100644 --- a/app/src/main/java/gm/tieba/tabswitch/hooker/TSPreference.java +++ b/app/src/main/java/gm/tieba/tabswitch/hooker/TSPreference.java @@ -200,6 +200,7 @@ private LinearLayout createRootPreference(final Activity activity) { preferenceLayout.addView(new SwitchButtonHolder(activity, "浏览历史增加搜索", "history_cache", SwitchButtonHolder.TYPE_SWITCH)); preferenceLayout.addView(new SwitchButtonHolder(activity, "楼层增加点按效果", "ripple", SwitchButtonHolder.TYPE_SWITCH)); preferenceLayout.addView(new SwitchButtonHolder(activity, "长按下载保存全部图片", "save_images", SwitchButtonHolder.TYPE_SWITCH)); + preferenceLayout.addView(new SwitchButtonHolder(activity, "弹窗自由复制", "select_clipboard", SwitchButtonHolder.TYPE_SWITCH)); preferenceLayout.addView(TSPreferenceHelper.createTextView(isPurgeEnabled ? "垂手可得" : "自动化")); final SwitchButtonHolder autoSign = new SwitchButtonHolder(activity, "自动签到", "auto_sign", SwitchButtonHolder.TYPE_SWITCH); diff --git a/app/src/main/java/gm/tieba/tabswitch/hooker/add/SelectClipboard.java b/app/src/main/java/gm/tieba/tabswitch/hooker/add/SelectClipboard.java new file mode 100644 index 00000000..7fb38d9b --- /dev/null +++ b/app/src/main/java/gm/tieba/tabswitch/hooker/add/SelectClipboard.java @@ -0,0 +1,80 @@ +package gm.tieba.tabswitch.hooker.add; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.ClipboardManager; +import android.content.Context; +import android.view.View; +import android.view.WindowManager; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import java.util.List; + +import de.robv.android.xposed.XC_MethodReplacement; +import de.robv.android.xposed.XposedHelpers; +import gm.tieba.tabswitch.XposedContext; +import gm.tieba.tabswitch.dao.AcRules; +import gm.tieba.tabswitch.hooker.IHooker; +import gm.tieba.tabswitch.hooker.Obfuscated; +import gm.tieba.tabswitch.hooker.deobfuscation.Matcher; +import gm.tieba.tabswitch.hooker.deobfuscation.MatcherProperties; +import gm.tieba.tabswitch.hooker.deobfuscation.SmaliMatcher; +import gm.tieba.tabswitch.util.ClassMatcherUtils; +import gm.tieba.tabswitch.util.DisplayUtils; +import gm.tieba.tabswitch.util.ReflectUtils; + +public class SelectClipboard extends XposedContext implements IHooker, Obfuscated { + @NonNull + @Override + public String key() { + return "select_clipboard"; + } + + @Override + public List matchers() { + return List.of( + new SmaliMatcher("Landroid/text/ClipboardManager;->setText(Ljava/lang/CharSequence;)V", + MatcherProperties.create().useClassMatcher(ClassMatcherUtils.className("com.baidu.tieba.tbadkCore.data.PostData"))) + ); + } + + public void hook() throws Throwable { + AcRules.findRule(matchers(), (matcher, clazz, method) -> { + switch (matcher) { + case "PostData/Landroid/text/ClipboardManager;->setText(Ljava/lang/CharSequence;)V": + XposedHelpers.findAndHookMethod(clazz, sClassLoader, method, new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { + Object tbRichText = ReflectUtils.getObjectField(param.thisObject, "com.baidu.tbadk.widget.richText.TbRichText"); + + Activity currentActivity = ReflectUtils.getCurrentActivity(); + AlertDialog alert = new AlertDialog.Builder(currentActivity, DisplayUtils.isLightMode(getContext()) ? + android.R.style.Theme_DeviceDefault_Light_Dialog_Alert : android.R.style.Theme_DeviceDefault_Dialog_Alert) + .setTitle("自由复制").setMessage(tbRichText.toString()) + .setNeutralButton("复制全部", (dialogInterface, i) -> { + ClipboardManager clipboardManager = (ClipboardManager) ReflectUtils.getTbadkCoreApplicationInst().getSystemService(Context.CLIPBOARD_SERVICE); + clipboardManager.setText(tbRichText.toString()); + }) + .setPositiveButton("完成", null).create(); + alert.show(); + + View messageView = alert.findViewById(android.R.id.message); + if (messageView instanceof TextView) { + ((TextView) messageView).setTextIsSelectable(true); + } + + WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); + layoutParams.copyFrom(alert.getWindow().getAttributes()); + layoutParams.width = DisplayUtils.getDisplayWidth(getContext()); + alert.getWindow().setAttributes(layoutParams); + + return null; + } + }); + break; + } + }); + } +} diff --git a/app/src/main/java/gm/tieba/tabswitch/util/ReflectUtils.java b/app/src/main/java/gm/tieba/tabswitch/util/ReflectUtils.java index 2de75211..9697a1a5 100644 --- a/app/src/main/java/gm/tieba/tabswitch/util/ReflectUtils.java +++ b/app/src/main/java/gm/tieba/tabswitch/util/ReflectUtils.java @@ -1,5 +1,8 @@ package gm.tieba.tabswitch.util; +import android.app.Activity; +import android.app.Application; + import androidx.annotation.ColorInt; import java.lang.reflect.Field; @@ -217,4 +220,18 @@ public static Object callMethod(final Method method, final Object instance, fina public static Object callStaticMethod(final Method method, final Object... args) { return callMethod(method, null, args); } + + public static Application getTbadkCoreApplicationInst() { + return (Application) XposedHelpers.callStaticMethod( + XposedHelpers.findClass("com.baidu.tbadk.core.TbadkCoreApplication", sClassLoader), + "getInst" + ); + } + + public static Activity getCurrentActivity() { + return (Activity) XposedHelpers.callMethod( + getTbadkCoreApplicationInst(), + "getCurrentActivity" + ); + } }