manager: Update back navigation logic in FlashScreen for module flashing

This commit is contained in:
ShirkNeko
2025-06-02 04:10:41 +08:00
parent b8aaf918fe
commit 04e1b9bf77
3 changed files with 945 additions and 775 deletions

View File

@@ -201,13 +201,14 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
val onBack: () -> Unit = { val onBack: () -> Unit = {
if (currentFlashingStatus.value != FlashingStatus.FLASHING) { if (currentFlashingStatus.value != FlashingStatus.FLASHING) {
if (flashIt is FlashIt.FlashBoot) { if (flashIt is FlashIt.FlashModules) {
navigator.popBackStack() viewModel.markNeedRefresh()
viewModel.fetchModuleList()
navigator.navigate(ModuleScreenDestination)
} else { } else {
viewModel.markNeedRefresh() viewModel.markNeedRefresh()
viewModel.fetchModuleList() viewModel.fetchModuleList()
navigator.navigate(ModuleScreenDestination) { navigator.popBackStack()
}
} }
} }
} }

View File

@@ -66,6 +66,10 @@ import com.sukisu.ultra.ui.component.KsuIsValid
* @author ShirkNeko * @author ShirkNeko
* @date 2025/5/31. * @date 2025/5/31.
*/ */
private val SPACING_SMALL = 3.dp
private val SPACING_MEDIUM = 8.dp
private val SPACING_LARGE = 16.dp
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph> @Destination<RootGraph>
@Composable @Composable
@@ -82,9 +86,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
Scaffold( Scaffold(
topBar = { topBar = {
TopBar( TopBar(scrollBehavior = scrollBehavior)
scrollBehavior = scrollBehavior
)
}, },
snackbarHost = { SnackbarHost(snackBarHost) }, snackbarHost = { SnackbarHost(snackBarHost) },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
@@ -118,28 +120,16 @@ fun SettingScreen(navigator: DestinationsNavigator) {
} }
} }
// 配置 // 配置卡片
Card( SettingsGroupCard(
modifier = Modifier title = stringResource(R.string.configuration),
.fillMaxWidth() content = {
.padding(horizontal = 16.dp, vertical = 8.dp),
colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh),
elevation = getCardElevation()
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Text(
text = stringResource(R.string.configuration),
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
)
// 配置文件模板入口 // 配置文件模板入口
val profileTemplate = stringResource(id = R.string.settings_profile_template)
KsuIsValid { KsuIsValid {
SettingItem( SettingItem(
icon = Icons.Filled.Fence, icon = Icons.Filled.Fence,
title = profileTemplate, title = stringResource(R.string.settings_profile_template),
summary = stringResource(id = R.string.settings_profile_template_summary), summary = stringResource(R.string.settings_profile_template_summary),
onClick = { onClick = {
navigator.navigate(AppProfileTemplateScreenDestination) navigator.navigate(AppProfileTemplateScreenDestination)
} }
@@ -154,17 +144,18 @@ fun SettingScreen(navigator: DestinationsNavigator) {
KsuIsValid { KsuIsValid {
SwitchItem( SwitchItem(
icon = Icons.Filled.FolderDelete, icon = Icons.Filled.FolderDelete,
title = stringResource(id = R.string.settings_umount_modules_default), title = stringResource(R.string.settings_umount_modules_default),
summary = stringResource(id = R.string.settings_umount_modules_default_summary), summary = stringResource(R.string.settings_umount_modules_default_summary),
checked = umountChecked checked = umountChecked,
) { enabled -> onCheckedChange = { enabled ->
if (Natives.setDefaultUmountModules(enabled)) { if (Natives.setDefaultUmountModules(enabled)) {
umountChecked = enabled umountChecked = enabled
} }
} }
)
} }
// SU 禁用开关(仅在兼容版本显示) // SU 禁用开关
KsuIsValid { KsuIsValid {
if (Natives.version >= Natives.MINIMAL_SUPPORTED_SU_COMPAT) { if (Natives.version >= Natives.MINIMAL_SUPPORTED_SU_COMPAT) {
var isSuDisabled by rememberSaveable { var isSuDisabled by rememberSaveable {
@@ -172,135 +163,70 @@ fun SettingScreen(navigator: DestinationsNavigator) {
} }
SwitchItem( SwitchItem(
icon = Icons.Filled.RemoveModerator, icon = Icons.Filled.RemoveModerator,
title = stringResource(id = R.string.settings_disable_su), title = stringResource(R.string.settings_disable_su),
summary = stringResource(id = R.string.settings_disable_su_summary), summary = stringResource(R.string.settings_disable_su_summary),
checked = isSuDisabled checked = isSuDisabled,
) { enabled -> onCheckedChange = { enabled ->
val shouldEnable = !enabled val shouldEnable = !enabled
if (Natives.setSuEnabled(shouldEnable)) { if (Natives.setSuEnabled(shouldEnable)) {
isSuDisabled = enabled isSuDisabled = enabled
} }
} }
)
} }
} }
} }
}
// 应用设置
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh),
elevation = getCardElevation()
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Text(
text = stringResource(R.string.app_settings),
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
) )
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) // 应用设置卡片
SettingsGroupCard(
title = stringResource(R.string.app_settings),
content = {
// 更新检查开关 // 更新检查开关
var checkUpdate by rememberSaveable { var checkUpdate by rememberSaveable {
mutableStateOf( mutableStateOf(prefs.getBoolean("check_update", true))
prefs.getBoolean("check_update", true)
)
} }
SwitchItem( SwitchItem(
icon = Icons.Filled.Update, icon = Icons.Filled.Update,
title = stringResource(id = R.string.settings_check_update), title = stringResource(R.string.settings_check_update),
summary = stringResource(id = R.string.settings_check_update_summary), summary = stringResource(R.string.settings_check_update_summary),
checked = checkUpdate checked = checkUpdate,
) { enabled -> onCheckedChange = { enabled ->
prefs.edit { putBoolean("check_update", enabled) } prefs.edit { putBoolean("check_update", enabled) }
checkUpdate = enabled checkUpdate = enabled
} }
)
// WebUI引擎选择 // WebUI引擎选择
KsuIsValid { KsuIsValid {
val engineOptions = listOf( WebUIEngineSelector(
"default" to stringResource(id = R.string.engine_auto_select), selectedEngine = selectedEngine,
"wx" to stringResource(id = R.string.engine_force_webuix), onEngineSelected = { engine ->
"ksu" to stringResource(id = R.string.engine_force_ksu) selectedEngine = engine
) prefs.edit { putString("webui_engine", engine) }
var showEngineDialog by remember { mutableStateOf(false) }
SettingItem(
icon = Icons.Filled.WebAsset,
title = stringResource(id = R.string.use_webuix),
summary = engineOptions.find { it.first == selectedEngine }?.second
?: stringResource(id = R.string.engine_auto_select),
onClick = {
showEngineDialog = true
} }
) )
if (showEngineDialog) {
AlertDialog(
onDismissRequest = { showEngineDialog = false },
title = { Text(stringResource(id = R.string.use_webuix)) },
text = {
Column {
engineOptions.forEach { (value, label) ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
selectedEngine = value
prefs.edit {
putString("webui_engine", value)
}
showEngineDialog = false
}
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = selectedEngine == value,
onClick = null
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = label)
}
}
}
},
confirmButton = {
TextButton(
onClick = { showEngineDialog = false }
) {
Text(stringResource(id = R.string.cancel))
}
}
)
}
} }
// Web调试和Web X Eruda 开关 // Web调试和Web X Eruda 开关
var enableWebDebugging by rememberSaveable { var enableWebDebugging by rememberSaveable {
mutableStateOf( mutableStateOf(prefs.getBoolean("enable_web_debugging", false))
prefs.getBoolean("enable_web_debugging", false)
)
} }
var useWebUIXEruda by rememberSaveable { var useWebUIXEruda by rememberSaveable {
mutableStateOf( mutableStateOf(prefs.getBoolean("use_webuix_eruda", false))
prefs.getBoolean("use_webuix_eruda", false)
)
} }
KsuIsValid { KsuIsValid {
SwitchItem( SwitchItem(
icon = Icons.Filled.DeveloperMode, icon = Icons.Filled.DeveloperMode,
title = stringResource(id = R.string.enable_web_debugging), title = stringResource(R.string.enable_web_debugging),
summary = stringResource(id = R.string.enable_web_debugging_summary), summary = stringResource(R.string.enable_web_debugging_summary),
checked = enableWebDebugging checked = enableWebDebugging,
) { enabled -> onCheckedChange = { enabled ->
prefs.edit { putBoolean("enable_web_debugging", enabled) } prefs.edit { putBoolean("enable_web_debugging", enabled) }
enableWebDebugging = enabled enableWebDebugging = enabled
} }
)
AnimatedVisibility( AnimatedVisibility(
visible = enableWebDebugging && selectedEngine == "wx", visible = enableWebDebugging && selectedEngine == "wx",
@@ -309,79 +235,53 @@ fun SettingScreen(navigator: DestinationsNavigator) {
) { ) {
SwitchItem( SwitchItem(
icon = Icons.Filled.FormatListNumbered, icon = Icons.Filled.FormatListNumbered,
title = stringResource(id = R.string.use_webuix_eruda), title = stringResource(R.string.use_webuix_eruda),
summary = stringResource(id = R.string.use_webuix_eruda_summary), summary = stringResource(R.string.use_webuix_eruda_summary),
checked = useWebUIXEruda checked = useWebUIXEruda,
) { enabled -> onCheckedChange = { enabled ->
prefs.edit { putBoolean("use_webuix_eruda", enabled) } prefs.edit { putBoolean("use_webuix_eruda", enabled) }
useWebUIXEruda = enabled useWebUIXEruda = enabled
} }
)
} }
} }
// 更多设置 // 更多设置
SettingItem( SettingItem(
icon = Icons.Filled.Settings, icon = Icons.Filled.Settings,
title = stringResource(id = R.string.more_settings), title = stringResource(R.string.more_settings),
summary = stringResource(id = R.string.more_settings), summary = stringResource(R.string.more_settings),
onClick = { onClick = {
navigator.navigate(MoreSettingsScreenDestination) navigator.navigate(MoreSettingsScreenDestination)
} }
) )
} }
}
// 工具
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh),
elevation = getCardElevation()
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Text(
text = stringResource(R.string.tools),
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
) )
// 工具卡片
SettingsGroupCard(
title = stringResource(R.string.tools),
content = {
var showBottomsheet by remember { mutableStateOf(false) } var showBottomsheet by remember { mutableStateOf(false) }
SettingItem( SettingItem(
icon = Icons.Filled.BugReport, icon = Icons.Filled.BugReport,
title = stringResource(id = R.string.send_log), title = stringResource(R.string.send_log),
onClick = { onClick = {
showBottomsheet = true showBottomsheet = true
} }
) )
if (showBottomsheet) { if (showBottomsheet) {
ModalBottomSheet( LogBottomSheet(
onDismissRequest = { showBottomsheet = false }, onDismiss = { showBottomsheet = false },
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, onSaveLog = {
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) {
LogActionButton(
icon = Icons.Filled.Save,
text = stringResource(R.string.save_log),
onClick = {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm") val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm")
val current = LocalDateTime.now().format(formatter) val current = LocalDateTime.now().format(formatter)
exportBugreportLauncher.launch("KernelSU_bugreport_${current}.tar.gz") exportBugreportLauncher.launch("KernelSU_bugreport_${current}.tar.gz")
showBottomsheet = false showBottomsheet = false
} },
) onShareLog = {
LogActionButton(
icon = Icons.Filled.Share,
text = stringResource(R.string.send_log),
onClick = {
scope.launch { scope.launch {
val bugreport = loadingDialog.withLoading { val bugreport = loadingDialog.withLoading {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@@ -389,8 +289,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
} }
} }
val uri: Uri = val uri = FileProvider.getUriForFile(
FileProvider.getUriForFile(
context, context,
"${BuildConfig.APPLICATION_ID}.fileprovider", "${BuildConfig.APPLICATION_ID}.fileprovider",
bugreport bugreport
@@ -414,9 +313,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
} }
) )
} }
Spacer(modifier = Modifier.height(16.dp))
}
}
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
if (lkmMode) { if (lkmMode) {
@@ -425,23 +321,12 @@ fun SettingScreen(navigator: DestinationsNavigator) {
} }
} }
} }
}
// 关于
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh),
elevation = getCardElevation()
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Text(
text = stringResource(R.string.about),
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
) )
// 关于卡片
SettingsGroupCard(
title = stringResource(R.string.about),
content = {
SettingItem( SettingItem(
icon = Icons.Filled.Info, icon = Icons.Filled.Info,
title = stringResource(R.string.about), title = stringResource(R.string.about),
@@ -450,13 +335,127 @@ fun SettingScreen(navigator: DestinationsNavigator) {
} }
) )
} }
} )
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(SPACING_LARGE))
} }
} }
} }
@Composable
private fun SettingsGroupCard(
title: String,
content: @Composable ColumnScope.() -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = SPACING_LARGE, vertical = SPACING_MEDIUM),
colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh),
elevation = getCardElevation()
) {
Column(
modifier = Modifier.padding(vertical = SPACING_MEDIUM)
) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(horizontal = SPACING_LARGE, vertical = SPACING_MEDIUM)
)
content()
}
}
}
@Composable
private fun WebUIEngineSelector(
selectedEngine: String,
onEngineSelected: (String) -> Unit
) {
var showDialog by remember { mutableStateOf(false) }
val engineOptions = listOf(
"default" to stringResource(R.string.engine_auto_select),
"wx" to stringResource(R.string.engine_force_webuix),
"ksu" to stringResource(R.string.engine_force_ksu)
)
SettingItem(
icon = Icons.Filled.WebAsset,
title = stringResource(R.string.use_webuix),
summary = engineOptions.find { it.first == selectedEngine }?.second
?: stringResource(R.string.engine_auto_select),
onClick = { showDialog = true }
)
if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false },
title = { Text(stringResource(R.string.use_webuix)) },
text = {
Column {
engineOptions.forEach { (value, label) ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
onEngineSelected(value)
showDialog = false
}
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = selectedEngine == value,
onClick = null
)
Spacer(modifier = Modifier.width(SPACING_MEDIUM))
Text(text = label)
}
}
}
},
confirmButton = {
TextButton(onClick = { showDialog = false }) {
Text(stringResource(R.string.cancel))
}
}
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun LogBottomSheet(
onDismiss: () -> Unit,
onSaveLog: () -> Unit,
onShareLog: () -> Unit
) {
ModalBottomSheet(
onDismissRequest = onDismiss,
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(SPACING_LARGE),
horizontalArrangement = Arrangement.SpaceEvenly
) {
LogActionButton(
icon = Icons.Filled.Save,
text = stringResource(R.string.save_log),
onClick = onSaveLog
)
LogActionButton(
icon = Icons.Filled.Share,
text = stringResource(R.string.send_log),
onClick = onShareLog
)
}
Spacer(modifier = Modifier.height(SPACING_LARGE))
}
}
@Composable @Composable
fun LogActionButton( fun LogActionButton(
icon: ImageVector, icon: ImageVector,
@@ -467,7 +466,7 @@ fun LogActionButton(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier modifier = Modifier
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding(8.dp) .padding(SPACING_MEDIUM)
) { ) {
Box( Box(
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
@@ -483,10 +482,10 @@ fun LogActionButton(
modifier = Modifier.size(24.dp) modifier = Modifier.size(24.dp)
) )
} }
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(SPACING_MEDIUM))
Text( Text(
text = text, text = text,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium
) )
} }
} }
@@ -502,7 +501,7 @@ fun SettingItem(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 12.dp), .padding(horizontal = SPACING_LARGE, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Icon( Icon(
@@ -510,20 +509,20 @@ fun SettingItem(
contentDescription = null, contentDescription = null,
tint = MaterialTheme.colorScheme.primary, tint = MaterialTheme.colorScheme.primary,
modifier = Modifier modifier = Modifier
.padding(end = 16.dp) .padding(end = SPACING_LARGE)
.size(24.dp) .size(24.dp)
) )
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( Text(
text = title, text = title,
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium
) )
Spacer(modifier = Modifier.height(3.dp))
if (summary != null) { if (summary != null) {
Spacer(modifier = Modifier.height(SPACING_SMALL))
Text( Text(
text = summary, text = summary,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium
) )
} }
} }
@@ -536,6 +535,50 @@ fun SettingItem(
} }
} }
@Composable
fun SwitchItem(
icon: ImageVector,
title: String,
summary: String? = null,
checked: Boolean,
onCheckedChange: (Boolean) -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onCheckedChange(!checked) }
.padding(horizontal = SPACING_LARGE, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = icon,
contentDescription = null,
tint = if (checked) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier
.padding(end = SPACING_LARGE)
.size(24.dp)
)
Column(modifier = Modifier.weight(1f)) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium
)
if (summary != null) {
Spacer(modifier = Modifier.height(SPACING_SMALL))
Text(
text = summary,
style = MaterialTheme.typography.bodyMedium
)
}
}
Switch(
checked = checked,
onCheckedChange = onCheckedChange
)
}
}
@Composable @Composable
fun UninstallItem( fun UninstallItem(
navigator: DestinationsNavigator, navigator: DestinationsNavigator,
@@ -638,10 +681,6 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
MaterialTheme.colorScheme.primaryContainer MaterialTheme.colorScheme.primaryContainer
else else
Color.Transparent Color.Transparent
val borderColor = if (isSelected)
MaterialTheme.colorScheme.primary
else
Color.Transparent
val contentColor = if (isSelected) val contentColor = if (isSelected)
MaterialTheme.colorScheme.onPrimaryContainer MaterialTheme.colorScheme.onPrimaryContainer
else else