You've already forked KernelSU-Next
mirror of
https://github.com/KernelSU-Next/KernelSU-Next.git
synced 2025-08-27 23:46:34 +00:00
manager: allow multiple modules to be installed sequentially
This commit is contained in:
@@ -53,6 +53,7 @@ import com.ramcosta.composedestinations.annotation.RootGraph
|
|||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
@@ -76,6 +77,43 @@ enum class FlashingStatus {
|
|||||||
FAILED
|
FAILED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lets you flash modules sequentially when mutiple zipUris are selected
|
||||||
|
fun flashModulesSequentially(
|
||||||
|
uris: List<Uri>,
|
||||||
|
onFinish: (Boolean, Int) -> Unit,
|
||||||
|
onStdout: (String) -> Unit,
|
||||||
|
onStderr: (String) -> Unit
|
||||||
|
) {
|
||||||
|
val iterator = uris.iterator()
|
||||||
|
|
||||||
|
// Start processing from the first module inside a coroutine
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
// Define the recursive function within the coroutine
|
||||||
|
suspend fun processNext() {
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
// Flash the current module
|
||||||
|
flashModule(iterator.next(), onFinish = { showReboot, code ->
|
||||||
|
// If successful, continue to the next one
|
||||||
|
if (code == 0) {
|
||||||
|
// Recursively call to process the next module
|
||||||
|
launch {
|
||||||
|
processNext()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onFinish(showReboot, code) // If failed, finish the process
|
||||||
|
}
|
||||||
|
}, onStdout, onStderr)
|
||||||
|
} else {
|
||||||
|
// No more modules to process, finish the process
|
||||||
|
onFinish(true, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the process
|
||||||
|
processNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author weishu
|
* @author weishu
|
||||||
* @date 2023/1/1.
|
* @date 2023/1/1.
|
||||||
@@ -202,6 +240,8 @@ sealed class FlashIt : Parcelable {
|
|||||||
|
|
||||||
data class FlashModule(val uri: Uri) : FlashIt()
|
data class FlashModule(val uri: Uri) : FlashIt()
|
||||||
|
|
||||||
|
data class FlashModules(val uris: List<Uri>) : FlashIt()
|
||||||
|
|
||||||
data object FlashRestore : FlashIt()
|
data object FlashRestore : FlashIt()
|
||||||
|
|
||||||
data object FlashUninstall : FlashIt()
|
data object FlashUninstall : FlashIt()
|
||||||
@@ -224,6 +264,10 @@ fun flashIt(
|
|||||||
|
|
||||||
is FlashIt.FlashModule -> flashModule(flashIt.uri, onFinish, onStdout, onStderr)
|
is FlashIt.FlashModule -> flashModule(flashIt.uri, onFinish, onStdout, onStderr)
|
||||||
|
|
||||||
|
is FlashIt.FlashModules -> {
|
||||||
|
flashModulesSequentially(flashIt.uris, onFinish, onStdout, onStderr)
|
||||||
|
}
|
||||||
|
|
||||||
FlashIt.FlashRestore -> restoreBoot(onFinish, onStdout, onStderr)
|
FlashIt.FlashRestore -> restoreBoot(onFinish, onStdout, onStderr)
|
||||||
|
|
||||||
FlashIt.FlashUninstall -> uninstallPermanently(onFinish, onStdout, onStderr)
|
FlashIt.FlashUninstall -> uninstallPermanently(onFinish, onStdout, onStderr)
|
||||||
|
|||||||
@@ -135,6 +135,8 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
var zipUri by remember { mutableStateOf<Uri?>(null) }
|
var zipUri by remember { mutableStateOf<Uri?>(null) }
|
||||||
|
var zipUris by remember { mutableStateOf<List<Uri>>(emptyList()) }
|
||||||
|
|
||||||
var showConfirmDialog by remember { mutableStateOf(false) }
|
var showConfirmDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val webUILauncher = rememberLauncherForActivityResult(
|
val webUILauncher = rememberLauncherForActivityResult(
|
||||||
@@ -216,18 +218,27 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
return@rememberLauncherForActivityResult
|
return@rememberLauncherForActivityResult
|
||||||
}
|
}
|
||||||
val data = result.data ?: return@rememberLauncherForActivityResult
|
val data = result.data ?: return@rememberLauncherForActivityResult
|
||||||
val uri = data.data ?: return@rememberLauncherForActivityResult
|
val clipData = data.clipData
|
||||||
|
|
||||||
// save the selected Uri and trigger confirmation dialog
|
val uris = mutableListOf<Uri>()
|
||||||
zipUri = uri
|
if (clipData != null) {
|
||||||
showConfirmDialog = true
|
for (i in 0 until clipData.itemCount) {
|
||||||
|
clipData.getItemAt(i)?.uri?.let { uris.add(it) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data.data?.let { uris.add(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
zipUris = uris
|
||||||
|
showConfirmDialog = uris.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
},
|
},
|
||||||
@@ -239,17 +250,16 @@ 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 ->
|
||||||
// confirmation dialog
|
// Confirmation dialog
|
||||||
if (showConfirmDialog && zipUri != null) {
|
if (showConfirmDialog && zipUris.isNotEmpty()) {
|
||||||
val moduleName = getFileName(context, zipUri!!)
|
val moduleNames = zipUris.joinToString("\n") { getFileName(context, it) }
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = { showConfirmDialog = false },
|
onDismissRequest = { showConfirmDialog = false },
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(onClick = {
|
TextButton(onClick = {
|
||||||
showConfirmDialog = false
|
showConfirmDialog = false
|
||||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(zipUri!!)))
|
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(zipUris)))
|
||||||
|
|
||||||
viewModel.markNeedRefresh()
|
viewModel.markNeedRefresh()
|
||||||
}) {
|
}) {
|
||||||
Text(stringResource(R.string.confirm))
|
Text(stringResource(R.string.confirm))
|
||||||
@@ -263,13 +273,11 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
title = { Text(stringResource(R.string.module)) },
|
title = { Text(stringResource(R.string.module)) },
|
||||||
text = {
|
text = {
|
||||||
Text(
|
Text(
|
||||||
stringResource(R.string.module_install_prompt_with_name, moduleName)
|
stringResource(R.string.module_install_prompt_with_name, moduleNames)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
when {
|
when {
|
||||||
hasMagisk -> {
|
hasMagisk -> {
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
Reference in New Issue
Block a user