diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/compose/theme/ShxTheme.kt b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/compose/theme/ShxTheme.kt index 47e3b4a54..4669f593f 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/compose/theme/ShxTheme.kt +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/compose/theme/ShxTheme.kt @@ -13,9 +13,13 @@ import android.content.Context import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.MaterialExpressiveTheme +import androidx.compose.material3.MaterialShapes +import androidx.compose.material3.MotionScheme import androidx.compose.material3.Shapes import androidx.compose.material3.Typography +import androidx.compose.material3.toShape import androidx.compose.material3.darkColorScheme import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme @@ -27,6 +31,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight @@ -38,8 +43,41 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -val ConnectionCardRadius = 16.dp -val ScreenPadding = 16.dp +/** Spacing scale; use these instead of ad-hoc .dp literals. */ +object ShxSpacing +{ + val xs = 4.dp + val sm = 8.dp + val md = 12.dp + val lg = 16.dp + val xl = 24.dp + val xxl = 32.dp +} + +/** Shape tokens. [scheme] feeds MaterialTheme.shapes; the rest are named radii. */ +object ShxShapes +{ + val cardRadius = 16.dp + + val scheme = Shapes( + extraSmall = RoundedCornerShape(6.dp), + small = RoundedCornerShape(10.dp), + medium = RoundedCornerShape(14.dp), + large = RoundedCornerShape(16.dp), + extraLarge = RoundedCornerShape(22.dp), + ) + + /** Scalloped "cookie" used by the hero connect button (Expressive MaterialShapes). */ + @ExperimentalMaterial3ExpressiveApi + val hero: Shape + @Composable get() = MaterialShapes.Cookie9Sided.toShape() +} + +@Deprecated("Use ShxShapes.cardRadius", ReplaceWith("ShxShapes.cardRadius")) +val ConnectionCardRadius = ShxShapes.cardRadius + +@Deprecated("Use ShxSpacing.lg", ReplaceWith("ShxSpacing.lg")) +val ScreenPadding = ShxSpacing.lg val ProtonPurpleLight = Color(0xFF6D4AFF) val ProtonPurpleDark = Color(0xFF9C7CFF) @@ -49,34 +87,34 @@ val SuccessGreenDark = Color(0xFF28A671) private val LightColors = lightColorScheme( primary = ProtonPurpleLight, onPrimary = Color.White, - primaryContainer = Color(0xFFEDE7FF), - onPrimaryContainer = Color(0xFF2E1A7A), + primaryContainer = Color(0xFFE7DFFF), + onPrimaryContainer = Color(0xFF1F0A6B), inversePrimary = ProtonPurpleDark, - secondary = Color(0xFF625B71), + secondary = Color(0xFF5E5A7D), onSecondary = Color.White, - secondaryContainer = Color(0xFFE8DEF8), - onSecondaryContainer = Color(0xFF1D192B), + secondaryContainer = Color(0xFFE4E0F9), + onSecondaryContainer = Color(0xFF1A1833), tertiary = SuccessGreenLight, onTertiary = Color.White, tertiaryContainer = Color(0xFFC8F2DA), onTertiaryContainer = Color(0xFF07391D), - background = Color(0xFFF7F7F8), - onBackground = Color(0xFF1B1B20), - surface = Color(0xFFF7F7F8), - onSurface = Color(0xFF1B1B20), - surfaceVariant = Color(0xFFEBEBEF), - onSurfaceVariant = Color(0xFF57575F), - surfaceDim = Color(0xFFDADAE0), - surfaceBright = Color(0xFFF7F7F8), - surfaceContainerLowest = Color.White, - surfaceContainerLow = Color(0xFFF2F2F4), - surfaceContainer = Color.White, - surfaceContainerHigh = Color(0xFFF2F2F4), - surfaceContainerHighest = Color(0xFFEBEBEF), - outline = Color(0xFF797981), - outlineVariant = Color(0xFFD0D0D6), - inverseSurface = Color(0xFF2F2F35), - inverseOnSurface = Color(0xFFF2F2F4), + background = Color(0xFFFCF8FF), + onBackground = Color(0xFF1C1B20), + surface = Color(0xFFFCF8FF), + onSurface = Color(0xFF1C1B20), + surfaceVariant = Color(0xFFE5E1EC), + onSurfaceVariant = Color(0xFF48454E), + surfaceDim = Color(0xFFDCD8E0), + surfaceBright = Color(0xFFFCF8FF), + surfaceContainerLowest = Color(0xFFFFFFFF), + surfaceContainerLow = Color(0xFFF6F2FA), + surfaceContainer = Color(0xFFF0ECF4), + surfaceContainerHigh = Color(0xFFEAE6EE), + surfaceContainerHighest = Color(0xFFE5E1E9), + outline = Color(0xFF79767F), + outlineVariant = Color(0xFFC9C5D0), + inverseSurface = Color(0xFF313036), + inverseOnSurface = Color(0xFFF3EFF7), error = Color(0xFFD8303C), onError = Color.White, errorContainer = Color(0xFFFFDAD6), @@ -90,31 +128,31 @@ private val DarkColors = darkColorScheme( primaryContainer = Color(0xFF3B2C88), onPrimaryContainer = Color(0xFFEDE7FF), inversePrimary = ProtonPurpleLight, - secondary = Color(0xFFCCC2DC), - onSecondary = Color(0xFF332D41), - secondaryContainer = Color(0xFF4A4458), - onSecondaryContainer = Color(0xFFE8DEF8), + secondary = Color(0xFFCBC4E8), + onSecondary = Color(0xFF322F4D), + secondaryContainer = Color(0xFF484564), + onSecondaryContainer = Color(0xFFE4E0F9), tertiary = SuccessGreenDark, onTertiary = Color(0xFF00391B), tertiaryContainer = Color(0xFF005228), onTertiaryContainer = Color(0xFFC8F2DA), - background = Color(0xFF17171A), - onBackground = Color(0xFFE6E6E9), - surface = Color(0xFF17171A), - onSurface = Color(0xFFE6E6E9), - surfaceVariant = Color(0xFF2C2C31), - onSurfaceVariant = Color(0xFFC0C0C8), - surfaceDim = Color(0xFF17171A), - surfaceBright = Color(0xFF3B3B41), - surfaceContainerLowest = Color(0xFF121214), - surfaceContainerLow = Color(0xFF1F1F23), - surfaceContainer = Color(0xFF242428), - surfaceContainerHigh = Color(0xFF2C2C31), - surfaceContainerHighest = Color(0xFF36363B), - outline = Color(0xFF8B8B93), - outlineVariant = Color(0xFF3D3D43), - inverseSurface = Color(0xFFE6E6E9), - inverseOnSurface = Color(0xFF2C2C31), + background = Color(0xFF141318), + onBackground = Color(0xFFE5E1E9), + surface = Color(0xFF141318), + onSurface = Color(0xFFE5E1E9), + surfaceVariant = Color(0xFF48454E), + onSurfaceVariant = Color(0xFFC9C5D0), + surfaceDim = Color(0xFF141318), + surfaceBright = Color(0xFF3A383E), + surfaceContainerLowest = Color(0xFF0F0D13), + surfaceContainerLow = Color(0xFF1C1B20), + surfaceContainer = Color(0xFF201F24), + surfaceContainerHigh = Color(0xFF2B292F), + surfaceContainerHighest = Color(0xFF36343A), + outline = Color(0xFF938F99), + outlineVariant = Color(0xFF48454E), + inverseSurface = Color(0xFFE5E1E9), + inverseOnSurface = Color(0xFF313036), error = Color(0xFFFF8A8C), onError = Color(0xFF490005), errorContainer = Color(0xFF68000E), @@ -163,14 +201,6 @@ private val DarkStatusColors = ShxStatusColors( * semantics must stay green/purple/red regardless of the user's wallpaper. */ val LocalShxStatusColors = staticCompositionLocalOf { LightStatusColors } -private val ShxShapes = Shapes( - extraSmall = RoundedCornerShape(6.dp), - small = RoundedCornerShape(10.dp), - medium = RoundedCornerShape(14.dp), - large = RoundedCornerShape(16.dp), - extraLarge = RoundedCornerShape(22.dp), -) - /** * Process-wide holder of the "use system colors" preference, so flipping it in * Settings re-themes every open Compose surface without recreating activities. @@ -236,6 +266,7 @@ private fun buildTypography(): Typography private val ShxTypography = buildTypography() +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun ShxTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { @@ -251,11 +282,10 @@ fun ShxTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () } val statusColors = if (darkTheme) DarkStatusColors else LightStatusColors CompositionLocalProvider(LocalShxStatusColors provides statusColors) { - /* MaterialExpressiveTheme/MotionScheme недоступны: в material3 1.4.0 они internal, - а 1.5.0-alpha требует compose 1.10+ (minSdk 23). Вернуться при подъеме minSdk. */ - MaterialTheme( + MaterialExpressiveTheme( colorScheme = colors, - shapes = ShxShapes, + motionScheme = MotionScheme.expressive(), + shapes = ShxShapes.scheme, typography = ShxTypography, content = content, )