Optimize the error handling logic for loading and unloading modules, this will fix the file type error, and more detail handling-2

This commit is contained in:
ShirkNeko
2025-04-02 00:36:08 +08:00
parent fbf2799674
commit d925036bd6
7 changed files with 151 additions and 47 deletions

View File

@@ -49,6 +49,7 @@ import shirkneko.zako.sukisu.ui.theme.KernelSUTheme
import shirkneko.zako.sukisu.ui.theme.loadCustomBackground import shirkneko.zako.sukisu.ui.theme.loadCustomBackground
import shirkneko.zako.sukisu.ui.theme.loadThemeMode import shirkneko.zako.sukisu.ui.theme.loadThemeMode
import shirkneko.zako.sukisu.ui.util.LocalSnackbarHost import shirkneko.zako.sukisu.ui.util.LocalSnackbarHost
import shirkneko.zako.sukisu.ui.util.getKpmVersion
import shirkneko.zako.sukisu.ui.util.rootAvailable import shirkneko.zako.sukisu.ui.util.rootAvailable
import shirkneko.zako.sukisu.ui.util.install import shirkneko.zako.sukisu.ui.util.install
@@ -107,6 +108,7 @@ private fun BottomBar(navController: NavHostController) {
val navigator = navController.rememberDestinationsNavigator() val navigator = navController.rememberDestinationsNavigator()
val isManager = Natives.becomeManager(ksuApp.packageName) val isManager = Natives.becomeManager(ksuApp.packageName)
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable() val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
val kpmVersion = getKpmVersion()
// 获取卡片颜色和透明度 // 获取卡片颜色和透明度
val cardColor = MaterialTheme.colorScheme.secondaryContainer val cardColor = MaterialTheme.colorScheme.secondaryContainer
@@ -122,35 +124,69 @@ private fun BottomBar(navController: NavHostController) {
) )
) { ) {
BottomBarDestination.entries.forEach { destination -> BottomBarDestination.entries.forEach { destination ->
if (!fullFeatured && destination.rootRequired) return@forEach if (destination == BottomBarDestination.Kpm) {
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) if (kpmVersion.isNotEmpty() && !kpmVersion.startsWith("Error")) {
NavigationBarItem( if (!fullFeatured && destination.rootRequired) return@forEach
selected = isCurrentDestOnBackStack, val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
onClick = { NavigationBarItem(
if (isCurrentDestOnBackStack) { selected = isCurrentDestOnBackStack,
navigator.popBackStack(destination.direction, false) onClick = {
} if (isCurrentDestOnBackStack) {
navigator.navigate(destination.direction) { navigator.popBackStack(destination.direction, false)
popUpTo(NavGraphs.root) { }
saveState = true navigator.navigate(destination.direction) {
popUpTo(NavGraphs.root) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = {
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
}
},
label = { Text(stringResource(destination.label)) },
alwaysShowLabel = false,
colors = androidx.compose.material3.NavigationBarItemDefaults.colors(
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant
)
)
}
} else {
if (!fullFeatured && destination.rootRequired) return@forEach
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
NavigationBarItem(
selected = isCurrentDestOnBackStack,
onClick = {
if (isCurrentDestOnBackStack) {
navigator.popBackStack(destination.direction, false)
} }
launchSingleTop = true navigator.navigate(destination.direction) {
restoreState = true popUpTo(NavGraphs.root) {
} saveState = true
}, }
icon = { launchSingleTop = true
if (isCurrentDestOnBackStack) { restoreState = true
Icon(destination.iconSelected, stringResource(destination.label)) }
} else { },
Icon(destination.iconNotSelected, stringResource(destination.label)) icon = {
} if (isCurrentDestOnBackStack) {
}, Icon(destination.iconSelected, stringResource(destination.label))
label = { Text(stringResource(destination.label)) }, } else {
alwaysShowLabel = false, Icon(destination.iconNotSelected, stringResource(destination.label))
colors = androidx.compose.material3.NavigationBarItemDefaults.colors( }
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant },
label = { Text(stringResource(destination.label)) },
alwaysShowLabel = false,
colors = androidx.compose.material3.NavigationBarItemDefaults.colors(
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant
)
) )
) }
} }
} }
} }

View File

@@ -21,8 +21,8 @@ enum class BottomBarDestination(
val rootRequired: Boolean, val rootRequired: Boolean,
) { ) {
Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false), Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false),
Kpm(KpmScreenDestination, R.string.kpm_title, Icons.Filled.Build, Icons.Outlined.Build, true),
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true), SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true),
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true), Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true),
Kpm(KpmScreenDestination, R.string.kpm_title, Icons.Filled.Build, Icons.Outlined.Build, true),
Settings(SettingScreenDestination, R.string.settings, Icons.Filled.Settings, Icons.Outlined.Settings, false), Settings(SettingScreenDestination, R.string.settings, Icons.Filled.Settings, Icons.Outlined.Settings, false),
} }

View File

@@ -51,6 +51,10 @@ import androidx.compose.animation.shrinkVertically
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import shirkneko.zako.sukisu.ui.theme.CardConfig import shirkneko.zako.sukisu.ui.theme.CardConfig
import androidx.core.content.edit import androidx.core.content.edit
import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.Scanner
import java.util.zip.GZIPInputStream
import kotlin.random.Random import kotlin.random.Random
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -333,6 +337,14 @@ private fun StatusCard(
text = stringResource(R.string.home_module_count, getModuleCount()), text = stringResource(R.string.home_module_count, getModuleCount()),
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium
) )
val kpmVersion = getKpmVersion()
if (kpmVersion.isNotEmpty() && !kpmVersion.startsWith("Error")) {
Spacer(Modifier.height(4.dp))
Text(
text = stringResource(R.string.home_kpm_module, getKpmModuleCount()),
style = MaterialTheme.typography.bodyMedium
)
}
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
val suSFS = getSuSFS() val suSFS = getSuSFS()
@@ -555,18 +567,28 @@ private fun InfoCard() {
InfoCardItem(stringResource(R.string.home_selinux_status), getSELinuxStatus()) InfoCardItem(stringResource(R.string.home_selinux_status), getSELinuxStatus())
if (!isSimpleMode){
Spacer(Modifier.height(16.dp)) if (!isSimpleMode) {
val kpmVersion = getKpmVersion() val kpmVersion = getKpmVersion()
val displayVersion = if (kpmVersion.isEmpty() || kpmVersion.startsWith("Error")) { var displayVersion: String
stringResource(R.string.not_supported) val isKpmConfigured = checkKpmConfigured()
if (kpmVersion.isEmpty() || kpmVersion.startsWith("Error")) {
val statusText = if (isKpmConfigured) {
stringResource(R.string.kernel_patched)
} else {
stringResource(R.string.kernel_not_enabled)
}
displayVersion = "${stringResource(R.string.not_supported)} ($statusText)"
} else { } else {
kpmVersion displayVersion = "${stringResource(R.string.supported)} ($kpmVersion)"
} }
Spacer(Modifier.height(16.dp))
InfoCardItem(stringResource(R.string.home_kpm_version), displayVersion) InfoCardItem(stringResource(R.string.home_kpm_version), displayVersion)
} }
if (!isSimpleMode) { if (!isSimpleMode) {
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
@@ -645,4 +667,24 @@ private fun getDeviceModel(context: Context): String {
} catch (e: Exception) { } catch (e: Exception) {
Build.DEVICE Build.DEVICE
} }
}
private fun checkKpmConfigured(): Boolean {
try {
val process = Runtime.getRuntime().exec("su -c cat /proc/config.gz")
val inputStream = process.inputStream
val gzipInputStream = GZIPInputStream(inputStream)
val reader = BufferedReader(InputStreamReader(gzipInputStream))
var line: String?
while (reader.readLine().also { line = it } != null) {
if (line?.contains("CONFIG_KPM=y") == true) {
return true
}
}
reader.close()
} catch (e: Exception) {
e.printStackTrace()
}
return false
} }

View File

@@ -4,8 +4,6 @@ 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
@@ -92,6 +90,15 @@ fun KpmScreen(
} }
} }
if (!tempFile.name.endsWith(".kpm")) {
snackBarHost.showSnackbar(
message = "文件类型不正确,请选择 .kpm 文件",
duration = SnackbarDuration.Short
)
tempFile.delete()
return@launch
}
val confirmResult = confirmDialog.awaitConfirm( val confirmResult = confirmDialog.awaitConfirm(
title = kpmInstall, title = kpmInstall,
content = kpmInstallConfirm, content = kpmInstallConfirm,
@@ -102,10 +109,15 @@ fun KpmScreen(
if (confirmResult == ConfirmResult.Confirmed) { if (confirmResult == ConfirmResult.Confirmed) {
val success = loadingDialog.withLoading { val success = loadingDialog.withLoading {
try { try {
loadKpmModule(tempFile.absolutePath) val loadResult = loadKpmModule(tempFile.absolutePath)
true if (true && loadResult.startsWith("Error")) {
Log.e("KsuCli", "Failed to load KPM module: $loadResult")
false
} else {
true
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e("KsuCli", "Failed to load KPM module: ${e.message}") Log.e("KsuCli", "Failed to load KPM module: ${e.message}", e)
false false
} }
} }
@@ -234,13 +246,19 @@ fun KpmScreen(
if (confirmResult == ConfirmResult.Confirmed) { if (confirmResult == ConfirmResult.Confirmed) {
val success = loadingDialog.withLoading { val success = loadingDialog.withLoading {
try { try {
unloadKpmModule(module.id) val unloadResult = unloadKpmModule(module.id)
true if (true && unloadResult.startsWith("Error")) {
Log.e("KsuCli", "Failed to unload KPM module: $unloadResult")
false
} else {
true
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e("KsuCli", "Failed to unload KPM module: ${e.message}") Log.e("KsuCli", "Failed to unload KPM module: ${e.message}", e)
false false
} }
} }
if (success) { if (success) {
viewModel.fetchModuleList() viewModel.fetchModuleList()
snackBarHost.showSnackbar( snackBarHost.showSnackbar(

View File

@@ -486,18 +486,16 @@ private fun getKpmmgrPath(): String {
} }
fun loadKpmModule(path: String, args: String? = null): Boolean { fun loadKpmModule(path: String, args: String? = null): String {
val shell = getRootShell() val shell = getRootShell()
val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}" val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}"
val result = ShellUtils.fastCmd(shell, cmd) return ShellUtils.fastCmd(shell, cmd)
return result.contains("Success", ignoreCase = true)
} }
fun unloadKpmModule(name: String): Boolean { fun unloadKpmModule(name: String): String {
val shell = getRootShell() val shell = getRootShell()
val cmd = "${getKpmmgrPath()} unload $name" val cmd = "${getKpmmgrPath()} unload $name"
val result = ShellUtils.fastCmd(shell, cmd) return ShellUtils.fastCmd(shell, cmd)
return result.trim().isEmpty() || result.trim() == "0"
} }
fun getKpmModuleCount(): Int { fun getKpmModuleCount(): Int {

View File

@@ -239,6 +239,11 @@
<string name="kpm_control_success">成功</string> <string name="kpm_control_success">成功</string>
<string name="kpm_control_failed">错误</string> <string name="kpm_control_failed">错误</string>
<string name="not_supported">不支持</string> <string name="not_supported">不支持</string>
<string name="supported">支持</string>
<string name="home_kpm_module">KPM 模块数:%d </string>
<string name="kpm_invalid_file">KPM 文件无效</string>
<string name="kernel_patched">内核未进行补丁</string>
<string name="kernel_not_enabled">内核未配置</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>

View File

@@ -245,4 +245,9 @@
<string name="kpm_control_failed">failed</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>
<string name="not_supported">unsupported</string> <string name="not_supported">unsupported</string>
<string name="supported">supported</string>
<string name="home_kpm_module">Number of KPM modules%d </string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
</resources> </resources>