From d0a35c1a9ab1d1aafba1a14e40b8a6f055d8da82 Mon Sep 17 00:00:00 2001 From: Rifat Azad <33044977+rifsxd@users.noreply.github.com> Date: Sun, 23 Feb 2025 10:10:58 +0600 Subject: [PATCH] manager: allow multiple modules to be installed sequentially (#2459) It's now okay to merge --- .../me/weishu/kernelsu/ui/screen/Flash.kt | 33 +++++++++++---- .../me/weishu/kernelsu/ui/screen/Module.kt | 40 ++++++++++++++++--- .../java/me/weishu/kernelsu/ui/util/KsuCli.kt | 14 ------- manager/app/src/main/res/values/strings.xml | 2 + 4 files changed, 62 insertions(+), 27 deletions(-) diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt index 4719004e2dea..6f1ece5a92c2 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt @@ -56,7 +56,7 @@ import me.weishu.kernelsu.ui.component.KeyEventBlocker import me.weishu.kernelsu.ui.util.FlashResult import me.weishu.kernelsu.ui.util.LkmSelection import me.weishu.kernelsu.ui.util.LocalSnackbarHost -import me.weishu.kernelsu.ui.util.flashModules +import me.weishu.kernelsu.ui.util.flashModule import me.weishu.kernelsu.ui.util.installBoot import me.weishu.kernelsu.ui.util.reboot import me.weishu.kernelsu.ui.util.restoreBoot @@ -66,16 +66,33 @@ import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +/** + * @author weishu + * @date 2023/1/1. + */ + enum class FlashingStatus { FLASHING, SUCCESS, FAILED } -/** - * @author weishu - * @date 2023/1/1. - */ +// Lets you flash modules sequentially when mutiple zipUris are selected +fun flashModulesSequentially( + uris: List, + onStdout: (String) -> Unit, + onStderr: (String) -> Unit +): FlashResult { + for (uri in uris) { + flashModule(uri, onStdout, onStderr).apply { + if (code != 0) { + return FlashResult(code, err, showReboot) + } + } + } + return FlashResult(0, "", true) +} + @OptIn(ExperimentalMaterial3Api::class) @Composable @Destination @@ -192,7 +209,7 @@ sealed class FlashIt : Parcelable { data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) : FlashIt() - data class FlashModules(val uri: List) : FlashIt() + data class FlashModules(val uris: List) : FlashIt() data object FlashRestore : FlashIt() @@ -213,7 +230,9 @@ fun flashIt( onStderr ) - is FlashIt.FlashModules -> flashModules(flashIt.uri, onStdout, onStderr) + is FlashIt.FlashModules -> { + flashModulesSequentially(flashIt.uris, onStdout, onStderr) + } FlashIt.FlashRestore -> restoreBoot(onStdout, onStderr) diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt index dba15dedce3b..e2c4b9b8e2b9 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt @@ -36,6 +36,7 @@ import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.outlined.PlayArrow import androidx.compose.material.icons.outlined.Download import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Checkbox @@ -56,6 +57,7 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult import androidx.compose.material3.Switch import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.material3.rememberTopAppBarState @@ -104,6 +106,7 @@ import me.weishu.kernelsu.ui.util.hasMagisk import me.weishu.kernelsu.ui.util.reboot import me.weishu.kernelsu.ui.util.toggleModule import me.weishu.kernelsu.ui.util.uninstallModule +import me.weishu.kernelsu.ui.util.getFileName import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel import me.weishu.kernelsu.ui.webui.WebUIActivity @@ -200,6 +203,12 @@ fun ModuleScreen(navigator: DestinationsNavigator) { floatingActionButton = { if (!hideInstallButton) { val moduleInstall = stringResource(id = R.string.module_install) + val confirmTitle = stringResource(R.string.module) + var zipUris by remember { mutableStateOf>(emptyList()) } + val confirmDialog = rememberConfirmDialog(onConfirm = { + navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(zipUris))) + viewModel.markNeedRefresh() + }) val selectZipLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartActivityForResult() ) { @@ -207,20 +216,38 @@ fun ModuleScreen(navigator: DestinationsNavigator) { return@rememberLauncherForActivityResult } val data = it.data ?: return@rememberLauncherForActivityResult - val uri = data.data ?: return@rememberLauncherForActivityResult - - navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(listOf(uri)))) + val clipData = data.clipData - viewModel.markNeedRefresh() + val uris = mutableListOf() + if (clipData != null) { + for (i in 0 until clipData.itemCount) { + clipData.getItemAt(i)?.uri?.let { uris.add(it) } + } + } else { + data.data?.let { uris.add(it) } + } - Log.i("ModuleScreen", "select zip result: ${it.data}") + if (uris.size == 1) { + navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(listOf(uris.first())))) + } else if (uris.size > 1) { + // multiple files selected + val moduleNames = uris.mapIndexed { index, uri -> "\n${index + 1}. ${uri.getFileName(context)}" }.joinToString("") + val confirmContent = context.getString(R.string.module_install_prompt_with_name, moduleNames) + zipUris = uris + confirmDialog.showConfirm( + title = confirmTitle, + content = confirmContent, + markdown = true + ) + } } ExtendedFloatingActionButton( onClick = { - // select the zip file to install + // Select the zip files to install val intent = Intent(Intent.ACTION_GET_CONTENT).apply { type = "application/zip" + putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) } selectZipLauncher.launch(intent) }, @@ -232,6 +259,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) { contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), snackbarHost = { SnackbarHost(hostState = snackBarHost) } ) { innerPadding -> + when { hasMagisk -> { Box( diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt index 7ab5a1b56f67..ac2a58e797e6 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt @@ -191,20 +191,6 @@ fun flashModule( } } -fun flashModules( - uris: List, - onStdout: (String) -> Unit, - onStderr: (String) -> Unit -): FlashResult { - for (uri in uris) { - val result = flashModule(uri, onStdout, onStderr) - if (result.code != 0) { - return result - } - } - return FlashResult(0, "", true) -} - fun runModuleAction( moduleId: String, onStdout: (String) -> Unit, onStderr: (String) -> Unit ): Boolean { diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index a52eec520a37..8fd40f19849d 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -23,8 +23,10 @@ Failed to disable module: %s No module installed Module + The following modules will be installed: %1$s Sort (Action first) Sort (Enabled first) + Confirm Uninstall Install Install