fix(ui): stabilize theme and navigation behavior
This commit is contained in:
-2
@@ -57,7 +57,6 @@ class SettingsScreenTest {
|
||||
"settings-theme-mode",
|
||||
"settings-color-style",
|
||||
"settings-color-standard",
|
||||
"settings-predictive-back",
|
||||
"settings-interface-scale",
|
||||
)
|
||||
composeRule.onAllNodesWithTag("settings-renderer-selector").assertCountEquals(0)
|
||||
@@ -74,7 +73,6 @@ class SettingsScreenTest {
|
||||
"settings-blur",
|
||||
"settings-floating-navigation",
|
||||
"settings-liquid-glass",
|
||||
"settings-predictive-back",
|
||||
"settings-interface-scale",
|
||||
)
|
||||
composeRule.onAllNodesWithTag("settings-renderer-selector").assertCountEquals(0)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<application
|
||||
android:name=".ABoxManagerApplication"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:enableOnBackInvokedCallback="false"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
|
||||
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -20,6 +19,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.flagship.abox.manager.designsystem.ABoxLoadingIndicator
|
||||
import com.flagship.abox.manager.designsystem.ABoxSurface
|
||||
import com.flagship.abox.manager.designsystem.ABoxTheme
|
||||
import com.flagship.abox.manager.designsystem.ABoxBackLayer
|
||||
import com.flagship.abox.manager.domain.ProviderProfile
|
||||
import com.flagship.abox.manager.domain.Username
|
||||
import com.flagship.abox.manager.ui.login.LoginRoute
|
||||
@@ -108,13 +108,7 @@ private fun AuthenticatedNavHost(
|
||||
onShowAccountDialogChange: (Boolean) -> Unit,
|
||||
onShowSettingsChange: (Boolean) -> Unit,
|
||||
) {
|
||||
if (showSettings) {
|
||||
BackHandler { onShowSettingsChange(false) }
|
||||
SettingsRoute(
|
||||
experienceState = settingsExperienceState,
|
||||
onExit = { onShowSettingsChange(false) },
|
||||
)
|
||||
} else {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
AuthenticatedNavigation(
|
||||
username = username,
|
||||
profile = profile,
|
||||
@@ -122,6 +116,17 @@ private fun AuthenticatedNavHost(
|
||||
onProviderClick = { onShowAccountDialogChange(true) },
|
||||
onAvatarClick = { onShowAccountDialogChange(true) },
|
||||
)
|
||||
if (showSettings) {
|
||||
ABoxBackLayer(
|
||||
onBack = { onShowSettingsChange(false) },
|
||||
enabled = settingsExperienceState.isHome,
|
||||
) {
|
||||
SettingsRoute(
|
||||
experienceState = settingsExperienceState,
|
||||
onExit = { onShowSettingsChange(false) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showAccountDialog) {
|
||||
@@ -131,6 +136,7 @@ private fun AuthenticatedNavHost(
|
||||
onDismissRequest = { onShowAccountDialogChange(false) },
|
||||
onOpenSettings = {
|
||||
onShowAccountDialogChange(false)
|
||||
settingsExperienceState.showTheme(returnToHome = false)
|
||||
onShowSettingsChange(true)
|
||||
},
|
||||
onSwitchProvider = {
|
||||
|
||||
+46
-78
@@ -1,15 +1,11 @@
|
||||
package com.flagship.abox.manager.ui.navigation
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.PredictiveBackHandler
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -17,14 +13,11 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.Saver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
@@ -60,9 +53,6 @@ import com.flagship.abox.manager.ui.shell.MainAppShell
|
||||
import com.flagship.abox.manager.ui.shell.MainTab
|
||||
import com.flagship.abox.manager.ui.shell.detailPresentation
|
||||
import com.flagship.abox.manager.ui.shell.widthClass
|
||||
import com.flagship.abox.manager.designsystem.LocalABoxPredictiveBackEnabled
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import com.flagship.abox.manager.ui.socket.SocketScreen
|
||||
import com.flagship.abox.manager.ui.socket.SocketUiEvent
|
||||
import com.flagship.abox.manager.ui.socket.SocketViewModel
|
||||
@@ -169,12 +159,11 @@ fun AuthenticatedNavigationStateHost(
|
||||
}
|
||||
|
||||
val selection = selectedDevice ?: return@LaunchedEffect
|
||||
if (savedSelection != selection) {
|
||||
savedSelection = selection
|
||||
}
|
||||
|
||||
when (detailPresentation(windowSizeClass, selection)) {
|
||||
DetailPresentation.PANE -> {
|
||||
if (savedSelection != selection) {
|
||||
savedSelection = selection
|
||||
}
|
||||
if (currentRoute != MainTab.DEVICES.route) {
|
||||
navController.popBackStack(MainTab.DEVICES.route, inclusive = false)
|
||||
}
|
||||
@@ -184,6 +173,9 @@ fun AuthenticatedNavigationStateHost(
|
||||
navController.navigate(deviceRoute(selection)) {
|
||||
launchSingleTop = true
|
||||
}
|
||||
savedSelection = null
|
||||
} else if (savedSelection != null) {
|
||||
savedSelection = null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,60 +186,25 @@ fun AuthenticatedNavigationStateHost(
|
||||
selectedDevice = selectedDevice,
|
||||
detailPresentation = selectedPresentation,
|
||||
)
|
||||
val canNavigateBack = selectedDevice != null || currentRoute != MainTab.DEVICES.route
|
||||
val predictiveBackEnabled = LocalABoxPredictiveBackEnabled.current &&
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
|
||||
var backProgress by remember { mutableFloatStateOf(0f) }
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
.clip(RoundedCornerShape((28f * backProgress).dp))
|
||||
.graphicsLayer {
|
||||
val scale = 1f - 0.04f * backProgress
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
translationX = 28.dp.toPx() * backProgress
|
||||
},
|
||||
) {
|
||||
content(
|
||||
snapshot,
|
||||
{ tab ->
|
||||
savedSelection = null
|
||||
if (tab == currentTab) {
|
||||
navController.popBackStack(tab.route, inclusive = false)
|
||||
} else {
|
||||
navController.navigate(tab.route) {
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
popUpTo(navController.graph.startDestinationId) {
|
||||
saveState = true
|
||||
}
|
||||
content(
|
||||
snapshot,
|
||||
{ tab ->
|
||||
savedSelection = null
|
||||
if (tab == currentTab) {
|
||||
navController.popBackStack(tab.route, inclusive = false)
|
||||
} else {
|
||||
navController.navigate(tab.route) {
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
popUpTo(navController.graph.startDestinationId) {
|
||||
saveState = true
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deviceName, type ->
|
||||
savedSelection = DeviceSelection(profileId, deviceName, type)
|
||||
},
|
||||
{ savedSelection = null },
|
||||
)
|
||||
}
|
||||
|
||||
PredictiveBackHandler(enabled = canNavigateBack && predictiveBackEnabled) { events ->
|
||||
try {
|
||||
events.collect { event -> backProgress = event.progress }
|
||||
savedSelection = null
|
||||
navController.popCurrentDestinationUnlessRoot()
|
||||
} catch (cancelled: CancellationException) {
|
||||
throw cancelled
|
||||
} finally {
|
||||
backProgress = 0f
|
||||
}
|
||||
}
|
||||
|
||||
BackHandler(enabled = canNavigateBack && !predictiveBackEnabled) {
|
||||
savedSelection = null
|
||||
navController.popCurrentDestinationUnlessRoot()
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deviceName, type -> savedSelection = DeviceSelection(profileId, deviceName, type) },
|
||||
{ savedSelection = null },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -355,6 +312,23 @@ private fun AuthenticatedNavHost(
|
||||
onLogout: () -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
) {
|
||||
val activeBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val activeRoute = activeBackStackEntry?.actualRoute()
|
||||
var backNavigationInFlight by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(activeRoute) {
|
||||
backNavigationInFlight = false
|
||||
}
|
||||
val navigateBackFromDetail: () -> Unit = {
|
||||
val liveRoute = navController.currentBackStackEntry?.actualRoute()
|
||||
val isMainDestination = MainTab.entries.any { it.route == liveRoute }
|
||||
if (!backNavigationInFlight && !isMainDestination) {
|
||||
backNavigationInFlight = true
|
||||
if (!navController.popBackStack()) {
|
||||
backNavigationInFlight = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = MainTab.DEVICES.route,
|
||||
@@ -427,14 +401,14 @@ private fun AuthenticatedNavHost(
|
||||
deviceDetailGraph(
|
||||
onBackClick = {
|
||||
onClearDeviceSelection()
|
||||
navController.popBackStack()
|
||||
navigateBackFromDetail()
|
||||
},
|
||||
)
|
||||
sceneEditorDestination(
|
||||
onBack = navController::popBackStack,
|
||||
onBack = navigateBackFromDetail,
|
||||
onSaved = { navController.popBackStack(MainTab.SCENES.route, inclusive = false) },
|
||||
)
|
||||
sceneExecutionDestination(onBack = navController::popBackStack)
|
||||
sceneExecutionDestination(onBack = navigateBackFromDetail)
|
||||
composable(
|
||||
route = Routes.INFRARED_HUB,
|
||||
arguments = listOf(
|
||||
@@ -445,7 +419,7 @@ private fun AuthenticatedNavHost(
|
||||
InfraredHubRoute(
|
||||
onBack = {
|
||||
onClearDeviceSelection()
|
||||
navController.popBackStack()
|
||||
navigateBackFromDetail()
|
||||
},
|
||||
onOpenRemote = { remoteId ->
|
||||
navController.navigate(
|
||||
@@ -467,7 +441,7 @@ private fun AuthenticatedNavHost(
|
||||
),
|
||||
) {
|
||||
RemotePanelRoute(
|
||||
onBack = navController::popBackStack,
|
||||
onBack = navigateBackFromDetail,
|
||||
onSessionInvalid = onLogout,
|
||||
)
|
||||
}
|
||||
@@ -479,7 +453,7 @@ private fun AuthenticatedNavHost(
|
||||
),
|
||||
) {
|
||||
InfraredControlRoute(
|
||||
onBack = navController::popBackStack,
|
||||
onBack = navigateBackFromDetail,
|
||||
onSessionInvalid = onLogout,
|
||||
)
|
||||
}
|
||||
@@ -561,9 +535,3 @@ private fun NavBackStackEntry.actualRoute(): String? {
|
||||
}
|
||||
return route
|
||||
}
|
||||
|
||||
private fun NavHostController.popCurrentDestinationUnlessRoot() {
|
||||
if (currentBackStackEntry?.actualRoute() != MainTab.DEVICES.route) {
|
||||
popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
+66
-44
@@ -1,6 +1,5 @@
|
||||
package com.flagship.abox.manager.ui.preferences
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
@@ -58,6 +57,9 @@ import com.flagship.abox.manager.designsystem.LocalABoxShapes
|
||||
import com.flagship.abox.manager.designsystem.icon.ABoxIcon
|
||||
import com.flagship.abox.manager.designsystem.icon.ABoxIconKey
|
||||
import com.flagship.abox.manager.preferences.AccentPreference
|
||||
import com.flagship.abox.manager.designsystem.tokens.ABoxAccentPalette
|
||||
import com.flagship.abox.manager.designsystem.ABoxBackLayer
|
||||
import com.flagship.abox.manager.designsystem.ABoxSurface
|
||||
import com.flagship.abox.manager.preferences.AppThemeMode
|
||||
import com.flagship.abox.manager.preferences.DesignRenderer
|
||||
import com.flagship.abox.manager.preferences.InterfaceScale
|
||||
@@ -70,11 +72,27 @@ private enum class SettingsDestination { HOME, THEME }
|
||||
@Stable
|
||||
class SettingsExperienceState(initialDestination: String = SettingsDestination.HOME.name) {
|
||||
var destination: String by mutableStateOf(initialDestination)
|
||||
var themeBackReturnsHome: Boolean by mutableStateOf(true)
|
||||
val isHome: Boolean get() = destination == SettingsDestination.HOME.name
|
||||
|
||||
fun showHome() {
|
||||
destination = SettingsDestination.HOME.name
|
||||
}
|
||||
|
||||
fun showTheme(returnToHome: Boolean = true) {
|
||||
themeBackReturnsHome = returnToHome
|
||||
destination = SettingsDestination.THEME.name
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Saver: Saver<SettingsExperienceState, String> = Saver(
|
||||
save = { it.destination },
|
||||
restore = { SettingsExperienceState(it) },
|
||||
save = { "${it.destination}|${it.themeBackReturnsHome}" },
|
||||
restore = { saved ->
|
||||
val parts = saved.split('|', limit = 2)
|
||||
SettingsExperienceState(parts.first()).apply {
|
||||
themeBackReturnsHome = parts.getOrNull(1)?.toBooleanStrictOrNull() ?: true
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -98,22 +116,29 @@ fun SettingsExperience(
|
||||
) {
|
||||
val destination = runCatching { SettingsDestination.valueOf(experienceState.destination) }
|
||||
.getOrDefault(SettingsDestination.HOME)
|
||||
BackHandler {
|
||||
if (destination == SettingsDestination.THEME) experienceState.destination = SettingsDestination.HOME.name
|
||||
else onExit()
|
||||
}
|
||||
when (destination) {
|
||||
SettingsDestination.HOME -> SettingsHomeScreen(
|
||||
state = state,
|
||||
onOpenTheme = { experienceState.destination = SettingsDestination.THEME.name },
|
||||
onBack = onExit,
|
||||
onAction = onAction,
|
||||
)
|
||||
SettingsDestination.THEME -> ThemeSettingsScreen(
|
||||
state = state,
|
||||
onBack = { experienceState.destination = SettingsDestination.HOME.name },
|
||||
onAction = onAction,
|
||||
)
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
ABoxSurface(modifier = Modifier.fillMaxSize()) {
|
||||
SettingsHomeScreen(
|
||||
state = state,
|
||||
onOpenTheme = { experienceState.showTheme(returnToHome = true) },
|
||||
onBack = onExit,
|
||||
onAction = onAction,
|
||||
)
|
||||
}
|
||||
if (destination == SettingsDestination.THEME) {
|
||||
val leaveTheme = {
|
||||
if (experienceState.themeBackReturnsHome) experienceState.showHome() else onExit()
|
||||
}
|
||||
ABoxBackLayer(onBack = leaveTheme) {
|
||||
ABoxSurface(modifier = Modifier.fillMaxSize()) {
|
||||
ThemeSettingsScreen(
|
||||
state = state,
|
||||
onBack = leaveTheme,
|
||||
onAction = onAction,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +243,7 @@ private fun ThemeSettingsScreen(
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.MaterialThemeOptions(state: SettingsUiState, onAction: (SettingsUiAction) -> Unit) {
|
||||
val manualColorsEnabled = !state.preferences.dynamicColorEnabled || Build.VERSION.SDK_INT < Build.VERSION_CODES.S
|
||||
SettingsGroup(modifier = Modifier.testTag("settings-material-colors")) {
|
||||
SettingsSwitchRow(
|
||||
icon = ABoxIconKey.THEME,
|
||||
@@ -238,6 +264,7 @@ private fun ColumnScope.MaterialThemeOptions(state: SettingsUiState, onAction: (
|
||||
values = ColorStyle.entries,
|
||||
label = { colorStyleLabel(it) },
|
||||
onSelected = { onAction(SettingsUiAction.SetColorStyle(it)) },
|
||||
enabled = manualColorsEnabled,
|
||||
)
|
||||
EnumSettingRow(
|
||||
modifier = Modifier.testTag("settings-color-standard"),
|
||||
@@ -247,8 +274,8 @@ private fun ColumnScope.MaterialThemeOptions(state: SettingsUiState, onAction: (
|
||||
values = ColorStandard.entries,
|
||||
label = { colorStandardLabel(it) },
|
||||
onSelected = { onAction(SettingsUiAction.SetColorStandard(it)) },
|
||||
enabled = manualColorsEnabled,
|
||||
)
|
||||
PredictiveBackSetting(state, onAction)
|
||||
SettingsGroup(modifier = Modifier.testTag("settings-interface-scale")) { ScaleSelector(state, onAction) }
|
||||
}
|
||||
|
||||
@@ -303,26 +330,9 @@ private fun ColumnScope.MiuixThemeOptions(state: SettingsUiState, onAction: (Set
|
||||
},
|
||||
) { onAction(SettingsUiAction.SetLiquidGlass(it)) }
|
||||
}
|
||||
PredictiveBackSetting(state, onAction)
|
||||
SettingsGroup(modifier = Modifier.testTag("settings-interface-scale")) { ScaleSelector(state, onAction) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PredictiveBackSetting(state: SettingsUiState, onAction: (SettingsUiAction) -> Unit) {
|
||||
val supported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
|
||||
SettingsGroup(modifier = Modifier.testTag("settings-predictive-back")) {
|
||||
SettingsSwitchRow(
|
||||
icon = ABoxIconKey.BACK,
|
||||
title = stringResource(R.string.settings_predictive_back),
|
||||
description = stringResource(R.string.settings_predictive_back_description),
|
||||
checked = state.preferences.predictiveBackEnabled,
|
||||
tag = "settings-predictive-back-switch",
|
||||
enabled = supported,
|
||||
unavailableDescription = if (supported) null else stringResource(R.string.settings_requires_android_13),
|
||||
) { onAction(SettingsUiAction.SetPredictiveBack(it)) }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsPage(
|
||||
title: String,
|
||||
@@ -448,15 +458,17 @@ private fun <T> EnumSettingRow(
|
||||
values: List<T>,
|
||||
label: @Composable (T) -> String,
|
||||
onSelected: (T) -> Unit,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
SettingsGroup(modifier = modifier) {
|
||||
Box(Modifier.fillMaxWidth()) {
|
||||
Box(Modifier.fillMaxWidth().alpha(if (enabled) 1f else 0.42f)) {
|
||||
SettingsLinkRow(
|
||||
icon = icon,
|
||||
title = title,
|
||||
description = stringResource(R.string.settings_tap_to_choose),
|
||||
value = value,
|
||||
enabled = enabled,
|
||||
onClick = { expanded = true },
|
||||
)
|
||||
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
||||
@@ -478,18 +490,29 @@ private fun <T> EnumSettingRow(
|
||||
|
||||
@Composable
|
||||
private fun AccentSelector(state: SettingsUiState, onAction: (SettingsUiAction) -> Unit) {
|
||||
val dark = when (state.preferences.themeMode) {
|
||||
AppThemeMode.DARK -> true
|
||||
AppThemeMode.LIGHT -> false
|
||||
AppThemeMode.SYSTEM -> androidx.compose.foundation.isSystemInDarkTheme()
|
||||
}
|
||||
val manualColorsEnabled = !state.preferences.dynamicColorEnabled || Build.VERSION.SDK_INT < Build.VERSION_CODES.S
|
||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
Text(stringResource(R.string.settings_accent_label), style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().alpha(if (state.preferences.dynamicColorEnabled) 0.42f else 1f),
|
||||
modifier = Modifier.fillMaxWidth().alpha(if (manualColorsEnabled) 1f else 0.42f),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
AccentPreference.entries.forEach { accent ->
|
||||
val color = accentColor(accent)
|
||||
val color = ABoxAccentPalette.preview(
|
||||
accent = accent,
|
||||
style = state.preferences.colorStyle,
|
||||
standard = state.preferences.colorStandard,
|
||||
dark = dark,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier.size(44.dp).background(color, CircleShape)
|
||||
.then(if (state.preferences.accentPreference == accent) Modifier.border(3.dp, MaterialTheme.colorScheme.onSurface, CircleShape) else Modifier)
|
||||
.clickable(enabled = !state.preferences.dynamicColorEnabled) { onAction(SettingsUiAction.SetAccent(accent)) },
|
||||
.clickable(enabled = manualColorsEnabled) { onAction(SettingsUiAction.SetAccent(accent)) },
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
if (state.preferences.accentPreference == accent) Icon(Icons.Outlined.Check, null, tint = Color.White)
|
||||
@@ -518,8 +541,8 @@ private fun ScaleSelector(state: SettingsUiState, onAction: (SettingsUiAction) -
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsLinkRow(icon: ABoxIconKey, title: String, description: String, value: String?, modifier: Modifier = Modifier, onClick: () -> Unit) {
|
||||
Row(modifier.fillMaxWidth().clickable(onClick = onClick).padding(14.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
private fun SettingsLinkRow(icon: ABoxIconKey, title: String, description: String, value: String?, modifier: Modifier = Modifier, enabled: Boolean = true, onClick: () -> Unit) {
|
||||
Row(modifier.fillMaxWidth().clickable(enabled = enabled, onClick = onClick).padding(14.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
SettingsRowHeader(icon, title, description, Modifier.weight(1f))
|
||||
if (value != null) Text(value, color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
Icon(Icons.Outlined.ChevronRight, null, tint = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
@@ -570,4 +593,3 @@ private fun SettingsRowHeader(icon: ABoxIconKey, title: String, description: Str
|
||||
@Composable private fun themeLabel(mode: AppThemeMode) = stringResource(when (mode) { AppThemeMode.SYSTEM -> R.string.settings_theme_system; AppThemeMode.LIGHT -> R.string.settings_theme_light; AppThemeMode.DARK -> R.string.settings_theme_dark })
|
||||
@Composable private fun colorStyleLabel(value: ColorStyle) = stringResource(when (value) { ColorStyle.TONAL_SPOT -> R.string.settings_color_style_tonal; ColorStyle.EXPRESSIVE -> R.string.settings_color_style_expressive; ColorStyle.FIDELITY -> R.string.settings_color_style_fidelity })
|
||||
@Composable private fun colorStandardLabel(value: ColorStandard) = stringResource(when (value) { ColorStandard.SPEC_2021 -> R.string.settings_color_standard_2021; ColorStandard.SPEC_2025 -> R.string.settings_color_standard_2025 })
|
||||
private fun accentColor(accent: AccentPreference) = when (accent) { AccentPreference.BLUE -> Color(0xFF4F6FA9); AccentPreference.GREEN -> Color(0xFF2F7D57); AccentPreference.AMBER -> Color(0xFFA66700); AccentPreference.ROSE -> Color(0xFFA94C68); AccentPreference.VIOLET -> Color(0xFF7659A8) }
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Logout
|
||||
import androidx.compose.material.icons.outlined.CloudDone
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.outlined.Palette
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -107,7 +107,7 @@ fun AccountProviderDialog(
|
||||
}
|
||||
|
||||
AccountSheetAction(
|
||||
icon = Icons.Outlined.Settings,
|
||||
icon = Icons.Outlined.Palette,
|
||||
label = stringResource(R.string.account_interface_settings),
|
||||
description = stringResource(R.string.account_interface_settings_desc),
|
||||
tag = "account_dialog_open_settings",
|
||||
|
||||
@@ -265,7 +265,7 @@
|
||||
<string name="settings_style_section">样式</string>
|
||||
<string name="settings_renderer_label">渲染器</string>
|
||||
<string name="settings_renderer_description">选择应用使用的界面渲染方式</string>
|
||||
<string name="settings_renderer_material3">Material 3</string>
|
||||
<string name="settings_renderer_material3">Material</string>
|
||||
<string name="settings_renderer_miuix">Miuix</string>
|
||||
<string name="settings_theme_section">主题</string>
|
||||
<string name="settings_theme_label">主题模式</string>
|
||||
@@ -291,7 +291,7 @@
|
||||
<string name="settings_theme_entry">主题</string>
|
||||
<string name="settings_theme_entry_description">配色、明暗、视觉效果与缩放</string>
|
||||
<string name="settings_theme_detail_title">主题</string>
|
||||
<string name="settings_renderer_primary">Material 3</string>
|
||||
<string name="settings_renderer_primary">Material</string>
|
||||
<string name="settings_renderer_secondary">Miuix</string>
|
||||
<string name="settings_navigation_floating_description">让底部导航独立悬浮在页面内容上方</string>
|
||||
<string name="settings_accent_label">强调色</string>
|
||||
@@ -344,8 +344,8 @@
|
||||
<string name="account_current">当前账户</string>
|
||||
<string name="account_greeting">%1$s,您好</string>
|
||||
<string name="account_connection_method">连接方式</string>
|
||||
<string name="account_interface_settings">界面设置</string>
|
||||
<string name="account_interface_settings_desc">主题、渲染风格与交互偏好</string>
|
||||
<string name="account_interface_settings">主题</string>
|
||||
<string name="account_interface_settings_desc">配色、明暗、视觉效果与缩放</string>
|
||||
<string name="account_logout">退出当前会话</string>
|
||||
<string name="account_logout_desc">结束当前连接并返回登录流程</string>
|
||||
<string name="account_logout_confirm">确定退出当前会话吗?</string>
|
||||
|
||||
@@ -265,7 +265,7 @@
|
||||
<string name="settings_style_section">Style</string>
|
||||
<string name="settings_renderer_label">Renderer</string>
|
||||
<string name="settings_renderer_description">Choose the interface renderer used by the app</string>
|
||||
<string name="settings_renderer_material3">Material 3</string>
|
||||
<string name="settings_renderer_material3">Material</string>
|
||||
<string name="settings_renderer_miuix">Miuix</string>
|
||||
<string name="settings_theme_section">Theme</string>
|
||||
<string name="settings_theme_label">Theme mode</string>
|
||||
@@ -291,7 +291,7 @@
|
||||
<string name="settings_theme_entry">Theme</string>
|
||||
<string name="settings_theme_entry_description">Colors, brightness, effects and scale</string>
|
||||
<string name="settings_theme_detail_title">Theme</string>
|
||||
<string name="settings_renderer_primary">Material 3</string>
|
||||
<string name="settings_renderer_primary">Material</string>
|
||||
<string name="settings_renderer_secondary">Miuix</string>
|
||||
<string name="settings_navigation_floating_description">Use a detached navigation surface above page content</string>
|
||||
<string name="settings_accent_label">Accent color</string>
|
||||
@@ -344,8 +344,8 @@
|
||||
<string name="account_current">Current account</string>
|
||||
<string name="account_greeting">Hello, %1$s</string>
|
||||
<string name="account_connection_method">Connection method</string>
|
||||
<string name="account_interface_settings">Interface settings</string>
|
||||
<string name="account_interface_settings_desc">Theme, renderer and interaction preferences</string>
|
||||
<string name="account_interface_settings">Theme</string>
|
||||
<string name="account_interface_settings_desc">Colors, brightness, effects and scale</string>
|
||||
<string name="account_logout">Sign out</string>
|
||||
<string name="account_logout_desc">End the current connection and return to sign-in</string>
|
||||
<string name="account_logout_confirm">Sign out of the current session?</string>
|
||||
|
||||
Reference in New Issue
Block a user