Light and dark in the hub's own colours, dynamic colour by default (refs #2) 206d05a042e7
The Material schemes take the hub's palette (templates/style.css) for when dynamic colour is off; wallpaper colours otherwise. The window behind Compose is the hub's page colour in both themes, so a dark phone never flashes white. Status colours (open, closed) as a composition local. The launcher icon's mipmap-anydpi-v26 folder is plain mipmap-anydpi, since the minimum is 31.
- Author
- Andy McCall @andymccall <[email protected]>
- via
- agent
agent:claude@andymccall <[email protected]> · keyless, under your grant for changes,refs,seal · key f4ec0f2e7863
- When
- · 2026-10-24 22:23 UTC
- Signature
- ◈ sealed by agent:claude@andymccall
- Hash
206d05a042e743fbe1193eb933d8ef8892b8bd83f9c8ad8643d1b22577424945- Depends on
- 57c0824300db e0e4a1c48a77
- Depended on by
- b610bc6eeab7 840c75813f86 3f3508bd5f16 0a2858c0dad3
- In bookmarks
- main
- In tags
- v0.1.0 v0.1.1 v0.1.2 v0.1.3 v0.1.4 v0.1.5 v0.1.6 v0.1.7 v0.1.8 v0.1.9
- Edits
- 20 atoms, 6 tree operations, 8 files
Browse the tree at this change
renamed app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml → app/src/main/res/mipmap-anydpi/ic_launcher.xml +0 −0
| … | 6 unchanged lines |
renamed app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml → app/src/main/res/mipmap-anydpi/ic_launcher_round.xml +0 −0
| … | 6 unchanged lines |
added app/src/main/res/values-night/themes.xml +7 −0
| 1 | + | <?xml version="1.0" encoding="utf-8"?> |
|
| 2 | + | <resources> |
|
| 3 | + | <!-- Dark: the hub's dark page colour, so a dark phone never flashes white. --> |
|
| 4 | + | <style name="Theme.OxygenHub" parent="android:Theme.Material.NoActionBar"> |
|
| 5 | + | <item name="android:windowBackground">@color/window_dark</item> |
|
| 6 | + | </style> |
|
| 7 | + | </resources> |
removed app/src/main/res/mipmap-anydpi-v26 +0 −0
No line changes.
modified app/src/main/java/io/oxygenhub/android/ui/theme/Color.kt +29 −6
| … | 1 unchanged lines |
||
| 2 | 2 | ||
| 3 | 3 | import androidx.compose.ui.graphics.Color |
|
| 4 | 4 | ||
| 5 | - | val Purple80 = Color(0xFFD0BCFF) |
|
| 6 | - | val PurpleGrey80 = Color(0xFFCCC2DC) |
|
| 7 | - | val Pink80 = Color(0xFFEFB8C8) |
|
| 5 | + | // The hub's own palette (oxygenhub templates/style.css), for phones without |
|
| 6 | + | // dynamic colour or when the user turns it off. |
|
| 8 | 7 | ||
| 9 | - | val Purple40 = Color(0xFF6650a4) |
|
| 10 | - | val PurpleGrey40 = Color(0xFF625b71) |
|
| 11 | - | val Pink40 = Color(0xFF7D5260) |
|
| 8 | + | // Dark |
|
| 9 | + | val DarkBackground = Color(0xFF070C18) |
|
| 10 | + | val DarkSurface = Color(0xFF0E1526) |
|
| 11 | + | val DarkSurfaceSoft = Color(0xFF141D33) |
|
| 12 | + | val DarkInk = Color(0xFFE6ECF7) |
|
| 13 | + | val DarkMute = Color(0xFF8592AD) |
|
| 14 | + | val DarkLine = Color(0xFF1F2B47) |
|
| 15 | + | val DarkAccent = Color(0xFF00E0F0) |
|
| 16 | + | val DarkAccent2 = Color(0xFF0072FF) |
|
| 17 | + | val DarkBand = Color(0xFF0B3F9E) |
|
| 18 | + | val DarkOk = Color(0xFF5FE3A1) |
|
| 19 | + | val DarkBad = Color(0xFFFF5E7A) |
|
| 20 | + | val DarkWarn = Color(0xFFB48CFF) |
|
| 21 | + | ||
| 22 | + | // Light |
|
| 23 | + | val LightBackground = Color(0xFFF4F7FC) |
|
| 24 | + | val LightSurface = Color(0xFFFFFFFF) |
|
| 25 | + | val LightSurfaceSoft = Color(0xFFE9EEF8) |
|
| 26 | + | val LightInk = Color(0xFF1B2333) |
|
| 27 | + | val LightMute = Color(0xFF5F6A81) |
|
| 28 | + | val LightLine = Color(0xFFD6DDED) |
|
| 29 | + | val LightAccent = Color(0xFF0064E0) |
|
| 30 | + | val LightAccent2 = Color(0xFF00B8D4) |
|
| 31 | + | val LightBand = Color(0xFF0B3F9E) |
|
| 32 | + | val LightOk = Color(0xFF1A7850) |
|
| 33 | + | val LightBad = Color(0xFFC62D47) |
|
| 34 | + | val LightWarn = Color(0xFF6B3FD1) |
modified app/src/main/java/io/oxygenhub/android/ui/theme/Theme.kt +60 −28
| 1 | 1 | package io.oxygenhub.android.ui.theme |
|
| 2 | 2 | ||
| 3 | - | import android.app.Activity |
|
| 4 | - | import android.os.Build |
|
| 5 | 3 | import androidx.compose.foundation.isSystemInDarkTheme |
|
| 4 | + | import androidx.compose.material3.ColorScheme |
|
| 6 | 5 | import androidx.compose.material3.MaterialTheme |
|
| 7 | 6 | import androidx.compose.material3.darkColorScheme |
|
| 8 | 7 | import androidx.compose.material3.dynamicDarkColorScheme |
|
| 9 | 8 | import androidx.compose.material3.dynamicLightColorScheme |
|
| 10 | 9 | import androidx.compose.material3.lightColorScheme |
|
| 11 | 10 | import androidx.compose.runtime.Composable |
|
| 11 | + | import androidx.compose.runtime.Immutable |
|
| 12 | + | import androidx.compose.runtime.staticCompositionLocalOf |
|
| 13 | + | import androidx.compose.ui.graphics.Color |
|
| 12 | 14 | import androidx.compose.ui.platform.LocalContext |
|
| 13 | 15 | ||
| 14 | 16 | private val DarkColorScheme = darkColorScheme( |
|
| 15 | - | primary = Purple80, |
|
| 16 | - | secondary = PurpleGrey80, |
|
| 17 | - | tertiary = Pink80 |
|
| 17 | + | primary = DarkAccent, |
|
| 18 | + | onPrimary = DarkBackground, |
|
| 19 | + | primaryContainer = DarkBand, |
|
| 20 | + | onPrimaryContainer = Color.White, |
|
| 21 | + | secondary = DarkAccent2, |
|
| 22 | + | onSecondary = Color.White, |
|
| 23 | + | tertiary = DarkWarn, |
|
| 24 | + | background = DarkBackground, |
|
| 25 | + | onBackground = DarkInk, |
|
| 26 | + | surface = DarkBackground, |
|
| 27 | + | onSurface = DarkInk, |
|
| 28 | + | surfaceVariant = DarkSurfaceSoft, |
|
| 29 | + | onSurfaceVariant = DarkMute, |
|
| 30 | + | surfaceContainerLowest = DarkBackground, |
|
| 31 | + | surfaceContainerLow = DarkSurface, |
|
| 32 | + | surfaceContainer = DarkSurface, |
|
| 33 | + | surfaceContainerHigh = DarkSurfaceSoft, |
|
| 34 | + | surfaceContainerHighest = DarkSurfaceSoft, |
|
| 35 | + | outline = DarkMute, |
|
| 36 | + | outlineVariant = DarkLine, |
|
| 37 | + | error = DarkBad, |
|
| 18 | 38 | ) |
|
| 19 | 39 | ||
| 20 | 40 | private val LightColorScheme = lightColorScheme( |
|
| 21 | - | primary = Purple40, |
|
| 22 | - | secondary = PurpleGrey40, |
|
| 23 | - | tertiary = Pink40 |
|
| 24 | - | ||
| 25 | - | /* Other default colors to override |
|
| 26 | - | background = Color(0xFFFFFBFE), |
|
| 27 | - | surface = Color(0xFFFFFBFE), |
|
| 41 | + | primary = LightAccent, |
|
| 28 | 42 | onPrimary = Color.White, |
|
| 43 | + | primaryContainer = LightBand, |
|
| 44 | + | onPrimaryContainer = Color.White, |
|
| 45 | + | secondary = LightAccent2, |
|
| 29 | 46 | onSecondary = Color.White, |
|
| 30 | - | onTertiary = Color.White, |
|
| 31 | - | onBackground = Color(0xFF1C1B1F), |
|
| 32 | - | onSurface = Color(0xFF1C1B1F), |
|
| 33 | - | */ |
|
| 47 | + | tertiary = LightWarn, |
|
| 48 | + | background = LightBackground, |
|
| 49 | + | onBackground = LightInk, |
|
| 50 | + | surface = LightBackground, |
|
| 51 | + | onSurface = LightInk, |
|
| 52 | + | surfaceVariant = LightSurfaceSoft, |
|
| 53 | + | onSurfaceVariant = LightMute, |
|
| 54 | + | surfaceContainerLowest = LightSurface, |
|
| 55 | + | surfaceContainerLow = LightSurface, |
|
| 56 | + | surfaceContainer = LightSurface, |
|
| 57 | + | surfaceContainerHigh = LightSurfaceSoft, |
|
| 58 | + | surfaceContainerHighest = LightSurfaceSoft, |
|
| 59 | + | outline = LightMute, |
|
| 60 | + | outlineVariant = LightLine, |
|
| 61 | + | error = LightBad, |
|
| 34 | 62 | ) |
|
| 63 | + | ||
| 64 | + | /** Colours Material has no slot for: an open or closed state. */ |
|
| 65 | + | @Immutable |
|
| 66 | + | data class StatusColors(val ok: Color, val bad: Color, val warn: Color) |
|
| 67 | + | ||
| 68 | + | val LocalStatusColors = staticCompositionLocalOf { StatusColors(LightOk, LightBad, LightWarn) } |
|
| 35 | 69 | ||
| 36 | 70 | @Composable |
|
| 37 | 71 | fun OxygenHubTheme( |
|
| 38 | 72 | darkTheme: Boolean = isSystemInDarkTheme(), |
|
| 39 | - | // Dynamic color is available on Android 12+ |
|
| 73 | + | // Wallpaper colours (Android 12, the app's minimum, has them); the |
|
| 74 | + | // hub's own palette when turned off. |
|
| 40 | 75 | dynamicColor: Boolean = true, |
|
| 41 | - | content: @Composable () -> Unit |
|
| 76 | + | content: @Composable () -> Unit, |
|
| 42 | 77 | ) { |
|
| 43 | - | val colorScheme = when { |
|
| 44 | - | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { |
|
| 78 | + | val colorScheme: ColorScheme = when { |
|
| 79 | + | dynamicColor -> { |
|
| 45 | 80 | val context = LocalContext.current |
|
| 46 | 81 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) |
|
| 47 | 82 | } |
|
| 48 | - | ||
| 49 | 83 | darkTheme -> DarkColorScheme |
|
| 50 | 84 | else -> LightColorScheme |
|
| 51 | 85 | } |
|
| 52 | - | ||
| 53 | - | MaterialTheme( |
|
| 54 | - | colorScheme = colorScheme, |
|
| 55 | - | typography = Typography, |
|
| 56 | - | content = content |
|
| 57 | - | ) |
|
| 58 | - | } |
|
| 86 | + | val status = if (darkTheme) StatusColors(DarkOk, DarkBad, DarkWarn) else StatusColors(LightOk, LightBad, LightWarn) |
|
| 87 | + | androidx.compose.runtime.CompositionLocalProvider(LocalStatusColors provides status) { |
|
| 88 | + | MaterialTheme(colorScheme = colorScheme, typography = Typography, content = content) |
|
| 89 | + | } |
|
| 90 | + | } |
modified app/src/main/res/values/colors.xml +4 −8
| 1 | 1 | <?xml version="1.0" encoding="utf-8"?> |
|
| 2 | 2 | <resources> |
|
| 3 | - | <color name="purple_200">#FFBB86FC</color> |
|
| 4 | - | <color name="purple_500">#FF6200EE</color> |
|
| 5 | - | <color name="purple_700">#FF3700B3</color> |
|
| 6 | - | <color name="teal_200">#FF03DAC5</color> |
|
| 7 | - | <color name="teal_700">#FF018786</color> |
|
| 8 | - | <color name="black">#FF000000</color> |
|
| 9 | - | <color name="white">#FFFFFFFF</color> |
|
| 10 | - | </resources> |
|
| 3 | + | <!-- The hub's page backgrounds (oxygenhub templates/style.css, the bg colour). --> |
|
| 4 | + | <color name="window_light">#FFF4F7FC</color> |
|
| 5 | + | <color name="window_dark">#FF070C18</color> |
|
| 6 | + | </resources> |
modified app/src/main/res/values/themes.xml +5 −3
| 1 | 1 | <?xml version="1.0" encoding="utf-8"?> |
|
| 2 | 2 | <resources> |
|
| 3 | - | ||
| 4 | - | <style name="Theme.OxygenHub" parent="android:Theme.Material.Light.NoActionBar" /> |
|
| 5 | - | </resources> |
|
| 3 | + | <!-- The window behind Compose, until it draws: the hub's light page colour. --> |
|
| 4 | + | <style name="Theme.OxygenHub" parent="android:Theme.Material.Light.NoActionBar"> |
|
| 5 | + | <item name="android:windowBackground">@color/window_light</item> |
|
| 6 | + | </style> |
|
| 7 | + | </resources> |