diff --git a/manager/app/src/main/AndroidManifest.xml b/manager/app/src/main/AndroidManifest.xml
index c3ec0dea..9c4fdfea 100644
--- a/manager/app/src/main/AndroidManifest.xml
+++ b/manager/app/src/main/AndroidManifest.xml
@@ -24,6 +24,15 @@
+
+
+
+
+
+
+
+
+
{
+ intent.data ?: intent.getParcelableExtra(Intent.EXTRA_STREAM)
+ }
+ else -> null
+ }?.takeIf { it.toString().endsWith(".zip", ignoreCase = true) }
+
setContent {
// Read AMOLED mode preference
val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
@@ -82,6 +94,18 @@ class MainActivity : ComponentActivity() {
val snackBarHostState = remember { SnackbarHostState() }
val currentDestination = navController.currentBackStackEntryAsState()?.value?.destination
+ val navigator = navController.rememberDestinationsNavigator()
+
+ LaunchedEffect(zipUri) {
+ if (zipUri != null) {
+ navigator.navigate(
+ FlashScreenDestination(
+ FlashIt.FlashModules(listOf(zipUri))
+ )
+ )
+ }
+ }
+
val showBottomBar = when (currentDestination?.route) {
FlashScreenDestination.route -> false // Hide for FlashScreenDestination
ExecuteModuleActionScreenDestination.route -> false // Hide for ExecuteModuleActionScreen
diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Flash.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Flash.kt
index b3b9e82a..9d5bbb63 100644
--- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Flash.kt
+++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Flash.kt
@@ -63,6 +63,8 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import com.rifsxd.ksunext.R
+import com.rifsxd.ksunext.ui.component.rememberConfirmDialog
+import com.rifsxd.ksunext.ui.component.ConfirmResult
import com.rifsxd.ksunext.ui.component.KeyEventBlocker
import com.rifsxd.ksunext.ui.util.FlashResult
import com.rifsxd.ksunext.ui.util.LkmSelection
@@ -138,12 +140,41 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
// Disable back button if flashing is running
}
- LaunchedEffect(Unit) {
- if (text.isNotEmpty()) {
- return@LaunchedEffect
+ val confirmDialog = rememberConfirmDialog()
+ var confirmed by rememberSaveable { mutableStateOf(flashIt !is FlashIt.FlashModules) }
+ var pendingFlashIt by rememberSaveable { mutableStateOf(null) }
+
+ LaunchedEffect(flashIt) {
+ if (flashIt is FlashIt.FlashModules && !confirmed) {
+ val uris = flashIt.uris
+ 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)
+ val confirmTitle = context.getString(R.string.module)
+ val result = confirmDialog.awaitConfirm(
+ title = confirmTitle,
+ content = confirmContent,
+ markdown = true
+ )
+ if (result == ConfirmResult.Confirmed) {
+ confirmed = true
+ pendingFlashIt = flashIt
+ } else {
+ // User cancelled, go back
+ navigator.popBackStack()
+ }
+ } else {
+ confirmed = true
+ pendingFlashIt = flashIt
}
+ }
+
+ LaunchedEffect(confirmed, pendingFlashIt) {
+ if (!confirmed || pendingFlashIt == null || text.isNotEmpty()) return@LaunchedEffect
withContext(Dispatchers.IO) {
- flashIt(flashIt, onStdout = {
+ flashIt(pendingFlashIt!!, onStdout = {
tempText = "$it\n"
if (tempText.startsWith("[H[J")) { // clear command
text = tempText.substring(6)
@@ -282,6 +313,19 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
}
}
+fun Uri.getFileName(context: Context): String {
+ val contentResolver = context.contentResolver
+ val cursor = contentResolver.query(this, null, null, null, null)
+ return cursor?.use {
+ val nameIndex = it.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME)
+ if (it.moveToFirst() && nameIndex != -1) {
+ it.getString(nameIndex)
+ } else {
+ this.lastPathSegment ?: "unknown.zip"
+ }
+ } ?: (this.lastPathSegment ?: "unknown.zip")
+}
+
@Parcelize
sealed class FlashIt : Parcelable {
data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) :
diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt
index 525c557f..a82bdc14 100644
--- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt
+++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt
@@ -291,15 +291,6 @@ 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 = {
- if (viewModel.zipUris.isNotEmpty()) {
- navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(viewModel.zipUris)))
- viewModel.clearZipUris()
- viewModel.markNeedRefresh()
- }
- })
val selectZipLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
@@ -322,19 +313,9 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
viewModel.updateZipUris(uris)
- // Show confirm dialog with selected zip file(s) name(s)
- 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
- )
-
+ navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(uris)))
+ viewModel.clearZipUris()
+ viewModel.markNeedRefresh()
}
ExtendedFloatingActionButton(