Skip to content

Commit 724a47c

Browse files
committed
refactor: shizuku
1 parent d0c4b12 commit 724a47c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+788
-1131
lines changed

app/src/main/kotlin/li/songe/gkd/App.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import kotlinx.coroutines.MainScope
1414
import kotlinx.serialization.Serializable
1515
import li.songe.gkd.data.selfAppInfo
1616
import li.songe.gkd.notif.initChannel
17-
import li.songe.gkd.service.StatusService
1817
import li.songe.gkd.service.clearHttpSubs
1918
import li.songe.gkd.shizuku.initShizuku
2019
import li.songe.gkd.store.initStore
@@ -126,6 +125,5 @@ class App : Application() {
126125
initSubsState()
127126
clearHttpSubs()
128127
syncFixState()
129-
StatusService.autoStart()
130128
}
131129
}

app/src/main/kotlin/li/songe/gkd/MainActivity.kt

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ class MainActivity : ComponentActivity() {
175175
intent = null
176176
}
177177
watchKeyboardVisible()
178+
StatusService.autoStart()
178179
setContent {
179180
val navController = rememberNavController()
180181
mainVm.updateNavController(navController)
@@ -227,9 +228,9 @@ class MainActivity : ComponentActivity() {
227228
super.onResume()
228229
if (isFirstResume && startTime - app.startTime < 2000) {
229230
isFirstResume = false
230-
return
231+
} else {
232+
syncFixState()
231233
}
232-
syncFixState()
233234
}
234235

235236
override fun onStop() {
@@ -291,18 +292,10 @@ private val syncStateMutex = Mutex()
291292
fun syncFixState() {
292293
appScope.launchTry(Dispatchers.IO) {
293294
syncStateMutex.withLock {
294-
// 每次切换页面更新记录桌面 appId
295295
updateLauncherAppId()
296-
297296
updateImeAppId()
298-
299-
// 由于某些机型的进程存在 安装缓存/崩溃缓存 导致服务状态可能不正确, 在此保证每次界面切换都能重新刷新状态
300297
updateServiceRunning()
301-
302-
// 用户在系统权限设置中切换权限后再切换回应用时能及时更新状态
303298
updatePermissionState()
304-
305-
// 自动重启无障碍服务
306299
fixRestartService()
307300
}
308301
}

app/src/main/kotlin/li/songe/gkd/MainViewModel.kt

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import com.ramcosta.composedestinations.spec.Direction
1818
import io.ktor.client.request.get
1919
import io.ktor.client.statement.bodyAsText
2020
import kotlinx.coroutines.Dispatchers
21+
import kotlinx.coroutines.delay
2122
import kotlinx.coroutines.flow.MutableStateFlow
2223
import kotlinx.coroutines.flow.update
2324
import kotlinx.coroutines.launch
@@ -29,8 +30,10 @@ import li.songe.gkd.data.importData
2930
import li.songe.gkd.db.DbSet
3031
import li.songe.gkd.permission.AuthReason
3132
import li.songe.gkd.permission.shizukuOkState
32-
import li.songe.gkd.shizuku.execCommandForResult
33+
import li.songe.gkd.shizuku.shizukuContextFlow
34+
import li.songe.gkd.shizuku.updateBinderMutex
3335
import li.songe.gkd.store.createTextFlow
36+
import li.songe.gkd.store.storeFlow
3437
import li.songe.gkd.ui.component.AlertDialogOptions
3538
import li.songe.gkd.ui.component.InputSubsLinkOption
3639
import li.songe.gkd.ui.component.RuleGroupState
@@ -95,7 +98,7 @@ class MainViewModel : ViewModel() {
9598
oldItem: SubsItem? = null,
9699
) = viewModelScope.launchTry(Dispatchers.IO) {
97100
if (updateSubsMutex.mutex.isLocked) return@launchTry
98-
updateSubsMutex.withLock {
101+
updateSubsMutex.withStateLock {
99102
val subItems = subsItemsFlow.value
100103
val text = try {
101104
client.get(url).bodyAsText()
@@ -256,24 +259,33 @@ class MainViewModel : ViewModel() {
256259
)
257260
}
258261

262+
263+
fun requestShizuku() = try {
264+
Shizuku.requestPermission(Activity.RESULT_OK)
265+
} catch (e: Throwable) {
266+
shizukuErrorFlow.value = e
267+
}
268+
259269
suspend fun grantPermissionByShizuku(command: String) {
260-
if (shizukuOkState.stateFlow.value) {
261-
try {
262-
execCommandForResult(command)
263-
return
264-
} catch (e: Exception) {
265-
toast("运行失败:${e.message}")
266-
LogUtils.d(e)
267-
}
268-
} else {
269-
try {
270-
Shizuku.requestPermission(Activity.RESULT_OK)
271-
} catch (e: Throwable) {
272-
LogUtils.d("Shizuku授权错误", e.message)
273-
shizukuErrorFlow.value = e
270+
if (updateBinderMutex.mutex.isLocked) {
271+
toast("正在连接 Shizuku 服务,请稍后")
272+
stopCoroutine()
273+
}
274+
if (!shizukuOkState.stateFlow.value) {
275+
requestShizuku()
276+
stopCoroutine()
277+
}
278+
if (!storeFlow.value.enableShizuku) {
279+
storeFlow.update { it.copy(enableShizuku = true) }
280+
delay(500)
281+
while (updateBinderMutex.mutex.isLocked) {
282+
delay(100)
274283
}
275284
}
276-
stopCoroutine()
285+
val service = shizukuContextFlow.value.serviceWrapper ?: stopCoroutine()
286+
if (!service.execCommandForResult(command).ok) {
287+
stopCoroutine()
288+
}
277289
}
278290

279291
val a11yServiceEnabledFlow = useA11yServiceEnabledFlow()

app/src/main/kotlin/li/songe/gkd/a11y/A11yFeat.kt

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import kotlinx.coroutines.launch
1919
import li.songe.gkd.appScope
2020
import li.songe.gkd.permission.shizukuOkState
2121
import li.songe.gkd.service.A11yService
22-
import li.songe.gkd.store.shizukuStoreFlow
22+
import li.songe.gkd.service.StatusService
2323
import li.songe.gkd.store.storeFlow
2424
import li.songe.gkd.util.SnapshotExt
2525
import li.songe.gkd.util.UpdateTimeOption
@@ -35,6 +35,7 @@ fun onA11yFeatInit() = service.run {
3535
useCaptureVolume()
3636
useRuleChangedLog()
3737
onA11yEvent { onA11yFeatEvent(it) }
38+
onCreated { StatusService.autoStart() }
3839
}
3940

4041
private fun A11yService.useAttachState() {
@@ -58,7 +59,7 @@ private var lastCheckShizukuTime = 0L
5859
context(event: AccessibilityEvent)
5960
private fun watchCheckShizukuState() {
6061
// 借助无障碍轮询校验 shizuku 权限, 因为 shizuku 可能无故被关闭
61-
if (shizukuStoreFlow.value.enableShizukuAnyFeat) {
62+
if (storeFlow.value.enableShizuku) {
6263
val t = System.currentTimeMillis()
6364
if (t - lastCheckShizukuTime > 60 * 60_000L) {
6465
lastCheckShizukuTime = t
@@ -121,21 +122,8 @@ private fun A11yService.useAliveOverlayView() {
121122
}
122123
wm.addView(tempView, lp)
123124
}
124-
125-
onA11yConnected {
126-
scope.launchTry(Dispatchers.Main) {
127-
storeFlow.mapState(scope) { s -> s.enableAbFloatWindow }.collect {
128-
if (it) {
129-
addA11View()
130-
} else {
131-
removeA11View()
132-
}
133-
}
134-
}
135-
}
136-
onDestroyed {
137-
removeA11View()
138-
}
125+
onA11yConnected { addA11View() }
126+
onDestroyed { removeA11View() }
139127
}
140128

141129
private const val volumeChangedAction = "android.media.VOLUME_CHANGED_ACTION"

app/src/main/kotlin/li/songe/gkd/a11y/A11yState.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import li.songe.gkd.data.RuleStatus
2626
import li.songe.gkd.db.DbSet
2727
import li.songe.gkd.store.actionCountFlow
2828
import li.songe.gkd.store.storeFlow
29+
import li.songe.gkd.util.PKG_FLAGS
2930
import li.songe.gkd.util.RuleSummary
3031
import li.songe.gkd.util.launchTry
3132
import li.songe.gkd.util.ruleSummaryFlow
@@ -71,7 +72,7 @@ private object ActivityCache : LruCache<Pair<String, String>, Boolean>(256) {
7172
override fun create(key: Pair<String, String>): Boolean = try {
7273
app.packageManager.getActivityInfo(
7374
ComponentName(key.first, key.second),
74-
PackageManager.MATCH_UNINSTALLED_PACKAGES
75+
PKG_FLAGS
7576
)
7677
true
7778
} catch (_: Exception) {

app/src/main/kotlin/li/songe/gkd/data/AppInfo.kt

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,65 +4,60 @@ import android.content.Intent
44
import android.content.pm.ApplicationInfo
55
import android.content.pm.PackageInfo
66
import android.content.pm.PackageManager
7-
import android.graphics.drawable.Drawable
87
import android.os.Build
98
import kotlinx.serialization.Serializable
10-
import kotlinx.serialization.Transient
119
import li.songe.gkd.app
1210

1311
@Serializable
1412
data class AppInfo(
1513
val id: String,
1614
val name: String,
17-
@Transient
18-
val icon: Drawable? = null,
1915
val versionCode: Int,
2016
val versionName: String?,
2117
val isSystem: Boolean,
2218
val mtime: Long,
2319
val hidden: Boolean,
24-
val userId: Int? = null, // null -> current user
25-
)
20+
val userId: Int? = null,
21+
) {
22+
override fun equals(other: Any?): Boolean {
23+
if (other !is AppInfo) return false
24+
return id == other.id && mtime == other.mtime
25+
}
26+
27+
override fun hashCode(): Int {
28+
var result = id.hashCode()
29+
result = 31 * result + mtime.hashCode()
30+
return result
31+
}
32+
}
2633

2734
val selfAppInfo by lazy {
28-
app.packageManager.getPackageInfo(
29-
app.packageName,
30-
PackageManager.MATCH_UNINSTALLED_PACKAGES
31-
).toAppInfo()
35+
app.packageManager.getPackageInfo(app.packageName, 0).toAppInfo()
3236
}
3337

34-
fun Drawable.safeGet(): Drawable? {
35-
return if (intrinsicHeight <= 0 || intrinsicWidth <= 0) {
36-
// https://github.com/gkd-kit/gkd/issues/924
37-
null
38+
val PackageInfo.compatVersionCode: Int
39+
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
40+
longVersionCode.toInt()
3841
} else {
39-
this
42+
@Suppress("DEPRECATION")
43+
versionCode
4044
}
41-
}
4245

43-
/**
44-
* 平均单次调用时间 11ms
45-
*/
4646
fun PackageInfo.toAppInfo(
4747
userId: Int? = null,
4848
hidden: Boolean? = null,
4949
): AppInfo {
5050
return AppInfo(
5151
id = packageName,
52-
versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
53-
longVersionCode.toInt()
54-
} else {
55-
@Suppress("DEPRECATION")
56-
versionCode
57-
},
52+
versionCode = compatVersionCode,
5853
versionName = versionName,
5954
mtime = lastUpdateTime,
6055
isSystem = applicationInfo?.let { it.flags and ApplicationInfo.FLAG_SYSTEM != 0 } ?: false,
6156
name = applicationInfo?.run { loadLabel(app.packageManager).toString() } ?: packageName,
62-
icon = applicationInfo?.loadIcon(app.packageManager)?.safeGet(),
6357
userId = userId,
6458
hidden = hidden ?: app.packageManager.queryIntentActivities(
65-
Intent(Intent.ACTION_MAIN).setPackage(packageName).addCategory(Intent.CATEGORY_LAUNCHER),
59+
Intent(Intent.ACTION_MAIN).setPackage(packageName)
60+
.addCategory(Intent.CATEGORY_LAUNCHER),
6661
PackageManager.MATCH_DISABLED_COMPONENTS
6762
).isEmpty(),
6863
)

app/src/main/kotlin/li/songe/gkd/data/ComplexSnapshot.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package li.songe.gkd.data
22

33
import kotlinx.serialization.Serializable
4-
import li.songe.gkd.util.getPkgInfo
4+
import li.songe.gkd.util.appInfoCacheFlow
55

66
@Serializable
77
data class ComplexSnapshot(
@@ -11,7 +11,7 @@ data class ComplexSnapshot(
1111
override val screenHeight: Int,
1212
override val screenWidth: Int,
1313
override val isLandscape: Boolean,
14-
val appInfo: AppInfo? = getPkgInfo(appId)?.toAppInfo(),
14+
val appInfo: AppInfo? = appInfoCacheFlow.value[appId],
1515
val gkdAppInfo: AppInfo? = selfAppInfo,
1616
val device: DeviceInfo = DeviceInfo.instance,
1717
val nodes: List<NodeInfo>,

app/src/main/kotlin/li/songe/gkd/data/GkdAction.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ import android.view.accessibility.AccessibilityNodeInfo
99
import com.blankj.utilcode.util.ScreenUtils
1010
import kotlinx.serialization.Serializable
1111
import li.songe.gkd.service.A11yService
12-
import li.songe.gkd.shizuku.safeLongTap
13-
import li.songe.gkd.shizuku.safeTap
12+
import li.songe.gkd.shizuku.shizukuContextFlow
1413

1514
@Serializable
1615
data class GkdAction(
@@ -62,7 +61,7 @@ sealed class ActionPerformer(val action: String) {
6261
return ActionResult(
6362
action = action,
6463
result = if (0 <= x && 0 <= y && x <= ScreenUtils.getScreenWidth() && y <= ScreenUtils.getScreenHeight()) {
65-
val result = safeTap(x, y)
64+
val result = shizukuContextFlow.value.serviceWrapper?.safeTap(x, y)
6665
if (result != null) {
6766
return ActionResult(action, result, true, position = x to y)
6867
}
@@ -121,14 +120,13 @@ sealed class ActionPerformer(val action: String) {
121120
val p = position?.calc(rect)
122121
val x = p?.first ?: ((rect.right + rect.left) / 2f)
123122
val y = p?.second ?: ((rect.bottom + rect.top) / 2f)
124-
// 500 https://cs.android.com/android/platform/superproject/+/android-8.1.0_r81:frameworks/base/core/java/android/view/ViewConfiguration.java;l=65
125-
// 400 https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/view/ViewConfiguration.java;drc=8b948e548b782592ae280a3cd9a91798afe6df9d;l=82
126123
// 某些系统的 ViewConfiguration.getLongPressTimeout() 返回 300 , 这将导致触发普通的 click 事件
127124
val longClickDuration = 500L
128125
return ActionResult(
129126
action = action,
130127
result = if (0 <= x && 0 <= y && x <= ScreenUtils.getScreenWidth() && y <= ScreenUtils.getScreenHeight()) {
131-
val result = safeLongTap(x, y, longClickDuration)
128+
val result =
129+
shizukuContextFlow.value.serviceWrapper?.safeTap(x, y, longClickDuration)
132130
if (result != null) {
133131
return ActionResult(action, result, true, position = x to y)
134132
}

app/src/main/kotlin/li/songe/gkd/permission/PermissionState.kt

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,16 @@ import com.hjq.permissions.permission.PermissionLists
1212
import com.hjq.permissions.permission.base.IPermission
1313
import com.ramcosta.composedestinations.generated.destinations.AppOpsAllowPageDestination
1414
import kotlinx.coroutines.CompletableDeferred
15-
import kotlinx.coroutines.Dispatchers
1615
import kotlinx.coroutines.flow.MutableStateFlow
1716
import kotlinx.coroutines.flow.updateAndGet
1817
import li.songe.gkd.MainActivity
1918
import li.songe.gkd.app
20-
import li.songe.gkd.appScope
2119
import li.songe.gkd.isActivityVisible
2220
import li.songe.gkd.shizuku.shizukuCheckGranted
2321
import li.songe.gkd.ui.share.LocalMainViewModel
24-
import li.songe.gkd.util.forceUpdateAppList
25-
import li.songe.gkd.util.initOrResetAppInfoCache
26-
import li.songe.gkd.util.launchTry
2722
import li.songe.gkd.util.mayQueryPkgNoAccessFlow
2823
import li.songe.gkd.util.toast
24+
import li.songe.gkd.util.updateAllAppInfo
2925
import li.songe.gkd.util.updateAppMutex
3026

3127
class PermissionState(
@@ -222,11 +218,7 @@ val shizukuOkState by lazy {
222218
fun updatePermissionState() {
223219
val stateChanged = canQueryPkgState.stateFlow.value != canQueryPkgState.updateAndGet()
224220
if (!updateAppMutex.mutex.isLocked && (stateChanged || mayQueryPkgNoAccessFlow.value)) {
225-
appScope.launchTry(Dispatchers.IO) {
226-
initOrResetAppInfoCache()
227-
}
228-
} else {
229-
forceUpdateAppList()
221+
updateAllAppInfo()
230222
}
231223
arrayOf(
232224
notificationState,

app/src/main/kotlin/li/songe/gkd/service/BaseTileService.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ abstract class BaseTileService : TileService(), OnTileLife {
2424
}
2525

2626
init {
27+
onTileClicked { StatusService.autoStart() }
2728
scope.launch {
2829
combine(
2930
activeFlow,

0 commit comments

Comments
 (0)