From f0b18a1e18b815c9a76007aa73b8b0e4d331659c Mon Sep 17 00:00:00 2001 From: weishu Date: Sat, 13 Apr 2024 12:46:48 +0800 Subject: [PATCH] manager: Add uninstall ui --- .../me/weishu/kernelsu/ui/screen/Settings.kt | 114 ++++++++++++++++++ .../src/main/res/values-zh-rCN/strings.xml | 7 ++ manager/app/src/main/res/values/strings.xml | 7 ++ 3 files changed, 128 insertions(+) diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt index 963e1c11..8cbd839f 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt @@ -3,16 +3,20 @@ package me.weishu.kernelsu.ui.screen import android.content.Context import android.content.Intent import android.net.Uri +import android.widget.Toast import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.Undo import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.BugReport import androidx.compose.material.icons.filled.Compress import androidx.compose.material.icons.filled.ContactPage +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.DeleteForever import androidx.compose.material.icons.filled.DeveloperMode import androidx.compose.material.icons.filled.Fence import androidx.compose.material.icons.filled.RemoveModerator @@ -31,10 +35,17 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.core.content.FileProvider +import com.maxkeppeker.sheets.core.models.base.Header +import com.maxkeppeker.sheets.core.models.base.IconSource +import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState +import com.maxkeppeler.sheets.list.ListDialog +import com.maxkeppeler.sheets.list.models.ListOption +import com.maxkeppeler.sheets.list.models.ListSelection import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator @@ -46,6 +57,7 @@ import me.weishu.kernelsu.Natives import me.weishu.kernelsu.R import me.weishu.kernelsu.ui.component.AboutDialog import me.weishu.kernelsu.ui.component.ConfirmResult +import me.weishu.kernelsu.ui.component.DialogHandle import me.weishu.kernelsu.ui.component.SwitchItem import me.weishu.kernelsu.ui.component.rememberConfirmDialog import me.weishu.kernelsu.ui.component.rememberCustomDialog @@ -200,6 +212,13 @@ fun SettingScreen(navigator: DestinationsNavigator) { } ) + val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode + if (lkmMode) { + UninstallItem { + loadingDialog.withLoading(it) + } + } + val about = stringResource(id = R.string.about) ListItem( leadingContent = { @@ -217,6 +236,101 @@ fun SettingScreen(navigator: DestinationsNavigator) { } } +@Composable +fun UninstallItem(withLoading: suspend (suspend () -> Unit) -> Unit) { + val context = LocalContext.current + val scope = rememberCoroutineScope() + val uninstallConfirmDialog = rememberConfirmDialog() + val showTodo = { + Toast.makeText(context, "TODO", Toast.LENGTH_SHORT).show() + } + val uninstallDialog = rememberUninstallDialog { uninstallType -> + scope.launch { + val result = uninstallConfirmDialog.awaitConfirm( + title = context.getString(uninstallType.title), + content = context.getString(uninstallType.message) + ) + if (result == ConfirmResult.Confirmed) { + withLoading { + when (uninstallType) { + UninstallType.TEMPORARY -> showTodo() + UninstallType.PERMANENT -> showTodo() + UninstallType.RESTORE_STOCK_IMAGE -> showTodo() + UninstallType.NONE -> Unit + } + } + } + } + } + val uninstall = stringResource(id = R.string.settings_uninstall) + ListItem( + leadingContent = { + Icon( + Icons.Filled.Delete, + uninstall + ) + }, + headlineContent = { Text(uninstall) }, + modifier = Modifier.clickable { + uninstallDialog.show() + } + ) +} + +enum class UninstallType(val title: Int, val message: Int, val icon: ImageVector) { + TEMPORARY( + R.string.settings_uninstall_temporary, + R.string.settings_uninstall_temporary_message, + Icons.Filled.Delete + ), + PERMANENT( + R.string.settings_uninstall_permanent, + R.string.settings_uninstall_permanent_message, + Icons.Filled.DeleteForever + ), + RESTORE_STOCK_IMAGE( + R.string.settings_restore_stock_image, + R.string.settings_restore_stock_image_message, + Icons.AutoMirrored.Filled.Undo + ), + NONE(0, 0, Icons.Filled.Delete) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle { + return rememberCustomDialog { dismiss -> + val options = listOf( + UninstallType.TEMPORARY, + UninstallType.PERMANENT, + UninstallType.RESTORE_STOCK_IMAGE + ) + val listOptions = options.map { + ListOption( + titleText = stringResource(it.title), + subtitleText = if (it.message != 0) stringResource(it.message) else null, + icon = IconSource(it.icon) + ) + } + + var selection = UninstallType.NONE + ListDialog(state = rememberUseCaseState(visible = true, onFinishedRequest = { + if (selection != UninstallType.NONE) { + onSelected(selection) + } + }, onCloseRequest = { + dismiss() + }), header = Header.Default( + title = stringResource(R.string.settings_uninstall), + ), selection = ListSelection.Single( + showRadioButtons = false, + options = listOptions, + ) { index, _ -> + selection = options[index] + }) + } +} + @OptIn(ExperimentalMaterial3Api::class) @Composable private fun TopBar(onBack: () -> Unit = {}) { 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 6d6b3c6e..3e1a8814 100644 --- a/manager/app/src/main/res/values-zh-rCN/strings.xml +++ b/manager/app/src/main/res/values-zh-rCN/strings.xml @@ -117,4 +117,11 @@ 选择 KMI 最小化稀疏文件 将模块所在的稀疏文件镜像调整为其实际大小,注意这可能导致模块工作异常,请仅在必要时(如备份)使用。 + 卸载 + 临时卸载 + 永久卸载 + 恢复原厂镜像 + 临时卸载 KernelSU,下次重启后恢复 + 完全并永久移除 KernelSU 和所有模块 + 恢复原厂镜像,一般在 OTA 前使用;如需卸载请使用“永久卸载” \ 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 cf7c88fe..91299724 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -119,4 +119,11 @@ Select KMI Minimize sparse image Resize the sparse image where the module is located to its actual size. Note that this may cause the module to work abnormally, so please only use when necessary (such as for backup) + Uninstall + Uninstall Temporarily + Uninstall Permanently + Restore Stock Image + Temporarily uninstall KernelSU, restore to original state after next reboot. + Uninstalling KernelSU(Root and all modules) completely and permanently. + Restore the stock factory image (if a backup exists), usually used before OTA; if you need to uninstall KernelSU, please use \"Permanent Uninstall\". \ No newline at end of file