From ca24085b5bfb3e675eed0f1b885f2acc3a5bc52d Mon Sep 17 00:00:00 2001 From: rifsxd Date: Sun, 1 Jun 2025 00:57:45 +0600 Subject: [PATCH] manager: add a separate customization screen in settings --- .../rifsxd/ksunext/ui/screen/Customization.kt | 176 ++++++++++++++++++ .../com/rifsxd/ksunext/ui/screen/Settings.kt | 74 ++------ manager/app/src/main/res/values/strings.xml | 1 + 3 files changed, 192 insertions(+), 59 deletions(-) create mode 100644 manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Customization.kt diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Customization.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Customization.kt new file mode 100644 index 00000000..86673472 --- /dev/null +++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Customization.kt @@ -0,0 +1,176 @@ +package com.rifsxd.ksunext.ui.screen + +import android.content.Context +import android.content.Intent +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.* +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.Text +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.material3.rememberTopAppBarState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.compose.dropUnlessResumed +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootGraph +import com.ramcosta.composedestinations.navigation.DestinationsNavigator +import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator +import com.rifsxd.ksunext.Natives +import com.rifsxd.ksunext.ksuApp +import com.rifsxd.ksunext.R +import com.rifsxd.ksunext.ui.component.SwitchItem +import com.rifsxd.ksunext.ui.util.LocalSnackbarHost +import com.rifsxd.ksunext.ui.util.* + + +/** + * @author rifsxd + * @date 2025/6/1. + */ +@OptIn(ExperimentalMaterial3Api::class) +@Destination +@Composable +fun CustomizationScreen(navigator: DestinationsNavigator) { + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + val snackBarHost = LocalSnackbarHost.current + + val isManager = Natives.becomeManager(ksuApp.packageName) + val ksuVersion = if (isManager) Natives.version else null + + Scaffold( + topBar = { + TopBar( + onBack = dropUnlessResumed { + navigator.popBackStack() + }, + scrollBehavior = scrollBehavior + ) + }, + snackbarHost = { SnackbarHost(snackBarHost) }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) + ) { paddingValues -> + + Column( + modifier = Modifier + .padding(paddingValues) + .nestedScroll(scrollBehavior.nestedScrollConnection) + .verticalScroll(rememberScrollState()) + ) { + + val context = LocalContext.current + val scope = rememberCoroutineScope() + + val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) + + var useLagacyUI by rememberSaveable { + mutableStateOf( + prefs.getBoolean("use_legacyui", false) + ) + } + SwitchItem( + icon = Icons.Filled.ColorLens, + title = stringResource(id = R.string.settings_legacyui), + summary = stringResource(id = R.string.settings_legacyui_summary), + checked = useLagacyUI + ) { + prefs.edit().putBoolean("use_legacyui", it).apply() + useLagacyUI = it + } + + var enableAmoled by rememberSaveable { + mutableStateOf( + prefs.getBoolean("enable_amoled", false) + ) + } + var showRestartDialog by remember { mutableStateOf(false) } + if (isSystemInDarkTheme()) { + SwitchItem( + icon = Icons.Filled.Contrast, + title = stringResource(id = R.string.settings_amoled_mode), + summary = stringResource(id = R.string.settings_amoled_mode_summary), + checked = enableAmoled + ) { checked -> + prefs.edit().putBoolean("enable_amoled", checked).apply() + enableAmoled = checked + showRestartDialog = true + } + if (showRestartDialog) { + AlertDialog( + onDismissRequest = { showRestartDialog = false }, + title = { Text(stringResource(R.string.restart_required)) }, + text = { Text(stringResource(R.string.restart_app_message)) }, + confirmButton = { + TextButton(onClick = { + showRestartDialog = false + // Restart the app + val packageManager = context.packageManager + val intent = packageManager.getLaunchIntentForPackage(context.packageName) + intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + Runtime.getRuntime().exit(0) + }) { + Text(stringResource(R.string.restart_app)) + } + }, + dismissButton = { + TextButton(onClick = { showRestartDialog = false }) { + Text(stringResource(R.string.later)) + } + } + ) + } + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun TopBar( + onBack: () -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null +) { + TopAppBar( + title = { Text(stringResource(R.string.customization)) }, navigationIcon = { + IconButton( + onClick = onBack + ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } + }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), + scrollBehavior = scrollBehavior + ) +} + +@Preview +@Composable +private fun CustomizationPreview() { + CustomizationScreen(EmptyDestinationsNavigator) +} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Settings.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Settings.kt index dffd8d61..e186583a 100644 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Settings.kt +++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Settings.kt @@ -66,6 +66,7 @@ import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination import com.ramcosta.composedestinations.generated.destinations.BackupRestoreScreenDestination +import com.ramcosta.composedestinations.generated.destinations.CustomizationScreenDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import kotlinx.coroutines.Dispatchers @@ -360,65 +361,6 @@ fun SettingScreen(navigator: DestinationsNavigator) { } } - var useLagacyUI by rememberSaveable { - mutableStateOf( - prefs.getBoolean("use_legacyui", false) - ) - } - SwitchItem( - icon = Icons.Filled.ColorLens, - title = stringResource(id = R.string.settings_legacyui), - summary = stringResource(id = R.string.settings_legacyui_summary), - checked = useLagacyUI - ) { - prefs.edit().putBoolean("use_legacyui", it).apply() - useLagacyUI = it - } - - var enableAmoled by rememberSaveable { - mutableStateOf( - prefs.getBoolean("enable_amoled", false) - ) - } - var showRestartDialog by remember { mutableStateOf(false) } - if (isSystemInDarkTheme()) { - SwitchItem( - icon = Icons.Filled.Contrast, - title = stringResource(id = R.string.settings_amoled_mode), - summary = stringResource(id = R.string.settings_amoled_mode_summary), - checked = enableAmoled - ) { checked -> - prefs.edit().putBoolean("enable_amoled", checked).apply() - enableAmoled = checked - showRestartDialog = true - } - if (showRestartDialog) { - AlertDialog( - onDismissRequest = { showRestartDialog = false }, - title = { Text(stringResource(R.string.restart_required)) }, - text = { Text(stringResource(R.string.restart_app_message)) }, - confirmButton = { - TextButton(onClick = { - showRestartDialog = false - // Restart the app - val packageManager = context.packageManager - val intent = packageManager.getLaunchIntentForPackage(context.packageName) - intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) - context.startActivity(intent) - Runtime.getRuntime().exit(0) - }) { - Text(stringResource(R.string.restart_app)) - } - }, - dismissButton = { - TextButton(onClick = { showRestartDialog = false }) { - Text(stringResource(R.string.later)) - } - } - ) - } - } - if (isOverlayAvailable && useOverlayFs) { val shrink = stringResource(id = R.string.shrink_sparse_image) val shrinkMessage = stringResource(id = R.string.shrink_sparse_image_message) @@ -443,6 +385,20 @@ fun SettingScreen(navigator: DestinationsNavigator) { ) } + val customization = stringResource(id = R.string.customization) + ListItem( + leadingContent = { + Icon( + Icons.Filled.Palette, + customization + ) + }, + headlineContent = { Text(customization) }, + modifier = Modifier.clickable { + navigator.navigate(CustomizationScreenDestination) + } + ) + if (ksuVersion != null) { val backupRestore = stringResource(id = R.string.backup_restore) ListItem( diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index ae3b3b52..65029bf9 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -215,4 +215,5 @@ Use WebUI X instead of WebUI, which supports more APIs. Inject Eruda into WebUI X Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on. + Customization