优化KPM模块加载和卸载逻辑,改进错误处理,增强模块信息获取功能
This commit is contained in:
@@ -103,18 +103,15 @@ fun KpmScreen(
|
||||
if (confirmResult == ConfirmResult.Confirmed) {
|
||||
val success = loadingDialog.withLoading {
|
||||
try {
|
||||
val process = ProcessBuilder("nsenter", "-t", "1", "-m").start()
|
||||
process.waitFor()
|
||||
loadKpmModule(tempFile.absolutePath)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
Log.e("KsuCli", "Failed to execute nsenter command: ${e.message}")
|
||||
"failed"
|
||||
Log.e("KsuCli", "Failed to load KPM module: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
Log.d("KsuCli", "loadKpmModule result: $success")
|
||||
|
||||
if (success.contains("Success", ignoreCase = true)) {
|
||||
if (success) {
|
||||
viewModel.fetchModuleList()
|
||||
snackBarHost.showSnackbar(
|
||||
message = kpmInstallSuccess,
|
||||
@@ -136,7 +133,7 @@ fun KpmScreen(
|
||||
viewModel.fetchModuleList()
|
||||
}
|
||||
}
|
||||
// 使用 SharedPreferences 存储声明是否关闭的状态
|
||||
|
||||
val sharedPreferences = context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
|
||||
var isNoticeClosed by remember { mutableStateOf(sharedPreferences.getBoolean("is_notice_closed", false)) }
|
||||
|
||||
@@ -196,7 +193,7 @@ fun KpmScreen(
|
||||
)
|
||||
IconButton(onClick = {
|
||||
isNoticeClosed = true
|
||||
sharedPreferences.edit() { putBoolean("is_notice_closed", true) }
|
||||
sharedPreferences.edit { putBoolean("is_notice_closed", true) }
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Close,
|
||||
@@ -241,10 +238,15 @@ fun KpmScreen(
|
||||
)
|
||||
if (confirmResult == ConfirmResult.Confirmed) {
|
||||
val success = loadingDialog.withLoading {
|
||||
unloadKpmModule(module.id)
|
||||
try {
|
||||
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()
|
||||
snackBarHost.showSnackbar(
|
||||
message = kpmUninstallSuccess,
|
||||
@@ -323,7 +325,8 @@ private fun KpmModuleItem(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
FilledTonalButton(
|
||||
onClick = onControl
|
||||
onClick = onControl,
|
||||
enabled = module.hasAction
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Settings,
|
||||
|
||||
@@ -490,14 +490,14 @@ fun loadKpmModule(path: String, args: String? = null): Boolean {
|
||||
val shell = getRootShell()
|
||||
val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}"
|
||||
val result = ShellUtils.fastCmd(shell, cmd)
|
||||
return result.contains("Success")
|
||||
return result.contains("Success", ignoreCase = true)
|
||||
}
|
||||
|
||||
fun unloadKpmModule(name: String): Boolean {
|
||||
val shell = getRootShell()
|
||||
val cmd = "${getKpmmgrPath()} unload $name"
|
||||
val result = ShellUtils.fastCmd(shell, cmd)
|
||||
return result.trim().toIntOrNull() == 0
|
||||
return result.trim().isEmpty() || result.trim() == "0"
|
||||
}
|
||||
|
||||
fun getKpmModuleCount(): Int {
|
||||
@@ -510,15 +510,23 @@ fun getKpmModuleCount(): Int {
|
||||
fun listKpmModules(): String {
|
||||
val shell = getRootShell()
|
||||
val cmd = "${getKpmmgrPath()} list"
|
||||
val result = ShellUtils.fastCmd(shell, cmd)
|
||||
return result.trim()
|
||||
return try {
|
||||
ShellUtils.fastCmd(shell, cmd).trim()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to list KPM modules", e)
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
fun getKpmModuleInfo(name: String): String {
|
||||
val shell = getRootShell()
|
||||
val cmd = "${getKpmmgrPath()} info $name"
|
||||
val result = ShellUtils.fastCmd(shell, cmd)
|
||||
return result.trim()
|
||||
return try {
|
||||
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 {
|
||||
|
||||
@@ -15,7 +15,6 @@ class KpmViewModel : ViewModel() {
|
||||
var moduleList by mutableStateOf(emptyList<ModuleInfo>())
|
||||
private set
|
||||
|
||||
|
||||
var search by mutableStateOf("")
|
||||
internal set
|
||||
|
||||
@@ -25,18 +24,6 @@ class KpmViewModel : ViewModel() {
|
||||
var currentModuleDetail by mutableStateOf("")
|
||||
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() {
|
||||
viewModelScope.launch {
|
||||
@@ -45,37 +32,78 @@ class KpmViewModel : ViewModel() {
|
||||
val moduleCount = getKpmModuleCount()
|
||||
Log.d("KsuCli", "Module count: $moduleCount")
|
||||
|
||||
val moduleInfo = listKpmModules()
|
||||
Log.d("KsuCli", "Module info: $moduleInfo")
|
||||
|
||||
val modules = parseModuleList(moduleInfo)
|
||||
moduleList = modules
|
||||
moduleList = getAllKpmModuleInfo()
|
||||
|
||||
// 获取 KPM 版本信息
|
||||
val kpmVersion = getKpmVersion()
|
||||
Log.d("KsuCli", "KPM Version: $kpmVersion")
|
||||
} catch (e: Exception) {
|
||||
Log.e("KsuCli", "获取模块列表失败", e)
|
||||
} finally {
|
||||
isRefreshing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseModuleList(output: String): List<ModuleInfo> {
|
||||
return output.split("\n").mapNotNull { line ->
|
||||
if (line.isBlank()) return@mapNotNull null
|
||||
val parts = line.split("|")
|
||||
if (parts.size < 7) return@mapNotNull null
|
||||
private fun getAllKpmModuleInfo(): List<ModuleInfo> {
|
||||
val result = mutableListOf<ModuleInfo>()
|
||||
try {
|
||||
val moduleNames = listKpmModules()
|
||||
.split("\n")
|
||||
.filter { it.isNotBlank() }
|
||||
|
||||
ModuleInfo(
|
||||
id = parts[0].trim(),
|
||||
name = parts[1].trim(),
|
||||
version = parts[2].trim(),
|
||||
author = parts[3].trim(),
|
||||
description = parts[4].trim(),
|
||||
args = parts[6].trim(),
|
||||
enabled = true,
|
||||
hasAction = controlKpmModule(parts[0].trim()).isNotBlank()
|
||||
)
|
||||
for (name in moduleNames) {
|
||||
try {
|
||||
val moduleInfo = parseModuleInfo(name)
|
||||
moduleInfo?.let { result.add(it) }
|
||||
} catch (e: Exception) {
|
||||
Log.e("KsuCli", "Error processing module $name", e)
|
||||
}
|
||||
}
|
||||
} 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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user