From 5f0d3158e181930d56926ff9a71bbb2debd124e2 Mon Sep 17 00:00:00 2001 From: LiuYi <1354548951@qq.com> Date: Sat, 13 May 2023 09:48:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=BB=91=E5=90=8D=E5=8D=95?= =?UTF-8?q?=E6=94=AF=E6=8C=81,=20=E5=85=B6=E4=B8=AD=E5=8C=85=E6=8B=AC?= =?UTF-8?q?=E7=A3=81=E8=B4=B4=E3=80=81=E8=B7=B3=E8=BD=AC=E3=80=81=E5=88=86?= =?UTF-8?q?=E4=BA=AB=E3=80=81=E5=B9=B3=E8=A1=8C=E5=B0=8F=E7=AA=97=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=B9=B3=E8=A1=8C=E5=B0=8F=E7=AA=97=E7=9A=84?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E9=80=BB=E8=BE=91=EF=BC=8C=E5=87=8F=E5=B0=91?= =?UTF-8?q?=E6=97=A0=E9=99=90=E6=B7=BB=E5=8A=A0=E5=BA=94=E7=94=A8=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E5=86=85=E5=AD=98=E8=BF=87=E5=A4=9A=E7=9A=84=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E5=87=8F=E5=B0=91=E5=BA=94=E7=94=A8=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=20=E5=8E=BB=E9=99=A4=E6=82=AC=E6=B5=AE=E7=90=83?= =?UTF-8?q?=E7=9A=84=E5=B9=B3=E8=A1=8C=E5=B0=8F=E7=AA=97=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/deploymentTargetDropDown.xml | 17 -- app/build.gradle | 1 + .../java/org/liuyi/mifreeformx/BlackList.kt | 48 ++++ .../mifreeformx/activity/MainActivity.kt | 7 + .../activity/page/AppSelectPage.kt | 87 +++++++ .../JumpAndShareBetweenApplicationsPage.kt | 17 ++ .../page/NotificationAndControlCenterPage.kt | 5 + .../activity/page/ParallelSmallWindowPage.kt | 5 + .../mifreeformx/adapter/AppSelectAdapter.kt | 61 +++++ .../org/liuyi/mifreeformx/bean/BlackList.kt | 27 ++ .../liuyi/mifreeformx/bean/BlackListBean.kt | 75 ++++++ .../proxy/systemui/DependencyProxy.kt | 24 -- .../xposed/hooker/FrameworkBaseHooker.kt | 234 ++++++------------ .../xposed/hooker/SystemUiHooker.kt | 33 +-- .../xposed/operation/AppJumpOpt.kt | 37 +++ .../xposed/operation/AppShareOpt.kt | 44 ++++ .../operation/ParallelSmallWindowOpt.kt | 52 ++++ app/src/main/res/values/strings.xml | 1 + build.gradle | 2 +- 19 files changed, 560 insertions(+), 217 deletions(-) delete mode 100644 .idea/deploymentTargetDropDown.xml create mode 100644 app/src/main/java/org/liuyi/mifreeformx/BlackList.kt create mode 100644 app/src/main/java/org/liuyi/mifreeformx/activity/page/AppSelectPage.kt create mode 100644 app/src/main/java/org/liuyi/mifreeformx/adapter/AppSelectAdapter.kt create mode 100644 app/src/main/java/org/liuyi/mifreeformx/bean/BlackList.kt create mode 100644 app/src/main/java/org/liuyi/mifreeformx/bean/BlackListBean.kt delete mode 100644 app/src/main/java/org/liuyi/mifreeformx/proxy/systemui/DependencyProxy.kt create mode 100644 app/src/main/java/org/liuyi/mifreeformx/xposed/operation/AppJumpOpt.kt create mode 100644 app/src/main/java/org/liuyi/mifreeformx/xposed/operation/AppShareOpt.kt create mode 100644 app/src/main/java/org/liuyi/mifreeformx/xposed/operation/ParallelSmallWindowOpt.kt diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml deleted file mode 100644 index 1501d99..0000000 --- a/.idea/deploymentTargetDropDown.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 55794ad..2954412 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,6 +53,7 @@ dependencies { implementation project(path: ':blockmiui') implementation 'androidx.annotation:annotation-jvm:1.6.0' + implementation 'androidx.collection:collection:1.2.0' // 基础依赖 implementation 'com.highcapable.yukihookapi:api:1.1.11' diff --git a/app/src/main/java/org/liuyi/mifreeformx/BlackList.kt b/app/src/main/java/org/liuyi/mifreeformx/BlackList.kt new file mode 100644 index 0000000..2eba336 --- /dev/null +++ b/app/src/main/java/org/liuyi/mifreeformx/BlackList.kt @@ -0,0 +1,48 @@ +package org.liuyi.mifreeformx + +import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData +import org.liuyi.mifreeformx.bean.BlackListBean + +/** + * @Author: Liuyi + * @Date: 2023/05/09/9:47:41 + * @Description: + */ + + +object BlackList { + + object AppJumpSourceBlacklist : + BlackListBean(PrefsData("app_jump_source_blacklist", setOf("com.android.providers.media.module"))) + + object AppJumpTargetBlacklist : BlackListBean( + PrefsData( + "app_jump_target_blacklist", + setOf("com.android.camera", "com.miui.tsmclient", "com.lbe.security.miui") + ) + ) + + object AppShareSourceBlacklist : + BlackListBean(PrefsData("app_share_source_blacklist", setOf("com.android.providers.media.module"))) + + object AppShareTargetBlacklist : BlackListBean( + PrefsData( + "app_share_target_blacklist", + setOf("com.android.camera", "com.miui.tsmclient", "com.lbe.security.miui") + ) + ) + + object ParallelFreeformBlacklist : BlackListBean( + PrefsData( + "parallel_small_window_blacklist", + setOf("com.android.camera", "com.miui.tsmclient", "com.lbe.security.miui") + ) + ) + + object TileBlacklist : BlackListBean( + PrefsData( + "tile_blacklist", + setOf("com.android.camera", "com.miui.tsmclient", "com.lbe.security.miui") + ) + ) +} \ No newline at end of file diff --git a/app/src/main/java/org/liuyi/mifreeformx/activity/MainActivity.kt b/app/src/main/java/org/liuyi/mifreeformx/activity/MainActivity.kt index de58e61..e7cc4cc 100644 --- a/app/src/main/java/org/liuyi/mifreeformx/activity/MainActivity.kt +++ b/app/src/main/java/org/liuyi/mifreeformx/activity/MainActivity.kt @@ -3,7 +3,9 @@ package org.liuyi.mifreeformx.activity import android.os.Bundle import cn.fkj233.ui.activity.MIUIActivity import cn.fkj233.ui.dialog.MIUIDialog +import com.blankj.utilcode.util.AppUtils import com.highcapable.yukihookapi.YukiHookAPI +import com.highcapable.yukihookapi.hook.factory.prefs import org.liuyi.mifreeformx.R import org.liuyi.mifreeformx.activity.page.* @@ -14,6 +16,10 @@ import org.liuyi.mifreeformx.activity.page.* */ class MainActivity : MIUIActivity() { + companion object { + lateinit var appsInfo: Any + } + init { registerPage(MainPage::class.java) registerPage(NotificationAndControlCenterPage::class.java) @@ -22,6 +28,7 @@ class MainActivity : MIUIActivity() { registerPage(MiscellaneousPage::class.java) registerPage(MenuPage::class.java) registerPage(AboutPage::class.java) + registerPage(AppSelectPage::class.java) } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/org/liuyi/mifreeformx/activity/page/AppSelectPage.kt b/app/src/main/java/org/liuyi/mifreeformx/activity/page/AppSelectPage.kt new file mode 100644 index 0000000..727f68b --- /dev/null +++ b/app/src/main/java/org/liuyi/mifreeformx/activity/page/AppSelectPage.kt @@ -0,0 +1,87 @@ +package org.liuyi.mifreeformx.activity.page + +import android.annotation.SuppressLint +import cn.fkj233.ui.activity.annotation.BMPage +import cn.fkj233.ui.activity.fragment.MIUIFragment +import com.blankj.utilcode.util.AppUtils +import com.highcapable.yukihookapi.hook.factory.prefs +import com.highcapable.yukihookapi.hook.log.loggerD +import org.liuyi.mifreeformx.R +import org.liuyi.mifreeformx.adapter.AppSelectAdapter +import org.liuyi.mifreeformx.bean.BlackListBean +import org.liuyi.mifreeformx.utils.PinyinUtils +import kotlin.concurrent.thread + +/** + * @Author: Liuyi + * @Date: 2023/05/07/13:26:35 + * @Description: + */ +@SuppressLint("NonConstantResourceId") +@BMPage(key = "AppSelectPage", titleId = R.string.select_app) +class AppSelectPage : MyBasePage() { + + init { + skipLoadItem = true + } + + companion object { + init { + thread { allAppInfo } + } + + val allAppInfo: MutableList by lazy { AppUtils.getAppsInfo() } + var currentBlackList: BlackListBean? = null + } + + private var lastBlackList: BlackListBean? = null + + + private val mAppSelectAdapter by lazy { + AppSelectAdapter(activity, allAppInfo.toMutableList(), mutableSetOf(), setOf()) { _, _ -> + lastBlackList?.addAll(activity.prefs(), selectedSet) + } + } + + + override fun onCreate() { + List { + adapter = mAppSelectAdapter + } + } + + override fun asyncInit(fragment: MIUIFragment) { + fragment.showLoading() + // 初始化 + mAppSelectAdapter.apply { + val current = currentBlackList?.getAll(activity.prefs()) + + mAppInfoList = current?.let { + allAppInfo.toMutableList().apply { + sortWith { a1, a2 -> + if (current.contains(a1.packageName) && current.contains(a2.packageName)) { + PinyinUtils.ccs2Pinyin(a1.name).compareTo(PinyinUtils.ccs2Pinyin(a2.name)) + } else if (current.contains(a1.packageName)) { + -1 + } else if (current.contains(a2.packageName)) { + 1 + } else PinyinUtils.ccs2Pinyin(a1.name).compareTo(PinyinUtils.ccs2Pinyin(a2.name)) + } + } + } ?: mutableListOf() + + disEnabledSet = currentBlackList?.forceList ?: setOf() + selectedSet = current?.toMutableSet() ?: mutableSetOf() + + loggerD(msg = "当前黑名单: $currentBlackList") + loggerD(msg = "上次黑名单: $lastBlackList") + loggerD(msg = "不可选项: $disEnabledSet") + loggerD(msg = "当前选择项: $selectedSet") + notifyDataSetChanged() + } + lastBlackList = currentBlackList + currentBlackList = null + fragment.initData() + fragment.closeLoading() + } +} diff --git a/app/src/main/java/org/liuyi/mifreeformx/activity/page/JumpAndShareBetweenApplicationsPage.kt b/app/src/main/java/org/liuyi/mifreeformx/activity/page/JumpAndShareBetweenApplicationsPage.kt index c852be7..a02acb0 100644 --- a/app/src/main/java/org/liuyi/mifreeformx/activity/page/JumpAndShareBetweenApplicationsPage.kt +++ b/app/src/main/java/org/liuyi/mifreeformx/activity/page/JumpAndShareBetweenApplicationsPage.kt @@ -3,6 +3,7 @@ package org.liuyi.mifreeformx.activity.page import android.annotation.SuppressLint import cn.fkj233.ui.activity.annotation.BMPage import cn.fkj233.ui.activity.view.TextSummaryV +import org.liuyi.mifreeformx.BlackList import org.liuyi.mifreeformx.DataConst import org.liuyi.mifreeformx.R @@ -27,6 +28,14 @@ class JumpAndShareBetweenApplicationsPage : MyBasePage() { ), createSwitchV(DataConst.APP_JUMP) ) + TextSummaryWithArrow(TextSummaryV(text = "源黑名单", tips = "不使用小窗处理打开其他应用") { + AppSelectPage.currentBlackList = BlackList.AppJumpSourceBlacklist + showFragment("AppSelectPage") + }) + TextSummaryWithArrow(TextSummaryV(text = "目标黑名单", tips = "对将打开的应用不处理") { + AppSelectPage.currentBlackList = BlackList.AppJumpTargetBlacklist + showFragment("AppSelectPage") + }) Line() TitleText(textId = R.string.share_between_applications) @@ -44,5 +53,13 @@ class JumpAndShareBetweenApplicationsPage : MyBasePage() { ), createSwitchV(DataConst.SHARE_TO_APP_FORCE_NEW_TASK) ) + TextSummaryWithArrow(TextSummaryV(text = "源黑名单", tips = "不处理当前应用的分享") { + AppSelectPage.currentBlackList = BlackList.AppShareSourceBlacklist + showFragment("AppSelectPage") + }) + TextSummaryWithArrow(TextSummaryV(text = "目标黑名单", tips = "不处理分享的目标应用") { + AppSelectPage.currentBlackList = BlackList.AppShareTargetBlacklist + showFragment("AppSelectPage") + }) } } \ No newline at end of file diff --git a/app/src/main/java/org/liuyi/mifreeformx/activity/page/NotificationAndControlCenterPage.kt b/app/src/main/java/org/liuyi/mifreeformx/activity/page/NotificationAndControlCenterPage.kt index f9f7011..8fb9b3e 100644 --- a/app/src/main/java/org/liuyi/mifreeformx/activity/page/NotificationAndControlCenterPage.kt +++ b/app/src/main/java/org/liuyi/mifreeformx/activity/page/NotificationAndControlCenterPage.kt @@ -3,6 +3,7 @@ package org.liuyi.mifreeformx.activity.page import android.annotation.SuppressLint import cn.fkj233.ui.activity.annotation.BMPage import cn.fkj233.ui.activity.view.TextSummaryV +import org.liuyi.mifreeformx.BlackList import org.liuyi.mifreeformx.DataConst import org.liuyi.mifreeformx.R @@ -47,6 +48,10 @@ class NotificationAndControlCenterPage : MyBasePage() { ), createSwitchV(DataConst.FORCE_CONTROL_ALL_OPEN) ) + TextSummaryWithArrow(TextSummaryV(text = "黑名单") { + AppSelectPage.currentBlackList = BlackList.TileBlacklist + showFragment("AppSelectPage") + }) } } \ No newline at end of file diff --git a/app/src/main/java/org/liuyi/mifreeformx/activity/page/ParallelSmallWindowPage.kt b/app/src/main/java/org/liuyi/mifreeformx/activity/page/ParallelSmallWindowPage.kt index 2c44e28..189a4f0 100644 --- a/app/src/main/java/org/liuyi/mifreeformx/activity/page/ParallelSmallWindowPage.kt +++ b/app/src/main/java/org/liuyi/mifreeformx/activity/page/ParallelSmallWindowPage.kt @@ -3,6 +3,7 @@ package org.liuyi.mifreeformx.activity.page import android.annotation.SuppressLint import cn.fkj233.ui.activity.annotation.BMPage import cn.fkj233.ui.activity.view.TextSummaryV +import org.liuyi.mifreeformx.BlackList import org.liuyi.mifreeformx.DataConst import org.liuyi.mifreeformx.R @@ -23,5 +24,9 @@ class ParallelSmallWindowPage : MyBasePage() { ), createSwitchV(DataConst.PARALLEL_MULTI_WINDOW_PLUS) ) + TextSummaryWithArrow(TextSummaryV(text = "黑名单") { + AppSelectPage.currentBlackList = BlackList.ParallelFreeformBlacklist + showFragment("AppSelectPage") + }) } } \ No newline at end of file diff --git a/app/src/main/java/org/liuyi/mifreeformx/adapter/AppSelectAdapter.kt b/app/src/main/java/org/liuyi/mifreeformx/adapter/AppSelectAdapter.kt new file mode 100644 index 0000000..ff7435d --- /dev/null +++ b/app/src/main/java/org/liuyi/mifreeformx/adapter/AppSelectAdapter.kt @@ -0,0 +1,61 @@ +package org.liuyi.mifreeformx.adapter + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.CompoundButton +import android.widget.LinearLayout +import cn.fkj233.ui.activity.fragment.MIUIFragment +import cn.fkj233.ui.activity.view.ImageTextSummaryWithSwitchV +import cn.fkj233.ui.activity.view.ImageV +import cn.fkj233.ui.activity.view.SwitchV +import cn.fkj233.ui.activity.view.TextSummaryV +import com.blankj.utilcode.util.AppUtils.AppInfo + +/** + * @Author: Liuyi + * @Date: 2023/05/08/22:21:49 + * @Description: + */ +class AppSelectAdapter( + var mContext: Context, + var mAppInfoList: MutableList, + var selectedSet: MutableSet, + var disEnabledSet: Set, + private val block: AppSelectAdapter.(Boolean, AppInfo) -> Unit +) : BaseAdapter() { + + + override fun getCount() = mAppInfoList.size + + override fun getItem(position: Int) = mAppInfoList[position] + + override fun getItemId(position: Int) = position.toLong() + + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + return LinearLayout(mContext).also { layout -> + mAppInfoList[position].run { + SwitchV("").let { switchV -> + ImageTextSummaryWithSwitchV( + ImageV(icon), + TextSummaryV(text = name, tips = packageName), + switchV + ).let { + it.onDraw(MIUIFragment(""), layout, it.create(mContext) {}) + switchV.switch.apply { + isChecked = selectedSet.contains(packageName) + isEnabled = !disEnabledSet.contains(packageName) + setOnCheckedChangeListener { _, isChecked -> + if (isChecked) selectedSet += this@run.packageName + else selectedSet -= this@run.packageName + block(this@AppSelectAdapter, isChecked, this@run) + } + } + } + } + } + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/liuyi/mifreeformx/bean/BlackList.kt b/app/src/main/java/org/liuyi/mifreeformx/bean/BlackList.kt new file mode 100644 index 0000000..7017e94 --- /dev/null +++ b/app/src/main/java/org/liuyi/mifreeformx/bean/BlackList.kt @@ -0,0 +1,27 @@ +package org.liuyi.mifreeformx.bean + +import android.app.Activity +import com.highcapable.yukihookapi.hook.param.PackageParam +import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookPrefsBridge + +/** + * @Author: Liuyi + * @Date: 2023/05/12/20:32:26 + * @Description: + */ +interface BlackList { + + fun add(prefs: YukiHookPrefsBridge? = null, item: String) + + fun addAll(prefs: YukiHookPrefsBridge? = null, items: Set) + + fun remove(prefs: YukiHookPrefsBridge? = null, item: String) + + fun contains(prefs: YukiHookPrefsBridge? = null, item: String): Boolean + + fun size(prefs: YukiHookPrefsBridge? = null, item: String): Int + + fun clear(prefs: YukiHookPrefsBridge? = null, item: String) + + fun getAll(prefs: YukiHookPrefsBridge? = null): Set +} \ No newline at end of file diff --git a/app/src/main/java/org/liuyi/mifreeformx/bean/BlackListBean.kt b/app/src/main/java/org/liuyi/mifreeformx/bean/BlackListBean.kt new file mode 100644 index 0000000..ad19722 --- /dev/null +++ b/app/src/main/java/org/liuyi/mifreeformx/bean/BlackListBean.kt @@ -0,0 +1,75 @@ +package org.liuyi.mifreeformx.bean + +import android.content.Context +import android.content.Intent +import com.highcapable.yukihookapi.hook.log.loggerD +import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookPrefsBridge +import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData + +/** + * @Author: Liuyi + * @Date: 2023/05/12/20:31:43 + * @Description: + */ +open class BlackListBean(private val prefsData: PrefsData>? = null) : BlackList { + + var forceList: Set = prefsData?.value?: setOf() + + override fun add(prefs: YukiHookPrefsBridge?, item: String) { + if (item in forceList) { + loggerD(msg = "$item is forced to open") + return + } + if (prefs != null && prefsData != null) { + val mutableSet = prefs.get(prefsData).toMutableSet() + mutableSet.add(item) + prefs.edit { put(prefsData, mutableSet) } + } + } + + override fun addAll(prefs: YukiHookPrefsBridge?, items: Set) { + loggerD(msg = "$prefs $items $prefsData") + if (prefs != null && prefsData != null) { + prefs.edit { put(prefsData, items) } + } + } + + override fun remove(prefs: YukiHookPrefsBridge?, item: String) { + if (item in forceList) { + loggerD(msg = "$item is forced to open") + return + } + if (prefs != null && prefsData != null) { + val mutableSet = prefs.get(prefsData).toMutableSet() + mutableSet.remove(item) + prefs.edit { put(prefsData, mutableSet) } + } + } + + override fun contains(prefs: YukiHookPrefsBridge?, item: String): Boolean { + return item in forceList || prefsData?.let { prefs?.get(it)?.contains(item) } ?: false + } + + override fun size(prefs: YukiHookPrefsBridge?, item: String): Int { + return if (prefs != null && prefsData != null) { + forceList.toMutableSet().apply { addAll(prefs.get(prefsData)) }.size + } else forceList.size + } + + override fun clear(prefs: YukiHookPrefsBridge?, item: String) { + if (prefs != null && prefsData != null) { + prefs.edit { put(prefsData, setOf()) } + } + } + + override fun getAll(prefs: YukiHookPrefsBridge?): Set { + return if (prefs != null && prefsData != null) { + forceList.toMutableSet().apply { addAll(prefs.get(prefsData)) } + } else forceList + } + + fun contains(prefs: YukiHookPrefsBridge? = null, intent: Intent, context: Context? = null): Boolean { + val comparable = intent.component ?: context?.packageManager?.let { intent.resolveActivity(it) } + return comparable?.let { contains(prefs, it.packageName) } ?: false + } +} \ No newline at end of file diff --git a/app/src/main/java/org/liuyi/mifreeformx/proxy/systemui/DependencyProxy.kt b/app/src/main/java/org/liuyi/mifreeformx/proxy/systemui/DependencyProxy.kt deleted file mode 100644 index b551d90..0000000 --- a/app/src/main/java/org/liuyi/mifreeformx/proxy/systemui/DependencyProxy.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.liuyi.mifreeformx.proxy.systemui - -import android.content.ComponentName -import com.highcapable.yukihookapi.hook.factory.method -import org.liuyi.mifreeformx.xposed.base.ReflectStaticMethod -import org.liuyi.mifreeformx.xposed.hooker.SystemUiHooker.toClass - -/** - * @Author: Liuyi - * @Date: 2023/05/06/23:42:13 - * @Description: - */ -object DependencyProxy { - internal val clazz by lazy { "com.android.systemui.Dependency".toClass() } - - - internal val getTopActivity by lazy { - ReflectStaticMethod { - clazz.method { name("getTopActivity") }.give()!! - } - } - - -} \ No newline at end of file diff --git a/app/src/main/java/org/liuyi/mifreeformx/xposed/hooker/FrameworkBaseHooker.kt b/app/src/main/java/org/liuyi/mifreeformx/xposed/hooker/FrameworkBaseHooker.kt index 781f7eb..c3c52f7 100644 --- a/app/src/main/java/org/liuyi/mifreeformx/xposed/hooker/FrameworkBaseHooker.kt +++ b/app/src/main/java/org/liuyi/mifreeformx/xposed/hooker/FrameworkBaseHooker.kt @@ -1,18 +1,23 @@ package org.liuyi.mifreeformx.xposed.hooker import android.annotation.SuppressLint -import android.app.ActivityOptions -import android.content.Context import android.content.Intent import android.os.Bundle -import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker import com.highcapable.yukihookapi.hook.log.loggerD +import org.liuyi.mifreeformx.BlackList import org.liuyi.mifreeformx.DataConst import org.liuyi.mifreeformx.intent_extra.FreeFormIntent import org.liuyi.mifreeformx.intent_extra.forceFreeFromMode import org.liuyi.mifreeformx.intent_extra.getFreeFormMode import org.liuyi.mifreeformx.intent_extra.setFreeFromBundle +import org.liuyi.mifreeformx.proxy.framework.ActivityTaskManagerService +import org.liuyi.mifreeformx.proxy.framework.RootWindowContainer +import org.liuyi.mifreeformx.proxy.framework.SafeActivityOptions import org.liuyi.mifreeformx.utils.* +import org.liuyi.mifreeformx.xposed.base.LyBaseHooker +import org.liuyi.mifreeformx.xposed.operation.AppJumpOpt +import org.liuyi.mifreeformx.xposed.operation.AppShareOpt +import org.liuyi.mifreeformx.xposed.operation.ParallelSmallWindowOpt /** * @Author: Liuyi @@ -21,80 +26,13 @@ import org.liuyi.mifreeformx.utils.* */ @SuppressLint("QueryPermissionsNeeded") -object FrameworkBaseHooker : YukiBaseHooker() { - - private val targetBlacklist = - listOf("com.android.camera", "com.miui.tsmclient", "com.lbe.security.miui") - - // 禁止小窗黑名单 - private fun isInBlacklist(context: Context? = null, intent: Intent?): Boolean { - intent ?: return false - intent.component?.let { - if (targetBlacklist.contains(it.packageName)) return true - } - intent.`package`?.let { - if (targetBlacklist.contains(it)) return true - } - context ?: return false - context.packageManager?.let { - val componentName = intent.resolveActivity(it) - componentName?.run { - if (targetBlacklist.contains(packageName)) return true - } - } - return false - } - - // 应用间跳转 - private fun isAppJump( - callingThread: Any?, callingPackage: String?, intent: Intent, context: Context - ): Boolean { - // 排除系统调用 - callingThread ?: return false - // 排除如,系统后台进入,获取桌面进入 - if (intent.flags and Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED != 0) return false - if (callingPackage == "com.miui.securityadd") return false - if (intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0) { - // 包含 new 标签 - val componentName = intent.component ?: intent.resolveActivity(context.packageManager) - loggerD(msg = "$componentName") - // 修复微信分享回调导致源activity使用小窗 - if (componentName.className.endsWith(".wxapi.WXEntryActivity")) return false - componentName?.packageName?.let { name -> - loggerD(msg = name) - if (callingPackage != name) { - // 判断为应用间跳转 - return true - } - } - } - return false - } - - // 应用间分享 - private fun isShareToApp(callingPackage: String?, intent: Intent?): Boolean { - if (intent?.`package` == callingPackage) return false - intent?.action.let { - if (it == Intent.ACTION_SEND) return true - // 使用全屏打开 miui 系统选择分享界面 - if (it == "miui.intent.action.MIUI_CHOOSER") return false - } - intent?.component?.let { - // 微信分享sdk - if (it.className == "com.tencent.mm.plugin.base.stub.WXEntryActivity") return true - if (it.packageName == callingPackage) return false - } - if (intent?.clipData != null) return true - intent?.data?.let { - // QQ分享sdk - if (it.scheme == "mqqapi" && it.host == "share") return true - } - return false - } +object FrameworkBaseHooker : LyBaseHooker() { override fun onHook() { + var rootWindowContainer: RootWindowContainer? = null + val activityTaskManagerService: ActivityTaskManagerService? = null "com.android.server.wm.ActivityTaskManagerService".hook { /** * 用户应用调用会来到这里 @@ -109,37 +47,39 @@ object FrameworkBaseHooker : YukiBaseHooker() { // 全局管控,只要在intent设置了 FreeFormIntent 都会优先判断是否开启小窗 val caller = args[1] as String? val intent = Intent(args[3] as? Intent?) + val atmService = activityTaskManagerService ?: instance.getProxyAs() + rootWindowContainer = rootWindowContainer ?: atmService.mRootWindowContainer args[3] = intent - val context = instance.getFieldValueOrNull("mContext") as Context? - if (isInBlacklist(context, intent)) return@beforeHook + val context = atmService.mContext ?: return@beforeHook + val callee = intent.resolveActivity(context.packageManager)?.packageName ?: return@beforeHook - if (context != null) { - if (prefs.get(DataConst.PARALLEL_MULTI_WINDOW_PLUS)) { - if (caller == "com.miui.touchassistant" || caller == "com.miui.securitycenter") { - if (!intent.isSameApp(caller) && intent.action == Intent.ACTION_MAIN) { - intent.forceFreeFromMode() - intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - } - } - } - if (prefs.get(DataConst.APP_JUMP) - && isAppJump(args[0], caller, intent, context) - ) { - intent.forceFreeFromMode() - } - if (prefs.get(DataConst.SHARE_TO_APP) - && isShareToApp(args[1] as? String?, intent) - ) { - intent.forceFreeFromMode() - intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - if (prefs.get(DataConst.SHARE_TO_APP_FORCE_NEW_TASK)) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - } - if (intent.getFreeFormMode() == FreeFormIntent.FREE_FORM_EXTRA_FORCE) { - args[10] = args[10] ?: getBasicBundle() - intent.setFreeFromBundle(args[10] as Bundle, context) - } + if (prefs.get(DataConst.PARALLEL_MULTI_WINDOW_PLUS) + && ParallelSmallWindowOpt.isFromSidebar(caller, intent) + && !BlackList.ParallelFreeformBlacklist.contains(prefs, callee) + ) { + ParallelSmallWindowOpt.handle(intent, atmService, rootWindowContainer) + } + + if (prefs.get(DataConst.APP_JUMP) + && AppJumpOpt.isAppJump(args[0], caller, intent, context) + && caller != null + && !BlackList.AppJumpSourceBlacklist.contains(prefs, caller) + && !BlackList.AppJumpTargetBlacklist.contains(prefs, callee) + ) { + intent.forceFreeFromMode() + } + + if (prefs.get(DataConst.SHARE_TO_APP) + && AppShareOpt.isShareToApp(args[1] as? String?, intent) + && caller != null + && !BlackList.AppShareSourceBlacklist.contains(prefs, caller) + && !BlackList.AppShareTargetBlacklist.contains(prefs, callee) + ) { + AppShareOpt.handle(intent) + } + if (intent.getFreeFormMode() == FreeFormIntent.FREE_FORM_EXTRA_FORCE) { + args[10] = args[10] ?: getBasicBundle() + intent.setFreeFromBundle(args[10] as Bundle, context) } } } @@ -149,43 +89,21 @@ object FrameworkBaseHooker : YukiBaseHooker() { injectMember { method { name("startActivityAsCaller") } beforeHook { - if (prefs.get(DataConst.SHARE_TO_APP)) { - val context = instance.getFieldValueOrNull("mContext") as? Context? - val intent = Intent(args[2] as? Intent?) - args[2] = intent - if (!isInBlacklist(context, intent) && context != null) { - if (isShareToApp(args[1] as? String?, intent)) { - // 开启分享应用 - intent.forceFreeFromMode() - // 强制添加 new task 标签 - if (prefs.get(DataConst.SHARE_TO_APP_FORCE_NEW_TASK)) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - } - if (intent.getFreeFormMode() == FreeFormIntent.FREE_FORM_EXTRA_FORCE) { - args[9] = args[9] ?: getBasicBundle() - intent.setFreeFromBundle(args[9] as Bundle, context) - } - } + val atmService = activityTaskManagerService ?: instance.getProxyAs() + val context = atmService.mContext ?: return@beforeHook + val intent = Intent(args[2] as? Intent?) + val caller = args[1] as? String? + args[2] = intent + + if (prefs.get(DataConst.SHARE_TO_APP) + && AppShareOpt.isShareToApp(caller, intent) + && !BlackList.AppShareTargetBlacklist.contains(prefs, intent, context) + ) { + AppShareOpt.handle(intent) } - if (prefs.get(DataConst.SHARE_TO_APP)) { - val context = instance.getFieldValueOrNull("mContext") as? Context? - val intent = Intent(args[2] as? Intent?) - args[2] = intent - if (!isInBlacklist(context, intent) && context != null) { - if (isShareToApp(args[1] as? String?, intent)) { - // 开启分享应用 - intent.forceFreeFromMode() - // 强制添加 new task 标签 - if (prefs.get(DataConst.SHARE_TO_APP_FORCE_NEW_TASK)) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - } - if (intent.getFreeFormMode() == FreeFormIntent.FREE_FORM_EXTRA_FORCE) { - args[9] = args[9] ?: getBasicBundle() - intent.setFreeFromBundle(args[9] as Bundle, context) - } - } + if (intent.getFreeFormMode() == FreeFormIntent.FREE_FORM_EXTRA_FORCE) { + args[9] = args[9] ?: getBasicBundle() + intent.setFreeFromBundle(args[9] as Bundle, context) } } } @@ -205,30 +123,28 @@ object FrameworkBaseHooker : YukiBaseHooker() { loggerD(msg = "${args.asList()}") val intent = Intent(args[5] as? Intent?) args[5] = intent - val context = instanceOrNull?.getFieldValueOrNull("mService") - ?.getFieldValueOrNull("mContext") as? Context? - - val callingPackage = args[3] as? String? - - if ( - context != null - && callingPackage != "com.android.providers.media.module" // 排除【媒体存储设备】 + val realCallingPid = args[1] as Int + val atmService = activityTaskManagerService ?: instance.getFieldValueByName("mService").getProxyAs() + rootWindowContainer = rootWindowContainer ?: atmService.mRootWindowContainer + val caller = args[3] as? String? + val context = atmService.mContext ?: return@beforeHook + val safeActivityOptions = args[11]?.getProxyAs() ?: return@beforeHook + if (AppShareOpt.isShareToApp(caller, intent) && prefs.get(DataConst.SHARE_TO_APP) + && !BlackList.AppShareTargetBlacklist.contains(prefs, intent, context) ) { - if (isShareToApp(callingPackage, intent)) { - if (prefs.get(DataConst.SHARE_TO_APP)) { - val mOriginalOptions = - (args[11]?.getFieldValueOrNull("mOriginalOptions") as? ActivityOptions?) - mOriginalOptions?.setLaunchWindowingModeExt(5) - mOriginalOptions?.launchBounds = - MiuiMultiWindowUtils.getFreeformRect(context) - } - } else { - if (prefs.get(DataConst.PARALLEL_MULTI_WINDOW_PLUS)) { - intent.removeFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - } + safeActivityOptions.mOriginalOptions?.apply { + setLaunchWindowingModeExt(5) + launchBounds = MiuiMultiWindowUtils.getFreeformRect(context) } } + if ( + prefs.get(DataConst.PARALLEL_MULTI_WINDOW_PLUS) + && realCallingPid == context.getPidFromPackageNameExt("com.android.systemui") + && !BlackList.ParallelFreeformBlacklist.contains(prefs, intent, context) + && safeActivityOptions.mOriginalOptions?.getLaunchWindowingModeExt() == 5 + ) { + ParallelSmallWindowOpt.handle(intent, atmService, rootWindowContainer) + } } } } diff --git a/app/src/main/java/org/liuyi/mifreeformx/xposed/hooker/SystemUiHooker.kt b/app/src/main/java/org/liuyi/mifreeformx/xposed/hooker/SystemUiHooker.kt index b5457f2..d7550d9 100644 --- a/app/src/main/java/org/liuyi/mifreeformx/xposed/hooker/SystemUiHooker.kt +++ b/app/src/main/java/org/liuyi/mifreeformx/xposed/hooker/SystemUiHooker.kt @@ -1,14 +1,18 @@ package org.liuyi.mifreeformx.xposed.hooker import android.annotation.SuppressLint +import android.app.PendingIntent import android.content.Context import android.content.Intent -import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.log.loggerD +import org.liuyi.mifreeformx.BlackList import org.liuyi.mifreeformx.DataConst import org.liuyi.mifreeformx.intent_extra.forceFreeFromMode -import org.liuyi.mifreeformx.proxy.systemui.CommonUtil +import org.liuyi.mifreeformx.proxy.systemui.AppMiniWindowManager import org.liuyi.mifreeformx.proxy.systemui.CentralSurfacesImpl +import org.liuyi.mifreeformx.proxy.systemui.CommonUtil +import org.liuyi.mifreeformx.proxy.systemui.Dependency +import org.liuyi.mifreeformx.proxy.systemui.MiuiExpandableNotificationRow import org.liuyi.mifreeformx.utils.* import org.liuyi.mifreeformx.xposed.base.LyBaseHooker @@ -49,7 +53,9 @@ object SystemUiHooker : LyBaseHooker() { val componentName = intent.resolveActivity(context!!.packageManager) loggerD(msg = "$topActivity and $componentName") // 如果是顶部App 则不处理 - if (topActivity?.packageName == componentName.packageName) return@beforeHook + if (topActivity?.packageName == componentName.packageName + || BlackList.TileBlacklist.contains(prefs, componentName.packageName) + ) return@beforeHook if (prefs.get(DataConst.FORCE_CONTROL_ALL_OPEN) || isTile(intent)) { intent.forceFreeFromMode() var flag = args[5] as Int @@ -75,23 +81,18 @@ object SystemUiHooker : LyBaseHooker() { loggerD(msg = "${this.args.asList()}") if (prefs.get(DataConst.OPEN_NOTICE)) { // 加载类 - val dependencyClass = "com.android.systemui.Dependency".toClass() val appMiniWindowManagerClass = "com.android.systemui.statusbar.notification.policy.AppMiniWindowManager".toClass() - // 逻辑开始 - val appMiniWindowManager = dependencyClass.method { - name("get") - param(Class::class.java) - }.get().call(appMiniWindowManagerClass) + val appMiniWindowManager = + Dependency.Proxy.get(appMiniWindowManagerClass).getProxyAs() - appMiniWindowManagerClass.method { name("launchMiniWindowActivity") } - .get(appMiniWindowManager).let { - val targetPkg = args[3]?.javaClass - ?.method { name("getMiniWindowTargetPkg") } - ?.get(args[3])?.invoke() - it.call(targetPkg, args[0]) - } + args[3]?.getProxyAs()?.let { + appMiniWindowManager.launchMiniWindowActivity( + it.getMiniWindowTargetPkg(), args[0] as PendingIntent + ) + resultNull() + } } } } diff --git a/app/src/main/java/org/liuyi/mifreeformx/xposed/operation/AppJumpOpt.kt b/app/src/main/java/org/liuyi/mifreeformx/xposed/operation/AppJumpOpt.kt new file mode 100644 index 0000000..3c7045f --- /dev/null +++ b/app/src/main/java/org/liuyi/mifreeformx/xposed/operation/AppJumpOpt.kt @@ -0,0 +1,37 @@ +package org.liuyi.mifreeformx.xposed.operation + +import android.content.Context +import android.content.Intent +import com.highcapable.yukihookapi.hook.log.loggerD + +/** + * @Author: Liuyi + * @Date: 2023/05/13/7:36:24 + * @Description: + */ +object AppJumpOpt { + + // 应用间跳转 + fun isAppJump(callingThread: Any?, callingPackage: String?, intent: Intent, context: Context): Boolean { + // 排除系统调用 + callingThread ?: return false + // 排除如,系统后台进入,获取桌面进入 + if (intent.flags and Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED != 0) return false + if (callingPackage == "com.miui.securityadd") return false + if (intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0) { + // 包含 new 标签 + val componentName = intent.component ?: intent.resolveActivity(context.packageManager) + loggerD(msg = "$componentName") + // 修复微信分享回调导致源activity使用小窗 + if (componentName.className.endsWith(".wxapi.WXEntryActivity")) return false + componentName?.packageName?.let { name -> + loggerD(msg = name) + if (callingPackage != name) { + // 判断为应用间跳转 + return true + } + } + } + return false + } +} \ No newline at end of file diff --git a/app/src/main/java/org/liuyi/mifreeformx/xposed/operation/AppShareOpt.kt b/app/src/main/java/org/liuyi/mifreeformx/xposed/operation/AppShareOpt.kt new file mode 100644 index 0000000..f89d63c --- /dev/null +++ b/app/src/main/java/org/liuyi/mifreeformx/xposed/operation/AppShareOpt.kt @@ -0,0 +1,44 @@ +package org.liuyi.mifreeformx.xposed.operation + +import android.content.Intent +import org.liuyi.mifreeformx.DataConst +import org.liuyi.mifreeformx.intent_extra.forceFreeFromMode +import org.liuyi.mifreeformx.xposed.hooker.FrameworkBaseHooker + +/** + * @Author: Liuyi + * @Date: 2023/05/13/7:39:12 + * @Description: + */ +object AppShareOpt { + + // 应用间分享 + internal fun isShareToApp(callingPackage: String?, intent: Intent?): Boolean { + if (intent?.`package` == callingPackage) return false + intent?.action.let { + if (it == Intent.ACTION_SEND) return true + // 使用全屏打开 miui 系统选择分享界面 + if (it == "miui.intent.action.MIUI_CHOOSER") return false + } + intent?.component?.let { + // 微信分享sdk + if (it.className == "com.tencent.mm.plugin.base.stub.WXEntryActivity") return true + if (it.packageName == callingPackage) return false + } + if (intent?.clipData != null) return true + intent?.data?.let { + // QQ分享sdk + if (it.scheme == "mqqapi" && it.host == "share") return true + } + return false + } + + internal fun handle(intent: Intent): Boolean { + intent.forceFreeFromMode() + intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) + if (FrameworkBaseHooker.prefs.get(DataConst.SHARE_TO_APP_FORCE_NEW_TASK)) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/org/liuyi/mifreeformx/xposed/operation/ParallelSmallWindowOpt.kt b/app/src/main/java/org/liuyi/mifreeformx/xposed/operation/ParallelSmallWindowOpt.kt new file mode 100644 index 0000000..62248d3 --- /dev/null +++ b/app/src/main/java/org/liuyi/mifreeformx/xposed/operation/ParallelSmallWindowOpt.kt @@ -0,0 +1,52 @@ +package org.liuyi.mifreeformx.xposed.operation + +import android.content.Intent +import com.highcapable.yukihookapi.hook.log.loggerD +import org.liuyi.mifreeformx.proxy.framework.ActivityTaskManagerService +import org.liuyi.mifreeformx.proxy.framework.RootWindowContainer +import org.liuyi.mifreeformx.utils.isSameApp + +/** + * @Author: Liuyi + * @Date: 2023/05/13/7:08:07 + * @Description: + */ +object ParallelSmallWindowOpt { + + /** + * 判断是否来自侧边栏 + * 逻辑来自手机管家 + */ + internal fun isFromSidebar(callingPackage: String?, intent: Intent): Boolean { + return callingPackage == "com.miui.securitycenter" + && !intent.isSameApp(callingPackage) + && intent.action == Intent.ACTION_MAIN + && intent.categories.contains(Intent.CATEGORY_LAUNCHER) + && intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0 + } + + /** + * + * @param intent + * @param atmService + * @param container + * @return 是否运行其他的分支 + */ + internal fun handle( + intent: Intent, + atmService: ActivityTaskManagerService, + container: RootWindowContainer? + ): Boolean { + loggerD(msg = "开启平行小窗: $this") + atmService.getTasks(Int.MAX_VALUE).orEmpty().lastOrNull { taskInfo -> + // 获得栈底最后一个符合条件的task + taskInfo != null && taskInfo.baseActivity == intent.component + && taskInfo.isRunning && taskInfo.topActivity == intent.component + }?.also { + // 如果获取到了将其放入堆栈前 + loggerD(msg = "已存在一个任务: $it") + container?.anyTaskForId(it.taskId)?.moveToFront("god let me do it") + } ?: intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) + return false + } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1b34fcc..3ca8451 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -68,4 +68,5 @@ 基于 MIUI13(Android12)适配的系统拓展Xposed模块 小窗失去焦点时行为 点击小窗外时触发小窗行为,多小窗情景下谨慎使用 + 选择应用 \ No newline at end of file diff --git a/build.gradle b/build.gradle index 25ad99d..84be749 100644 --- a/build.gradle +++ b/build.gradle @@ -2,5 +2,5 @@ plugins { id 'com.android.application' version '8.0.1' apply false id 'com.android.library' version '8.0.1' apply false - id 'org.jetbrains.kotlin.android' version '1.8.20' apply false + id 'org.jetbrains.kotlin.android' version '1.8.21' apply false } \ No newline at end of file