diff --git a/app/src/main/java/li/songe/gkd/data/ComplexSnapshot.kt b/app/src/main/java/li/songe/gkd/data/ComplexSnapshot.kt index e3e6a7a..1d62f05 100644 --- a/app/src/main/java/li/songe/gkd/data/ComplexSnapshot.kt +++ b/app/src/main/java/li/songe/gkd/data/ComplexSnapshot.kt @@ -4,7 +4,7 @@ import com.blankj.utilcode.util.AppUtils import com.blankj.utilcode.util.ScreenUtils import kotlinx.serialization.Serializable import li.songe.gkd.service.GkdAbService -import li.songe.gkd.service.activityIdFlow +import li.songe.gkd.service.topActivityFlow @Serializable data class ComplexSnapshot( @@ -28,7 +28,7 @@ data class ComplexSnapshot( fun createComplexSnapshot(): ComplexSnapshot { val currentAbNode = GkdAbService.currentAbNode val appId = currentAbNode?.packageName?.toString() - val currentActivityId = activityIdFlow.value + val currentActivityId = topActivityFlow.value?.activityId val appInfo = if (appId == null) null else AppUtils.getAppInfo(appId) return ComplexSnapshot( diff --git a/app/src/main/java/li/songe/gkd/data/NodeInfo.kt b/app/src/main/java/li/songe/gkd/data/NodeInfo.kt index a1bed5e..9ac07e5 100644 --- a/app/src/main/java/li/songe/gkd/data/NodeInfo.kt +++ b/app/src/main/java/li/songe/gkd/data/NodeInfo.kt @@ -4,8 +4,8 @@ import android.view.accessibility.AccessibilityNodeInfo import com.blankj.utilcode.util.LogUtils import com.blankj.utilcode.util.ToastUtils import kotlinx.serialization.Serializable -import li.songe.gkd.service.activityIdFlow import li.songe.gkd.service.forEachIndexed +import li.songe.gkd.service.topActivityFlow import java.util.ArrayDeque @Serializable @@ -45,7 +45,7 @@ data class NodeInfo( // Failed to allocate a 245237304 byte allocation with 100663296 free bytes and 106MB until OOM ToastUtils.showShort("节点数量至多保留50000,丢弃后续节点") LogUtils.w( - nodeInfo.packageName, activityIdFlow.value, "节点数量过多" + nodeInfo.packageName, topActivityFlow.value?.activityId, "节点数量过多" ) break } diff --git a/app/src/main/java/li/songe/gkd/service/AbState.kt b/app/src/main/java/li/songe/gkd/service/AbState.kt index 31ce42e..5844af3 100644 --- a/app/src/main/java/li/songe/gkd/service/AbState.kt +++ b/app/src/main/java/li/songe/gkd/service/AbState.kt @@ -8,18 +8,27 @@ import li.songe.gkd.appScope import li.songe.gkd.data.Rule import li.songe.gkd.util.appIdToRulesFlow -val activityIdFlow by lazy { - MutableStateFlow(null) +data class TopActivity( + val appId: String, + val activityId: String? = null, +) + +val topActivityFlow by lazy { + MutableStateFlow(null) } -val appIdFlow by lazy { - MutableStateFlow(null) -} +//val activityIdFlow by lazy { +// MutableStateFlow(null) +//} +// +//val appIdFlow by lazy { +// MutableStateFlow(null) +//} val currentRulesFlow by lazy { - combine(appIdToRulesFlow, appIdFlow, activityIdFlow) { appIdToRules, appId, activityId -> - (appIdToRules[appId] ?: emptyList()).filter { rule -> - rule.matchActivityId(activityId) + combine(appIdToRulesFlow, topActivityFlow) { appIdToRules, topActivity -> + (appIdToRules[topActivity?.appId] ?: emptyList()).filter { rule -> + rule.matchActivityId(topActivity?.activityId) } }.stateIn(appScope, SharingStarted.Eagerly, emptyList()) } diff --git a/app/src/main/java/li/songe/gkd/service/GkdAbService.kt b/app/src/main/java/li/songe/gkd/service/GkdAbService.kt index deb1432..da0f490 100644 --- a/app/src/main/java/li/songe/gkd/service/GkdAbService.kt +++ b/app/src/main/java/li/songe/gkd/service/GkdAbService.kt @@ -1,8 +1,9 @@ package li.songe.gkd.service +import android.content.ComponentName +import android.content.pm.PackageManager import android.graphics.Bitmap import android.os.Build -import android.util.Log import android.view.Display import android.view.KeyEvent import android.view.accessibility.AccessibilityEvent @@ -15,8 +16,10 @@ import com.blankj.utilcode.util.ToastUtils import io.ktor.client.request.get import io.ktor.client.statement.bodyAsText import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.delay import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import li.songe.gkd.composition.CompositionAbService @@ -42,6 +45,7 @@ import li.songe.selector.Selector import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine +@OptIn(FlowPreview::class) class GkdAbService : CompositionAbService({ useLifeCycleLog() @@ -52,8 +56,7 @@ class GkdAbService : CompositionAbService({ service = context onDestroy { service = null - appIdFlow.value = null - activityIdFlow.value = null + topActivityFlow.value = null } ManageService.start(context) @@ -61,9 +64,32 @@ class GkdAbService : CompositionAbService({ ManageService.stop(context) } + + var isScreenLock = false + scope.launchWhile(IO) { + isScreenLock = ScreenUtils.isScreenLock() + delay(1000) + } + + var okShizuku = false + scope.launchWhileTry(interval = 500) { // 根据 shizuku 获取 activityId, 准确 + if (isScreenLock) return@launchWhileTry + if (shizukuIsSafeOK()) { + val topActivity = + activityTaskManager.getTasks(1, false, true)?.firstOrNull()?.topActivity + if (topActivity != null) { + topActivityFlow.value = TopActivity(topActivity.packageName, topActivity.className) + okShizuku = true + return@launchWhileTry + } + } + okShizuku = false + } + var lastKeyEventTime = -1L onKeyEvent { event -> // 当按下音量键时捕获快照 - if (storeFlow.value.captureVolumeKey && (event?.keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || event?.keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { + val keyCode = event?.keyCode ?: return@onKeyEvent + if (storeFlow.value.captureVolumeKey && (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { val et = System.currentTimeMillis() if (et - lastKeyEventTime > 3000) { lastKeyEventTime = et @@ -81,47 +107,38 @@ class GkdAbService : CompositionAbService({ onServiceConnected { serviceConnected = true } onInterrupt { serviceConnected = false } - onAccessibilityEvent { event -> // 根据事件获取 activityId, 概率不准确 - val appId = safeActiveWindow?.packageName?.toString() ?: return@onAccessibilityEvent - appIdFlow.value = appId - when (event?.eventType) { - AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, AccessibilityEvent.TYPE_WINDOWS_CHANGED -> { - val activityId = event.className?.toString() ?: return@onAccessibilityEvent - if (activityId == "com.miui.home.launcher.Launcher") { // 小米桌面 bug - if (appId != "com.miui.home") { - return@onAccessibilityEvent - } - } + fun isActivity(appId: String, activityId: String): Boolean { + return try { + packageManager.getActivityInfo(ComponentName(appId, activityId), 0) + } catch (e: PackageManager.NameNotFoundException) { + null + } != null + } - if (activityId.startsWith("android.") || activityId.startsWith("androidx.") || activityId.startsWith( - "com.android." - ) + onAccessibilityEvent { event -> // 根据事件获取 activityId, 概率不准确 + if (okShizuku || event == null) return@onAccessibilityEvent + + + when (event.eventType) { + AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED -> { + val rightAppId = safeActiveWindow?.packageName ?: return@onAccessibilityEvent + val newAppId = event.packageName?.toString() ?: return@onAccessibilityEvent + val newActivityId = event.className?.toString() ?: return@onAccessibilityEvent + if (rightAppId.contentEquals(newAppId) && (newActivityId.startsWith(newAppId) || isActivity( + newAppId, newActivityId + )) ) { - return@onAccessibilityEvent + topActivityFlow.value = TopActivity(newAppId, newActivityId) + } else { + topActivityFlow.value = + TopActivity(rightAppId.toString(), topActivityFlow.value?.activityId) } - activityIdFlow.value = activityId } else -> {} } } - scope.launchWhileTry(interval = 500) { // 根据 shizuku 获取 activityId, 准确 - if (shizukuIsSafeOK()) { - val topActivity = - activityTaskManager.getTasks(1, false, true)?.firstOrNull()?.topActivity - if (topActivity != null) { - appIdFlow.value = topActivity.packageName - activityIdFlow.value = topActivity.className - } - } - } - - var isScreenLock = false - scope.launchWhile(IO) { - isScreenLock = ScreenUtils.isScreenLock() - delay(1000) - } var lastToastTime = -1L @@ -157,8 +174,8 @@ class GkdAbService : CompositionAbService({ ) scope.launchTry(IO) { val clickLog = ClickLog( - appId = appIdFlow.value, - activityId = activityIdFlow.value, + appId = topActivityFlow.value?.appId, + activityId = topActivityFlow.value?.activityId, subsId = rule.subsItem.id, groupKey = rule.group.key, ruleIndex = rule.index, @@ -173,7 +190,6 @@ class GkdAbService : CompositionAbService({ } scope.launchTry { - storeFlow.map { s -> s.updateSubsInterval }.collect { updateSubsInterval -> if (updateSubsInterval <= 0) { // clear @@ -187,7 +203,7 @@ class GkdAbService : CompositionAbService({ var lastUpdateSubsTime = System.currentTimeMillis() scope.launchWhile(IO) { // 自动从网络更新订阅文件 delay(60_000) - if (!NetworkUtils.isAvailable() || storeFlow.value.updateSubsInterval <= 0) return@launchWhile + if (!NetworkUtils.isAvailable() || storeFlow.value.updateSubsInterval <= 0 || isScreenLock) return@launchWhile if (System.currentTimeMillis() - lastUpdateSubsTime < storeFlow.value.updateSubsInterval.coerceAtLeast( 60 * 60_000 ) @@ -198,7 +214,7 @@ class GkdAbService : CompositionAbService({ if (subsItem.updateUrl == null) return@forEach try { val newSubsRaw = - SubscriptionRaw.parse5(Singleton.client.get(subsItem.updateUrl).bodyAsText()) + SubscriptionRaw.parse(Singleton.client.get(subsItem.updateUrl).bodyAsText()) if (newSubsRaw.id != subsItem.id) { return@forEach } @@ -225,14 +241,20 @@ class GkdAbService : CompositionAbService({ } scope.launch { - combine(appIdFlow, activityIdFlow, currentRulesFlow) { appId, activityId, currentRules -> - appId to activityId to currentRules - }.collect { - val (appId, activityId) = it.first - val currentRules = it.second - Log.d("GkdAbService", "appId:$appId, activityId:$activityId") - if (currentRules.isNotEmpty()) { - LogUtils.d(*currentRules.map { r -> r.rule.matches }.toTypedArray()) + combine(topActivityFlow, currentRulesFlow) { topActivity, currentRules -> + topActivity to currentRules + }.debounce(100).collect { (topActivity, currentRules) -> + if (storeFlow.value.enableService) { + LogUtils.d( + topActivity?.appId, + topActivity?.activityId, + *currentRules.map { r -> r.rule.matches }.toTypedArray() + ) + } else { + LogUtils.d( + topActivity?.appId, + topActivity?.activityId, + ) } } }