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: add support for opening zip file and directly flash module
This commit is contained in:
@@ -24,6 +24,15 @@
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:mimeType="application/zip" />
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="content" />
|
||||
<data android:pathPattern=".*\\.zip" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
|
||||
@@ -55,6 +55,10 @@ import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
|
||||
import com.rifsxd.ksunext.ui.util.rootAvailable
|
||||
import com.rifsxd.ksunext.ui.util.install
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import com.rifsxd.ksunext.ui.screen.FlashIt
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -70,6 +74,14 @@ class MainActivity : ComponentActivity() {
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
if (isManager) install()
|
||||
|
||||
// Check if launched with a ZIP file
|
||||
val zipUri: Uri? = when (intent?.action) {
|
||||
Intent.ACTION_VIEW, Intent.ACTION_SEND -> {
|
||||
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
|
||||
|
||||
@@ -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<FlashIt?>(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) :
|
||||
|
||||
@@ -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<List<Uri>>(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(
|
||||
|
||||
Reference in New Issue
Block a user