diff --git a/app/src/main/kotlin/li/songe/gkd/App.kt b/app/src/main/kotlin/li/songe/gkd/App.kt index b8bf097e..12f52561 100644 --- a/app/src/main/kotlin/li/songe/gkd/App.kt +++ b/app/src/main/kotlin/li/songe/gkd/App.kt @@ -8,6 +8,7 @@ import android.content.ClipboardManager import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.provider.Settings import android.view.WindowManager @@ -23,6 +24,7 @@ import li.songe.gkd.service.initA11yWhiteAppList import li.songe.gkd.shizuku.initShizuku import li.songe.gkd.store.initStore import li.songe.gkd.util.AndroidTarget +import li.songe.gkd.util.PKG_FLAGS import li.songe.gkd.util.SafeR import li.songe.gkd.util.initAppState import li.songe.gkd.util.initSubsState @@ -111,6 +113,12 @@ class App : Application() { return intent.resolveActivity(packageManager)?.packageName } + fun getPkgInfo(appId: String): PackageInfo? = try { + packageManager.getPackageInfo(appId, PKG_FLAGS) + } catch (_: PackageManager.NameNotFoundException) { + null + } + fun resolveAppId(action: String, category: String? = null): String? { val intent = Intent(action) if (category != null) { diff --git a/app/src/main/kotlin/li/songe/gkd/MainActivity.kt b/app/src/main/kotlin/li/songe/gkd/MainActivity.kt index 3659d8b6..d9a8b537 100644 --- a/app/src/main/kotlin/li/songe/gkd/MainActivity.kt +++ b/app/src/main/kotlin/li/songe/gkd/MainActivity.kt @@ -192,7 +192,9 @@ class MainActivity : ComponentActivity() { } watchKeyboardVisible() StatusService.autoStart() - topAppIdFlow.value = META.appId + if (storeFlow.value.enableBlockA11yAppList) { + topAppIdFlow.value = META.appId + } setContent { val latestInsets = TopAppBarDefaults.windowInsets val density = LocalDensity.current diff --git a/app/src/main/kotlin/li/songe/gkd/a11y/A11yState.kt b/app/src/main/kotlin/li/songe/gkd/a11y/A11yState.kt index 1e6d39d0..bee4d0f0 100644 --- a/app/src/main/kotlin/li/songe/gkd/a11y/A11yState.kt +++ b/app/src/main/kotlin/li/songe/gkd/a11y/A11yState.kt @@ -20,15 +20,19 @@ import li.songe.gkd.data.AttrInfo import li.songe.gkd.data.ResetMatchType import li.songe.gkd.data.ResolvedRule import li.songe.gkd.data.RuleStatus +import li.songe.gkd.data.isSystem import li.songe.gkd.db.DbSet +import li.songe.gkd.shizuku.safeInvokeMethod import li.songe.gkd.store.actionCountFlow import li.songe.gkd.store.blockA11yAppListFlow import li.songe.gkd.store.blockMatchAppListFlow import li.songe.gkd.store.storeFlow +import li.songe.gkd.util.AndroidTarget import li.songe.gkd.util.PKG_FLAGS import li.songe.gkd.util.RuleSummary import li.songe.gkd.util.launchTry import li.songe.gkd.util.ruleSummaryFlow +import li.songe.gkd.util.systemUiAppId data class TopActivity( val appId: String = "", @@ -52,6 +56,10 @@ data class TopActivity( fun sameAs(a: String, b: String?): Boolean { return appId == a && activityId == b } + + fun sameAs(cn: ComponentName): Boolean { + return appId == cn.packageName && activityId == cn.className + } } val topActivityFlow = MutableStateFlow(TopActivity()) @@ -242,11 +250,32 @@ var appChangeTime = 0L var imeAppId = "" var launcherAppId = "" +var systemRecentCn = ComponentName("", "") fun updateSystemDefaultAppId() { - launcherAppId = app.resolveAppId(Intent.ACTION_MAIN, Intent.CATEGORY_HOME) ?: "" imeAppId = app.getSecureString(Settings.Secure.DEFAULT_INPUT_METHOD) ?.let(ComponentName::unflattenFromString)?.packageName ?: "" + val launcherCn = Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME) + .resolveActivity(app.packageManager) + launcherAppId = launcherCn.packageName + if (app.getPkgInfo(launcherAppId)?.applicationInfo?.isSystem == true) { + systemRecentCn = launcherCn + } else { + safeInvokeMethod { + if (AndroidTarget.P) { + systemRecentCn = ComponentName.unflattenFromString( + app.getString(com.android.internal.R.string.config_recentsComponentName) + ) ?: systemRecentCn + } + } + if (systemRecentCn.packageName.isEmpty()) { + // https://github.com/android-cs/8/blob/main/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java + systemRecentCn = ComponentName( + systemUiAppId, + "$systemUiAppId.recents.RecentsActivity", + ) + } + } } private val actionLogMutex = Mutex() diff --git a/app/src/main/kotlin/li/songe/gkd/data/AppInfo.kt b/app/src/main/kotlin/li/songe/gkd/data/AppInfo.kt index 5c328be7..a820e485 100644 --- a/app/src/main/kotlin/li/songe/gkd/data/AppInfo.kt +++ b/app/src/main/kotlin/li/songe/gkd/data/AppInfo.kt @@ -55,6 +55,9 @@ private val PackageInfo.isOverlay: Boolean false } +val ApplicationInfo.isSystem: Boolean + get() = flags and ApplicationInfo.FLAG_SYSTEM != 0 + fun PackageInfo.toAppInfo( userId: Int = currentUserId, ) = AppInfo( @@ -63,7 +66,7 @@ fun PackageInfo.toAppInfo( versionCode = compatVersionCode, versionName = versionName, mtime = lastUpdateTime, - isSystem = applicationInfo?.let { it.flags and ApplicationInfo.FLAG_SYSTEM != 0 } ?: false, + isSystem = applicationInfo?.isSystem ?: false, name = applicationInfo?.run { loadLabel(app.packageManager).toString() } ?: packageName, hidden = activities?.isEmpty() != false || isOverlay, enabled = applicationInfo?.enabled ?: true, diff --git a/app/src/main/kotlin/li/songe/gkd/service/GkdTileService.kt b/app/src/main/kotlin/li/songe/gkd/service/GkdTileService.kt index 844908ea..67f51b20 100644 --- a/app/src/main/kotlin/li/songe/gkd/service/GkdTileService.kt +++ b/app/src/main/kotlin/li/songe/gkd/service/GkdTileService.kt @@ -10,7 +10,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import li.songe.gkd.META -import li.songe.gkd.a11y.launcherAppId +import li.songe.gkd.a11y.systemRecentCn +import li.songe.gkd.a11y.topActivityFlow import li.songe.gkd.a11y.topAppIdFlow import li.songe.gkd.accessRestrictedSettingsShowFlow import li.songe.gkd.app @@ -22,7 +23,6 @@ import li.songe.gkd.store.blockA11yAppListFlow import li.songe.gkd.store.storeFlow import li.songe.gkd.util.launchTry import li.songe.gkd.util.mapState -import li.songe.gkd.util.systemUiAppId import li.songe.gkd.util.toast class GkdTileService : BaseTileService() { @@ -141,15 +141,13 @@ val a11yPartDisabledFlow by lazy { } } - fun initA11yWhiteAppList() { - val actualFlow = a11yPartDisabledFlow.drop(1) + val actualFlow = topAppIdFlow.drop(1) appScope.launch(Dispatchers.Main) { - actualFlow.collect { disabled -> - if (!disabled) { - val appId = topAppIdFlow.value - if (appId == launcherAppId || appId == systemUiAppId) { - // 检测最近任务界面,开启或关闭无障碍会造成卡顿 + actualFlow.collect { appId -> + if (!blockA11yAppListFlow.value.contains(appId)) { + if (topActivityFlow.value.sameAs(systemRecentCn)) { + // 切换无障碍会造成卡顿,在最近任务界面时,延迟这个卡顿 appScope.launch { delay(A11Y_WHITE_APP_AWAIT_TIME) if (appId == topAppIdFlow.value) { @@ -163,8 +161,8 @@ fun initA11yWhiteAppList() { } } appScope.launch(Dispatchers.Main) { - actualFlow.debounce(A11Y_WHITE_APP_AWAIT_TIME).collect { disabled -> - if (disabled) { + actualFlow.debounce(A11Y_WHITE_APP_AWAIT_TIME).collect { appId -> + if (blockA11yAppListFlow.value.contains(appId)) { forcedUpdateA11yService(true) } } diff --git a/app/src/main/kotlin/li/songe/gkd/shizuku/ShizukuApi.kt b/app/src/main/kotlin/li/songe/gkd/shizuku/ShizukuApi.kt index 03c9de48..3e1fb74e 100644 --- a/app/src/main/kotlin/li/songe/gkd/shizuku/ShizukuApi.kt +++ b/app/src/main/kotlin/li/songe/gkd/shizuku/ShizukuApi.kt @@ -22,7 +22,6 @@ import rikka.shizuku.Shizuku import rikka.shizuku.ShizukuBinderWrapper import rikka.shizuku.SystemServiceHelper -// shizuku 会概率断开 inline fun safeInvokeMethod( block: () -> T ): T? = try { diff --git a/app/src/main/kotlin/li/songe/gkd/util/AppInfoState.kt b/app/src/main/kotlin/li/songe/gkd/util/AppInfoState.kt index bc605e8c..a74b72f8 100644 --- a/app/src/main/kotlin/li/songe/gkd/util/AppInfoState.kt +++ b/app/src/main/kotlin/li/songe/gkd/util/AppInfoState.kt @@ -4,7 +4,6 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.graphics.drawable.Drawable import androidx.core.content.ContextCompat @@ -99,12 +98,6 @@ private val packageReceiver by lazy { const val PKG_FLAGS = PackageManager.MATCH_UNINSTALLED_PACKAGES or PackageManager.GET_ACTIVITIES -private fun getPkgInfo(appId: String): PackageInfo? = try { - app.packageManager.getPackageInfo(appId, PKG_FLAGS) -} catch (_: PackageManager.NameNotFoundException) { - null -} - val updateAppMutex = MutexState() private fun updateOtherUserAppInfo(userAppInfoMap: Map? = null) { @@ -146,7 +139,7 @@ private fun updatePartAppInfo( val newAppMap = HashMap(userAppInfoMapFlow.value) val newIconMap = HashMap(userAppIconMapFlow.value) appIds.forEach { appId -> - val info = getPkgInfo(appId) + val info = app.getPkgInfo(appId) if (info != null) { newAppMap[appId] = info.toAppInfo() } else { @@ -184,7 +177,7 @@ fun updateAllAppInfo( ) }.flatten() .map { it.activityInfo.packageName }.toSet() - .filter { !newAppMap.contains(it) }.mapNotNull { getPkgInfo(it) } + .filter { !newAppMap.contains(it) }.mapNotNull { app.getPkgInfo(it) } visiblePkgList.forEach { packageInfo -> newAppMap[packageInfo.packageName] = packageInfo.toAppInfo() packageInfo.pkgIcon?.let { icon -> diff --git a/hidden_api/src/main/java/com/android/internal/R.java b/hidden_api/src/main/java/com/android/internal/R.java new file mode 100644 index 00000000..ec78c28e --- /dev/null +++ b/hidden_api/src/main/java/com/android/internal/R.java @@ -0,0 +1,15 @@ +package com.android.internal; + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +/** + * @noinspection unused + */ +public class R { + public static final class string { + @RequiresApi(api = Build.VERSION_CODES.P) + public static int config_recentsComponentName; + } +}