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() initFolder()
} }
updatePermissionState() appScope.launch(Dispatchers.IO) {
updatePermissionState()
}
// 进程崩溃后重新打开应用, 由于存在缓存导致服务状态可能不正确, 在此保证每次界面切换都能重新刷新状态 // 进程崩溃后重新打开应用, 由于存在缓存导致服务状态可能不正确, 在此保证每次界面切换都能重新刷新状态
appScope.launch(Dispatchers.IO) { appScope.launch(Dispatchers.IO) {

View File

@ -12,20 +12,22 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.updateAndGet import kotlinx.coroutines.flow.updateAndGet
import li.songe.gkd.app import li.songe.gkd.app
import li.songe.gkd.appScope 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.shizuku.shizukuIsSafeOK
import li.songe.gkd.util.initOrResetAppInfoCache import li.songe.gkd.util.initOrResetAppInfoCache
import li.songe.gkd.util.launchTry import li.songe.gkd.util.launchTry
class PermissionState( class PermissionState(
val check: () -> Boolean, val check: suspend () -> Boolean,
val request: (suspend (context: Activity) -> PermissionResult)? = null, val request: (suspend (context: Activity) -> PermissionResult)? = null,
/** /**
* show it when user doNotAskAgain * show it when user doNotAskAgain
*/ */
val reason: AuthReason? = null, val reason: AuthReason? = null,
) { ) {
val stateFlow = MutableStateFlow(check()) val stateFlow = MutableStateFlow(false)
fun updateAndGet(): Boolean { suspend fun updateAndGet(): Boolean {
return stateFlow.updateAndGet { check() } return stateFlow.updateAndGet { check() }
} }
} }
@ -120,21 +122,32 @@ val canSaveToAlbumState by lazy {
val shizukuOkState by lazy { val shizukuOkState by lazy {
PermissionState( 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( arrayOf(
notificationState, notificationState,
canDrawOverlaysState, canDrawOverlaysState,
canSaveToAlbumState, canSaveToAlbumState,
shizukuOkState shizukuOkState
).forEach { it.updateAndGet() } ).forEach { it.updateAndGet() }
if (canQueryPkgState.stateFlow.value != canQueryPkgState.updateAndGet()) { if (canQueryPkgState.stateFlow.value != canQueryPkgState.updateAndGet()) {
appScope.launchTry { appScope.launchTry {
initOrResetAppInfoCache() initOrResetAppInfoCache()
} }
} }
checkLoading.value = false
} }

View File

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

View File

@ -57,6 +57,7 @@ import com.ramcosta.composedestinations.annotation.RootNavGraph
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import li.songe.gkd.MainActivity import li.songe.gkd.MainActivity
import li.songe.gkd.app
import li.songe.gkd.appScope import li.songe.gkd.appScope
import li.songe.gkd.debug.FloatingService import li.songe.gkd.debug.FloatingService
import li.songe.gkd.debug.HttpService 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.newUserService
import li.songe.gkd.shizuku.safeGetTasks import li.songe.gkd.shizuku.safeGetTasks
import li.songe.gkd.ui.component.AuthCard 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.SettingItem
import li.songe.gkd.ui.component.TextSwitch 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.destinations.SnapshotPageDestination
import li.songe.gkd.ui.style.itemPadding import li.songe.gkd.ui.style.itemPadding
import li.songe.gkd.ui.style.titleItemPadding import li.songe.gkd.ui.style.titleItemPadding
import li.songe.gkd.util.LocalLauncher import li.songe.gkd.util.LocalLauncher
import li.songe.gkd.util.LocalNavController import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.ProfileTransitions import li.songe.gkd.util.ProfileTransitions
import li.songe.gkd.util.appInfoCacheFlow
import li.songe.gkd.util.json import li.songe.gkd.util.json
import li.songe.gkd.util.launchAsFn import li.songe.gkd.util.launchAsFn
import li.songe.gkd.util.launchTry import li.songe.gkd.util.launchTry
import li.songe.gkd.util.navigate import li.songe.gkd.util.navigate
import li.songe.gkd.util.openApp
import li.songe.gkd.util.openUri import li.songe.gkd.util.openUri
import li.songe.gkd.util.storeFlow import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.toast import li.songe.gkd.util.toast
@ -93,6 +99,7 @@ import rikka.shizuku.Shizuku
fun AdvancedPage() { fun AdvancedPage() {
val context = LocalContext.current as MainActivity val context = LocalContext.current as MainActivity
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val dialog = useDialog()
val launcher = LocalLauncher.current val launcher = LocalLauncher.current
val navController = LocalNavController.current val navController = LocalNavController.current
val store by storeFlow.collectAsState() val store by storeFlow.collectAsState()
@ -117,7 +124,8 @@ fun AdvancedPage() {
) )
} }
}, title = { Text(text = "高级模式") }, actions = {}) }, title = { Text(text = "高级模式") }, actions = {})
}) { contentPadding -> }
) { contentPadding ->
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -133,13 +141,13 @@ fun AdvancedPage() {
val shizukuOk by shizukuOkState.stateFlow.collectAsState() val shizukuOk by shizukuOkState.stateFlow.collectAsState()
if (!shizukuOk) { if (!shizukuOk) {
AuthCard(title = "Shizuku授权", AuthCard(title = "Shizuku授权",
desc = "高级模式:准确别界面ID,强制模拟点击", desc = "高级模式:准确别界面ID,强制模拟点击",
onAuthClick = { onAuthClick = {
try { try {
Shizuku.requestPermission(Activity.RESULT_OK) Shizuku.requestPermission(Activity.RESULT_OK)
} catch (e: Exception) { } catch (e: Exception) {
LogUtils.d("Shizuku授权错误", e) LogUtils.d("Shizuku授权错误", e.message)
toast("Shizuku可能没有运行") showShizukuErrorDialog(dialog)
} }
}) })
ShizukuFragment(false) 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) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
tryStartActivity(intent) 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("请检查此应用是否安装")
}
}