mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-16 11:42:22 +08:00
This commit is contained in:
parent
017422cc03
commit
001bb5683d
|
@ -97,7 +97,9 @@ class MainActivity : CompositionActivity({
|
|||
initFolder()
|
||||
}
|
||||
|
||||
appScope.launch(Dispatchers.IO) {
|
||||
updatePermissionState()
|
||||
}
|
||||
|
||||
// 进程崩溃后重新打开应用, 由于存在缓存导致服务状态可能不正确, 在此保证每次界面切换都能重新刷新状态
|
||||
appScope.launch(Dispatchers.IO) {
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = "我知道了")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
72
app/src/main/kotlin/li/songe/gkd/ui/component/Dialog.kt
Normal file
72
app/src/main/kotlin/li/songe/gkd/ui/component/Dialog.kt
Normal 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
|
||||
}
|
|
@ -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("请检查此应用是否安装")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user