perf: check shizuku
Some checks are pending
Build-Apk / build (push) Waiting to run

This commit is contained in:
lisonge 2024-07-29 21:42:08 +08:00
parent 017422cc03
commit 001bb5683d
6 changed files with 155 additions and 13 deletions

View File

@ -97,7 +97,9 @@ class MainActivity : CompositionActivity({
initFolder()
}
appScope.launch(Dispatchers.IO) {
updatePermissionState()
}
// 进程崩溃后重新打开应用, 由于存在缓存导致服务状态可能不正确, 在此保证每次界面切换都能重新刷新状态
appScope.launch(Dispatchers.IO) {

View File

@ -12,20 +12,22 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.updateAndGet
import li.songe.gkd.app
import li.songe.gkd.appScope
import li.songe.gkd.shizuku.newActivityTaskManager
import li.songe.gkd.shizuku.safeGetTasks
import li.songe.gkd.shizuku.shizukuIsSafeOK
import li.songe.gkd.util.initOrResetAppInfoCache
import li.songe.gkd.util.launchTry
class PermissionState(
val check: () -> Boolean,
val check: suspend () -> Boolean,
val request: (suspend (context: Activity) -> PermissionResult)? = null,
/**
* show it when user doNotAskAgain
*/
val reason: AuthReason? = null,
) {
val stateFlow = MutableStateFlow(check())
fun updateAndGet(): Boolean {
val stateFlow = MutableStateFlow(false)
suspend fun updateAndGet(): Boolean {
return stateFlow.updateAndGet { check() }
}
}
@ -120,21 +122,32 @@ val canSaveToAlbumState by lazy {
val shizukuOkState by lazy {
PermissionState(
check = { shizukuIsSafeOK() },
check = {
shizukuIsSafeOK() && (try {
// 打开 shizuku 点击右上角停止, 此时 shizukuIsSafeOK() == true, 因此需要二次检查状态
newActivityTaskManager()?.safeGetTasks(log = false)
true
} catch (e: Exception) {
false
})
},
)
}
fun updatePermissionState() {
private val checkLoading = MutableStateFlow(false)
suspend fun updatePermissionState() {
if (checkLoading.value) return
checkLoading.value = true
arrayOf(
notificationState,
canDrawOverlaysState,
canSaveToAlbumState,
shizukuOkState
).forEach { it.updateAndGet() }
if (canQueryPkgState.stateFlow.value != canQueryPkgState.updateAndGet()) {
appScope.launchTry {
initOrResetAppInfoCache()
}
}
checkLoading.value = false
}

View File

@ -66,7 +66,7 @@ fun newActivityTaskManager(): IActivityTaskManager? {
*/
private var getTasksFcType: Int? = null
fun IActivityTaskManager.safeGetTasks(): List<ActivityManager.RunningTaskInfo>? {
fun IActivityTaskManager.safeGetTasks(log: Boolean = true): List<ActivityManager.RunningTaskInfo>? {
if (getTasksFcType == null) {
val fcs = this::class.declaredMemberFunctions
val parameters = fcs.find { d -> d.name == "getTasks" }?.parameters
@ -95,7 +95,9 @@ fun IActivityTaskManager.safeGetTasks(): List<ActivityManager.RunningTaskInfo>?
else -> null
}
} catch (e: Exception) {
if (log) {
LogUtils.d(e)
}
null
}
}

View File

@ -57,6 +57,7 @@ import com.ramcosta.composedestinations.annotation.RootNavGraph
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.update
import li.songe.gkd.MainActivity
import li.songe.gkd.app
import li.songe.gkd.appScope
import li.songe.gkd.debug.FloatingService
import li.songe.gkd.debug.HttpService
@ -70,18 +71,23 @@ import li.songe.gkd.shizuku.newActivityTaskManager
import li.songe.gkd.shizuku.newUserService
import li.songe.gkd.shizuku.safeGetTasks
import li.songe.gkd.ui.component.AuthCard
import li.songe.gkd.ui.component.DialogApiInjection
import li.songe.gkd.ui.component.SettingItem
import li.songe.gkd.ui.component.TextSwitch
import li.songe.gkd.ui.component.build
import li.songe.gkd.ui.component.useDialog
import li.songe.gkd.ui.destinations.SnapshotPageDestination
import li.songe.gkd.ui.style.itemPadding
import li.songe.gkd.ui.style.titleItemPadding
import li.songe.gkd.util.LocalLauncher
import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.ProfileTransitions
import li.songe.gkd.util.appInfoCacheFlow
import li.songe.gkd.util.json
import li.songe.gkd.util.launchAsFn
import li.songe.gkd.util.launchTry
import li.songe.gkd.util.navigate
import li.songe.gkd.util.openApp
import li.songe.gkd.util.openUri
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.toast
@ -93,6 +99,7 @@ import rikka.shizuku.Shizuku
fun AdvancedPage() {
val context = LocalContext.current as MainActivity
val scope = rememberCoroutineScope()
val dialog = useDialog()
val launcher = LocalLauncher.current
val navController = LocalNavController.current
val store by storeFlow.collectAsState()
@ -117,7 +124,8 @@ fun AdvancedPage() {
)
}
}, title = { Text(text = "高级模式") }, actions = {})
}) { contentPadding ->
}
) { contentPadding ->
Column(
modifier = Modifier
.fillMaxSize()
@ -133,13 +141,13 @@ fun AdvancedPage() {
val shizukuOk by shizukuOkState.stateFlow.collectAsState()
if (!shizukuOk) {
AuthCard(title = "Shizuku授权",
desc = "高级模式:准确别界面ID,强制模拟点击",
desc = "高级模式:准确别界面ID,强制模拟点击",
onAuthClick = {
try {
Shizuku.requestPermission(Activity.RESULT_OK)
} catch (e: Exception) {
LogUtils.d("Shizuku授权错误", e)
toast("Shizuku可能没有运行")
LogUtils.d("Shizuku授权错误", e.message)
showShizukuErrorDialog(dialog)
}
})
ShizukuFragment(false)
@ -474,3 +482,38 @@ private fun ShizukuFragment(enabled: Boolean = true) {
})
}
private fun showShizukuErrorDialog(dialog: DialogApiInjection) {
val appId = "moe.shizuku.privileged.api"
val installed = appInfoCacheFlow.value.contains(appId)
dialog.build(
title = "授权错误",
text = if (installed) {
"Shizuku 授权失败, 请检查是否运行"
} else {
"Shizuku 未安装, 请先下载后安装"
},
confirmButton = {
if (installed) {
TextButton(onClick = {
dialog.dismiss()
app.openApp(appId)
}) {
Text(text = "打开 Shizuku")
}
} else {
TextButton(onClick = {
dialog.dismiss()
app.openUri("https://shizuku.rikka.app/")
}) {
Text(text = "去下载")
}
}
},
dismissButton = {
TextButton(onClick = dialog.dismiss) {
Text(text = "我知道了")
}
}
)
}

View File

@ -0,0 +1,72 @@
package li.songe.gkd.ui.component
import androidx.compose.foundation.Image
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.vector.ImageVector
data class DialogOptions internal constructor(
val onDismissRequest: (() -> Unit)? = null,
val confirmButton: @Composable () -> Unit,
val dismissButton: @Composable (() -> Unit)? = null,
val icon: @Composable (() -> Unit)? = null,
val title: @Composable (() -> Unit)? = null,
val text: @Composable (() -> Unit)? = null,
)
class DialogApiInjection(
val create: (options: DialogOptions) -> Unit,
val dismiss: () -> Unit,
)
fun DialogApiInjection.build(
title: String,
text: String,
confirmButton: @Composable () -> Unit,
dismissButton: @Composable (() -> Unit)? = null,
icon: ImageVector? = null,
) {
return create(
DialogOptions(
confirmButton = confirmButton,
dismissButton = dismissButton,
icon = icon?.let {
{ Image(imageVector = icon, contentDescription = null) }
},
title = {
Text(text = title)
},
text = {
Text(text = text)
},
)
)
}
@Composable
fun useDialog(): DialogApiInjection {
var options by remember { mutableStateOf<DialogOptions?>(null) }
val apiInjection = remember {
DialogApiInjection(
create = { options = it },
dismiss = { options = null }
)
}
options?.let {
AlertDialog(
onDismissRequest = it.onDismissRequest ?: apiInjection.dismiss,
confirmButton = it.confirmButton,
dismissButton = it.dismissButton,
icon = it.icon,
title = it.title,
text = it.text,
)
}
return apiInjection
}

View File

@ -49,3 +49,13 @@ fun Context.openUri(uri: String) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
tryStartActivity(intent)
}
fun Context.openApp(appId: String) {
val intent = packageManager.getLaunchIntentForPackage(appId)
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
tryStartActivity(intent)
} else {
toast("请检查此应用是否安装")
}
}