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 = {
if (currentFlashingStatus.value != FlashingStatus.FLASHING) {
if (flashIt is FlashIt.FlashBoot) {
navigator.popBackStack()
if (flashIt is FlashIt.FlashModules) {
viewModel.markNeedRefresh()
viewModel.fetchModuleList()
navigator.navigate(ModuleScreenDestination)
} else {
viewModel.markNeedRefresh()
viewModel.fetchModuleList()
navigator.navigate(ModuleScreenDestination) {
}
navigator.popBackStack()
}
}
}

View File

@@ -66,6 +66,10 @@ import com.sukisu.ultra.ui.component.KsuIsValid
* @author ShirkNeko
* @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)
@Destination<RootGraph>
@Composable
@@ -82,9 +86,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
Scaffold(
topBar = {
TopBar(
scrollBehavior = scrollBehavior
)
TopBar(scrollBehavior = scrollBehavior)
},
snackbarHost = { SnackbarHost(snackBarHost) },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
@@ -118,28 +120,16 @@ 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.configuration),
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
)
// 配置卡片
SettingsGroupCard(
title = stringResource(R.string.configuration),
content = {
// 配置文件模板入口
val profileTemplate = stringResource(id = R.string.settings_profile_template)
KsuIsValid {
SettingItem(
icon = Icons.Filled.Fence,
title = profileTemplate,
summary = stringResource(id = R.string.settings_profile_template_summary),
title = stringResource(R.string.settings_profile_template),
summary = stringResource(R.string.settings_profile_template_summary),
onClick = {
navigator.navigate(AppProfileTemplateScreenDestination)
}
@@ -154,17 +144,18 @@ fun SettingScreen(navigator: DestinationsNavigator) {
KsuIsValid {
SwitchItem(
icon = Icons.Filled.FolderDelete,
title = stringResource(id = R.string.settings_umount_modules_default),
summary = stringResource(id = R.string.settings_umount_modules_default_summary),
checked = umountChecked
) { enabled ->
if (Natives.setDefaultUmountModules(enabled)) {
umountChecked = enabled
title = stringResource(R.string.settings_umount_modules_default),
summary = stringResource(R.string.settings_umount_modules_default_summary),
checked = umountChecked,
onCheckedChange = { enabled ->
if (Natives.setDefaultUmountModules(enabled)) {
umountChecked = enabled
}
}
}
)
}
// SU 禁用开关(仅在兼容版本显示)
// SU 禁用开关
KsuIsValid {
if (Natives.version >= Natives.MINIMAL_SUPPORTED_SU_COMPAT) {
var isSuDisabled by rememberSaveable {
@@ -172,135 +163,70 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}
SwitchItem(
icon = Icons.Filled.RemoveModerator,
title = stringResource(id = R.string.settings_disable_su),
summary = stringResource(id = R.string.settings_disable_su_summary),
checked = isSuDisabled
) { enabled ->
val shouldEnable = !enabled
if (Natives.setSuEnabled(shouldEnable)) {
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)
// 更新检查开关
var checkUpdate by rememberSaveable {
mutableStateOf(
prefs.getBoolean("check_update", true)
)
}
SwitchItem(
icon = Icons.Filled.Update,
title = stringResource(id = R.string.settings_check_update),
summary = stringResource(id = R.string.settings_check_update_summary),
checked = checkUpdate
) { enabled ->
prefs.edit { putBoolean("check_update", enabled) }
checkUpdate = enabled
}
// WebUI引擎选择
KsuIsValid {
val engineOptions = listOf(
"default" to stringResource(id = R.string.engine_auto_select),
"wx" to stringResource(id = R.string.engine_force_webuix),
"ksu" to stringResource(id = R.string.engine_force_ksu)
)
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))
title = stringResource(R.string.settings_disable_su),
summary = stringResource(R.string.settings_disable_su_summary),
checked = isSuDisabled,
onCheckedChange = { enabled ->
val shouldEnable = !enabled
if (Natives.setSuEnabled(shouldEnable)) {
isSuDisabled = enabled
}
}
)
}
}
}
)
// 应用设置卡片
SettingsGroupCard(
title = stringResource(R.string.app_settings),
content = {
// 更新检查开关
var checkUpdate by rememberSaveable {
mutableStateOf(prefs.getBoolean("check_update", true))
}
SwitchItem(
icon = Icons.Filled.Update,
title = stringResource(R.string.settings_check_update),
summary = stringResource(R.string.settings_check_update_summary),
checked = checkUpdate,
onCheckedChange = { enabled ->
prefs.edit { putBoolean("check_update", enabled) }
checkUpdate = enabled
}
)
// WebUI引擎选择
KsuIsValid {
WebUIEngineSelector(
selectedEngine = selectedEngine,
onEngineSelected = { engine ->
selectedEngine = engine
prefs.edit { putString("webui_engine", engine) }
}
)
}
// Web调试和Web X Eruda 开关
var enableWebDebugging by rememberSaveable {
mutableStateOf(
prefs.getBoolean("enable_web_debugging", false)
)
mutableStateOf(prefs.getBoolean("enable_web_debugging", false))
}
var useWebUIXEruda by rememberSaveable {
mutableStateOf(
prefs.getBoolean("use_webuix_eruda", false)
)
mutableStateOf(prefs.getBoolean("use_webuix_eruda", false))
}
KsuIsValid {
SwitchItem(
icon = Icons.Filled.DeveloperMode,
title = stringResource(id = R.string.enable_web_debugging),
summary = stringResource(id = R.string.enable_web_debugging_summary),
checked = enableWebDebugging
) { enabled ->
prefs.edit { putBoolean("enable_web_debugging", enabled) }
enableWebDebugging = enabled
}
title = stringResource(R.string.enable_web_debugging),
summary = stringResource(R.string.enable_web_debugging_summary),
checked = enableWebDebugging,
onCheckedChange = { enabled ->
prefs.edit { putBoolean("enable_web_debugging", enabled) }
enableWebDebugging = enabled
}
)
AnimatedVisibility(
visible = enableWebDebugging && selectedEngine == "wx",
@@ -309,113 +235,83 @@ fun SettingScreen(navigator: DestinationsNavigator) {
) {
SwitchItem(
icon = Icons.Filled.FormatListNumbered,
title = stringResource(id = R.string.use_webuix_eruda),
summary = stringResource(id = R.string.use_webuix_eruda_summary),
checked = useWebUIXEruda
) { enabled ->
prefs.edit { putBoolean("use_webuix_eruda", enabled) }
useWebUIXEruda = enabled
}
title = stringResource(R.string.use_webuix_eruda),
summary = stringResource(R.string.use_webuix_eruda_summary),
checked = useWebUIXEruda,
onCheckedChange = { enabled ->
prefs.edit { putBoolean("use_webuix_eruda", enabled) }
useWebUIXEruda = enabled
}
)
}
}
// 更多设置
SettingItem(
icon = Icons.Filled.Settings,
title = stringResource(id = R.string.more_settings),
summary = stringResource(id = R.string.more_settings),
title = stringResource(R.string.more_settings),
summary = stringResource(R.string.more_settings),
onClick = {
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) }
SettingItem(
icon = Icons.Filled.BugReport,
title = stringResource(id = R.string.send_log),
title = stringResource(R.string.send_log),
onClick = {
showBottomsheet = true
}
)
if (showBottomsheet) {
ModalBottomSheet(
onDismissRequest = { showBottomsheet = false },
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
) {
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 current = LocalDateTime.now().format(formatter)
exportBugreportLauncher.launch("KernelSU_bugreport_${current}.tar.gz")
showBottomsheet = false
}
)
LogActionButton(
icon = Icons.Filled.Share,
text = stringResource(R.string.send_log),
onClick = {
scope.launch {
val bugreport = loadingDialog.withLoading {
withContext(Dispatchers.IO) {
getBugreportFile(context)
}
}
val uri: Uri =
FileProvider.getUriForFile(
context,
"${BuildConfig.APPLICATION_ID}.fileprovider",
bugreport
)
val shareIntent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_STREAM, uri)
setDataAndType(uri, "application/gzip")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(
Intent.createChooser(
shareIntent,
context.getString(R.string.send_log)
)
)
showBottomsheet = false
LogBottomSheet(
onDismiss = { showBottomsheet = false },
onSaveLog = {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm")
val current = LocalDateTime.now().format(formatter)
exportBugreportLauncher.launch("KernelSU_bugreport_${current}.tar.gz")
showBottomsheet = false
},
onShareLog = {
scope.launch {
val bugreport = loadingDialog.withLoading {
withContext(Dispatchers.IO) {
getBugreportFile(context)
}
}
)
val uri = FileProvider.getUriForFile(
context,
"${BuildConfig.APPLICATION_ID}.fileprovider",
bugreport
)
val shareIntent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_STREAM, uri)
setDataAndType(uri, "application/gzip")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(
Intent.createChooser(
shareIntent,
context.getString(R.string.send_log)
)
)
showBottomsheet = false
}
}
Spacer(modifier = Modifier.height(16.dp))
}
)
}
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
@@ -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(
icon = Icons.Filled.Info,
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
fun LogActionButton(
icon: ImageVector,
@@ -467,7 +466,7 @@ fun LogActionButton(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.clickable(onClick = onClick)
.padding(8.dp)
.padding(SPACING_MEDIUM)
) {
Box(
contentAlignment = Alignment.Center,
@@ -483,10 +482,10 @@ fun LogActionButton(
modifier = Modifier.size(24.dp)
)
}
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.height(SPACING_MEDIUM))
Text(
text = text,
style = MaterialTheme.typography.bodyMedium,
style = MaterialTheme.typography.bodyMedium
)
}
}
@@ -502,7 +501,7 @@ fun SettingItem(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 12.dp),
.padding(horizontal = SPACING_LARGE, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
@@ -510,20 +509,20 @@ fun SettingItem(
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier
.padding(end = 16.dp)
.padding(end = SPACING_LARGE)
.size(24.dp)
)
Column(modifier = Modifier.weight(1f)) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
style = MaterialTheme.typography.titleMedium
)
Spacer(modifier = Modifier.height(3.dp))
if (summary != null) {
Spacer(modifier = Modifier.height(SPACING_SMALL))
Text(
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
fun UninstallItem(
navigator: DestinationsNavigator,
@@ -638,10 +681,6 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
MaterialTheme.colorScheme.primaryContainer
else
Color.Transparent
val borderColor = if (isSelected)
MaterialTheme.colorScheme.primary
else
Color.Transparent
val contentColor = if (isSelected)
MaterialTheme.colorScheme.onPrimaryContainer
else