Skip to content

Commit

Permalink
perf: check shizuku
Browse files Browse the repository at this point in the history
  • Loading branch information
lisonge committed Jul 29, 2024
1 parent 017422c commit 001bb56
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 13 deletions.
4 changes: 3 additions & 1 deletion app/src/main/kotlin/li/songe/gkd/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ class MainActivity : CompositionActivity({
initFolder()
}

updatePermissionState()
appScope.launch(Dispatchers.IO) {
updatePermissionState()
}

// 进程崩溃后重新打开应用, 由于存在缓存导致服务状态可能不正确, 在此保证每次界面切换都能重新刷新状态
appScope.launch(Dispatchers.IO) {
Expand Down
25 changes: 19 additions & 6 deletions app/src/main/kotlin/li/songe/gkd/permission/PermissionState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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() }
}
}
Expand Down Expand Up @@ -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
}
6 changes: 4 additions & 2 deletions app/src/main/kotlin/li/songe/gkd/shizuku/ShizukuApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -95,7 +95,9 @@ fun IActivityTaskManager.safeGetTasks(): List<ActivityManager.RunningTaskInfo>?
else -> null
}
} catch (e: Exception) {
LogUtils.d(e)
if (log) {
LogUtils.d(e)
}
null
}
}
Expand Down
51 changes: 47 additions & 4 deletions app/src/main/kotlin/li/songe/gkd/ui/AdvancedPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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()
Expand All @@ -117,7 +124,8 @@ fun AdvancedPage() {
)
}
}, title = { Text(text = "高级模式") }, actions = {})
}) { contentPadding ->
}
) { contentPadding ->
Column(
modifier = Modifier
.fillMaxSize()
Expand All @@ -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)
Expand Down Expand Up @@ -473,4 +481,39 @@ 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 = "我知道了")
}
}
)
}
72 changes: 72 additions & 0 deletions app/src/main/kotlin/li/songe/gkd/ui/component/Dialog.kt
Original file line number Diff line number Diff line change
@@ -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
}
10 changes: 10 additions & 0 deletions app/src/main/kotlin/li/songe/gkd/util/IntentExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,14 @@ fun Context.openUri(uri: String) {
val intent = Intent(Intent.ACTION_VIEW, u)
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("请检查此应用是否安装")
}
}

0 comments on commit 001bb56

Please sign in to comment.