fix(ui): stabilize theme and navigation behavior

This commit is contained in:
2026-08-27 23:20:39 +08:00
parent bdeddf46e2
commit a5f31486b4
14 changed files with 322 additions and 180 deletions
@@ -38,6 +38,7 @@ dependencies {
implementation(libs.compose.ui.tooling.preview)
implementation(libs.compose.material3)
implementation(libs.compose.material.icons.extended)
implementation(libs.androidx.activity.compose)
implementation(libs.miuix.ui.android)
debugImplementation(libs.compose.ui.tooling)
screenshotTestImplementation(libs.compose.screenshot.test)
@@ -1,5 +1,7 @@
package com.flagship.abox.manager.designsystem
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
@@ -45,6 +47,9 @@ import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -677,3 +682,16 @@ fun RowScope.ABoxNavigationBarItem(
),
)
}
@Composable
fun ABoxBackLayer(
onBack: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
content: @Composable () -> Unit,
) {
BackHandler(enabled = enabled, onBack = onBack)
Box(modifier = modifier.fillMaxSize()) {
content()
}
}
@@ -101,7 +101,7 @@ internal fun ABoxThemeContent(
LocalABoxBlurEnabled provides resolution.blurEnabled,
LocalABoxNavigationPresentation provides preferences.navigationPresentation,
LocalABoxLiquidGlassEnabled provides preferences.liquidGlassEnabled,
LocalABoxPredictiveBackEnabled provides preferences.predictiveBackEnabled,
LocalABoxPredictiveBackEnabled provides false,
) {
renderer.Render(
resolution = resolution,
@@ -2,10 +2,13 @@ package com.flagship.abox.manager.designsystem.tokens
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.flagship.abox.manager.preferences.AppThemeMode
import com.flagship.abox.manager.preferences.AccentPreference
import com.flagship.abox.manager.preferences.ColorStandard
import com.flagship.abox.manager.preferences.ColorStyle
import com.flagship.abox.manager.preferences.DesignRenderer
import com.flagship.abox.manager.preferences.UiPreferences
@@ -122,7 +125,13 @@ object ABoxThemeResolver {
val colorTokens = if (preferences.dynamicColorEnabled && sdkInt >= 31 && dynamicColorTokens != null) {
dynamicColorTokens
} else {
brandTokens.withAccent(preferences.accentPreference, isDarkTheme)
ABoxAccentPalette.resolve(
base = brandTokens,
accent = preferences.accentPreference,
style = preferences.colorStyle,
standard = preferences.colorStandard,
dark = isDarkTheme,
)
}
val shapeTokens = when (preferences.renderer) {
DesignRenderer.MATERIAL3 -> ABoxThemeDefaults.material3Shapes
@@ -139,13 +148,86 @@ object ABoxThemeResolver {
}
}
private fun ABoxColorTokens.withAccent(accent: AccentPreference, dark: Boolean): ABoxColorTokens {
val selected = when (accent) {
AccentPreference.BLUE -> if (dark) Color(0xFF9CCAFF) else Color(0xFF385F9D)
AccentPreference.GREEN -> if (dark) Color(0xFF7EDFA7) else Color(0xFF247A4B)
AccentPreference.AMBER -> if (dark) Color(0xFFFFC46B) else Color(0xFF8A5800)
AccentPreference.ROSE -> if (dark) Color(0xFFFFB1C1) else Color(0xFFA43C59)
AccentPreference.VIOLET -> if (dark) Color(0xFFD4BBFF) else Color(0xFF6D52A2)
object ABoxAccentPalette {
fun preview(
accent: AccentPreference,
style: ColorStyle,
standard: ColorStandard,
dark: Boolean,
): Color = resolve(
base = if (dark) ABoxThemeDefaults.darkColors else ABoxThemeDefaults.lightColors,
accent = accent,
style = style,
standard = standard,
dark = dark,
).primary
fun resolve(
base: ABoxColorTokens,
accent: AccentPreference,
style: ColorStyle,
standard: ColorStandard,
dark: Boolean,
): ABoxColorTokens {
val source = source(accent, dark)
val primary = when (style) {
ColorStyle.TONAL_SPOT -> lerp(source, base.surface, if (dark) 0.08f else 0.12f)
ColorStyle.EXPRESSIVE -> expressive(accent, dark)
ColorStyle.FIDELITY -> source
}
val deviceSensor = when (style) {
ColorStyle.EXPRESSIVE -> expressiveSensor(accent, dark)
ColorStyle.FIDELITY -> lerp(source, base.success, 0.38f)
ColorStyle.TONAL_SPOT -> base.deviceSensor
}
val deviceInfrared = when (style) {
ColorStyle.EXPRESSIVE -> expressiveInfrared(accent, dark)
ColorStyle.FIDELITY -> lerp(source, base.deviceInfrared, 0.46f)
ColorStyle.TONAL_SPOT -> base.deviceInfrared
}
val styled = base.copy(
primary = primary,
deviceSocket = primary,
deviceSensor = deviceSensor,
deviceInfrared = deviceInfrared,
)
return when (standard) {
ColorStandard.SPEC_2021 -> styled
ColorStandard.SPEC_2025 -> styled.copy(
surface = if (dark) Color(0xFF0C1114) else Color(0xFFFAF9FC),
surfaceContainer = if (dark) Color(0xFF242C30) else Color(0xFFE9E8EE),
outline = if (dark) Color(0xFFA7B2B7) else Color(0xFF596267),
primary = lerp(primary, if (dark) Color.White else Color.Black, 0.08f),
deviceSocket = lerp(primary, if (dark) Color.White else Color.Black, 0.08f),
)
}
}
private fun source(accent: AccentPreference, dark: Boolean): Color = when (accent) {
AccentPreference.BLUE -> if (dark) Color(0xFF9CCAFF) else Color(0xFF4F6FA9)
AccentPreference.GREEN -> if (dark) Color(0xFF7EDFA7) else Color(0xFF2F7D57)
AccentPreference.AMBER -> if (dark) Color(0xFFFFC46B) else Color(0xFFA66700)
AccentPreference.ROSE -> if (dark) Color(0xFFFFB1C1) else Color(0xFFA94C68)
AccentPreference.VIOLET -> if (dark) Color(0xFFD4BBFF) else Color(0xFF7659A8)
}
private fun expressive(accent: AccentPreference, dark: Boolean): Color = when (accent) {
AccentPreference.BLUE -> if (dark) Color(0xFF72D6FF) else Color(0xFF006B91)
AccentPreference.GREEN -> if (dark) Color(0xFF69E6B1) else Color(0xFF007A54)
AccentPreference.AMBER -> if (dark) Color(0xFFFFC24B) else Color(0xFF9A5C00)
AccentPreference.ROSE -> if (dark) Color(0xFFFFA8C4) else Color(0xFFB12F62)
AccentPreference.VIOLET -> if (dark) Color(0xFFC9B5FF) else Color(0xFF7048B8)
}
private fun expressiveSensor(accent: AccentPreference, dark: Boolean): Color = when (accent) {
AccentPreference.BLUE, AccentPreference.VIOLET -> if (dark) Color(0xFF78DFA2) else Color(0xFF26764A)
AccentPreference.GREEN, AccentPreference.AMBER -> if (dark) Color(0xFF79D6F3) else Color(0xFF006985)
AccentPreference.ROSE -> if (dark) Color(0xFFFFC45F) else Color(0xFF8B5B00)
}
private fun expressiveInfrared(accent: AccentPreference, dark: Boolean): Color = when (accent) {
AccentPreference.BLUE, AccentPreference.GREEN -> if (dark) Color(0xFFD1B8FF) else Color(0xFF7453A5)
AccentPreference.AMBER, AccentPreference.ROSE -> if (dark) Color(0xFF9FCBFF) else Color(0xFF38639A)
AccentPreference.VIOLET -> if (dark) Color(0xFFFFB0C7) else Color(0xFFA43E62)
}
return copy(primary = selected, deviceSocket = selected)
}
@@ -26,8 +26,26 @@ class ABoxThemeResolverTest {
)
assertFalse(light.isDarkTheme)
assertEquals(ABoxThemeDefaults.lightColors, light.colorTokens)
assertEquals(ABoxThemeDefaults.darkColors, dark.colorTokens)
assertEquals(
ABoxAccentPalette.resolve(
ABoxThemeDefaults.lightColors,
UiPreferences().accentPreference,
UiPreferences().colorStyle,
UiPreferences().colorStandard,
false,
),
light.colorTokens,
)
assertEquals(
ABoxAccentPalette.resolve(
ABoxThemeDefaults.darkColors,
UiPreferences().accentPreference,
UiPreferences().colorStyle,
UiPreferences().colorStandard,
true,
),
dark.colorTokens,
)
}
@Test
@@ -42,7 +60,16 @@ class ABoxThemeResolverTest {
dynamicColorTokens = dynamicTokens,
)
assertEquals(ABoxThemeDefaults.lightColors, resolution.colorTokens)
assertEquals(
ABoxAccentPalette.resolve(
ABoxThemeDefaults.lightColors,
UiPreferences().accentPreference,
UiPreferences().colorStyle,
UiPreferences().colorStandard,
false,
),
resolution.colorTokens,
)
}
@Test
@@ -57,7 +84,16 @@ class ABoxThemeResolverTest {
dynamicColorTokens = dynamicTokens,
)
assertEquals(ABoxThemeDefaults.darkColors, resolution.colorTokens)
assertEquals(
ABoxAccentPalette.resolve(
ABoxThemeDefaults.darkColors,
UiPreferences().accentPreference,
UiPreferences().colorStyle,
UiPreferences().colorStandard,
true,
),
resolution.colorTokens,
)
}
@Test