mirror of
https://github.com/tiann/KernelSU.git
synced 2025-02-20 11:43:32 +08:00
Compare commits
4 Commits
d50ce78e4a
...
499c22c8db
Author | SHA1 | Date | |
---|---|---|---|
|
499c22c8db | ||
|
6e3d556960 | ||
|
1186259365 | ||
|
e681c8fbec |
@ -53,6 +53,7 @@ import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.KeyEventBlocker
|
||||
import me.weishu.kernelsu.ui.util.FlashResult
|
||||
import me.weishu.kernelsu.ui.util.LkmSelection
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.flashModule
|
||||
@ -65,23 +66,40 @@ import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/1/1.
|
||||
*/
|
||||
|
||||
enum class FlashingStatus {
|
||||
FLASHING,
|
||||
SUCCESS,
|
||||
FAILED
|
||||
}
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/1/1.
|
||||
*/
|
||||
// Lets you flash modules sequentially when mutiple zipUris are selected
|
||||
fun flashModulesSequentially(
|
||||
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)
|
||||
@Composable
|
||||
@Destination<RootGraph>
|
||||
fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
|
||||
var text by rememberSaveable { mutableStateOf("") }
|
||||
var tempText : String
|
||||
var tempText: String
|
||||
val logContent = rememberSaveable { StringBuilder() }
|
||||
var showFloatAction by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
@ -98,16 +116,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
return@LaunchedEffect
|
||||
}
|
||||
withContext(Dispatchers.IO) {
|
||||
flashIt(flashIt, onFinish = { showReboot, code ->
|
||||
if (code != 0) {
|
||||
text += "Error: exit code = $code.\nPlease save and check the log.\n"
|
||||
}
|
||||
if (showReboot) {
|
||||
text += "\n\n\n"
|
||||
showFloatAction = true
|
||||
}
|
||||
flashing = if (code == 0) FlashingStatus.SUCCESS else FlashingStatus.FAILED
|
||||
}, onStdout = {
|
||||
flashIt(flashIt, onStdout = {
|
||||
tempText = "$it\n"
|
||||
if (tempText.startsWith("[H[J")) { // clear command
|
||||
text = tempText.substring(6)
|
||||
@ -117,7 +126,16 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
logContent.append(it).append("\n")
|
||||
}, onStderr = {
|
||||
logContent.append(it).append("\n")
|
||||
})
|
||||
}).apply {
|
||||
if (code != 0) {
|
||||
text += "Error code: $code.\n $err Please save and check the log.\n"
|
||||
}
|
||||
if (showReboot) {
|
||||
text += "\n\n\n"
|
||||
showFloatAction = true
|
||||
}
|
||||
flashing = if (code == 0) FlashingStatus.SUCCESS else FlashingStatus.FAILED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,31 +211,36 @@ sealed class FlashIt : Parcelable {
|
||||
|
||||
data class FlashModule(val uri: Uri) : FlashIt()
|
||||
|
||||
data class FlashModules(val uris: List<Uri>) : FlashIt()
|
||||
|
||||
data object FlashRestore : FlashIt()
|
||||
|
||||
data object FlashUninstall : FlashIt()
|
||||
}
|
||||
|
||||
fun flashIt(
|
||||
flashIt: FlashIt, onFinish: (Boolean, Int) -> Unit,
|
||||
flashIt: FlashIt,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit
|
||||
) {
|
||||
when (flashIt) {
|
||||
): FlashResult {
|
||||
return when (flashIt) {
|
||||
is FlashIt.FlashBoot -> installBoot(
|
||||
flashIt.boot,
|
||||
flashIt.lkm,
|
||||
flashIt.ota,
|
||||
onFinish,
|
||||
onStdout,
|
||||
onStderr
|
||||
)
|
||||
|
||||
is FlashIt.FlashModule -> flashModule(flashIt.uri, onFinish, onStdout, onStderr)
|
||||
is FlashIt.FlashModule -> flashModule(flashIt.uri, onStdout, onStderr)
|
||||
|
||||
FlashIt.FlashRestore -> restoreBoot(onFinish, onStdout, onStderr)
|
||||
is FlashIt.FlashModules -> {
|
||||
flashModulesSequentially(flashIt.uris, onStdout, onStderr)
|
||||
}
|
||||
|
||||
FlashIt.FlashUninstall -> uninstallPermanently(onFinish, onStdout, onStderr)
|
||||
FlashIt.FlashRestore -> restoreBoot(onStdout, onStderr)
|
||||
|
||||
FlashIt.FlashUninstall -> uninstallPermanently(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.Download
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
@ -56,6 +57,7 @@ import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.SnackbarResult
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
@ -103,6 +105,7 @@ import me.weishu.kernelsu.ui.util.hasMagisk
|
||||
import me.weishu.kernelsu.ui.util.reboot
|
||||
import me.weishu.kernelsu.ui.util.toggleModule
|
||||
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.webui.WebUIActivity
|
||||
import okhttp3.OkHttpClient
|
||||
@ -200,6 +203,12 @@ 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 = {
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(zipUris)))
|
||||
viewModel.markNeedRefresh()
|
||||
})
|
||||
val selectZipLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult()
|
||||
) {
|
||||
@ -207,20 +216,38 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
val data = it.data ?: return@rememberLauncherForActivityResult
|
||||
val uri = data.data ?: return@rememberLauncherForActivityResult
|
||||
val clipData = data.clipData
|
||||
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(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()
|
||||
|
||||
Log.i("ModuleScreen", "select zip result: ${it.data}")
|
||||
if (uris.size == 1) {
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uris.first())))
|
||||
} 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(
|
||||
onClick = {
|
||||
// select the zip file to install
|
||||
// Select the zip files to install
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
type = "application/zip"
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
}
|
||||
selectZipLauncher.launch(intent)
|
||||
},
|
||||
@ -232,6 +259,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
snackbarHost = { SnackbarHost(hostState = snackBarHost) }
|
||||
) { innerPadding ->
|
||||
|
||||
when {
|
||||
hasMagisk -> {
|
||||
Box(
|
||||
|
@ -33,6 +33,11 @@ private fun getKsuDaemonPath(): String {
|
||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud.so"
|
||||
}
|
||||
|
||||
data class FlashResult(val code: Int, val err: String, val showReboot: Boolean) {
|
||||
constructor(result: Shell.Result, showReboot: Boolean) : this(result.code, result.err.joinToString("\n"), showReboot)
|
||||
constructor(result: Shell.Result) : this(result, result.isSuccess)
|
||||
}
|
||||
|
||||
object KsuCli {
|
||||
val SHELL: Shell = createRootShell()
|
||||
val GLOBAL_MNT_SHELL: Shell = createRootShell(true)
|
||||
@ -167,10 +172,9 @@ private fun flashWithIO(
|
||||
|
||||
fun flashModule(
|
||||
uri: Uri,
|
||||
onFinish: (Boolean, Int) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
): FlashResult {
|
||||
val resolver = ksuApp.contentResolver
|
||||
with(resolver.openInputStream(uri)) {
|
||||
val file = File(ksuApp.cacheDir, "module.zip")
|
||||
@ -183,8 +187,7 @@ fun flashModule(
|
||||
|
||||
file.delete()
|
||||
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
return FlashResult(result)
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,21 +216,19 @@ fun runModuleAction(
|
||||
}
|
||||
|
||||
fun restoreBoot(
|
||||
onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): FlashResult {
|
||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so")
|
||||
val result = flashWithIO("${getKsuDaemonPath()} boot-restore -f --magiskboot $magiskboot", onStdout, onStderr)
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
return FlashResult(result)
|
||||
}
|
||||
|
||||
fun uninstallPermanently(
|
||||
onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): FlashResult {
|
||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so")
|
||||
val result = flashWithIO("${getKsuDaemonPath()} uninstall --magiskboot $magiskboot", onStdout, onStderr)
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
return FlashResult(result)
|
||||
}
|
||||
|
||||
suspend fun shrinkModules(): Boolean = withContext(Dispatchers.IO) {
|
||||
@ -245,10 +246,9 @@ fun installBoot(
|
||||
bootUri: Uri?,
|
||||
lkm: LkmSelection,
|
||||
ota: Boolean,
|
||||
onFinish: (Boolean, Int) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit,
|
||||
): Boolean {
|
||||
): FlashResult {
|
||||
val resolver = ksuApp.contentResolver
|
||||
|
||||
val bootFile = bootUri?.let { uri ->
|
||||
@ -311,8 +311,7 @@ fun installBoot(
|
||||
lkmFile?.delete()
|
||||
|
||||
// if boot uri is empty, it is direct install, when success, we should show reboot button
|
||||
onFinish(bootUri == null && result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
return FlashResult(result, bootUri == null && result.isSuccess)
|
||||
}
|
||||
|
||||
fun reboot(reason: String = "") {
|
||||
|
@ -23,8 +23,10 @@
|
||||
<string name="module_failed_to_disable">Failed to disable module: %s</string>
|
||||
<string name="module_empty">No module installed</string>
|
||||
<string name="module">Module</string>
|
||||
<string name="module_install_prompt_with_name">Do you want to continue installing module %1$s?</string>
|
||||
<string name="module_sort_action_first">Sort (Action first)</string>
|
||||
<string name="module_sort_enabled_first">Sort (Enabled first)</string>
|
||||
<string name="confirm">Confirm</string>
|
||||
<string name="uninstall">Uninstall</string>
|
||||
<string name="module_install">Install</string>
|
||||
<string name="install">Install</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user