diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/kernelFlash/AnyKernel3Flow.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/kernelFlash/AnyKernel3Flow.kt new file mode 100644 index 00000000..cc55ff78 --- /dev/null +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/kernelFlash/AnyKernel3Flow.kt @@ -0,0 +1,252 @@ +package com.sukisu.ultra.ui.kernelFlash + +import android.net.Uri +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Security +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.core.net.toUri +import com.sukisu.ultra.R +import com.sukisu.ultra.ui.screen.InstallMethod +import top.yukonga.miuix.kmp.basic.Icon +import top.yukonga.miuix.kmp.basic.Text +import top.yukonga.miuix.kmp.basic.TextButton +import top.yukonga.miuix.kmp.extra.SuperArrow +import top.yukonga.miuix.kmp.extra.SuperDialog +import top.yukonga.miuix.kmp.theme.MiuixTheme +import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme + +enum class KpmPatchOption { + FOLLOW_KERNEL, + PATCH_KPM, + UNDO_PATCH_KPM +} + +@Stable +data class AnyKernel3State( + val kpmPatchOption: KpmPatchOption, + val showSlotSelectionDialog: Boolean, + val showKpmPatchDialog: Boolean, + val onHorizonKernelSelected: (InstallMethod.HorizonKernel) -> Unit, + val onSlotSelected: (String) -> Unit, + val onDismissSlotDialog: () -> Unit, + val onOptionSelected: (KpmPatchOption) -> Unit, + val onDismissPatchDialog: () -> Unit +) + +@Composable +fun rememberAnyKernel3State( + installMethodState: MutableState, + preselectedKernelUri: String?, + horizonKernelSummary: String, + isAbDevice: Boolean +): AnyKernel3State { + var kpmPatchOption by remember { mutableStateOf(KpmPatchOption.FOLLOW_KERNEL) } + var showSlotSelectionDialog by remember { mutableStateOf(false) } + var showKpmPatchDialog by remember { mutableStateOf(false) } + var tempKernelUri by remember { mutableStateOf(null) } + + val onHorizonKernelSelected: (InstallMethod.HorizonKernel) -> Unit = { method -> + val uri = method.uri + if (uri != null) { + if (isAbDevice) { + tempKernelUri = uri + showSlotSelectionDialog = true + } else { + installMethodState.value = method + showKpmPatchDialog = true + } + } + } + + val onSlotSelected: (String) -> Unit = { slot -> + val uri = tempKernelUri + if (uri != null) { + installMethodState.value = InstallMethod.HorizonKernel( + uri = uri, + slot = slot, + summary = horizonKernelSummary + ) + tempKernelUri = null + showSlotSelectionDialog = false + showKpmPatchDialog = true + } + } + + val onDismissSlotDialog = { + showSlotSelectionDialog = false + tempKernelUri = null + } + + val onOptionSelected: (KpmPatchOption) -> Unit = { option -> + kpmPatchOption = option + showKpmPatchDialog = false + } + + val onDismissPatchDialog = { + showKpmPatchDialog = false + } + + LaunchedEffect(preselectedKernelUri, isAbDevice, horizonKernelSummary) { + preselectedKernelUri?.let { uriString -> + runCatching { uriString.toUri() } + .getOrNull() + ?.let { preselectedUri -> + val method = InstallMethod.HorizonKernel( + uri = preselectedUri, + summary = horizonKernelSummary + ) + if (isAbDevice) { + tempKernelUri = preselectedUri + showSlotSelectionDialog = true + } else { + installMethodState.value = method + showKpmPatchDialog = true + } + } + } + } + + return AnyKernel3State( + kpmPatchOption = kpmPatchOption, + showSlotSelectionDialog = showSlotSelectionDialog, + showKpmPatchDialog = showKpmPatchDialog, + onHorizonKernelSelected = onHorizonKernelSelected, + onSlotSelected = onSlotSelected, + onDismissSlotDialog = onDismissSlotDialog, + onOptionSelected = onOptionSelected, + onDismissPatchDialog = onDismissPatchDialog + ) +} + +@Composable +fun KpmPatchSelectionDialog( + show: Boolean, + currentOption: KpmPatchOption, + onDismiss: () -> Unit, + onOptionSelected: (KpmPatchOption) -> Unit +) { + var selectedOption by remember { mutableStateOf(currentOption) } + val showDialog = remember { mutableStateOf(show) } + + LaunchedEffect(show) { + showDialog.value = show + if (show) { + selectedOption = currentOption + } + } + + SuperDialog( + show = showDialog, + insideMargin = DpSize(0.dp, 0.dp), + onDismissRequest = { + showDialog.value = false + onDismiss() + }, + content = { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 24.dp) + ) { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp, vertical = 12.dp), + text = stringResource(id = R.string.kpm_patch_options), + fontSize = MiuixTheme.textStyles.title4.fontSize, + fontWeight = FontWeight.Medium, + textAlign = TextAlign.Center, + color = colorScheme.onSurface + ) + + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp, vertical = 8.dp), + text = stringResource(id = R.string.kpm_patch_description), + fontSize = MiuixTheme.textStyles.body2.fontSize, + color = colorScheme.onSurfaceVariantSummary, + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.height(12.dp)) + + val options = listOf( + KpmPatchOption.FOLLOW_KERNEL to stringResource(R.string.kpm_follow_kernel_file), + KpmPatchOption.PATCH_KPM to stringResource(R.string.enable_kpm_patch), + KpmPatchOption.UNDO_PATCH_KPM to stringResource(R.string.enable_kpm_undo_patch) + ) + + options.forEach { (option, title) -> + SuperArrow( + title = title, + onClick = { + selectedOption = option + }, + leftAction = { + Icon( + imageVector = Icons.Filled.Security, + contentDescription = null, + tint = if (selectedOption == option) { + colorScheme.primary + } else { + colorScheme.onSurfaceVariantSummary + } + ) + }, + insideMargin = PaddingValues(horizontal = 24.dp, vertical = 12.dp) + ) + } + + Spacer(modifier = Modifier.height(12.dp)) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + TextButton( + text = stringResource(android.R.string.cancel), + onClick = { + showDialog.value = false + onDismiss() + }, + modifier = Modifier.weight(1f) + ) + TextButton( + text = stringResource(android.R.string.ok), + onClick = { + onOptionSelected(selectedOption) + showDialog.value = false + onDismiss() + }, + modifier = Modifier.weight(1f) + ) + } + } + } + ) +} + diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Install.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Install.kt index 0c67f5c4..74e74916 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Install.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Install.kt @@ -14,7 +14,6 @@ import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -47,13 +46,10 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.SdStorage import androidx.compose.material.icons.filled.Security -import androidx.core.net.toUri import androidx.lifecycle.compose.dropUnlessResumed import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootGraph @@ -69,7 +65,10 @@ import com.sukisu.ultra.R import com.sukisu.ultra.getKernelVersion import com.sukisu.ultra.ui.component.ChooseKmiDialog import com.sukisu.ultra.ui.component.rememberConfirmDialog +import com.sukisu.ultra.ui.kernelFlash.KpmPatchOption +import com.sukisu.ultra.ui.kernelFlash.KpmPatchSelectionDialog import com.sukisu.ultra.ui.kernelFlash.component.SlotSelectionDialog +import com.sukisu.ultra.ui.kernelFlash.rememberAnyKernel3State import com.sukisu.ultra.ui.util.LkmSelection import com.sukisu.ultra.ui.util.getAvailablePartitions import com.sukisu.ultra.ui.util.getCurrentKmi @@ -94,11 +93,6 @@ import top.yukonga.miuix.kmp.icon.MiuixIcons import top.yukonga.miuix.kmp.icon.icons.useful.Back import top.yukonga.miuix.kmp.icon.icons.useful.Edit import top.yukonga.miuix.kmp.icon.icons.useful.Move -import top.yukonga.miuix.kmp.extra.SuperDialog -import top.yukonga.miuix.kmp.basic.TextButton -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.unit.DpSize import top.yukonga.miuix.kmp.theme.MiuixTheme import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme import top.yukonga.miuix.kmp.utils.getWindowSize @@ -109,12 +103,6 @@ import top.yukonga.miuix.kmp.utils.scrollEndHaptic * @author weishu * @date 2024/3/12. */ -enum class KpmPatchOption { - FOLLOW_KERNEL, - PATCH_KPM, - UNDO_PATCH_KPM -} - @Composable @Destination fun InstallScreen( @@ -122,19 +110,15 @@ fun InstallScreen( preselectedKernelUri: String? = null ) { val context = LocalContext.current - var installMethod by remember { + val installMethodState = remember { mutableStateOf(null) } + var installMethod by installMethodState var lkmSelection by remember { mutableStateOf(LkmSelection.KmiNone) } - var kpmPatchOption by remember { mutableStateOf(KpmPatchOption.FOLLOW_KERNEL) } - var showSlotSelectionDialog by remember { mutableStateOf(false) } - var showKpmPatchDialog by remember { mutableStateOf(false) } - var tempKernelUri by remember { mutableStateOf(null) } - val kernelVersion = getKernelVersion() val isGKI = kernelVersion.isGKI() val isAbDevice = produceState(initialValue = false) { @@ -145,28 +129,14 @@ fun InstallScreen( var partitionsState by remember { mutableStateOf>(emptyList()) } var hasCustomSelected by remember { mutableStateOf(false) } val horizonKernelSummary = stringResource(R.string.horizon_kernel_summary) - - // 处理预选的内核文件 - LaunchedEffect(preselectedKernelUri) { - preselectedKernelUri?.let { uriString -> - try { - val preselectedUri = uriString.toUri() - val horizonMethod = InstallMethod.HorizonKernel( - uri = preselectedUri, - summary = horizonKernelSummary - ) - installMethod = horizonMethod - tempKernelUri = preselectedUri - if (isAbDevice) { - showSlotSelectionDialog = true - } else { - showKpmPatchDialog = true - } - } catch (e: Exception) { - e.printStackTrace() - } - } - } + // AnyKernel3 刷写状态 + val anyKernel3State = rememberAnyKernel3State( + installMethodState = installMethodState, + preselectedKernelUri = preselectedKernelUri, + horizonKernelSummary = horizonKernelSummary, + isAbDevice = isAbDevice + ) + val kpmPatchOption = anyKernel3State.kpmPatchOption val onInstall = { installMethod?.let { method -> @@ -203,33 +173,24 @@ fun InstallScreen( } // 槽位选择对话框 - if (showSlotSelectionDialog && isAbDevice) { + if (anyKernel3State.showSlotSelectionDialog && isAbDevice) { SlotSelectionDialog( show = true, - onDismiss = { showSlotSelectionDialog = false }, + onDismiss = { anyKernel3State.onDismissSlotDialog() }, onSlotSelected = { slot -> - showSlotSelectionDialog = false - val horizonMethod = InstallMethod.HorizonKernel( - uri = tempKernelUri, - slot = slot, - summary = horizonKernelSummary - ) - installMethod = horizonMethod - // 槽位选择后,显示 KPM 补丁选择对话框 - showKpmPatchDialog = true + anyKernel3State.onSlotSelected(slot) } ) } // KPM补丁选择对话框 - if (showKpmPatchDialog) { + if (anyKernel3State.showKpmPatchDialog) { KpmPatchSelectionDialog( show = true, - currentOption = kpmPatchOption, - onDismiss = { showKpmPatchDialog = false }, + currentOption = anyKernel3State.kpmPatchOption, + onDismiss = { anyKernel3State.onDismissPatchDialog() }, onOptionSelected = { option -> - kpmPatchOption = option - showKpmPatchDialog = false + anyKernel3State.onOptionSelected(option) } ) } @@ -318,13 +279,7 @@ fun InstallScreen( SelectInstallMethod( onSelected = { method -> if (method is InstallMethod.HorizonKernel && method.uri != null) { - if (isAbDevice) { - tempKernelUri = method.uri - showSlotSelectionDialog = true - } else { - installMethod = method - showKpmPatchDialog = true - } + anyKernel3State.onHorizonKernelSelected(method) } else { installMethod = method } @@ -377,7 +332,7 @@ fun InstallScreen( ) } } - // LKM 上传选项(仅 GKI) + // LKM 上传(仅 GKI) if (isGKI) { Card( modifier = Modifier @@ -405,7 +360,7 @@ fun InstallScreen( } } - // AnyKernel3 相关信息显示 + // AnyKernel3 刷写 (installMethod as? InstallMethod.HorizonKernel)?.let { method -> if (method.slot != null) { Card( @@ -449,7 +404,7 @@ fun InstallScreen( leftAction = { Icon( Icons.Filled.Security, - tint = if (kpmPatchOption == KpmPatchOption.PATCH_KPM) + tint = if (kpmPatchOption == KpmPatchOption.PATCH_KPM) colorScheme.primary else colorScheme.secondary, @@ -649,121 +604,6 @@ private fun TopBar( ) } -@Composable -private fun KpmPatchSelectionDialog( - show: Boolean, - currentOption: KpmPatchOption, - onDismiss: () -> Unit, - onOptionSelected: (KpmPatchOption) -> Unit -) { - var selectedOption by remember { mutableStateOf(currentOption) } - val showDialog = remember { mutableStateOf(show) } - - LaunchedEffect(show) { - showDialog.value = show - if (show) { - selectedOption = currentOption - } - } - - SuperDialog( - show = showDialog, - insideMargin = DpSize(0.dp, 0.dp), - onDismissRequest = { - showDialog.value = false - onDismiss() - }, - content = { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 24.dp) - ) { - // 标题 - Text( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 24.dp, vertical = 12.dp), - text = stringResource(id = R.string.kpm_patch_options), - fontSize = MiuixTheme.textStyles.title4.fontSize, - fontWeight = FontWeight.Medium, - textAlign = TextAlign.Center, - color = colorScheme.onSurface - ) - - // 描述 - Text( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 24.dp, vertical = 8.dp), - text = stringResource(id = R.string.kpm_patch_description), - fontSize = MiuixTheme.textStyles.body2.fontSize, - color = colorScheme.onSurfaceVariantSummary, - textAlign = TextAlign.Center - ) - - Spacer(modifier = Modifier.height(12.dp)) - - // 选项列表 - val options = listOf( - KpmPatchOption.FOLLOW_KERNEL to stringResource(R.string.kpm_follow_kernel_file), - KpmPatchOption.PATCH_KPM to stringResource(R.string.enable_kpm_patch), - KpmPatchOption.UNDO_PATCH_KPM to stringResource(R.string.enable_kpm_undo_patch) - ) - - options.forEach { (option, title) -> - SuperArrow( - title = title, - onClick = { - selectedOption = option - }, - leftAction = { - Icon( - imageVector = Icons.Filled.Security, - contentDescription = null, - tint = if (selectedOption == option) { - colorScheme.primary - } else { - colorScheme.onSurfaceVariantSummary - } - ) - }, - insideMargin = PaddingValues(horizontal = 24.dp, vertical = 12.dp) - ) - } - - Spacer(modifier = Modifier.height(12.dp)) - - // 按钮行 - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 24.dp), - horizontalArrangement = Arrangement.spacedBy(12.dp) - ) { - TextButton( - text = stringResource(android.R.string.cancel), - onClick = { - showDialog.value = false - onDismiss() - }, - modifier = Modifier.weight(1f) - ) - TextButton( - text = stringResource(android.R.string.ok), - onClick = { - onOptionSelected(selectedOption) - showDialog.value = false - onDismiss() - }, - modifier = Modifier.weight(1f) - ) - } - } - } - ) -} - private fun isKoFile(context: Context, uri: Uri): Boolean { val seg = uri.lastPathSegment ?: "" if (seg.endsWith(".ko", ignoreCase = true)) return true @@ -787,4 +627,4 @@ private fun isKoFile(context: Context, uri: Uri): Boolean { } catch (_: Throwable) { false } -} +} \ No newline at end of file