| 1 |
package io.oxygenhub.android.ui.theme |
| 2 |
|
| 3 |
import androidx.compose.foundation.isSystemInDarkTheme |
| 4 |
import androidx.compose.material3.ColorScheme |
| 5 |
import androidx.compose.material3.MaterialTheme |
| 6 |
import androidx.compose.material3.darkColorScheme |
| 7 |
import androidx.compose.material3.dynamicDarkColorScheme |
| 8 |
import androidx.compose.material3.dynamicLightColorScheme |
| 9 |
import androidx.compose.material3.lightColorScheme |
| 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 |
| 14 |
import androidx.compose.ui.graphics.lerp |
| 15 |
import androidx.compose.ui.platform.LocalContext |
| 16 |
|
| 17 |
internal val DarkColorScheme = darkColorScheme( |
| 18 |
primary = DarkAccent, |
| 19 |
onPrimary = DarkBackground, |
| 20 |
primaryContainer = DarkBand, |
| 21 |
onPrimaryContainer = Color.White, |
| 22 |
secondary = DarkAccent2, |
| 23 |
onSecondary = Color.White, |
| 24 |
secondaryContainer = DarkSelected, |
| 25 |
onSecondaryContainer = DarkInk, |
| 26 |
tertiary = DarkWarn, |
| 27 |
background = DarkBackground, |
| 28 |
onBackground = DarkInk, |
| 29 |
surface = DarkBackground, |
| 30 |
onSurface = DarkInk, |
| 31 |
surfaceVariant = DarkSurfaceSoft, |
| 32 |
onSurfaceVariant = DarkMute, |
| 33 |
surfaceContainerLowest = DarkBackground, |
| 34 |
surfaceContainerLow = DarkPanel, |
| 35 |
surfaceContainer = DarkPanelMid, |
| 36 |
surfaceContainerHigh = DarkPanelHigh, |
| 37 |
surfaceContainerHighest = DarkPanelHighest, |
| 38 |
outline = DarkMute, |
| 39 |
outlineVariant = DarkEdge, |
| 40 |
error = DarkBad, |
| 41 |
) |
| 42 |
|
| 43 |
internal val LightColorScheme = lightColorScheme( |
| 44 |
primary = LightAccent, |
| 45 |
onPrimary = Color.White, |
| 46 |
primaryContainer = LightBand, |
| 47 |
onPrimaryContainer = Color.White, |
| 48 |
secondary = LightAccent2, |
| 49 |
onSecondary = Color.White, |
| 50 |
secondaryContainer = LightSelected, |
| 51 |
onSecondaryContainer = LightInk, |
| 52 |
tertiary = LightWarn, |
| 53 |
background = LightBackground, |
| 54 |
onBackground = LightInk, |
| 55 |
surface = LightBackground, |
| 56 |
onSurface = LightInk, |
| 57 |
surfaceVariant = LightSurfaceSoft, |
| 58 |
onSurfaceVariant = LightMute, |
| 59 |
surfaceContainerLowest = LightSurface, |
| 60 |
surfaceContainerLow = LightSurface, |
| 61 |
surfaceContainer = LightSurface, |
| 62 |
surfaceContainerHigh = LightSurfaceSoft, |
| 63 |
surfaceContainerHighest = LightSurfaceSoft, |
| 64 |
outline = LightMute, |
| 65 |
outlineVariant = LightLine, |
| 66 |
error = LightBad, |
| 67 |
) |
| 68 |
|
| 69 |
/** Colours Material has no slot for: an open or closed state. */ |
| 70 |
@Immutable |
| 71 |
data class StatusColors(val ok: Color, val bad: Color, val warn: Color) |
| 72 |
|
| 73 |
val LocalStatusColors = staticCompositionLocalOf { StatusColors(LightOk, LightBad, LightWarn) } |
| 74 |
|
| 75 |
/** |
| 76 |
* Wallpaper colours in the dark, with containers lifted off the background |
| 77 |
* (#24): Android's dark tones sit a shade apart, which a phone's screen hardly |
| 78 |
* shows. Each container a clear step up, and edges brighter than the default. |
| 79 |
*/ |
| 80 |
internal fun lifted(scheme: ColorScheme): ColorScheme { |
| 81 |
val step = { f: Float -> lerp(scheme.background, scheme.onSurface, f) } |
| 82 |
return scheme.copy( |
| 83 |
surfaceContainerLow = step(0.06f), |
| 84 |
surfaceContainer = step(0.09f), |
| 85 |
surfaceContainerHigh = step(0.12f), |
| 86 |
surfaceContainerHighest = step(0.15f), |
| 87 |
outlineVariant = step(0.26f), |
| 88 |
) |
| 89 |
} |
| 90 |
|
| 91 |
@Composable |
| 92 |
fun OxygenHubTheme( |
| 93 |
darkTheme: Boolean = isSystemInDarkTheme(), |
| 94 |
// The hub's own palette, unless Account's "Wallpaper colours" is on (#24). |
| 95 |
dynamicColor: Boolean = rememberWallpaperColours(), |
| 96 |
content: @Composable () -> Unit, |
| 97 |
) { |
| 98 |
val colorScheme: ColorScheme = when { |
| 99 |
dynamicColor -> { |
| 100 |
val context = LocalContext.current |
| 101 |
if (darkTheme) lifted(dynamicDarkColorScheme(context)) else dynamicLightColorScheme(context) |
| 102 |
} |
| 103 |
darkTheme -> DarkColorScheme |
| 104 |
else -> LightColorScheme |
| 105 |
} |
| 106 |
val status = if (darkTheme) StatusColors(DarkOk, DarkBad, DarkWarn) else StatusColors(LightOk, LightBad, LightWarn) |
| 107 |
androidx.compose.runtime.CompositionLocalProvider(LocalStatusColors provides status) { |
| 108 |
MaterialTheme(colorScheme = colorScheme, typography = Typography, content = content) |
| 109 |
} |
| 110 |
} |