Cherry-pick commit 00ae4f9
This commit is contained in:
@@ -3,15 +3,19 @@ package shirkneko.zako.sukisu.ui.screen
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.foundation.LocalIndication
|
import androidx.compose.foundation.LocalIndication
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.selection.toggleable
|
import androidx.compose.foundation.selection.toggleable
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
@@ -20,15 +24,19 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|||||||
import androidx.compose.material.icons.filled.FileUpload
|
import androidx.compose.material.icons.filled.FileUpload
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.Role
|
import androidx.compose.ui.semantics.Role
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import com.maxkeppeker.sheets.core.models.base.Header
|
import com.maxkeppeker.sheets.core.models.base.Header
|
||||||
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
|
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
|
||||||
import com.maxkeppeler.sheets.list.ListDialog
|
import com.maxkeppeler.sheets.list.ListDialog
|
||||||
@@ -49,6 +57,12 @@ import java.io.File
|
|||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
|
import shirkneko.zako.sukisu.ui.util.*
|
||||||
|
import shirkneko.zako.sukisu.utils.AssetsUtil
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author weishu
|
* @author weishu
|
||||||
@@ -68,6 +82,33 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
|||||||
showRebootDialog = true
|
showRebootDialog = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showRebootDialog) {
|
||||||
|
RebootDialog(
|
||||||
|
show = true,
|
||||||
|
onDismiss = { showRebootDialog = false },
|
||||||
|
onConfirm = {
|
||||||
|
showRebootDialog = false
|
||||||
|
try {
|
||||||
|
val process = Runtime.getRuntime().exec("su")
|
||||||
|
process.outputStream.bufferedWriter().use { writer ->
|
||||||
|
writer.write("svc power reboot\n")
|
||||||
|
writer.write("exit\n")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(context, R.string.failed_reboot, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
var installMethod by remember { mutableStateOf<InstallMethod?>(null) }
|
||||||
|
var lkmSelection by remember { mutableStateOf<LkmSelection>(LkmSelection.KmiNone) }
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
var showRebootDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val onFlashComplete = {
|
||||||
|
showRebootDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
if (showRebootDialog) {
|
if (showRebootDialog) {
|
||||||
RebootDialog(
|
RebootDialog(
|
||||||
show = true,
|
show = true,
|
||||||
@@ -108,9 +149,32 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Unit
|
||||||
|
when (method) {
|
||||||
|
is InstallMethod.HorizonKernel -> {
|
||||||
|
method.uri?.let { uri ->
|
||||||
|
val worker = HorizonKernelWorker(context)
|
||||||
|
worker.uri = uri
|
||||||
|
worker.setOnFlashCompleteListener(onFlashComplete)
|
||||||
|
worker.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val flashIt = FlashIt.FlashBoot(
|
||||||
|
boot = if (method is InstallMethod.SelectFile) method.uri else null,
|
||||||
|
lkm = lkmSelection,
|
||||||
|
ota = method is InstallMethod.DirectInstallToInactiveSlot
|
||||||
|
)
|
||||||
|
navigator.navigate(FlashScreenDestination(flashIt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Unit
|
Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val currentKmi by produceState(initialValue = "") {
|
||||||
|
value = getCurrentKmi()
|
||||||
|
}
|
||||||
val currentKmi by produceState(initialValue = "") {
|
val currentKmi by produceState(initialValue = "") {
|
||||||
value = getCurrentKmi()
|
value = getCurrentKmi()
|
||||||
}
|
}
|
||||||
@@ -129,8 +193,18 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
|||||||
onInstall()
|
onInstall()
|
||||||
}
|
}
|
||||||
Unit
|
Unit
|
||||||
|
Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val selectLkmLauncher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.StartActivityForResult()
|
||||||
|
) {
|
||||||
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
|
it.data?.data?.let { uri ->
|
||||||
|
lkmSelection = LkmSelection.LkmUri(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
val selectLkmLauncher = rememberLauncherForActivityResult(
|
val selectLkmLauncher = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.StartActivityForResult()
|
contract = ActivityResultContracts.StartActivityForResult()
|
||||||
) {
|
) {
|
||||||
@@ -152,6 +226,7 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopBar(
|
TopBar(
|
||||||
|
onBack = { navigator.popBackStack() },
|
||||||
onBack = { navigator.popBackStack() },
|
onBack = { navigator.popBackStack() },
|
||||||
onLkmUpload = onLkmUpload,
|
onLkmUpload = onLkmUpload,
|
||||||
scrollBehavior = scrollBehavior
|
scrollBehavior = scrollBehavior
|
||||||
@@ -160,6 +235,9 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
|||||||
contentWindowInsets = WindowInsets.safeDrawing.only(
|
contentWindowInsets = WindowInsets.safeDrawing.only(
|
||||||
WindowInsetsSides.Top + WindowInsetsSides.Horizontal
|
WindowInsetsSides.Top + WindowInsetsSides.Horizontal
|
||||||
)
|
)
|
||||||
|
contentWindowInsets = WindowInsets.safeDrawing.only(
|
||||||
|
WindowInsetsSides.Top + WindowInsetsSides.Horizontal
|
||||||
|
)
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -184,10 +262,14 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Button(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
Button(
|
Button(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
enabled = installMethod != null,
|
enabled = installMethod != null,
|
||||||
onClick = onClickNext
|
onClick = onClickNext
|
||||||
|
) {
|
||||||
|
onClick = onClickNext
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
stringResource(id = R.string.install_next),
|
stringResource(id = R.string.install_next),
|
||||||
@@ -382,6 +464,12 @@ sealed class InstallMethod {
|
|||||||
override val summary: String? = null
|
override val summary: String? = null
|
||||||
) : InstallMethod()
|
) : InstallMethod()
|
||||||
|
|
||||||
|
data class HorizonKernel(
|
||||||
|
val uri: Uri? = null,
|
||||||
|
@StringRes override val label: Int = R.string.horizon_kernel,
|
||||||
|
override val summary: String? = null
|
||||||
|
) : InstallMethod()
|
||||||
|
|
||||||
abstract val label: Int
|
abstract val label: Int
|
||||||
open val summary: String? = null
|
open val summary: String? = null
|
||||||
}
|
}
|
||||||
@@ -393,8 +481,15 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
|
|||||||
val selectFileTip = stringResource(
|
val selectFileTip = stringResource(
|
||||||
id = R.string.select_file_tip,
|
id = R.string.select_file_tip,
|
||||||
if (isInitBoot()) "init_boot" else "boot"
|
if (isInitBoot()) "init_boot" else "boot"
|
||||||
|
id = R.string.select_file_tip,
|
||||||
|
if (isInitBoot()) "init_boot" else "boot"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val radioOptions = mutableListOf<InstallMethod>(
|
||||||
|
InstallMethod.SelectFile(summary = selectFileTip)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
val radioOptions = mutableListOf<InstallMethod>(
|
val radioOptions = mutableListOf<InstallMethod>(
|
||||||
InstallMethod.SelectFile(summary = selectFileTip)
|
InstallMethod.SelectFile(summary = selectFileTip)
|
||||||
)
|
)
|
||||||
@@ -405,11 +500,14 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
|
|||||||
radioOptions.add(InstallMethod.DirectInstallToInactiveSlot)
|
radioOptions.add(InstallMethod.DirectInstallToInactiveSlot)
|
||||||
}
|
}
|
||||||
radioOptions.add(InstallMethod.HorizonKernel(summary = "Flashing the Anykernel3 Kernel"))
|
radioOptions.add(InstallMethod.HorizonKernel(summary = "Flashing the Anykernel3 Kernel"))
|
||||||
|
radioOptions.add(InstallMethod.HorizonKernel(summary = "Flashing the Anykernel3 Kernel"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectedOption by remember { mutableStateOf<InstallMethod?>(null) }
|
var selectedOption by remember { mutableStateOf<InstallMethod?>(null) }
|
||||||
var currentSelectingMethod by remember { mutableStateOf<InstallMethod?>(null) }
|
var currentSelectingMethod by remember { mutableStateOf<InstallMethod?>(null) }
|
||||||
|
|
||||||
|
var currentSelectingMethod by remember { mutableStateOf<InstallMethod?>(null) }
|
||||||
|
|
||||||
val selectImageLauncher = rememberLauncherForActivityResult(
|
val selectImageLauncher = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.StartActivityForResult()
|
contract = ActivityResultContracts.StartActivityForResult()
|
||||||
) {
|
) {
|
||||||
@@ -425,6 +523,17 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
|
|||||||
onSelected(it)
|
onSelected(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
val option = when (currentSelectingMethod) {
|
||||||
|
is InstallMethod.SelectFile -> InstallMethod.SelectFile(uri, summary = selectFileTip)
|
||||||
|
is InstallMethod.HorizonKernel -> InstallMethod.HorizonKernel(uri, summary = " Flashing the Anykernel3 Kernel")
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
option?.let {
|
||||||
|
selectedOption = it
|
||||||
|
onSelected(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,12 +545,22 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
|
|||||||
onDismiss = null
|
onDismiss = null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val confirmDialog = rememberConfirmDialog(
|
||||||
|
onConfirm = {
|
||||||
|
selectedOption = InstallMethod.DirectInstallToInactiveSlot
|
||||||
|
onSelected(InstallMethod.DirectInstallToInactiveSlot)
|
||||||
|
},
|
||||||
|
onDismiss = null
|
||||||
|
)
|
||||||
|
|
||||||
val dialogTitle = stringResource(id = android.R.string.dialog_alert_title)
|
val dialogTitle = stringResource(id = android.R.string.dialog_alert_title)
|
||||||
val dialogContent = stringResource(id = R.string.install_inactive_slot_warning)
|
val dialogContent = stringResource(id = R.string.install_inactive_slot_warning)
|
||||||
|
|
||||||
val onClick = { option: InstallMethod ->
|
val onClick = { option: InstallMethod ->
|
||||||
|
currentSelectingMethod = option
|
||||||
currentSelectingMethod = option
|
currentSelectingMethod = option
|
||||||
when (option) {
|
when (option) {
|
||||||
|
is InstallMethod.SelectFile, is InstallMethod.HorizonKernel -> {
|
||||||
is InstallMethod.SelectFile, is InstallMethod.HorizonKernel -> {
|
is InstallMethod.SelectFile, is InstallMethod.HorizonKernel -> {
|
||||||
selectImageLauncher.launch(Intent(Intent.ACTION_GET_CONTENT).apply {
|
selectImageLauncher.launch(Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
type = "application/*"
|
type = "application/*"
|
||||||
@@ -468,6 +587,7 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
|
|||||||
.toggleable(
|
.toggleable(
|
||||||
value = option.javaClass == selectedOption?.javaClass,
|
value = option.javaClass == selectedOption?.javaClass,
|
||||||
onValueChange = { onClick(option) },
|
onValueChange = { onClick(option) },
|
||||||
|
onValueChange = { onClick(option) },
|
||||||
role = Role.RadioButton,
|
role = Role.RadioButton,
|
||||||
indication = LocalIndication.current,
|
indication = LocalIndication.current,
|
||||||
interactionSource = interactionSource
|
interactionSource = interactionSource
|
||||||
@@ -476,6 +596,7 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
|
|||||||
RadioButton(
|
RadioButton(
|
||||||
selected = option.javaClass == selectedOption?.javaClass,
|
selected = option.javaClass == selectedOption?.javaClass,
|
||||||
onClick = { onClick(option) },
|
onClick = { onClick(option) },
|
||||||
|
onClick = { onClick(option) },
|
||||||
interactionSource = interactionSource
|
interactionSource = interactionSource
|
||||||
)
|
)
|
||||||
Column(
|
Column(
|
||||||
@@ -509,8 +630,10 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
|
|||||||
value = getSupportedKmis()
|
value = getSupportedKmis()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val options = supportedKmi.map { value ->
|
val options = supportedKmi.map { value ->
|
||||||
ListOption(titleText = value)
|
ListOption(titleText = value)
|
||||||
|
ListOption(titleText = value)
|
||||||
}
|
}
|
||||||
|
|
||||||
var selection by remember { mutableStateOf<String?>(null) }
|
var selection by remember { mutableStateOf<String?>(null) }
|
||||||
@@ -535,6 +658,27 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
|
|||||||
selection = option.titleText
|
selection = option.titleText
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ListDialog(
|
||||||
|
state = rememberUseCaseState(
|
||||||
|
visible = true,
|
||||||
|
onFinishedRequest = {
|
||||||
|
onSelected(selection)
|
||||||
|
},
|
||||||
|
onCloseRequest = {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
header = Header.Default(
|
||||||
|
title = stringResource(R.string.select_kmi),
|
||||||
|
),
|
||||||
|
selection = ListSelection.Single(
|
||||||
|
showRadioButtons = true,
|
||||||
|
options = options,
|
||||||
|
) { _, option ->
|
||||||
|
selection = option.titleText
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,6 +690,13 @@ private fun TopBar(
|
|||||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||||
) {
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
|
title = { Text(stringResource(R.string.install)) },
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = onBack) {
|
||||||
|
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
title = { Text(stringResource(R.string.install)) },
|
title = { Text(stringResource(R.string.install)) },
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = onBack) {
|
IconButton(onClick = onBack) {
|
||||||
@@ -560,10 +711,14 @@ private fun TopBar(
|
|||||||
windowInsets = WindowInsets.safeDrawing.only(
|
windowInsets = WindowInsets.safeDrawing.only(
|
||||||
WindowInsetsSides.Top + WindowInsetsSides.Horizontal
|
WindowInsetsSides.Top + WindowInsetsSides.Horizontal
|
||||||
),
|
),
|
||||||
|
windowInsets = WindowInsets.safeDrawing.only(
|
||||||
|
WindowInsetsSides.Top + WindowInsetsSides.Horizontal
|
||||||
|
),
|
||||||
scrollBehavior = scrollBehavior
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun SelectInstallPreview() {
|
fun SelectInstallPreview() {
|
||||||
|
|||||||
Reference in New Issue
Block a user