manager: allow multiple modules to be installed sequentially (#2459)
It's now okay to merge
This commit is contained in:
@@ -56,7 +56,7 @@ import me.weishu.kernelsu.ui.component.KeyEventBlocker
|
|||||||
import me.weishu.kernelsu.ui.util.FlashResult
|
import me.weishu.kernelsu.ui.util.FlashResult
|
||||||
import me.weishu.kernelsu.ui.util.LkmSelection
|
import me.weishu.kernelsu.ui.util.LkmSelection
|
||||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
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.installBoot
|
||||||
import me.weishu.kernelsu.ui.util.reboot
|
import me.weishu.kernelsu.ui.util.reboot
|
||||||
import me.weishu.kernelsu.ui.util.restoreBoot
|
import me.weishu.kernelsu.ui.util.restoreBoot
|
||||||
@@ -66,16 +66,33 @@ import java.text.SimpleDateFormat
|
|||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author weishu
|
||||||
|
* @date 2023/1/1.
|
||||||
|
*/
|
||||||
|
|
||||||
enum class FlashingStatus {
|
enum class FlashingStatus {
|
||||||
FLASHING,
|
FLASHING,
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
FAILED
|
FAILED
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Lets you flash modules sequentially when mutiple zipUris are selected
|
||||||
* @author weishu
|
fun flashModulesSequentially(
|
||||||
* @date 2023/1/1.
|
uris: List<Uri>,
|
||||||
*/
|
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)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@Destination<RootGraph>
|
@Destination<RootGraph>
|
||||||
@@ -192,7 +209,7 @@ sealed class FlashIt : Parcelable {
|
|||||||
data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) :
|
data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) :
|
||||||
FlashIt()
|
FlashIt()
|
||||||
|
|
||||||
data class FlashModules(val uri: List<Uri>) : FlashIt()
|
data class FlashModules(val uris: List<Uri>) : FlashIt()
|
||||||
|
|
||||||
data object FlashRestore : FlashIt()
|
data object FlashRestore : FlashIt()
|
||||||
|
|
||||||
@@ -213,7 +230,9 @@ fun flashIt(
|
|||||||
onStderr
|
onStderr
|
||||||
)
|
)
|
||||||
|
|
||||||
is FlashIt.FlashModules -> flashModules(flashIt.uri, onStdout, onStderr)
|
is FlashIt.FlashModules -> {
|
||||||
|
flashModulesSequentially(flashIt.uris, onStdout, onStderr)
|
||||||
|
}
|
||||||
|
|
||||||
FlashIt.FlashRestore -> restoreBoot(onStdout, onStderr)
|
FlashIt.FlashRestore -> restoreBoot(onStdout, onStderr)
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import androidx.compose.material.icons.filled.MoreVert
|
|||||||
import androidx.compose.material.icons.outlined.PlayArrow
|
import androidx.compose.material.icons.outlined.PlayArrow
|
||||||
import androidx.compose.material.icons.outlined.Download
|
import androidx.compose.material.icons.outlined.Download
|
||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
@@ -56,6 +57,7 @@ import androidx.compose.material3.SnackbarHostState
|
|||||||
import androidx.compose.material3.SnackbarResult
|
import androidx.compose.material3.SnackbarResult
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
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.reboot
|
||||||
import me.weishu.kernelsu.ui.util.toggleModule
|
import me.weishu.kernelsu.ui.util.toggleModule
|
||||||
import me.weishu.kernelsu.ui.util.uninstallModule
|
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.viewmodel.ModuleViewModel
|
||||||
import me.weishu.kernelsu.ui.webui.WebUIActivity
|
import me.weishu.kernelsu.ui.webui.WebUIActivity
|
||||||
|
|
||||||
@@ -200,6 +203,12 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
if (!hideInstallButton) {
|
if (!hideInstallButton) {
|
||||||
val moduleInstall = stringResource(id = R.string.module_install)
|
val moduleInstall = stringResource(id = R.string.module_install)
|
||||||
|
val confirmTitle = stringResource(R.string.module)
|
||||||
|
var zipUris by remember { mutableStateOf<List<Uri>>(emptyList()) }
|
||||||
|
val confirmDialog = rememberConfirmDialog(onConfirm = {
|
||||||
|
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(zipUris)))
|
||||||
|
viewModel.markNeedRefresh()
|
||||||
|
})
|
||||||
val selectZipLauncher = rememberLauncherForActivityResult(
|
val selectZipLauncher = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.StartActivityForResult()
|
contract = ActivityResultContracts.StartActivityForResult()
|
||||||
) {
|
) {
|
||||||
@@ -207,20 +216,38 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
return@rememberLauncherForActivityResult
|
return@rememberLauncherForActivityResult
|
||||||
}
|
}
|
||||||
val data = it.data ?: return@rememberLauncherForActivityResult
|
val data = it.data ?: return@rememberLauncherForActivityResult
|
||||||
val uri = data.data ?: return@rememberLauncherForActivityResult
|
val clipData = data.clipData
|
||||||
|
|
||||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(listOf(uri))))
|
val uris = mutableListOf<Uri>()
|
||||||
|
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) }
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.markNeedRefresh()
|
if (uris.size == 1) {
|
||||||
|
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(listOf(uris.first()))))
|
||||||
Log.i("ModuleScreen", "select zip result: ${it.data}")
|
} 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(
|
ExtendedFloatingActionButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
// select the zip file to install
|
// Select the zip files to install
|
||||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
type = "application/zip"
|
type = "application/zip"
|
||||||
|
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||||
}
|
}
|
||||||
selectZipLauncher.launch(intent)
|
selectZipLauncher.launch(intent)
|
||||||
},
|
},
|
||||||
@@ -232,6 +259,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||||
snackbarHost = { SnackbarHost(hostState = snackBarHost) }
|
snackbarHost = { SnackbarHost(hostState = snackBarHost) }
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
|
|
||||||
when {
|
when {
|
||||||
hasMagisk -> {
|
hasMagisk -> {
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
@@ -191,20 +191,6 @@ fun flashModule(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun flashModules(
|
|
||||||
uris: List<Uri>,
|
|
||||||
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(
|
fun runModuleAction(
|
||||||
moduleId: String, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
moduleId: String, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
|||||||
@@ -23,8 +23,10 @@
|
|||||||
<string name="module_failed_to_disable">Failed to disable module: %s</string>
|
<string name="module_failed_to_disable">Failed to disable module: %s</string>
|
||||||
<string name="module_empty">No module installed</string>
|
<string name="module_empty">No module installed</string>
|
||||||
<string name="module">Module</string>
|
<string name="module">Module</string>
|
||||||
|
<string name="module_install_prompt_with_name">The following modules will be installed: %1$s</string>
|
||||||
<string name="module_sort_action_first">Sort (Action first)</string>
|
<string name="module_sort_action_first">Sort (Action first)</string>
|
||||||
<string name="module_sort_enabled_first">Sort (Enabled first)</string>
|
<string name="module_sort_enabled_first">Sort (Enabled first)</string>
|
||||||
|
<string name="confirm">Confirm</string>
|
||||||
<string name="uninstall">Uninstall</string>
|
<string name="uninstall">Uninstall</string>
|
||||||
<string name="module_install">Install</string>
|
<string name="module_install">Install</string>
|
||||||
<string name="install">Install</string>
|
<string name="install">Install</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user