Skip to content

Commit

Permalink
perf: app search, activity listener
Browse files Browse the repository at this point in the history
  • Loading branch information
lisonge committed Jan 9, 2025
1 parent 337d42b commit 2e26326
Show file tree
Hide file tree
Showing 20 changed files with 768 additions and 327 deletions.
31 changes: 0 additions & 31 deletions app/src/main/kotlin/li/songe/gkd/App.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package li.songe.gkd

import android.app.Activity
import android.app.Application
import android.content.ComponentName
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.database.ContentObserver
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.text.TextUtils
import com.blankj.utilcode.util.LogUtils
Expand Down Expand Up @@ -97,35 +95,6 @@ class App : Application() {
META,
)
initFolder()
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
LogUtils.d("onActivityCreated", activity, savedInstanceState)
}

override fun onActivityStarted(activity: Activity) {
LogUtils.d("onActivityStarted", activity)
}

override fun onActivityResumed(activity: Activity) {
LogUtils.d("onActivityResumed", activity)
}

override fun onActivityPaused(activity: Activity) {
LogUtils.d("onActivityPaused", activity)
}

override fun onActivityStopped(activity: Activity) {
LogUtils.d("onActivityStopped", activity)
}

override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
LogUtils.d("onActivitySaveInstanceState", activity, outState)
}

override fun onActivityDestroyed(activity: Activity) {
LogUtils.d("onActivityDestroyed", activity)
}
})
app.contentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES),
false,
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/kotlin/li/songe/gkd/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import li.songe.gkd.permission.updatePermissionState
import li.songe.gkd.service.A11yService
import li.songe.gkd.service.ManageService
import li.songe.gkd.service.fixRestartService
import li.songe.gkd.service.updateDefaultInputAppId
import li.songe.gkd.service.updateLauncherAppId
import li.songe.gkd.ui.component.BuildDialog
import li.songe.gkd.ui.component.ShareDataDialog
Expand Down Expand Up @@ -188,6 +189,8 @@ fun syncFixState() {
// 每次切换页面更新记录桌面 appId
updateLauncherAppId()

updateDefaultInputAppId()

// 在某些机型由于未知原因创建失败, 在此保证每次界面切换都能重新检测创建
initFolder()

Expand Down
9 changes: 8 additions & 1 deletion app/src/main/kotlin/li/songe/gkd/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,11 @@ class MainViewModel : ViewModel() {
}
}
}
}
}

//private fun saveObjectClass(clazz: KClass<*>) {
// val declaredFunctionText = clazz.declaredFunctions.map {
// "${it.name}\n(${it.parameters.joinToString { p -> "${p.name}:${p.type}" }}):${it.returnType}"
// }.joinToString("\n\n")
// filesDir.resolve("object_class.txt").writeText(declaredFunctionText)
//}
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ fun updatePermissionState() {
writeSecureSettingsState,
shizukuOkState,
).forEach { it.updateAndGet() }
if (!updateAppMutex.mutex.isLocked && (canQueryPkgState.stateFlow.value != canQueryPkgState.updateAndGet() || mayQueryPkgNoAccessFlow.value)) {
val stateChanged = canQueryPkgState.stateFlow.value != canQueryPkgState.updateAndGet()
if (!updateAppMutex.mutex.isLocked && (stateChanged || mayQueryPkgNoAccessFlow.value)) {
appScope.launchTry(Dispatchers.IO) {
initOrResetAppInfoCache()
}
Expand Down
24 changes: 17 additions & 7 deletions app/src/main/kotlin/li/songe/gkd/service/A11yService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,6 @@ private fun A11yService.useMatchRule() {
}
val activityRule = getAndUpdateCurrentRules()
if (activityRule.skipMatch) {
if (META.debuggable) {
Log.d("queryEvents", "没有规则或者禁用匹配")
}
// 如果当前应用没有规则/暂停匹配, 则不去调用获取事件节点避免阻塞
return@launchQuery checkFutureJob()
}
Expand Down Expand Up @@ -414,7 +411,7 @@ private fun A11yService.useMatchRule() {
val evActivityId = a11yEvent.className
val oldAppId = topActivityFlow.value.appId
val rightAppId = if (oldAppId == evAppId) {
oldAppId
evAppId
} else {
getAppIdByCache(a11yEvent) ?: return@launchEvent
}
Expand Down Expand Up @@ -448,7 +445,7 @@ private fun A11yService.useMatchRule() {
if (rightAppId != topActivityFlow.value.appId) {
// 从 锁屏,下拉通知栏 返回等情况, 应用不会发送事件, 但是系统组件会发送事件
val shizukuTop = safeGetTopActivity()
if (shizukuTop?.appId == rightAppId) {
if (shizukuTop != null) {
updateTopActivity(shizukuTop)
} else {
updateTopActivity(TopActivity(rightAppId))
Expand All @@ -465,11 +462,24 @@ private fun A11yService.useMatchRule() {
newQueryTask(a11yEvent)
}

val skipAppId = "com.android.systemui"
onA11yEvent { event ->
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && skipAppId == event.packageName.toString()) {
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && event.packageName == "com.android.systemui") {
return@onA11yEvent
}
// if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
// && event.packageName == defaultInputAppId
// && event.className == "android.inputmethodservice.SoftInputWindow"
// ) {
// return@onA11yEvent
// }
if (event.packageName == defaultInputAppId && topActivityFlow.value.appId != defaultInputAppId) {
if (event.className == "android.inputmethodservice.SoftInputWindow") {
return@onA11yEvent
}
if (event.recordCount == 0 && event.action == 0 && !event.isFullScreen) {
return@onA11yEvent
}
}
// AccessibilityEvent 的 clear 方法会在后续时间被 某些系统 调用导致内部数据丢失, 导致异步子线程获取到的数据不一致
val a11yEvent = event.toA11yEvent() ?: return@onA11yEvent
if (a11yEvent.type == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/kotlin/li/songe/gkd/service/A11yState.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package li.songe.gkd.service

import android.content.ComponentName
import android.provider.Settings
import com.blankj.utilcode.util.LogUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -17,6 +19,7 @@ import li.songe.gkd.data.ResolvedRule
import li.songe.gkd.data.SubsConfig
import li.songe.gkd.db.DbSet
import li.songe.gkd.isActivityVisible
import li.songe.gkd.shizuku.activityTaskManagerFlow
import li.songe.gkd.util.RuleSummary
import li.songe.gkd.util.actionCountFlow
import li.songe.gkd.util.getDefaultLauncherActivity
Expand Down Expand Up @@ -44,6 +47,10 @@ private val activityLogMutex by lazy { Mutex() }
private var activityLogCount = 0
private var lastActivityChangeTime = 0L
fun updateTopActivity(topActivity: TopActivity) {
if (topActivity.activityId == null && activityTaskManagerFlow.value != null && topActivity.appId == launcherAppId) {
// 无障碍 appId 改变速度慢于系统 activity 栈变化
return
}
val isSameActivity = topActivityFlow.value.sameAs(topActivity)
if (isSameActivity) {
if (topActivityFlow.value.number == topActivity.number) {
Expand Down Expand Up @@ -195,6 +202,15 @@ fun updateLauncherAppId() {
launcherActivity = app.packageManager.getDefaultLauncherActivity()
}

var defaultInputAppId = ""
fun updateDefaultInputAppId() {
Settings.Secure.getString(app.contentResolver, Settings.Secure.DEFAULT_INPUT_METHOD)?.let {
ComponentName.unflattenFromString(it)?.let { comp ->
defaultInputAppId = comp.packageName
}
}
}

val clickLogMutex by lazy { Mutex() }
suspend fun insertClickLog(rule: ResolvedRule) {
val ctime = System.currentTimeMillis()
Expand Down
47 changes: 40 additions & 7 deletions app/src/main/kotlin/li/songe/gkd/shizuku/ActivityTaskManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@ import android.app.IActivityTaskManager
import android.view.Display
import com.blankj.utilcode.util.LogUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import li.songe.gkd.appScope
import li.songe.gkd.data.DeviceInfo
import li.songe.gkd.permission.shizukuOkState
import li.songe.gkd.service.A11yService
import li.songe.gkd.service.TopActivity
import li.songe.gkd.service.topActivityFlow
import li.songe.gkd.service.updateTopActivity
import li.songe.gkd.util.launchTry
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.toast
import rikka.shizuku.ShizukuBinderWrapper
Expand Down Expand Up @@ -66,8 +70,16 @@ private fun IActivityTaskManager.compatGetTasks(maxNum: Int = 1): List<ActivityM
// https://github.com/gkd-kit/gkd/issues/44
// fix java.lang.ClassNotFoundException:Didn't find class "android.app.IActivityTaskManager" on path: DexPathList
interface SafeActivityTaskManager {
val value: Any
fun compatGetTasks(maxNum: Int): List<ActivityManager.RunningTaskInfo>
fun compatGetTasks(): List<ActivityManager.RunningTaskInfo>
fun registerTaskStackListener(listener: TaskListener)
fun unregisterTaskStackListener(listener: TaskListener)

fun getTopActivity(): TopActivity? {
val top = compatGetTasks().firstOrNull()?.topActivity ?: return null
return TopActivity(appId = top.packageName, activityId = top.className)
}
}

private fun newActivityTaskManager(): SafeActivityTaskManager? {
Expand All @@ -78,8 +90,16 @@ private fun newActivityTaskManager(): SafeActivityTaskManager? {
}
val manager = service.let(::ShizukuBinderWrapper).let(IActivityTaskManager.Stub::asInterface)
return object : SafeActivityTaskManager {
override val value = manager
override fun compatGetTasks(maxNum: Int) = manager.compatGetTasks(maxNum)
override fun compatGetTasks() = manager.compatGetTasks()
override fun registerTaskStackListener(listener: TaskListener) {
manager.registerTaskStackListener(listener)
}

override fun unregisterTaskStackListener(listener: TaskListener) {
manager.unregisterTaskStackListener(listener)
}
}
}

Expand All @@ -89,19 +109,34 @@ private val shizukuActivityUsedFlow by lazy {
}.stateIn(appScope, SharingStarted.Eagerly, false)
}

private val activityTaskManagerFlow by lazy<StateFlow<SafeActivityTaskManager?>> {
private val taskListener by lazy {
TaskListener(onStackChanged = {
safeGetTopActivity()?.let {
appScope.launchTry(A11yService.eventThread) {
delay(200)
if (topActivityFlow.value != it) {
updateTopActivity(it)
}
}
}
})
}

val activityTaskManagerFlow by lazy<StateFlow<SafeActivityTaskManager?>> {
val stateFlow = MutableStateFlow<SafeActivityTaskManager?>(null)
appScope.launch(Dispatchers.IO) {
appScope.launchTry(Dispatchers.IO) {
shizukuActivityUsedFlow.collect {
stateFlow.value?.unregisterTaskStackListener(taskListener)
stateFlow.value = if (it) newActivityTaskManager() else null
stateFlow.value?.registerTaskStackListener(taskListener)
}
}
stateFlow
}

fun shizukuCheckActivity(): Boolean {
return (try {
newActivityTaskManager()?.compatGetTasks(1)?.isNotEmpty() == true
newActivityTaskManager()?.compatGetTasks()?.isNotEmpty() == true
} catch (e: Throwable) {
e.printStackTrace()
false
Expand All @@ -111,9 +146,7 @@ fun shizukuCheckActivity(): Boolean {
fun safeGetTopActivity(): TopActivity? {
if (!shizukuActivityUsedFlow.value) return null
try {
val taskManager = activityTaskManagerFlow.value ?: return null
val top = taskManager.compatGetTasks(1).lastOrNull()?.topActivity ?: return null
return TopActivity(appId = top.packageName, activityId = top.className)
return activityTaskManagerFlow.value?.getTopActivity()
} catch (e: Throwable) {
e.printStackTrace()
return null
Expand Down
Loading

0 comments on commit 2e26326

Please sign in to comment.