feat(identity): pure-black (AMOLED) dark theme

ColorScheme.toPureBlack() sets background, surface, surfaceDim and
surfaceContainerLowest to #000 and dims the other containers by scaling
their sRGB channels, so their hue tint carries over and grid tiles drawn
as containers over surface keep visible gaps. FloretExpressiveTheme takes
pureBlack to apply it to the dark scheme, dynamic or app-supplied.

core-prefs gains ThemeMode.BLACK (dark, pure black) and
ThemeMode.isPureBlack, stored under the same theme_mode key.
This commit is contained in:
makiolaj committed 2026-10-14 17:19:31 +02:00
1 parent 0b44d353d2
commit f1ded81c03
7 files changed
+164 -4

No files matched your search

@@ -4,16 +4,22 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
/** Light/dark override. [SYSTEM] follows the device setting. */
enum class ThemeMode { SYSTEM, LIGHT, DARK }
/**
* Light/dark override. [SYSTEM] follows the device setting; [BLACK] is dark
* with pure-black surfaces (the AMOLED option).
*/
enum class ThemeMode { SYSTEM, LIGHT, DARK, BLACK }
/** Resolve to a concrete dark flag; [SYSTEM] defers to [systemDark]. */
fun ThemeMode.isDark(systemDark: Boolean): Boolean = when (this) {
ThemeMode.SYSTEM -> systemDark
ThemeMode.LIGHT -> false
ThemeMode.DARK -> true
ThemeMode.DARK, ThemeMode.BLACK -> true
}
/** Whether the theme should draw pure-black surfaces; only [ThemeMode.BLACK] does. */
val ThemeMode.isPureBlack: Boolean get() = this == ThemeMode.BLACK
/** The two appearance choices every app in the family has. */
data class Appearance(
val themeMode: ThemeMode = ThemeMode.SYSTEM,
@@ -42,6 +42,24 @@ class ThemeModeTest {
assertThat(dark).isTrue()
}
@Test
fun `isDark lets BLACK override a light system`() {
val mode = ThemeMode.BLACK
val dark = mode.isDark(systemDark = false)
assertThat(dark).isTrue()
}
@Test
fun `isPureBlack is set only for BLACK`() {
val modes = ThemeMode.entries
val pureBlack = modes.filter { it.isPureBlack }
assertThat(pureBlack).containsExactly(ThemeMode.BLACK)
}
@Test
fun `Appearance defaults to following the system with dynamic colour on`() {
val appearance = Appearance()