diff --git a/manager/app/src/main/java/shirkneko/zako/sukisu/ui/screen/Home.kt b/manager/app/src/main/java/shirkneko/zako/sukisu/ui/screen/Home.kt index 5e0c162d..4b34f607 100644 --- a/manager/app/src/main/java/shirkneko/zako/sukisu/ui/screen/Home.kt +++ b/manager/app/src/main/java/shirkneko/zako/sukisu/ui/screen/Home.kt @@ -51,6 +51,7 @@ import androidx.compose.animation.shrinkVertically import androidx.compose.runtime.saveable.rememberSaveable import shirkneko.zako.sukisu.ui.theme.CardConfig import androidx.core.content.edit +import shirkneko.zako.sukisu.ui.util.KernelConfigUtils.isKpmEnabled @OptIn(ExperimentalMaterial3Api::class) @Destination(start = true) @@ -157,6 +158,7 @@ fun HomeScreen(navigator: DestinationsNavigator) { if (!isSimpleMode) { DonateCard() LearnMoreCard() + ContributionCard() } Spacer(Modifier) @@ -347,6 +349,15 @@ private fun StatusCard( style = MaterialTheme.typography.bodyMedium ) } + Spacer(modifier = Modifier.height(4.dp)) + + if (isKpmEnabled()) { + val kpmVersion = getKpmVersion() + Text( + text = stringResource(R.string.home_kpm_version, kpmVersion), + style = MaterialTheme.typography.bodyMedium + ) + } } } @@ -404,6 +415,32 @@ fun WarningCard( } } } +@Composable +fun ContributionCard() { + ElevatedCard( + colors = getCardColors(MaterialTheme.colorScheme.secondaryContainer), + elevation = CardDefaults.cardElevation(defaultElevation = getCardElevation()) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(24.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Column { + Text( + text = stringResource(R.string.home_ContributionCard_kernelsu), + style = MaterialTheme.typography.titleSmall + ) + Spacer(Modifier.height(4.dp)) + Text( + text = stringResource(R.string.home_click_to_ContributionCard_kernelsu), + style = MaterialTheme.typography.bodyMedium + ) + } + } + } +} @Composable fun LearnMoreCard() { 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 ecbc59a6..7054a447 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 @@ -1,6 +1,7 @@ package shirkneko.zako.sukisu.ui.screen import android.app.Activity.RESULT_OK +import android.content.Context import android.content.Intent import android.util.Log import androidx.activity.compose.rememberLauncherForActivityResult @@ -35,7 +36,15 @@ import shirkneko.zako.sukisu.ui.viewmodel.KpmViewModel import shirkneko.zako.sukisu.ui.util.loadKpmModule import shirkneko.zako.sukisu.ui.util.unloadKpmModule import java.io.File +import androidx.core.content.edit +import shirkneko.zako.sukisu.ui.theme.ThemeConfig + +/** + * KPM 管理界面 + * 以下内核模块功能由KernelPatch开发,经过修改后加入SukiSU Ultra的内核模块功能 + * 开发者:Shirkneko, Liaokong + */ @OptIn(ExperimentalMaterial3Api::class) @Destination @Composable @@ -48,6 +57,11 @@ fun KpmScreen( val snackBarHost = remember { SnackbarHostState() } val confirmDialog = rememberConfirmDialog() val loadingDialog = rememberLoadingDialog() + val cardColor = if (!ThemeConfig.useDynamicColor) { + ThemeConfig.currentTheme.ButtonContrast + } else { + MaterialTheme.colorScheme.secondaryContainer + } val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) @@ -116,6 +130,9 @@ 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)) } Scaffold( topBar = { @@ -151,69 +168,96 @@ fun KpmScreen( ) }, text = { Text(stringResource(R.string.kpm_install)) }, - containerColor = MaterialTheme.colorScheme.secondaryContainer, + containerColor = cardColor.copy(alpha = 1f), contentColor = MaterialTheme.colorScheme.onSecondaryContainer ) }, snackbarHost = { SnackbarHost(snackBarHost) } ) { padding -> - PullToRefreshBox( - onRefresh = { viewModel.fetchModuleList() }, - isRefreshing = viewModel.isRefreshing, - modifier = Modifier.padding(padding) - ) { - if (viewModel.moduleList.isEmpty()) { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center + Column(modifier = Modifier.padding(padding)) { + if (!isNoticeClosed) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically ) { Text( - stringResource(R.string.kpm_empty), + text = stringResource(R.string.kernel_module_notice), + modifier = Modifier.weight(1f), textAlign = TextAlign.Center ) + IconButton(onClick = { + isNoticeClosed = true + sharedPreferences.edit() { putBoolean("is_notice_closed", true) } + }) { + Icon( + imageVector = Icons.Outlined.Close, + contentDescription = stringResource(R.string.close_notice) + ) + } } - } else { - LazyColumn( - modifier = Modifier.fillMaxSize(), - contentPadding = PaddingValues(16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - items(viewModel.moduleList) { module -> - val kpmUninstallConfirm = String.format(kpmUninstallConfirmTemplate, module.name) - KpmModuleItem( - module = module, - onUninstall = { - scope.launch { - val confirmResult = confirmDialog.awaitConfirm( - title = kpmUninstall, - content = kpmUninstallConfirm, - confirm = uninstall, - dismiss = cancel - ) - if (confirmResult == ConfirmResult.Confirmed) { - val success = loadingDialog.withLoading { - unloadKpmModule(module.id) - } - Log.d("KsuCli", "unloadKpmModule result: $success") - if (success == "success") { - viewModel.fetchModuleList() - snackBarHost.showSnackbar( - message = kpmUninstallSuccess, - duration = SnackbarDuration.Long - ) - } else { - snackBarHost.showSnackbar( - message = kpmUninstallFailed, - duration = SnackbarDuration.Long - ) + } + + PullToRefreshBox( + onRefresh = { viewModel.fetchModuleList() }, + isRefreshing = viewModel.isRefreshing, + modifier = Modifier + ) { + if (viewModel.moduleList.isEmpty()) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text( + stringResource(R.string.kpm_empty), + textAlign = TextAlign.Center + ) + } + } else { + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + items(viewModel.moduleList) { module -> + val kpmUninstallConfirm = String.format(kpmUninstallConfirmTemplate, module.name) + KpmModuleItem( + module = module, + onUninstall = { + scope.launch { + val confirmResult = confirmDialog.awaitConfirm( + title = kpmUninstall, + content = kpmUninstallConfirm, + confirm = uninstall, + dismiss = cancel + ) + if (confirmResult == ConfirmResult.Confirmed) { + val success = loadingDialog.withLoading { + unloadKpmModule(module.id) + } + Log.d("KsuCli", "unloadKpmModule result: $success") + if (success == "success") { + viewModel.fetchModuleList() + snackBarHost.showSnackbar( + message = kpmUninstallSuccess, + duration = SnackbarDuration.Long + ) + } else { + snackBarHost.showSnackbar( + message = kpmUninstallFailed, + duration = SnackbarDuration.Long + ) + } } } + }, + onControl = { + viewModel.loadModuleDetail(module.id) } - }, - onControl = { - viewModel.loadModuleDetail(module.id) - } - ) + ) + } } } } diff --git a/manager/app/src/main/java/shirkneko/zako/sukisu/ui/util/KernelConfigUtils.kt b/manager/app/src/main/java/shirkneko/zako/sukisu/ui/util/KernelConfigUtils.kt new file mode 100644 index 00000000..ba32d3df --- /dev/null +++ b/manager/app/src/main/java/shirkneko/zako/sukisu/ui/util/KernelConfigUtils.kt @@ -0,0 +1,15 @@ +package shirkneko.zako.sukisu.ui.util + +import java.io.File + +object KernelConfigUtils { + + fun isKpmEnabled(): Boolean { + return try { + val config = File("/proc/config.gz").readText() + config.contains("CONFIG_KPM=y") + } catch (e: Exception) { + false + } + } +} \ No newline at end of file diff --git a/manager/app/src/main/res/values-zh-rCN/strings.xml b/manager/app/src/main/res/values-zh-rCN/strings.xml index 1e350a03..7a345adc 100644 --- a/manager/app/src/main/res/values-zh-rCN/strings.xml +++ b/manager/app/src/main/res/values-zh-rCN/strings.xml @@ -234,4 +234,9 @@ 确认安装吗? 安装kpm模块成功 安装kpm模块失败 + KPM 版本: %s + 关闭 + 以下内核模块功能由KernelPatch开发,经过修改后加入SukiSU Ultra的内核模块功能 + SukiSU Ultra展望 + SukiSU Ultra未来将会成为一个相对独立的KSU分支,但是依然感谢官方KernelSU和MKSU等做出的贡献 \ No newline at end of file diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index 9caafe45..861e94c3 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -236,6 +236,11 @@ Confirm installation? Installation of kpm module successful Installation of kpm module failed - kpm 参数 - kpm 控制 + kpm parameters + kpm control + KPM Version: %s + close + The following kernel module functions were developed by KernelPatch and modified to include the kernel module functions of SukiSU Ultra + SukiSU Ultra Look forward to + 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!