From a30dfbc15dffa1e5631a66dcd849c7dc86d52d74 Mon Sep 17 00:00:00 2001 From: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:34:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96KPM=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E5=92=8C=E5=8D=B8=E8=BD=BD=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E6=94=B9=E8=BF=9B=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=BC=BA=E6=A8=A1=E5=9D=97=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shirkneko/zako/sukisu/ui/screen/kpm.kt | 29 +++--- .../shirkneko/zako/sukisu/ui/util/KsuCli.kt | 20 ++-- .../zako/sukisu/ui/viewmodel/KpmViewModel.kt | 96 ++++++++++++------- 3 files changed, 92 insertions(+), 53 deletions(-) diff --git a/manager/app/src/main/java/shirkneko/zako/sukisu/ui/screen/kpm.kt b/manager/app/src/main/java/shirkneko/zako/sukisu/ui/screen/kpm.kt index 6c7729ba..ad627ea3 100644 --- a/manager/app/src/main/java/shirkneko/zako/sukisu/ui/screen/kpm.kt +++ b/manager/app/src/main/java/shirkneko/zako/sukisu/ui/screen/kpm.kt @@ -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, diff --git a/manager/app/src/main/java/shirkneko/zako/sukisu/ui/util/KsuCli.kt b/manager/app/src/main/java/shirkneko/zako/sukisu/ui/util/KsuCli.kt index 1b5df7db..d6374e78 100644 --- a/manager/app/src/main/java/shirkneko/zako/sukisu/ui/util/KsuCli.kt +++ b/manager/app/src/main/java/shirkneko/zako/sukisu/ui/util/KsuCli.kt @@ -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 { diff --git a/manager/app/src/main/java/shirkneko/zako/sukisu/ui/viewmodel/KpmViewModel.kt b/manager/app/src/main/java/shirkneko/zako/sukisu/ui/viewmodel/KpmViewModel.kt index 803c3222..36320e49 100644 --- a/manager/app/src/main/java/shirkneko/zako/sukisu/ui/viewmodel/KpmViewModel.kt +++ b/manager/app/src/main/java/shirkneko/zako/sukisu/ui/viewmodel/KpmViewModel.kt @@ -15,7 +15,6 @@ class KpmViewModel : ViewModel() { var moduleList by mutableStateOf(emptyList()) 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 { - 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 { + val result = mutableListOf() + 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}" + } } } @@ -90,4 +118,4 @@ class KpmViewModel : ViewModel() { val enabled: Boolean, val hasAction: Boolean ) -} \ No newline at end of file +}