mirror of
https://mirror.skon.top/github.com/gkd-kit/gkd
synced 2026-04-20 21:00:12 +08:00
perf: check shizuku state
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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<out String?>,
|
||||
grantResults: IntArray,
|
||||
deviceId: Int
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
private val activityVisibleFlow by lazy { MutableStateFlow(0) }
|
||||
@@ -227,34 +236,70 @@ fun syncFixState() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShizukuErrorDialog(stateFlow: MutableStateFlow<Boolean>) {
|
||||
private fun ShizukuErrorDialog(stateFlow: MutableStateFlow<Throwable?>) {
|
||||
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<Boolean>) {
|
||||
}
|
||||
},
|
||||
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()
|
||||
}
|
||||
@@ -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<Throwable?>(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()
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -126,7 +126,9 @@ val activityTaskManagerFlow by lazy<StateFlow<SafeActivityTaskManager?>> {
|
||||
val stateFlow = MutableStateFlow<SafeActivityTaskManager?>(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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Boolean>(Dispatchers.IO) {
|
||||
checkShizukuMutex.withLock {
|
||||
if (it) {
|
||||
toast("检测中")
|
||||
if (!shizukuCheckActivity()) {
|
||||
toast("检测失败,无法使用")
|
||||
return@launchAsFn
|
||||
}
|
||||
toast("已启用")
|
||||
}
|
||||
shizukuStoreFlow.update { s -> s.copy(enableActivity = it) }
|
||||
onCheckedChange = vm.viewModelScope.launchAsFn<Boolean>(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<Boolean>(Dispatchers.IO) {
|
||||
checkShizukuMutex.withLock {
|
||||
if (it) {
|
||||
toast("检测中")
|
||||
if (!shizukuCheckUserService()) {
|
||||
toast("检测失败,无法使用")
|
||||
return@launchAsFn
|
||||
}
|
||||
toast("已启用")
|
||||
}
|
||||
shizukuStoreFlow.update { s -> s.copy(enableTapClick = it) }
|
||||
onCheckedChange = vm.viewModelScope.launchAsFn<Boolean>(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<Boolean>(Dispatchers.IO) {
|
||||
checkShizukuMutex.withLock {
|
||||
if (it) {
|
||||
toast("检测中")
|
||||
if (!shizukuCheckWorkProfile()) {
|
||||
toast("检测失败,无法使用")
|
||||
return@launchAsFn
|
||||
}
|
||||
toast("已启用")
|
||||
}
|
||||
shizukuStoreFlow.update { s -> s.copy(enableWorkProfile = it) }
|
||||
onCheckedChange = vm.viewModelScope.launchAsFn<Boolean>(Dispatchers.IO) {
|
||||
if (it) {
|
||||
checkShizukuFeat { shizukuCheckWorkProfile() }
|
||||
}
|
||||
shizukuStoreFlow.update { s -> s.copy(enableWorkProfile = it) }
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -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<RootGraph>(style = ProfileTransitions::class)
|
||||
@Composable
|
||||
fun AppOpsAllowPage() {
|
||||
val context = LocalActivity.current as MainActivity
|
||||
val mainVm = LocalMainViewModel.current
|
||||
val navController = LocalNavController.current
|
||||
val vm = viewModel<AppOpsAllowVm>()
|
||||
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 = {
|
||||
|
||||
@@ -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<AuthA11yVm>()
|
||||
AuthButtonGroup(
|
||||
onClickShizuku = vm.viewModelScope.launchAsFn(Dispatchers.IO) {
|
||||
context.grantPermissionByShizuku(a11yCommandText)
|
||||
mainVm.grantPermissionByShizuku(a11yCommandText)
|
||||
successAuthExec()
|
||||
},
|
||||
onClickManual = {
|
||||
|
||||
@@ -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 <T> CoroutineScope.launchAsFn(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun stopCoroutine(): Nothing {
|
||||
coroutineContext[Job]?.cancel()
|
||||
yield()
|
||||
throw CancellationException("Coroutine stopped")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user