mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-16 03:32:38 +08:00
feat: auth writeSecureSettings
This commit is contained in:
parent
f859fa1d15
commit
2ce08c81a0
|
@ -11,7 +11,12 @@ import androidx.activity.compose.setContent
|
|||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.animation.core.AnimationConstants
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
|
@ -40,9 +45,12 @@ import li.songe.gkd.ui.component.BuildDialog
|
|||
import li.songe.gkd.ui.theme.AppTheme
|
||||
import li.songe.gkd.util.LocalNavController
|
||||
import li.songe.gkd.util.UpgradeDialog
|
||||
import li.songe.gkd.util.appInfoCacheFlow
|
||||
import li.songe.gkd.util.initFolder
|
||||
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.storeFlow
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
@ -81,6 +89,7 @@ class MainActivity : ComponentActivity() {
|
|||
navGraph = NavGraphs.root,
|
||||
navController = navController
|
||||
)
|
||||
ShizukuErrorDialog(mainVm.shizukuErrorFlow)
|
||||
AuthDialog(mainVm.authReasonFlow)
|
||||
BuildDialog(mainVm.dialogFlow)
|
||||
if (META.updateEnabled) {
|
||||
|
@ -183,3 +192,48 @@ private fun Activity.fixTopPadding() {
|
|||
ViewCompat.onApplyWindowInsets(view, windowInsets)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShizukuErrorDialog(stateFlow: MutableStateFlow<Boolean>) {
|
||||
val state = stateFlow.collectAsState()
|
||||
if (state.value) {
|
||||
val appId = "moe.shizuku.privileged.api"
|
||||
val appInfoCache = appInfoCacheFlow.collectAsState()
|
||||
val installed = appInfoCache.value.contains(appId)
|
||||
AlertDialog(
|
||||
onDismissRequest = { stateFlow.value = false },
|
||||
title = { Text(text = "授权错误") },
|
||||
text = {
|
||||
Text(
|
||||
text = if (installed) {
|
||||
"Shizuku 授权失败, 请检查是否运行"
|
||||
} else {
|
||||
"Shizuku 未安装, 请先下载后安装"
|
||||
}
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
if (installed) {
|
||||
TextButton(onClick = {
|
||||
stateFlow.value = false
|
||||
app.openApp(appId)
|
||||
}) {
|
||||
Text(text = "打开 Shizuku")
|
||||
}
|
||||
} else {
|
||||
TextButton(onClick = {
|
||||
stateFlow.value = false
|
||||
app.openUri("https://shizuku.rikka.app/")
|
||||
}) {
|
||||
Text(text = "去下载")
|
||||
}
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { stateFlow.value = false }) {
|
||||
Text(text = "我知道了")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ class MainViewModel : ViewModel() {
|
|||
|
||||
val updateStatus = UpdateStatus()
|
||||
|
||||
val shizukuErrorFlow = MutableStateFlow(false)
|
||||
|
||||
init {
|
||||
viewModelScope.launchTry(Dispatchers.IO) {
|
||||
val subsItems = DbSet.subsItemDao.queryAll()
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.app.ActivityManager
|
|||
import android.app.IActivityTaskManager
|
||||
import android.content.ComponentName
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.IPackageManager
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.IBinder
|
||||
import android.view.Display
|
||||
|
@ -56,6 +57,15 @@ fun newActivityTaskManager(): IActivityTaskManager? {
|
|||
return service.let(::ShizukuBinderWrapper).let(IActivityTaskManager.Stub::asInterface)
|
||||
}
|
||||
|
||||
fun newPackageManager(): IPackageManager? {
|
||||
val service = SystemServiceHelper.getSystemService("package")
|
||||
if (service == null) {
|
||||
LogUtils.d("shizuku 无法获取 package")
|
||||
return null
|
||||
}
|
||||
return service.let(::ShizukuBinderWrapper).let(IPackageManager.Stub::asInterface)
|
||||
}
|
||||
|
||||
/**
|
||||
* -1: invalid fc
|
||||
* 1: (int) -> List<Task>
|
||||
|
|
|
@ -62,10 +62,8 @@ import com.ramcosta.composedestinations.annotation.Destination
|
|||
import com.ramcosta.composedestinations.annotation.RootNavGraph
|
||||
import com.ramcosta.composedestinations.navigation.navigate
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
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
|
||||
|
@ -89,12 +87,10 @@ import li.songe.gkd.ui.style.itemPadding
|
|||
import li.songe.gkd.ui.style.titleItemPadding
|
||||
import li.songe.gkd.util.LocalNavController
|
||||
import li.songe.gkd.util.ProfileTransitions
|
||||
import li.songe.gkd.util.appInfoCacheFlow
|
||||
import li.songe.gkd.util.buildLogFile
|
||||
import li.songe.gkd.util.json
|
||||
import li.songe.gkd.util.launchAsFn
|
||||
import li.songe.gkd.util.launchTry
|
||||
import li.songe.gkd.util.openApp
|
||||
import li.songe.gkd.util.openUri
|
||||
import li.songe.gkd.util.privacyStoreFlow
|
||||
import li.songe.gkd.util.saveFileToDownloads
|
||||
|
@ -114,7 +110,6 @@ fun AdvancedPage() {
|
|||
val store by storeFlow.collectAsState()
|
||||
val snapshotCount by vm.snapshotCountFlow.collectAsState()
|
||||
|
||||
ShizukuErrorDialog(vm.shizukuErrorFlow)
|
||||
vm.uploadOptions.ShowDialog()
|
||||
|
||||
var showEditPortDlg by remember {
|
||||
|
@ -321,7 +316,7 @@ fun AdvancedPage() {
|
|||
Shizuku.requestPermission(Activity.RESULT_OK)
|
||||
} catch (e: Exception) {
|
||||
LogUtils.d("Shizuku授权错误", e.message)
|
||||
vm.shizukuErrorFlow.value = true
|
||||
context.mainVm.shizukuErrorFlow.value = true
|
||||
}
|
||||
})
|
||||
ShizukuFragment(false)
|
||||
|
@ -666,48 +661,3 @@ private fun ShizukuFragment(enabled: Boolean = true) {
|
|||
})
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShizukuErrorDialog(stateFlow: MutableStateFlow<Boolean>) {
|
||||
val state = stateFlow.collectAsState()
|
||||
if (state.value) {
|
||||
val appId = "moe.shizuku.privileged.api"
|
||||
val appInfoCache = appInfoCacheFlow.collectAsState()
|
||||
val installed = appInfoCache.value.contains(appId)
|
||||
AlertDialog(
|
||||
onDismissRequest = { stateFlow.value = false },
|
||||
title = { Text(text = "授权错误") },
|
||||
text = {
|
||||
Text(
|
||||
text = if (installed) {
|
||||
"Shizuku 授权失败, 请检查是否运行"
|
||||
} else {
|
||||
"Shizuku 未安装, 请先下载后安装"
|
||||
}
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
if (installed) {
|
||||
TextButton(onClick = {
|
||||
stateFlow.value = false
|
||||
app.openApp(appId)
|
||||
}) {
|
||||
Text(text = "打开 Shizuku")
|
||||
}
|
||||
} else {
|
||||
TextButton(onClick = {
|
||||
stateFlow.value = false
|
||||
app.openUri("https://shizuku.rikka.app/")
|
||||
}) {
|
||||
Text(text = "去下载")
|
||||
}
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { stateFlow.value = false }) {
|
||||
Text(text = "我知道了")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package li.songe.gkd.ui
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import li.songe.gkd.db.DbSet
|
||||
|
@ -12,7 +11,5 @@ class AdvancedVm : ViewModel() {
|
|||
val snapshotCountFlow =
|
||||
DbSet.snapshotDao.count().stateIn(viewModelScope, SharingStarted.Eagerly, 0)
|
||||
|
||||
val shizukuErrorFlow = MutableStateFlow(false)
|
||||
|
||||
val uploadOptions = UploadOptions(viewModelScope)
|
||||
}
|
314
app/src/main/kotlin/li/songe/gkd/ui/AuthA11yPage.kt
Normal file
314
app/src/main/kotlin/li/songe/gkd/ui/AuthA11yPage.kt
Normal file
|
@ -0,0 +1,314 @@
|
|||
package li.songe.gkd.ui
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.blankj.utilcode.util.ClipboardUtils
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootNavGraph
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import li.songe.gkd.META
|
||||
import li.songe.gkd.MainActivity
|
||||
import li.songe.gkd.permission.shizukuOkState
|
||||
import li.songe.gkd.permission.writeSecureSettingsState
|
||||
import li.songe.gkd.service.fixRestartService
|
||||
import li.songe.gkd.shizuku.newPackageManager
|
||||
import li.songe.gkd.util.LocalNavController
|
||||
import li.songe.gkd.util.ProfileTransitions
|
||||
import li.songe.gkd.util.launchAsFn
|
||||
import li.songe.gkd.util.openA11ySettings
|
||||
import li.songe.gkd.util.throttle
|
||||
import li.songe.gkd.util.toast
|
||||
import rikka.shizuku.Shizuku
|
||||
import java.io.DataOutputStream
|
||||
|
||||
@RootNavGraph
|
||||
@Destination(style = ProfileTransitions::class)
|
||||
@Composable
|
||||
fun AuthA11yPage() {
|
||||
val context = LocalContext.current as MainActivity
|
||||
val navController = LocalNavController.current
|
||||
|
||||
val vm = viewModel<AuthA11yVm>()
|
||||
val showCopyDlg by vm.showCopyDlgFlow.collectAsState()
|
||||
val writeSecureSettings by writeSecureSettingsState.stateFlow.collectAsState()
|
||||
var mode by remember { mutableIntStateOf(-1) }
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
|
||||
TopAppBar(scrollBehavior = scrollBehavior, navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}, title = {
|
||||
Text(text = "授权说明")
|
||||
}, actions = {})
|
||||
}) { contentPadding ->
|
||||
Column(
|
||||
modifier = Modifier.padding(contentPadding)
|
||||
) {
|
||||
if (writeSecureSettings) {
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
Text(
|
||||
text = "授权成功,请关闭此页面",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = "选择一个授权模式",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.padding(16.dp, 0.dp)
|
||||
.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = if (mode == 0) {
|
||||
MaterialTheme.colorScheme.primaryContainer
|
||||
} else {
|
||||
Color.Unspecified
|
||||
}
|
||||
),
|
||||
onClick = { mode = 0 }
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RadioButton(
|
||||
selected = mode == 0,
|
||||
onClick = { mode = 0 }
|
||||
)
|
||||
Text(
|
||||
text = "普通授权(简单)",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(16.dp, 0.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = "1. 授予[无障碍权限]"
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(16.dp, 0.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = "2. 无障碍服务关闭后需重新授权"
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(16.dp, 0.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
TextButton(onClick = throttle { openA11ySettings() }) {
|
||||
Text(
|
||||
text = "手动授权",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.padding(16.dp, 0.dp)
|
||||
.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = if (mode == 1) {
|
||||
MaterialTheme.colorScheme.primaryContainer
|
||||
} else {
|
||||
Color.Unspecified
|
||||
}
|
||||
),
|
||||
onClick = { mode = 1 }
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RadioButton(
|
||||
selected = mode == 1,
|
||||
onClick = { mode = 1 }
|
||||
)
|
||||
Text(text = "高级授权(推荐)")
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(16.dp, 0.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = "1. 授予[写入安全设置权限]"
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(16.dp, 0.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = "2. 授权永久有效, 可自动重启无障碍服务"
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(16.dp, 0.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = "3. 搭配通知栏快捷图标可实现无感重启, 无限保活"
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(16.dp, 0.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
TextButton(onClick = throttle(fn = vm.viewModelScope.launchAsFn(Dispatchers.IO) {
|
||||
context.grantPermissionByShizuku()
|
||||
})) {
|
||||
Text(
|
||||
text = "Shizuku授权",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
}
|
||||
TextButton(onClick = throttle(fn = vm.viewModelScope.launchAsFn(Dispatchers.IO) {
|
||||
grantPermissionByRoot()
|
||||
})) {
|
||||
Text(
|
||||
text = "ROOT授权",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
}
|
||||
TextButton(onClick = {
|
||||
vm.showCopyDlgFlow.value = true
|
||||
}) {
|
||||
Text(
|
||||
text = "手动授权",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showCopyDlg) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { vm.showCopyDlgFlow.value = false },
|
||||
title = { Text(text = "手动授权") },
|
||||
text = {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Text(text = "1. 有一台安装了 adb 的电脑\n\n2.手机开启调试模式后连接电脑授权调试\n\n3. 在电脑 cmd/pwsh 中运行如下命令")
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = commandText,
|
||||
modifier = Modifier
|
||||
.clip(MaterialTheme.shapes.extraSmall)
|
||||
.background(MaterialTheme.colorScheme.secondaryContainer)
|
||||
.padding(4.dp)
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
vm.showCopyDlgFlow.value = false
|
||||
ClipboardUtils.copyText(commandText)
|
||||
toast("复制成功")
|
||||
}) {
|
||||
Text(text = "复制并关闭")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { vm.showCopyDlgFlow.value = false }) {
|
||||
Text(text = "关闭")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val commandText by lazy { "adb pm grant ${META.appId} android.permission.WRITE_SECURE_SETTINGS" }
|
||||
|
||||
private suspend fun MainActivity.grantPermissionByShizuku() {
|
||||
if (shizukuOkState.stateFlow.value) {
|
||||
try {
|
||||
val service = newPackageManager()
|
||||
if (service != null) {
|
||||
service.grantRuntimePermission(
|
||||
META.appId,
|
||||
"android.permission.WRITE_SECURE_SETTINGS",
|
||||
0, // maybe others
|
||||
)
|
||||
delay(500)
|
||||
if (writeSecureSettingsState.updateAndGet()) {
|
||||
toast("授权成功")
|
||||
fixRestartService()
|
||||
}
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun grantPermissionByRoot() {
|
||||
var p: Process? = null
|
||||
try {
|
||||
p = Runtime.getRuntime().exec("su")
|
||||
val o = DataOutputStream(p.outputStream)
|
||||
o.writeBytes("pm grant ${META.appId} android.permission.WRITE_SECURE_SETTINGS\nexit\n")
|
||||
o.flush()
|
||||
o.close()
|
||||
p.waitFor()
|
||||
if (p.exitValue() == 0) {
|
||||
toast("授权成功")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
toast("授权失败:${e.message}")
|
||||
LogUtils.d(e)
|
||||
} finally {
|
||||
p?.destroy()
|
||||
}
|
||||
}
|
24
app/src/main/kotlin/li/songe/gkd/ui/AuthA11yVm.kt
Normal file
24
app/src/main/kotlin/li/songe/gkd/ui/AuthA11yVm.kt
Normal file
|
@ -0,0 +1,24 @@
|
|||
package li.songe.gkd.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import li.songe.gkd.permission.writeSecureSettingsState
|
||||
|
||||
class AuthA11yVm : ViewModel() {
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
while (isActive) {
|
||||
if (writeSecureSettingsState.updateAndGet()) {
|
||||
break
|
||||
}
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val showCopyDlgFlow = MutableStateFlow(false)
|
||||
}
|
|
@ -39,6 +39,7 @@ import li.songe.gkd.ui.component.AuthCard
|
|||
import li.songe.gkd.ui.component.SettingItem
|
||||
import li.songe.gkd.ui.component.TextSwitch
|
||||
import li.songe.gkd.ui.destinations.ActivityLogPageDestination
|
||||
import li.songe.gkd.ui.destinations.AuthA11yPageDestination
|
||||
import li.songe.gkd.ui.destinations.ClickLogPageDestination
|
||||
import li.songe.gkd.ui.destinations.SlowGroupPageDestination
|
||||
import li.songe.gkd.ui.style.EmptyHeight
|
||||
|
@ -46,7 +47,6 @@ import li.songe.gkd.ui.style.itemPadding
|
|||
import li.songe.gkd.util.HOME_PAGE_URL
|
||||
import li.songe.gkd.util.LocalNavController
|
||||
import li.songe.gkd.util.launchAsFn
|
||||
import li.songe.gkd.util.openA11ySettings
|
||||
import li.songe.gkd.util.openUri
|
||||
import li.songe.gkd.util.ruleSummaryFlow
|
||||
import li.songe.gkd.util.storeFlow
|
||||
|
@ -110,8 +110,7 @@ fun useControlPage(): ScaffoldExt {
|
|||
title = "无障碍授权",
|
||||
desc = if (a11yBroken) "服务故障,请重新授权" else "授权使无障碍服务运行",
|
||||
onAuthClick = {
|
||||
openA11ySettings()
|
||||
// TODO context.mainVm.showA11yAuthDlgFlow.value = true
|
||||
navController.navigate(AuthA11yPageDestination)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ public interface IPackageManager extends IInterface {
|
|||
|
||||
PackageInfo getPackageInfo(String packageName, long flags, int userId);
|
||||
|
||||
void grantRuntimePermission(String packageName, String permName, int user);
|
||||
|
||||
abstract class Stub {
|
||||
|
||||
public static IPackageManager asInterface(IBinder obj) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user