优化KPM模块加载和卸载逻辑,改进错误处理,增强模块信息获取功能

This commit is contained in:
ShirkNeko
2025-04-01 14:34:24 +08:00
parent c8b3e953ad
commit a30dfbc15d
3 changed files with 92 additions and 53 deletions

View File

@@ -103,18 +103,15 @@ 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,
@@ -136,7 +133,7 @@ fun KpmScreen(
viewModel.fetchModuleList() viewModel.fetchModuleList()
} }
} }
// 使用 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,
@@ -241,10 +238,15 @@ fun KpmScreen(
) )
if (confirmResult == ConfirmResult.Confirmed) { if (confirmResult == ConfirmResult.Confirmed) {
val success = loadingDialog.withLoading { val success = loadingDialog.withLoading {
try {
unloadKpmModule(module.id) unloadKpmModule(module.id)
true
} catch (e: Exception) {
Log.e("KsuCli", "Failed to unload KPM module: ${e.message}")
false
} }
Log.d("KsuCli", "unloadKpmModule result: $success") }
if (success.contains("Success", ignoreCase = true)) { if (success) {
viewModel.fetchModuleList() viewModel.fetchModuleList()
snackBarHost.showSnackbar( snackBarHost.showSnackbar(
message = kpmUninstallSuccess, message = kpmUninstallSuccess,
@@ -323,7 +325,8 @@ private fun KpmModuleItem(
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
FilledTonalButton( FilledTonalButton(
onClick = onControl onClick = onControl,
enabled = module.hasAction
) { ) {
Icon( Icon(
imageVector = Icons.Outlined.Settings, imageVector = Icons.Outlined.Settings,

View File

@@ -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 {
@@ -510,15 +510,23 @@ fun getKpmModuleCount(): Int {
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() ShellUtils.fastCmd(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() ShellUtils.fastCmd(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 {

View File

@@ -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,18 +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 {
@@ -45,38 +32,79 @@ 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 moduleNames = listKpmModules()
if (parts.size < 7) return@mapNotNull null .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.lines()
.filter { it.isNotBlank() && !it.startsWith("#") }
.associate { line ->
val parts = line.split("=", limit = 2)
if (parts.size == 2) {
parts[0].trim() to parts[1].trim()
} else {
parts[0].trim() to ""
}
}
return ModuleInfo(
id = name,
name = properties["name"] ?: name,
version = properties["version"] ?: "unknown",
author = properties["author"] ?: "unknown",
description = properties["description"] ?: "",
args = properties["args"] ?: "",
enabled = true, // 默认启用
hasAction = properties["has_action"]?.toBoolean() ?: false
) )
} }
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}"
}
}
} }