manager: Optimize the overall layout of KPM patches

This commit is contained in:
ShirkNeko
2025-09-10 22:05:30 +08:00
parent 42601b232c
commit e8852223c4
4 changed files with 174 additions and 171 deletions

View File

@@ -13,7 +13,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
@@ -61,14 +61,20 @@ import com.sukisu.ultra.ui.util.*
* @author ShirkNeko
* @date 2025/5/31.
*/
enum class KpmPatchOption {
FOLLOW_KERNEL,
PATCH_KPM,
UNDO_PATCH_KPM
}
@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>
@Composable
fun InstallScreen(navigator: DestinationsNavigator) {
var installMethod by remember { mutableStateOf<InstallMethod?>(null) }
var lkmSelection by remember { mutableStateOf<LkmSelection>(LkmSelection.KmiNone) }
var kpmPatchEnabled by remember { mutableStateOf(false) }
var kpmUndoPatch by remember { mutableStateOf(false) }
var kpmPatchOption by remember { mutableStateOf(KpmPatchOption.FOLLOW_KERNEL) }
val context = LocalContext.current
var showRebootDialog by remember { mutableStateOf(false) }
var showSlotSelectionDialog by remember { mutableStateOf(false) }
@@ -106,8 +112,8 @@ fun InstallScreen(navigator: DestinationsNavigator) {
KernelFlashScreenDestination(
kernelUri = uri,
selectedSlot = method.slot,
kpmPatchEnabled = kpmPatchEnabled,
kpmUndoPatch = kpmUndoPatch
kpmPatchEnabled = kpmPatchOption == KpmPatchOption.PATCH_KPM,
kpmUndoPatch = kpmPatchOption == KpmPatchOption.UNDO_PATCH_KPM
)
)
}
@@ -193,10 +199,8 @@ fun InstallScreen(navigator: DestinationsNavigator) {
installMethod = method
}
},
kpmPatchEnabled = kpmPatchEnabled,
onKpmPatchChanged = { kpmPatchEnabled = it },
kpmUndoPatch = kpmUndoPatch,
onKpmUndoPatchChanged = { kpmUndoPatch = it },
kpmPatchOption = kpmPatchOption,
onKpmPatchOptionChanged = { kpmPatchOption = it },
selectedMethod = installMethod
)
@@ -258,7 +262,7 @@ fun InstallScreen(navigator: DestinationsNavigator) {
}
// KPM 状态显示卡片
if (kpmPatchEnabled || kpmUndoPatch) {
if (kpmPatchOption != KpmPatchOption.FOLLOW_KERNEL) {
ElevatedCard(
colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant),
elevation = getCardElevation(),
@@ -273,13 +277,18 @@ fun InstallScreen(navigator: DestinationsNavigator) {
)
) {
Text(
text = stringResource(
if (kpmUndoPatch) R.string.kpm_undo_patch_enabled
else R.string.kpm_patch_enabled
),
text = when (kpmPatchOption) {
KpmPatchOption.PATCH_KPM -> stringResource(R.string.kpm_patch_enabled)
KpmPatchOption.UNDO_PATCH_KPM -> stringResource(R.string.kpm_undo_patch_enabled)
else -> ""
},
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(16.dp),
color = MaterialTheme.colorScheme.primary
color = when (kpmPatchOption) {
KpmPatchOption.PATCH_KPM -> MaterialTheme.colorScheme.primary
KpmPatchOption.UNDO_PATCH_KPM -> MaterialTheme.colorScheme.tertiary
else -> MaterialTheme.colorScheme.onSurface
}
)
}
}
@@ -364,10 +373,8 @@ sealed class InstallMethod {
private fun SelectInstallMethod(
isGKI: Boolean = false,
onSelected: (InstallMethod) -> Unit = {},
kpmPatchEnabled: Boolean = false,
onKpmPatchChanged: (Boolean) -> Unit = {},
kpmUndoPatch: Boolean = false,
onKpmUndoPatchChanged: (Boolean) -> Unit = {},
kpmPatchOption: KpmPatchOption = KpmPatchOption.FOLLOW_KERNEL,
onKpmPatchOptionChanged: (KpmPatchOption) -> Unit = {},
selectedMethod: InstallMethod? = null
) {
val rootAvailable = rootAvailable()
@@ -528,9 +535,9 @@ private fun SelectInstallMethod(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.toggleable(
value = option.javaClass == selectedOption?.javaClass,
onValueChange = { onClick(option) },
.selectable(
selected = option.javaClass == selectedOption?.javaClass,
onClick = { onClick(option) },
role = Role.RadioButton,
indication = LocalIndication.current,
interactionSource = interactionSource
@@ -578,7 +585,7 @@ private fun SelectInstallMethod(
elevation = getCardElevation(),
modifier = Modifier
.fillMaxWidth()
.padding(bottom = if (selectedMethod is InstallMethod.HorizonKernel) 0.dp else 12.dp)
.padding(bottom = 12.dp)
.clip(MaterialTheme.shapes.large)
) {
MaterialTheme(
@@ -635,9 +642,9 @@ private fun SelectInstallMethod(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.toggleable(
value = option.javaClass == selectedOption?.javaClass,
onValueChange = { onClick(option) },
.selectable(
selected = option.javaClass == selectedOption?.javaClass,
onClick = { onClick(option) },
role = Role.RadioButton,
indication = LocalIndication.current,
interactionSource = interactionSource
@@ -673,138 +680,128 @@ private fun SelectInstallMethod(
}
}
}
}
}
}
// KPM 修补选项卡片
// KPM修补
if (selectedMethod is InstallMethod.HorizonKernel && selectedMethod.uri != null) {
ElevatedCard(
colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant),
elevation = getCardElevation(),
Spacer(modifier = Modifier.height(16.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp)
.clip(MaterialTheme.shapes.large)
.padding(vertical = 8.dp)
) {
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(
surface = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else MaterialTheme.colorScheme.surfaceVariant
)
) {
ListItem(
leadingContent = {
Icon(
Icons.Filled.Security,
contentDescription = null,
tint = MaterialTheme.colorScheme.tertiary
tint = MaterialTheme.colorScheme.tertiary,
modifier = Modifier.size(20.dp)
)
},
headlineContent = {
Spacer(modifier = Modifier.width(8.dp))
Text(
stringResource(R.string.kpm_patch_options),
style = MaterialTheme.typography.titleMedium
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.tertiary
)
},
supportingContent = {
}
Text(
stringResource(R.string.kpm_patch_description),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(bottom = 12.dp)
)
KpmPatchOptionGroup(
selectedOption = kpmPatchOption,
onOptionChanged = onKpmPatchOptionChanged
)
}
)
}
}
}
}
}
}
Column(
modifier = Modifier.padding(
start = 16.dp,
end = 16.dp,
bottom = 16.dp
)
@Composable
private fun KpmPatchOptionGroup(
selectedOption: KpmPatchOption,
onOptionChanged: (KpmPatchOption) -> Unit
) {
// KPM 修补开关
Row(
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)
)
val descriptions = mapOf(
KpmPatchOption.FOLLOW_KERNEL to stringResource(R.string.kpm_follow_kernel_description),
KpmPatchOption.PATCH_KPM to stringResource(R.string.kpm_patch_switch_description),
KpmPatchOption.UNDO_PATCH_KPM to stringResource(R.string.kpm_undo_patch_switch_description)
)
Column {
options.forEach { (option, label) ->
val interactionSource = remember { MutableInteractionSource() }
Surface(
color = if (option == selectedOption)
MaterialTheme.colorScheme.primaryContainer.copy(alpha = cardAlpha)
else
MaterialTheme.colorScheme.surfaceContainer.copy(alpha = cardAlpha),
shape = MaterialTheme.shapes.medium,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 2.dp)
.clip(MaterialTheme.shapes.medium)
.clickable {
if (!kpmPatchEnabled) {
onKpmPatchChanged(true)
if (kpmUndoPatch) onKpmUndoPatchChanged(false)
} else {
onKpmPatchChanged(false)
}
}
.padding(vertical = 12.dp, horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Switch(
checked = kpmPatchEnabled,
onCheckedChange = { enabled ->
onKpmPatchChanged(enabled)
if (enabled && kpmUndoPatch) onKpmUndoPatchChanged(false)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.selectable(
selected = option == selectedOption,
onClick = { onOptionChanged(option) },
role = Role.RadioButton,
indication = LocalIndication.current,
interactionSource = interactionSource
)
.padding(vertical = 12.dp, horizontal = 12.dp)
) {
RadioButton(
selected = option == selectedOption,
onClick = null,
interactionSource = interactionSource,
colors = RadioButtonDefaults.colors(
selectedColor = when (option) {
KpmPatchOption.FOLLOW_KERNEL -> MaterialTheme.colorScheme.primary
KpmPatchOption.PATCH_KPM -> MaterialTheme.colorScheme.primary
KpmPatchOption.UNDO_PATCH_KPM -> MaterialTheme.colorScheme.tertiary
},
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colorScheme.primary,
checkedTrackColor = MaterialTheme.colorScheme.primaryContainer
unselectedColor = MaterialTheme.colorScheme.onSurfaceVariant
)
)
Spacer(modifier = Modifier.width(12.dp))
Column(modifier = Modifier.weight(1f)) {
Text(
text = stringResource(R.string.enable_kpm_patch),
style = MaterialTheme.typography.bodyLarge
text = label,
style = MaterialTheme.typography.bodyLarge,
color = if (option == selectedOption)
MaterialTheme.colorScheme.onPrimaryContainer
else
MaterialTheme.colorScheme.onSurface
)
descriptions[option]?.let { description ->
Text(
text = stringResource(R.string.kpm_patch_switch_description),
text = description,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
color = if (option == selectedOption)
MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.7f)
else
MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 2.dp)
)
}
}
// KPM 撤销修补开关
Row(
modifier = Modifier
.fillMaxWidth()
.clip(MaterialTheme.shapes.medium)
.clickable {
if (!kpmUndoPatch) {
onKpmUndoPatchChanged(true)
if (kpmPatchEnabled) onKpmPatchChanged(false)
} else {
onKpmUndoPatchChanged(false)
}
}
.padding(vertical = 12.dp, horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Switch(
checked = kpmUndoPatch,
onCheckedChange = { enabled ->
onKpmUndoPatchChanged(enabled)
if (enabled && kpmPatchEnabled) onKpmPatchChanged(false)
},
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colorScheme.tertiary,
checkedTrackColor = MaterialTheme.colorScheme.tertiaryContainer
)
)
Spacer(modifier = Modifier.width(12.dp))
Column(modifier = Modifier.weight(1f)) {
Text(
text = stringResource(R.string.enable_kpm_undo_patch),
style = MaterialTheme.typography.bodyLarge
)
Text(
text = stringResource(R.string.kpm_undo_patch_switch_description),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
}
}
}

View File

@@ -286,7 +286,7 @@ class HorizonKernelWorker(
}
/**
* 执行KMP修补操作
* 执行KPM修补操作
*/
private fun performKpmPatch() {
try {
@@ -311,11 +311,11 @@ class HorizonKernelWorker(
state.addLog(context.getString(R.string.kpm_found_image_file, imageFile))
// 复制KMP工具到Image文件所在目录
// 复制KPM工具到Image文件所在目录
runCommand(true, "cp $workDir/kptools $imageDir/")
runCommand(true, "cp $workDir/kpimg $imageDir/")
// 执行KMP修补命令
// 执行KPM修补命令
val patchCommand = if (kpmUndoPatch) {
"cd $imageDir && chmod a+rx kptools && ./kptools -u -s 123 -i Image -k kpimg -o oImage && mv oImage Image"
} else {
@@ -335,10 +335,10 @@ class HorizonKernelWorker(
else context.getString(R.string.kpm_patch_success)
)
// 清理KMP工具文件
// 清理KPM工具文件
runCommand(true, "rm -f $imageDir/kptools $imageDir/kpimg $imageDir/oImage")
// 使用Java方式重新打包ZIP文件
// 重新打包ZIP文件
val originalFileName = File(filePath).name
val patchedFilePath = "$workDir/patched_$originalFileName"

View File

@@ -650,4 +650,7 @@
<string name="kpm_undo_patch_failed">KPM 撤销修补失败</string>
<string name="kpm_repack_zip_failed">重新打包压缩文件失败</string>
<string name="kpm_patch_operation_failed">KPM 修补操作失败: %s</string>
<!-- KPM选项单选按钮组字符串 -->
<string name="kpm_follow_kernel_file">跟随内核</string>
<string name="kpm_follow_kernel_description">原样使用内核,不进行任何 KPM 修改</string>
</resources>

View File

@@ -488,7 +488,7 @@
<string name="kstat_full_clone_updated">Kstat full clone updated: %1$s</string>
<string name="add_kstat_statically_title">Add Kstat Static Configuration</string>
<string name="file_or_directory_path_label">File/Directory Path</string>
<string name="hint_use_default_value">Hint: You can use default to use the original value</string>
<string name="hint_use_default_value">Hint: You can use "default" to use the original value</string>
<string name="add_kstat_path_title">Add Kstat Path</string>
<string name="add">Add</string>
<string name="reset_kstat_config_title">Reset Kstat Configuration</string>
@@ -658,4 +658,7 @@ Important Note:\n
<string name="kpm_undo_patch_failed">KPM undo patch failed</string>
<string name="kpm_repack_zip_failed">Failed to repack zip file</string>
<string name="kpm_patch_operation_failed">KPM patch operation failed: %s</string>
<!-- KPM option radio group strings -->
<string name="kpm_follow_kernel_file">Follow Kernel</string>
<string name="kpm_follow_kernel_description">Use kernel as-is without any KPM modifications</string>
</resources>