From 3b78f002e0c4264e9dd1cb6d0201faed445b6327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E5=88=BA=E8=9E=88?= Date: Thu, 17 Jul 2025 19:46:51 +0800 Subject: [PATCH] perf: check shizuku state --- app/src/main/kotlin/li/songe/gkd/App.kt | 5 + .../main/kotlin/li/songe/gkd/MainActivity.kt | 120 +++++++++++------- .../main/kotlin/li/songe/gkd/MainViewModel.kt | 27 +++- .../songe/gkd/permission/PermissionState.kt | 2 + .../songe/gkd/shizuku/ActivityTaskManager.kt | 4 +- .../kotlin/li/songe/gkd/shizuku/ShizukuApi.kt | 3 +- .../li/songe/gkd/shizuku/UserService.kt | 13 +- .../kotlin/li/songe/gkd/ui/AdvancedPage.kt | 78 ++++++------ .../kotlin/li/songe/gkd/ui/AppOpsAllowPage.kt | 8 +- .../kotlin/li/songe/gkd/ui/AuthA11yPage.kt | 7 +- .../kotlin/li/songe/gkd/util/CoroutineExt.kt | 8 ++ 11 files changed, 170 insertions(+), 105 deletions(-) diff --git a/app/src/main/kotlin/li/songe/gkd/App.kt b/app/src/main/kotlin/li/songe/gkd/App.kt index 13d9518c..b12f349d 100644 --- a/app/src/main/kotlin/li/songe/gkd/App.kt +++ b/app/src/main/kotlin/li/songe/gkd/App.kt @@ -32,6 +32,7 @@ import li.songe.gkd.util.initSubsState import li.songe.gkd.util.json import li.songe.gkd.util.launchTry import li.songe.gkd.util.setReactiveToastStyle +import li.songe.gkd.util.toast import li.songe.json5.encodeToJson5String import org.lsposed.hiddenapibypass.HiddenApiBypass import rikka.shizuku.Shizuku @@ -123,12 +124,16 @@ class App : Application() { } ) Shizuku.addBinderReceivedListener { + LogUtils.d("Shizuku.addBinderReceivedListener") appScope.launchTry(Dispatchers.IO) { shizukuOkState.updateAndGet() } } Shizuku.addBinderDeadListener { + LogUtils.d("Shizuku.addBinderDeadListener") shizukuOkState.stateFlow.value = false + val prefix = if (isActivityVisible()) "" else "${META.appName}: " + toast("${prefix}已断开 Shizuku 服务") } appScope.launchTry(Dispatchers.IO) { initStore() diff --git a/app/src/main/kotlin/li/songe/gkd/MainActivity.kt b/app/src/main/kotlin/li/songe/gkd/MainActivity.kt index 497d4287..81ffef85 100644 --- a/app/src/main/kotlin/li/songe/gkd/MainActivity.kt +++ b/app/src/main/kotlin/li/songe/gkd/MainActivity.kt @@ -10,7 +10,24 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels import androidx.compose.animation.core.AnimationConstants +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.ContentCopy import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -18,10 +35,14 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.lifecycleScope import androidx.navigation.compose.rememberNavController -import com.blankj.utilcode.util.LogUtils import com.dylanc.activityresult.launcher.PickContentLauncher import com.dylanc.activityresult.launcher.StartActivityLauncher import com.ramcosta.composedestinations.DestinationsNavHost @@ -29,25 +50,21 @@ import com.ramcosta.composedestinations.generated.NavGraphs import com.ramcosta.composedestinations.generated.destinations.AuthA11YPageDestination import com.ramcosta.composedestinations.utils.currentDestinationAsState import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import kotlinx.coroutines.yield import li.songe.gkd.debug.FloatingService import li.songe.gkd.debug.HttpService import li.songe.gkd.debug.ScreenshotService import li.songe.gkd.permission.AuthDialog -import li.songe.gkd.permission.shizukuOkState 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.shizuku.execCommandForResult import li.songe.gkd.store.storeFlow import li.songe.gkd.ui.component.BuildDialog import li.songe.gkd.ui.component.ShareDataDialog @@ -61,15 +78,15 @@ import li.songe.gkd.util.EditGithubCookieDlg import li.songe.gkd.util.ShortUrlSet import li.songe.gkd.util.appInfoCacheFlow import li.songe.gkd.util.componentName +import li.songe.gkd.util.copyText import li.songe.gkd.util.fixSomeProblems import li.songe.gkd.util.launchTry import li.songe.gkd.util.map import li.songe.gkd.util.openApp import li.songe.gkd.util.openUri import li.songe.gkd.util.shizukuAppId +import li.songe.gkd.util.throttle import li.songe.gkd.util.toast -import rikka.shizuku.Shizuku -import kotlin.coroutines.coroutineContext import kotlin.reflect.KClass import kotlin.reflect.jvm.jvmName @@ -162,14 +179,6 @@ class MainActivity : ComponentActivity() { super.onBackPressed() } } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray, - deviceId: Int - ) { - } } private val activityVisibleFlow by lazy { MutableStateFlow(0) } @@ -227,34 +236,70 @@ fun syncFixState() { } @Composable -private fun ShizukuErrorDialog(stateFlow: MutableStateFlow) { +private fun ShizukuErrorDialog(stateFlow: MutableStateFlow) { val state = stateFlow.collectAsState().value - if (state) { + if (state != null) { + val errorText = remember { state.stackTraceToString() } val appInfoCache = appInfoCacheFlow.collectAsState().value val installed = appInfoCache.contains(shizukuAppId) AlertDialog( - onDismissRequest = { stateFlow.value = false }, + onDismissRequest = { stateFlow.value = null }, title = { Text(text = "授权错误") }, text = { - Text( - text = if (installed) { - "Shizuku 授权失败, 请检查是否运行" - } else { - "Shizuku 授权失败, 检测到 Shizuku 未安装, 请先下载后安装, 如果你是通过其它方式授权, 请忽略此提示自行查找原因" + Column { + Text( + text = if (installed) { + "Shizuku 授权失败, 请检查是否运行" + } else { + "Shizuku 授权失败, 检测到 Shizuku 未安装, 请先下载后安装, 如果你是通过其它方式授权, 请忽略此提示自行查找原因" + } + ) + Spacer(modifier = Modifier.height(8.dp)) + Box( + modifier = Modifier.fillMaxWidth() + ) { + SelectionContainer( + modifier = Modifier + .align(Alignment.TopStart) + .fillMaxWidth() + ) { + Text( + text = errorText, + modifier = Modifier + .clip(MaterialTheme.shapes.extraSmall) + .background(MaterialTheme.colorScheme.secondaryContainer) + .padding(8.dp) + .heightIn(max = 400.dp) + .verticalScroll(rememberScrollState()), + style = MaterialTheme.typography.bodySmall, + ) + } + Icon( + modifier = Modifier + .align(Alignment.TopEnd) + .clickable(onClick = throttle { + copyText(errorText) + }) + .padding(4.dp) + .size(20.dp), + imageVector = Icons.Outlined.ContentCopy, + contentDescription = null, + tint = MaterialTheme.colorScheme.tertiary.copy(alpha = 0.75f), + ) } - ) + } }, confirmButton = { if (installed) { TextButton(onClick = { - stateFlow.value = false + stateFlow.value = null openApp(shizukuAppId) }) { Text(text = "打开 Shizuku") } } else { TextButton(onClick = { - stateFlow.value = false + stateFlow.value = null openUri(ShortUrlSet.URL4) }) { Text(text = "去下载") @@ -262,7 +307,7 @@ private fun ShizukuErrorDialog(stateFlow: MutableStateFlow) { } }, dismissButton = { - TextButton(onClick = { stateFlow.value = false }) { + TextButton(onClick = { stateFlow.value = null }) { Text(text = "我知道了") } } @@ -321,24 +366,3 @@ fun AccessRestrictedSettingsDlg() { ) } } - -suspend fun MainActivity.grantPermissionByShizuku(command: String) { - if (shizukuOkState.stateFlow.value) { - try { - execCommandForResult(command) - return - } catch (e: Exception) { - toast("运行失败:${e.message}") - LogUtils.d(e) - } - } else { - try { - Shizuku.requestPermission(Activity.RESULT_OK) - } catch (e: Exception) { - LogUtils.d("Shizuku授权错误", e.message) - mainVm.shizukuErrorFlow.value = true - } - } - coroutineContext[Job]?.cancel() - yield() -} \ No newline at end of file diff --git a/app/src/main/kotlin/li/songe/gkd/MainViewModel.kt b/app/src/main/kotlin/li/songe/gkd/MainViewModel.kt index 7904a00e..dfd146eb 100644 --- a/app/src/main/kotlin/li/songe/gkd/MainViewModel.kt +++ b/app/src/main/kotlin/li/songe/gkd/MainViewModel.kt @@ -1,5 +1,6 @@ package li.songe.gkd +import android.app.Activity import android.content.ComponentName import android.content.Intent import android.net.Uri @@ -34,7 +35,9 @@ import li.songe.gkd.debug.FloatingTileService import li.songe.gkd.debug.HttpTileService import li.songe.gkd.debug.SnapshotTileService import li.songe.gkd.permission.AuthReason +import li.songe.gkd.permission.shizukuOkState import li.songe.gkd.service.MatchTileService +import li.songe.gkd.shizuku.execCommandForResult import li.songe.gkd.store.createTextFlow import li.songe.gkd.store.storeFlow import li.songe.gkd.ui.component.AlertDialogOptions @@ -53,11 +56,13 @@ import li.songe.gkd.util.componentName import li.songe.gkd.util.launchTry import li.songe.gkd.util.openUri import li.songe.gkd.util.openWeChatScaner +import li.songe.gkd.util.stopCoroutine import li.songe.gkd.util.subsFolder import li.songe.gkd.util.subsItemsFlow import li.songe.gkd.util.toast import li.songe.gkd.util.updateSubsMutex import li.songe.gkd.util.updateSubscription +import rikka.shizuku.Shizuku import java.lang.ref.WeakReference private var tempTermsAccepted = false @@ -87,7 +92,7 @@ class MainViewModel : ViewModel() { val updateStatus = if (META.updateEnabled) UpdateStatus(viewModelScope) else null - val shizukuErrorFlow = MutableStateFlow(false) + val shizukuErrorFlow = MutableStateFlow(null) val uploadOptions = UploadOptions(this) @@ -270,6 +275,26 @@ class MainViewModel : ViewModel() { ) } + suspend fun grantPermissionByShizuku(command: String) { + if (shizukuOkState.stateFlow.value) { + try { + execCommandForResult(command) + return + } catch (e: Exception) { + toast("运行失败:${e.message}") + LogUtils.d(e) + } + } else { + try { + Shizuku.requestPermission(Activity.RESULT_OK) + } catch (e: Throwable) { + LogUtils.d("Shizuku授权错误", e.message) + shizukuErrorFlow.value = e + } + } + stopCoroutine() + } + init { viewModelScope.launchTry(Dispatchers.IO) { val subsItems = DbSet.subsItemDao.queryAll() diff --git a/app/src/main/kotlin/li/songe/gkd/permission/PermissionState.kt b/app/src/main/kotlin/li/songe/gkd/permission/PermissionState.kt index 371569d7..3ab5a541 100644 --- a/app/src/main/kotlin/li/songe/gkd/permission/PermissionState.kt +++ b/app/src/main/kotlin/li/songe/gkd/permission/PermissionState.kt @@ -42,6 +42,8 @@ class PermissionState( val reason: AuthReason? = null, ) { val stateFlow = MutableStateFlow(false) + val value: Boolean + get() = stateFlow.value fun updateAndGet(): Boolean { return stateFlow.updateAndGet { check() } } diff --git a/app/src/main/kotlin/li/songe/gkd/shizuku/ActivityTaskManager.kt b/app/src/main/kotlin/li/songe/gkd/shizuku/ActivityTaskManager.kt index 6c3d3a64..a05ce62b 100644 --- a/app/src/main/kotlin/li/songe/gkd/shizuku/ActivityTaskManager.kt +++ b/app/src/main/kotlin/li/songe/gkd/shizuku/ActivityTaskManager.kt @@ -126,7 +126,9 @@ val activityTaskManagerFlow by lazy> { val stateFlow = MutableStateFlow(null) appScope.launchTry(Dispatchers.IO) { shizukuActivityUsedFlow.collect { - stateFlow.value?.unregisterTaskStackListener(taskListener) + if (shizukuOkState.value) { + stateFlow.value?.unregisterTaskStackListener(taskListener) + } stateFlow.value = if (it) newActivityTaskManager() else null stateFlow.value?.registerTaskStackListener(taskListener) } 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 c70a3786..3f4730eb 100644 --- a/app/src/main/kotlin/li/songe/gkd/shizuku/ShizukuApi.kt +++ b/app/src/main/kotlin/li/songe/gkd/shizuku/ShizukuApi.kt @@ -22,8 +22,7 @@ import rikka.shizuku.Shizuku fun shizukuCheckGranted(): Boolean { val granted = try { Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED - } catch (e: Exception) { - e.printStackTrace() + } catch (_: Exception) { false } if (!granted) return false diff --git a/app/src/main/kotlin/li/songe/gkd/shizuku/UserService.kt b/app/src/main/kotlin/li/songe/gkd/shizuku/UserService.kt index baa2359d..30e70009 100644 --- a/app/src/main/kotlin/li/songe/gkd/shizuku/UserService.kt +++ b/app/src/main/kotlin/li/songe/gkd/shizuku/UserService.kt @@ -116,14 +116,13 @@ private fun IUserService.execCommandForResult(command: String): Boolean? { private fun unbindUserService(serviceArgs: Shizuku.UserServiceArgs, connection: ServiceConnection) { + if (!shizukuOkState.stateFlow.value) return LogUtils.d("unbindUserService", serviceArgs) // https://github.com/RikkaApps/Shizuku-API/blob/master/server-shared/src/main/java/rikka/shizuku/server/UserServiceManager.java#L62 try { Shizuku.unbindUserService(serviceArgs, connection, false) Shizuku.unbindUserService(serviceArgs, connection, true) } catch (e: Exception) { - // binder haven't been received - e.printStackTrace() LogUtils.d(e) } } @@ -144,6 +143,10 @@ data class UserServiceWrapper( private val bindServiceMutex by lazy { Mutex() } suspend fun buildServiceWrapper(): UserServiceWrapper? { + if (bindServiceMutex.isLocked) { + toast("正在获取 Shizuku 服务,请稍后再试") + return null + } val serviceArgs = Shizuku .UserServiceArgs(UserService::class.componentName) .daemon(false) @@ -183,7 +186,7 @@ suspend fun buildServiceWrapper(): UserServiceWrapper? { } }.apply { if (this == null) { - toast("Shizuku获取绑定服务超时失败") + toast("获取 Shizuku 服务超时失败") unbindUserService(serviceArgs, connection) } } @@ -201,9 +204,9 @@ val serviceWrapperFlow by lazy { appScope.launch(Dispatchers.IO) { shizukuServiceUsedFlow.collect { if (it) { - stateFlow.update { it ?: buildServiceWrapper() } + stateFlow.update { s -> s ?: buildServiceWrapper() } } else { - stateFlow.update { it?.destroy(); null } + stateFlow.update { s -> s?.destroy(); null } } } } diff --git a/app/src/main/kotlin/li/songe/gkd/ui/AdvancedPage.kt b/app/src/main/kotlin/li/songe/gkd/ui/AdvancedPage.kt index 0739bdfb..10402cf5 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/AdvancedPage.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/AdvancedPage.kt @@ -63,8 +63,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.update import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withTimeoutOrNull import li.songe.gkd.MainActivity -import li.songe.gkd.appScope import li.songe.gkd.debug.FloatingService import li.songe.gkd.debug.HttpService import li.songe.gkd.debug.ScreenshotService @@ -94,6 +94,7 @@ import li.songe.gkd.util.appInfoCacheFlow import li.songe.gkd.util.launchAsFn import li.songe.gkd.util.shizukuAppId import li.songe.gkd.util.shizukuMiniVersionCode +import li.songe.gkd.util.stopCoroutine import li.songe.gkd.util.throttle import li.songe.gkd.util.toast import rikka.shizuku.Shizuku @@ -209,13 +210,13 @@ fun AdvancedPage() { onAuthClick = { try { Shizuku.requestPermission(Activity.RESULT_OK) - } catch (e: Exception) { + } catch (e: Throwable) { LogUtils.d("Shizuku授权错误", e.message) - mainVm.shizukuErrorFlow.value = true + mainVm.shizukuErrorFlow.value = e } }) } - ShizukuFragment(shizukuOk) + ShizukuFragment(vm, shizukuOk) val server by HttpService.httpServerFlow.collectAsState() val httpServerRunning = server != null @@ -474,8 +475,30 @@ fun AdvancedPage() { private val checkShizukuMutex by lazy { Mutex() } +private suspend fun checkShizukuFeat(block: suspend () -> Boolean) { + if (checkShizukuMutex.isLocked) { + toast("正在检测中, 请稍后再试") + stopCoroutine() + } + checkShizukuMutex.withLock { + toast("检测中") + val r = withTimeoutOrNull(3000) { + block() + } + if (r == null) { + toast("检测超时,请重试") + stopCoroutine() + } + if (!r) { + toast("检测失败,无法使用") + stopCoroutine() + } + toast("已启用") + } +} + @Composable -private fun ShizukuFragment(enabled: Boolean = true) { +private fun ShizukuFragment(vm: AdvancedVm, enabled: Boolean = true) { val shizukuStore by shizukuStoreFlow.collectAsState() val mainVm = LocalMainViewModel.current TextSwitch( @@ -487,18 +510,11 @@ private fun ShizukuFragment(enabled: Boolean = true) { }, checked = shizukuStore.enableActivity, enabled = enabled, - onCheckedChange = appScope.launchAsFn(Dispatchers.IO) { - checkShizukuMutex.withLock { - if (it) { - toast("检测中") - if (!shizukuCheckActivity()) { - toast("检测失败,无法使用") - return@launchAsFn - } - toast("已启用") - } - shizukuStoreFlow.update { s -> s.copy(enableActivity = it) } + onCheckedChange = vm.viewModelScope.launchAsFn(Dispatchers.IO) { + if (it) { + checkShizukuFeat { shizukuCheckActivity() } } + shizukuStoreFlow.update { s -> s.copy(enableActivity = it) } }) TextSwitch( @@ -510,18 +526,11 @@ private fun ShizukuFragment(enabled: Boolean = true) { }, checked = shizukuStore.enableTapClick, enabled = enabled, - onCheckedChange = appScope.launchAsFn(Dispatchers.IO) { - checkShizukuMutex.withLock { - if (it) { - toast("检测中") - if (!shizukuCheckUserService()) { - toast("检测失败,无法使用") - return@launchAsFn - } - toast("已启用") - } - shizukuStoreFlow.update { s -> s.copy(enableTapClick = it) } + onCheckedChange = vm.viewModelScope.launchAsFn(Dispatchers.IO) { + if (it) { + checkShizukuFeat { shizukuCheckUserService() } } + shizukuStoreFlow.update { s -> s.copy(enableTapClick = it) } }) @@ -534,18 +543,11 @@ private fun ShizukuFragment(enabled: Boolean = true) { }, checked = shizukuStore.enableWorkProfile, enabled = enabled, - onCheckedChange = appScope.launchAsFn(Dispatchers.IO) { - checkShizukuMutex.withLock { - if (it) { - toast("检测中") - if (!shizukuCheckWorkProfile()) { - toast("检测失败,无法使用") - return@launchAsFn - } - toast("已启用") - } - shizukuStoreFlow.update { s -> s.copy(enableWorkProfile = it) } + onCheckedChange = vm.viewModelScope.launchAsFn(Dispatchers.IO) { + if (it) { + checkShizukuFeat { shizukuCheckWorkProfile() } } + shizukuStoreFlow.update { s -> s.copy(enableWorkProfile = it) } }) } diff --git a/app/src/main/kotlin/li/songe/gkd/ui/AppOpsAllowPage.kt b/app/src/main/kotlin/li/songe/gkd/ui/AppOpsAllowPage.kt index 4d118905..db073a28 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/AppOpsAllowPage.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/AppOpsAllowPage.kt @@ -1,6 +1,5 @@ package li.songe.gkd.ui -import androidx.activity.compose.LocalActivity import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -32,12 +31,11 @@ import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootGraph import kotlinx.coroutines.Dispatchers import li.songe.gkd.META -import li.songe.gkd.MainActivity -import li.songe.gkd.grantPermissionByShizuku import li.songe.gkd.permission.foregroundServiceSpecialUseState import li.songe.gkd.ui.component.AuthButtonGroup import li.songe.gkd.ui.component.EmptyText import li.songe.gkd.ui.component.ManualAuthDialog +import li.songe.gkd.ui.local.LocalMainViewModel import li.songe.gkd.ui.local.LocalNavController import li.songe.gkd.ui.style.EmptyHeight import li.songe.gkd.ui.style.ProfileTransitions @@ -51,7 +49,7 @@ import li.songe.gkd.util.toast @Destination(style = ProfileTransitions::class) @Composable fun AppOpsAllowPage() { - val context = LocalActivity.current as MainActivity + val mainVm = LocalMainViewModel.current val navController = LocalNavController.current val vm = viewModel() val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() @@ -99,7 +97,7 @@ fun AppOpsAllowPage() { ) AuthButtonGroup( onClickShizuku = vm.viewModelScope.launchAsFn(Dispatchers.IO) { - context.grantPermissionByShizuku(appOpsCommand) + mainVm.grantPermissionByShizuku(appOpsCommand) toast("授权成功") }, onClickManual = { diff --git a/app/src/main/kotlin/li/songe/gkd/ui/AuthA11yPage.kt b/app/src/main/kotlin/li/songe/gkd/ui/AuthA11yPage.kt index 05f4ce07..58a029fa 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/AuthA11yPage.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/AuthA11yPage.kt @@ -1,7 +1,6 @@ package li.songe.gkd.ui import android.os.Build -import androidx.activity.compose.LocalActivity import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -36,8 +35,6 @@ import com.ramcosta.composedestinations.generated.destinations.WebViewPageDestin import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.update import li.songe.gkd.META -import li.songe.gkd.MainActivity -import li.songe.gkd.grantPermissionByShizuku import li.songe.gkd.permission.writeSecureSettingsState import li.songe.gkd.service.A11yService import li.songe.gkd.service.fixRestartService @@ -270,11 +267,11 @@ private fun successAuthExec() { @Composable private fun A11yAuthButtonGroup() { - val context = LocalActivity.current as MainActivity + val mainVm = LocalMainViewModel.current val vm = viewModel() AuthButtonGroup( onClickShizuku = vm.viewModelScope.launchAsFn(Dispatchers.IO) { - context.grantPermissionByShizuku(a11yCommandText) + mainVm.grantPermissionByShizuku(a11yCommandText) successAuthExec() }, onClickManual = { diff --git a/app/src/main/kotlin/li/songe/gkd/util/CoroutineExt.kt b/app/src/main/kotlin/li/songe/gkd/util/CoroutineExt.kt index 4f026027..3b501fef 100644 --- a/app/src/main/kotlin/li/songe/gkd/util/CoroutineExt.kt +++ b/app/src/main/kotlin/li/songe/gkd/util/CoroutineExt.kt @@ -5,9 +5,12 @@ import com.blankj.utilcode.util.LogUtils import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.Job import kotlinx.coroutines.launch +import kotlinx.coroutines.yield import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlin.coroutines.coroutineContext fun CoroutineScope.launchTry( context: CoroutineContext = EmptyCoroutineContext, @@ -65,3 +68,8 @@ fun CoroutineScope.launchAsFn( } } +suspend fun stopCoroutine(): Nothing { + coroutineContext[Job]?.cancel() + yield() + throw CancellationException("Coroutine stopped") +}