@@ -549,8 +549,18 @@ private fun InfoCard() {
|
|||||||
|
|
||||||
|
|
||||||
if (!isSimpleMode) {
|
if (!isSimpleMode) {
|
||||||
Spacer(Modifier.height(16.dp))
|
var showKpmVersion by remember { mutableStateOf(true) }
|
||||||
InfoCardItem(stringResource(R.string.home_kpm_version), getKpmVersion())
|
LaunchedEffect(Unit) {
|
||||||
|
try {
|
||||||
|
getKpmVersion()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showKpmVersion = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimatedVisibility(visible = showKpmVersion) {
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
InfoCardItem(stringResource(R.string.home_kpm_version), getKpmVersion())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
|
||||||
|
|
||||||
import android.app.Activity.RESULT_OK
|
import android.app.Activity.RESULT_OK
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.animation.core.Spring
|
||||||
|
import androidx.compose.animation.core.SpringSpec
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.*
|
import androidx.compose.material.icons.outlined.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -24,6 +23,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import shirkneko.zako.sukisu.R
|
import shirkneko.zako.sukisu.R
|
||||||
import shirkneko.zako.sukisu.ui.component.ConfirmResult
|
import shirkneko.zako.sukisu.ui.component.ConfirmResult
|
||||||
@@ -39,7 +39,6 @@ import java.io.File
|
|||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import shirkneko.zako.sukisu.ui.theme.ThemeConfig
|
import shirkneko.zako.sukisu.ui.theme.ThemeConfig
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KPM 管理界面
|
* KPM 管理界面
|
||||||
* 以下内核模块功能由KernelPatch开发,经过修改后加入SukiSU Ultra的内核模块功能
|
* 以下内核模块功能由KernelPatch开发,经过修改后加入SukiSU Ultra的内核模块功能
|
||||||
@@ -103,27 +102,24 @@ fun KpmScreen(
|
|||||||
if (confirmResult == ConfirmResult.Confirmed) {
|
if (confirmResult == ConfirmResult.Confirmed) {
|
||||||
val success = loadingDialog.withLoading {
|
val success = loadingDialog.withLoading {
|
||||||
try {
|
try {
|
||||||
val process = ProcessBuilder("nsenter", "-t", "1", "-m").start()
|
|
||||||
process.waitFor()
|
|
||||||
loadKpmModule(tempFile.absolutePath)
|
loadKpmModule(tempFile.absolutePath)
|
||||||
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("KsuCli", "Failed to execute nsenter command: ${e.message}")
|
Log.e("KsuCli", "Failed to load KPM module: ${e.message}")
|
||||||
"failed"
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d("KsuCli", "loadKpmModule result: $success")
|
if (success) {
|
||||||
|
|
||||||
if (success.contains("Success", ignoreCase = true)) {
|
|
||||||
viewModel.fetchModuleList()
|
viewModel.fetchModuleList()
|
||||||
snackBarHost.showSnackbar(
|
snackBarHost.showSnackbar(
|
||||||
message = kpmInstallSuccess,
|
message = kpmInstallSuccess,
|
||||||
duration = SnackbarDuration.Long
|
duration = SnackbarDuration.Short
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
snackBarHost.showSnackbar(
|
snackBarHost.showSnackbar(
|
||||||
message = kpmInstallFailed,
|
message = kpmInstallFailed,
|
||||||
duration = SnackbarDuration.Long
|
duration = SnackbarDuration.Short
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,11 +128,12 @@ fun KpmScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (viewModel.moduleList.isEmpty()) {
|
while(true) {
|
||||||
viewModel.fetchModuleList()
|
viewModel.fetchModuleList()
|
||||||
|
delay(5000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 使用 SharedPreferences 存储声明是否关闭的状态
|
|
||||||
val sharedPreferences = context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
|
val sharedPreferences = context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
|
||||||
var isNoticeClosed by remember { mutableStateOf(sharedPreferences.getBoolean("is_notice_closed", false)) }
|
var isNoticeClosed by remember { mutableStateOf(sharedPreferences.getBoolean("is_notice_closed", false)) }
|
||||||
|
|
||||||
@@ -196,7 +193,7 @@ fun KpmScreen(
|
|||||||
)
|
)
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
isNoticeClosed = true
|
isNoticeClosed = true
|
||||||
sharedPreferences.edit() { putBoolean("is_notice_closed", true) }
|
sharedPreferences.edit { putBoolean("is_notice_closed", true) }
|
||||||
}) {
|
}) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.Close,
|
imageVector = Icons.Outlined.Close,
|
||||||
@@ -206,64 +203,63 @@ fun KpmScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PullToRefreshBox(
|
if (viewModel.moduleList.isEmpty()) {
|
||||||
onRefresh = { viewModel.fetchModuleList() },
|
Box(
|
||||||
isRefreshing = viewModel.isRefreshing,
|
modifier = Modifier.fillMaxSize(),
|
||||||
modifier = Modifier
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
if (viewModel.moduleList.isEmpty()) {
|
Text(
|
||||||
Box(
|
stringResource(R.string.kpm_empty),
|
||||||
modifier = Modifier.fillMaxSize(),
|
textAlign = TextAlign.Center
|
||||||
contentAlignment = Alignment.Center
|
)
|
||||||
) {
|
}
|
||||||
Text(
|
} else {
|
||||||
stringResource(R.string.kpm_empty),
|
LazyColumn(
|
||||||
textAlign = TextAlign.Center
|
modifier = Modifier.fillMaxSize(),
|
||||||
)
|
contentPadding = PaddingValues(16.dp),
|
||||||
}
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
} else {
|
) {
|
||||||
LazyColumn(
|
items(viewModel.moduleList) { module ->
|
||||||
modifier = Modifier.fillMaxSize(),
|
val kpmUninstallConfirm = String.format(kpmUninstallConfirmTemplate, module.name)
|
||||||
contentPadding = PaddingValues(16.dp),
|
KpmModuleItem(
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
module = module,
|
||||||
) {
|
onUninstall = {
|
||||||
items(viewModel.moduleList) { module ->
|
scope.launch {
|
||||||
val kpmUninstallConfirm = String.format(kpmUninstallConfirmTemplate, module.name)
|
val confirmResult = confirmDialog.awaitConfirm(
|
||||||
KpmModuleItem(
|
title = kpmUninstall,
|
||||||
module = module,
|
content = kpmUninstallConfirm,
|
||||||
onUninstall = {
|
confirm = uninstall,
|
||||||
scope.launch {
|
dismiss = cancel
|
||||||
val confirmResult = confirmDialog.awaitConfirm(
|
)
|
||||||
title = kpmUninstall,
|
if (confirmResult == ConfirmResult.Confirmed) {
|
||||||
content = kpmUninstallConfirm,
|
val success = loadingDialog.withLoading {
|
||||||
confirm = uninstall,
|
try {
|
||||||
dismiss = cancel
|
|
||||||
)
|
|
||||||
if (confirmResult == ConfirmResult.Confirmed) {
|
|
||||||
val success = loadingDialog.withLoading {
|
|
||||||
unloadKpmModule(module.id)
|
unloadKpmModule(module.id)
|
||||||
}
|
true
|
||||||
Log.d("KsuCli", "unloadKpmModule result: $success")
|
} catch (e: Exception) {
|
||||||
if (success.contains("Success", ignoreCase = true)) {
|
Log.e("KsuCli", "Failed to unload KPM module: ${e.message}")
|
||||||
viewModel.fetchModuleList()
|
false
|
||||||
snackBarHost.showSnackbar(
|
|
||||||
message = kpmUninstallSuccess,
|
|
||||||
duration = SnackbarDuration.Long
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
snackBarHost.showSnackbar(
|
|
||||||
message = kpmUninstallFailed,
|
|
||||||
duration = SnackbarDuration.Long
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (success) {
|
||||||
|
viewModel.fetchModuleList()
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
message = kpmUninstallSuccess,
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
message = kpmUninstallFailed,
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
onControl = {
|
|
||||||
viewModel.loadModuleDetail(module.id)
|
|
||||||
}
|
}
|
||||||
)
|
},
|
||||||
}
|
onControl = {
|
||||||
|
viewModel.loadModuleDetail(module.id)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,6 +273,13 @@ private fun KpmModuleItem(
|
|||||||
onUninstall: () -> Unit,
|
onUninstall: () -> Unit,
|
||||||
onControl: () -> Unit
|
onControl: () -> Unit
|
||||||
) {
|
) {
|
||||||
|
val viewModel: KpmViewModel = viewModel()
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val snackBarHost = remember { SnackbarHostState() }
|
||||||
|
|
||||||
|
val successMessage = stringResource(R.string.kpm_control_success)
|
||||||
|
val failureMessage = stringResource(R.string.kpm_control_failed)
|
||||||
|
|
||||||
ElevatedCard(
|
ElevatedCard(
|
||||||
colors = getCardColors(MaterialTheme.colorScheme.secondaryContainer),
|
colors = getCardColors(MaterialTheme.colorScheme.secondaryContainer),
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = getCardElevation())
|
elevation = CardDefaults.cardElevation(defaultElevation = getCardElevation())
|
||||||
@@ -323,7 +326,18 @@ private fun KpmModuleItem(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
FilledTonalButton(
|
FilledTonalButton(
|
||||||
onClick = onControl
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
val result = viewModel.controlModule(module.id, module.args)
|
||||||
|
val message = when (result) {
|
||||||
|
0 -> successMessage
|
||||||
|
else -> failureMessage
|
||||||
|
}
|
||||||
|
snackBarHost.showSnackbar(message)
|
||||||
|
onControl()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled = module.hasAction
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.Settings,
|
imageVector = Icons.Outlined.Settings,
|
||||||
|
|||||||
@@ -490,14 +490,14 @@ fun loadKpmModule(path: String, args: String? = null): Boolean {
|
|||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}"
|
val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}"
|
||||||
val result = ShellUtils.fastCmd(shell, cmd)
|
val result = ShellUtils.fastCmd(shell, cmd)
|
||||||
return result.contains("Success")
|
return result.contains("Success", ignoreCase = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unloadKpmModule(name: String): Boolean {
|
fun unloadKpmModule(name: String): Boolean {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
val cmd = "${getKpmmgrPath()} unload $name"
|
val cmd = "${getKpmmgrPath()} unload $name"
|
||||||
val result = ShellUtils.fastCmd(shell, cmd)
|
val result = ShellUtils.fastCmd(shell, cmd)
|
||||||
return result.trim().toIntOrNull() == 0
|
return result.trim().isEmpty() || result.trim() == "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getKpmModuleCount(): Int {
|
fun getKpmModuleCount(): Int {
|
||||||
@@ -507,24 +507,40 @@ fun getKpmModuleCount(): Int {
|
|||||||
return result.trim().toIntOrNull() ?: 0
|
return result.trim().toIntOrNull() ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun runCmd(shell : Shell, cmd : String) : String {
|
||||||
|
return shell.newJob()
|
||||||
|
.add(cmd)
|
||||||
|
.to(mutableListOf<String>(), null)
|
||||||
|
.exec().out
|
||||||
|
.joinToString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
fun listKpmModules(): String {
|
fun listKpmModules(): String {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
val cmd = "${getKpmmgrPath()} list"
|
val cmd = "${getKpmmgrPath()} list"
|
||||||
val result = ShellUtils.fastCmd(shell, cmd)
|
return try {
|
||||||
return result.trim()
|
runCmd(shell, cmd).trim()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to list KPM modules", e)
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getKpmModuleInfo(name: String): String {
|
fun getKpmModuleInfo(name: String): String {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
val cmd = "${getKpmmgrPath()} info $name"
|
val cmd = "${getKpmmgrPath()} info $name"
|
||||||
val result = ShellUtils.fastCmd(shell, cmd)
|
return try {
|
||||||
return result.trim()
|
runCmd(shell, cmd).trim()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to get KPM module info: $name", e)
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun controlKpmModule(name: String, args: String? = null): Int {
|
fun controlKpmModule(name: String, args: String? = null): Int {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
val cmd = "${getKpmmgrPath()} control $name ${args ?: ""}"
|
val cmd = "${getKpmmgrPath()} control $name ${args ?: ""}"
|
||||||
val result = ShellUtils.fastCmd(shell, cmd)
|
val result = runCmd(shell, cmd)
|
||||||
return result.trim().toIntOrNull() ?: -1
|
return result.trim().toIntOrNull() ?: -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ class KpmViewModel : ViewModel() {
|
|||||||
var moduleList by mutableStateOf(emptyList<ModuleInfo>())
|
var moduleList by mutableStateOf(emptyList<ModuleInfo>())
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
|
||||||
var search by mutableStateOf("")
|
var search by mutableStateOf("")
|
||||||
internal set
|
internal set
|
||||||
|
|
||||||
@@ -25,19 +24,6 @@ class KpmViewModel : ViewModel() {
|
|||||||
var currentModuleDetail by mutableStateOf("")
|
var currentModuleDetail by mutableStateOf("")
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun loadModuleDetail(moduleId: String) {
|
|
||||||
viewModelScope.launch {
|
|
||||||
currentModuleDetail = withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
getKpmModuleInfo(moduleId)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
"无法获取模块详细信息: ${e.message}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Log.d("KsuCli", "Module detail: $currentModuleDetail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fetchModuleList() {
|
fun fetchModuleList() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
isRefreshing = true
|
isRefreshing = true
|
||||||
@@ -45,40 +31,96 @@ class KpmViewModel : ViewModel() {
|
|||||||
val moduleCount = getKpmModuleCount()
|
val moduleCount = getKpmModuleCount()
|
||||||
Log.d("KsuCli", "Module count: $moduleCount")
|
Log.d("KsuCli", "Module count: $moduleCount")
|
||||||
|
|
||||||
val moduleInfo = listKpmModules()
|
moduleList = getAllKpmModuleInfo()
|
||||||
Log.d("KsuCli", "Module info: $moduleInfo")
|
|
||||||
|
|
||||||
val modules = parseModuleList(moduleInfo)
|
|
||||||
moduleList = modules
|
|
||||||
|
|
||||||
// 获取 KPM 版本信息
|
// 获取 KPM 版本信息
|
||||||
val kpmVersion = getKpmVersion()
|
val kpmVersion = getKpmVersion()
|
||||||
Log.d("KsuCli", "KPM Version: $kpmVersion")
|
Log.d("KsuCli", "KPM Version: $kpmVersion")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KsuCli", "获取模块列表失败", e)
|
||||||
} finally {
|
} finally {
|
||||||
isRefreshing = false
|
isRefreshing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseModuleList(output: String): List<ModuleInfo> {
|
private fun getAllKpmModuleInfo(): List<ModuleInfo> {
|
||||||
return output.split("\n").mapNotNull { line ->
|
val result = mutableListOf<ModuleInfo>()
|
||||||
if (line.isBlank()) return@mapNotNull null
|
try {
|
||||||
val parts = line.split("|")
|
val str = listKpmModules()
|
||||||
if (parts.size < 7) return@mapNotNull null
|
val moduleNames = str
|
||||||
|
.split("\n")
|
||||||
|
.filter { it.isNotBlank() }
|
||||||
|
|
||||||
ModuleInfo(
|
for (name in moduleNames) {
|
||||||
id = parts[0].trim(),
|
try {
|
||||||
name = parts[1].trim(),
|
val moduleInfo = parseModuleInfo(name)
|
||||||
version = parts[2].trim(),
|
moduleInfo?.let { result.add(it) }
|
||||||
author = parts[3].trim(),
|
} catch (e: Exception) {
|
||||||
description = parts[4].trim(),
|
Log.e("KsuCli", "Error processing module $name", e)
|
||||||
args = parts[6].trim(),
|
}
|
||||||
enabled = true,
|
}
|
||||||
hasAction = controlKpmModule(parts[0].trim()).isNotBlank()
|
} catch (e: Exception) {
|
||||||
)
|
Log.e("KsuCli", "Failed to get module list", e)
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseModuleInfo(name: String): ModuleInfo? {
|
||||||
|
val info = getKpmModuleInfo(name)
|
||||||
|
if (info.isBlank()) return null
|
||||||
|
|
||||||
|
val properties = info.lineSequence()
|
||||||
|
.filter { line ->
|
||||||
|
val trimmed = line.trim()
|
||||||
|
trimmed.isNotEmpty() && !trimmed.startsWith("#")
|
||||||
|
}
|
||||||
|
.mapNotNull { line ->
|
||||||
|
line.split("=", limit = 2).let { parts ->
|
||||||
|
when (parts.size) {
|
||||||
|
2 -> parts[0].trim() to parts[1].trim()
|
||||||
|
1 -> parts[0].trim() to ""
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
return ModuleInfo(
|
||||||
|
id = name,
|
||||||
|
name = properties["name"] ?: name,
|
||||||
|
version = properties["version"] ?: "",
|
||||||
|
author = properties["author"] ?: "",
|
||||||
|
description = properties["description"] ?: "",
|
||||||
|
args = properties["args"] ?: "",
|
||||||
|
enabled = true,
|
||||||
|
hasAction = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadModuleDetail(moduleId: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
currentModuleDetail = withContext(Dispatchers.IO) {
|
||||||
|
getKpmModuleInfo(moduleId)
|
||||||
|
}
|
||||||
|
Log.d("KsuCli", "Module detail loaded: $currentModuleDetail")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KsuCli", "Failed to load module detail", e)
|
||||||
|
currentModuleDetail = "Error: ${e.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun controlModule(moduleId: String, args: String? = null): Int {
|
||||||
|
return try {
|
||||||
|
val result = controlKpmModule(moduleId, args)
|
||||||
|
Log.d("KsuCli", "Control module $moduleId result: $result")
|
||||||
|
result
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KsuCli", "Failed to control module $moduleId", e)
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class ModuleInfo(
|
data class ModuleInfo(
|
||||||
val id: String,
|
val id: String,
|
||||||
|
|||||||
@@ -236,6 +236,8 @@
|
|||||||
<string name="kpm_install_failed">加载kpm模块失败</string>
|
<string name="kpm_install_failed">加载kpm模块失败</string>
|
||||||
<string name="home_kpm_version">KPM 版本</string>
|
<string name="home_kpm_version">KPM 版本</string>
|
||||||
<string name="close_notice">关闭</string>
|
<string name="close_notice">关闭</string>
|
||||||
|
<string name="kpm_control_success">成功</string>
|
||||||
|
<string name="kpm_control_failed">错误</string>
|
||||||
<string name="kernel_module_notice">以下内核模块功能由KernelPatch开发,经过修改后加入SukiSU Ultra的内核模块功能</string>
|
<string name="kernel_module_notice">以下内核模块功能由KernelPatch开发,经过修改后加入SukiSU Ultra的内核模块功能</string>
|
||||||
<string name="home_ContributionCard_kernelsu">SukiSU Ultra展望</string>
|
<string name="home_ContributionCard_kernelsu">SukiSU Ultra展望</string>
|
||||||
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra未来将会成为一个相对独立的KSU分支,但是依然感谢官方KernelSU和MKSU等做出的贡献</string>
|
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra未来将会成为一个相对独立的KSU分支,但是依然感谢官方KernelSU和MKSU等做出的贡献</string>
|
||||||
|
|||||||
@@ -237,10 +237,12 @@
|
|||||||
<string name="kpm_install_success">Load of kpm module successful</string>
|
<string name="kpm_install_success">Load of kpm module successful</string>
|
||||||
<string name="kpm_install_failed">Load of kpm module failed</string>
|
<string name="kpm_install_failed">Load of kpm module failed</string>
|
||||||
<string name="kpm_args">kpm parameters</string>
|
<string name="kpm_args">kpm parameters</string>
|
||||||
<string name="kpm_control">kpm control</string>
|
<string name="kpm_control">fulfillment</string>
|
||||||
<string name="home_kpm_version">KPM Version</string>
|
<string name="home_kpm_version">KPM Version</string>
|
||||||
<string name="close_notice">close</string>
|
<string name="close_notice">close</string>
|
||||||
<string name="kernel_module_notice">The following kernel module functions were developed by KernelPatch and modified to include the kernel module functions of SukiSU Ultra</string>
|
<string name="kernel_module_notice">The following kernel module functions were developed by KernelPatch and modified to include the kernel module functions of SukiSU Ultra</string>
|
||||||
<string name="home_ContributionCard_kernelsu">SukiSU Ultra Look forward to</string>
|
<string name="home_ContributionCard_kernelsu">SukiSU Ultra Look forward to</string>
|
||||||
|
<string name="kpm_control_success">success</string>
|
||||||
|
<string name="kpm_control_failed">failed</string>
|
||||||
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but thanks to the official KernelSU and MKSU etc. for their contributions!</string>
|
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but thanks to the official KernelSU and MKSU etc. for their contributions!</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user