manager: add support for opening zip file and directly flash module

- refine zip intent method

- use MMRL method to handle zip, fix failed to open zip from Chrome

Co-Authored-By: Der_Googler <54764558+dergoogler@users.noreply.github.com>
Co-authored-by: rifsxd <rifat.44.azad.rifs@gmail.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
This commit is contained in:
ShirkNeko
2025-10-08 16:11:55 +08:00
parent 9c1ff635e3
commit 2f43ad4f76
3 changed files with 91 additions and 7 deletions

View File

@@ -30,6 +30,13 @@
<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="content" />
</intent-filter>
</activity>
<activity-alias

View File

@@ -2,6 +2,7 @@ package com.sukisu.ultra.ui
import android.content.Context
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
@@ -25,7 +26,9 @@ import com.ramcosta.composedestinations.DestinationsNavHost
import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle
import com.ramcosta.composedestinations.generated.NavGraphs
import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
import com.ramcosta.composedestinations.spec.NavHostGraphSpec
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ui.screen.BottomBarDestination
import com.sukisu.ultra.ui.theme.KernelSUTheme
@@ -34,6 +37,7 @@ import com.sukisu.ultra.ui.util.install
import com.sukisu.ultra.ui.viewmodel.HomeViewModel
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import com.sukisu.ultra.ui.webui.initPlatform
import com.sukisu.ultra.ui.screen.FlashIt
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import zako.zako.zako.zakoui.activity.component.BottomBar
@@ -83,6 +87,18 @@ class MainActivity : ComponentActivity() {
isInitialized = true
}
// Check if launched with a ZIP file
val zipUri: ArrayList<Uri>? = if (intent.data != null) {
arrayListOf(intent.data!!)
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableArrayListExtra("uris", Uri::class.java)
} else {
@Suppress("DEPRECATION")
intent.getParcelableArrayListExtra("uris")
}
}
setContent {
KernelSUTheme {
val navController = rememberNavController()
@@ -93,6 +109,18 @@ class MainActivity : ComponentActivity() {
BottomBarDestination.entries.map { it.direction.route }.toSet()
}
val navigator = navController.rememberDestinationsNavigator()
LaunchedEffect(zipUri) {
if (!zipUri.isNullOrEmpty()) {
navigator.navigate(
FlashScreenDestination(
FlashIt.FlashModules(zipUri)
)
)
}
}
val showBottomBar = when (currentDestination?.route) {
ExecuteModuleActionScreenDestination.route -> false
else -> true

View File

@@ -1,5 +1,6 @@
package com.sukisu.ultra.ui.screen
import android.content.Intent
import android.net.Uri
import android.os.Environment
import android.os.Parcelable
@@ -30,6 +31,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.activity.ComponentActivity
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
@@ -119,6 +121,24 @@ fun setModuleVerificationStatus(uri: Uri, isVerified: Boolean) {
@Destination<RootGraph>
fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
val context = LocalContext.current
// 是否通过从外部启动的模块安装
val isExternalInstall = remember {
when (flashIt) {
is FlashIt.FlashModule -> {
(context as? ComponentActivity)?.intent?.let { intent ->
intent.action == Intent.ACTION_VIEW || intent.action == Intent.ACTION_SEND
} ?: false
}
is FlashIt.FlashModules -> {
(context as? ComponentActivity)?.intent?.let { intent ->
intent.action == Intent.ACTION_VIEW || intent.action == Intent.ACTION_SEND
} ?: false
}
else -> false
}
}
var text by rememberSaveable { mutableStateOf("") }
var tempText: String
val logContent = rememberSaveable { StringBuilder() }
@@ -203,8 +223,21 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
if (showReboot) {
text += "\n\n\n"
showFloatAction = true
// 如果是内部安装,显示重启按钮后不自动返回
if (isExternalInstall) {
return@flashModuleUpdate
}
}
hasUpdateCompleted = true
// 如果是外部安装的模块更新且不需要重启,延迟后自动返回
if (isExternalInstall) {
scope.launch {
kotlinx.coroutines.delay(2000)
(context as? ComponentActivity)?.finish()
}
}
}, onStdout = {
tempText = "$it\n"
if (tempText.startsWith("")) { // clear command
@@ -297,6 +330,18 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
kotlinx.coroutines.delay(500)
navigator.navigate(FlashScreenDestination(nextFlashIt))
}
} else if (isExternalInstall && flashIt is FlashIt.FlashModules && flashIt.currentIndex >= flashIt.uris.size - 1) {
// 如果是外部安装且是最后一个模块,安装完成后自动返回
scope.launch {
kotlinx.coroutines.delay(2000)
(context as? ComponentActivity)?.finish()
}
} else if (isExternalInstall && flashIt is FlashIt.FlashModule) {
// 如果是外部安装单个模块,安装完成后自动返回
scope.launch {
kotlinx.coroutines.delay(2000)
(context as? ComponentActivity)?.finish()
}
}
}, onStdout = {
tempText = "$it\n"
@@ -319,14 +364,18 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
}
if (canGoBack) {
if (flashIt is FlashIt.FlashModules || flashIt is FlashIt.FlashModuleUpdate) {
viewModel.markNeedRefresh()
viewModel.fetchModuleList()
navigator.navigate(ModuleScreenDestination)
if (isExternalInstall) {
(context as? ComponentActivity)?.finish()
} else {
viewModel.markNeedRefresh()
viewModel.fetchModuleList()
navigator.popBackStack()
if (flashIt is FlashIt.FlashModules || flashIt is FlashIt.FlashModuleUpdate) {
viewModel.markNeedRefresh()
viewModel.fetchModuleList()
navigator.navigate(ModuleScreenDestination)
} else {
viewModel.markNeedRefresh()
viewModel.fetchModuleList()
navigator.popBackStack()
}
}
}
}