From 5525609355ddfcf4516688d253add8deb05d47d0 Mon Sep 17 00:00:00 2001
From: MM20 <15646950+MM2-0@users.noreply.github.com>
Date: Tue, 10 Jun 2025 18:41:18 +0200
Subject: [PATCH] Add material 3 high contrast theme
---
.../ColorSchemesSettingsScreenVM.kt | 3 +
core/i18n/src/main/res/values/strings.xml | 1 +
.../preferences/LauncherSettingsData.kt | 4 +
.../de/mm20/launcher2/themes/DefaultThemes.kt | 78 +++++++++++++++++++
.../mm20/launcher2/themes/ThemeRepository.kt | 14 ++++
5 files changed, 100 insertions(+)
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreenVM.kt
index a0acdb75..0a59622b 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreenVM.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreenVM.kt
@@ -11,6 +11,7 @@ import de.mm20.launcher2.preferences.ui.UiSettings
import de.mm20.launcher2.themes.BlackAndWhiteThemeId
import de.mm20.launcher2.themes.DefaultThemeId
import de.mm20.launcher2.themes.Colors
+import de.mm20.launcher2.themes.HighContrastThemeId
import de.mm20.launcher2.themes.ThemeRepository
import de.mm20.launcher2.themes.toLegacyJson
import de.mm20.launcher2.ui.R
@@ -32,6 +33,7 @@ class ColorSchemesSettingsScreenVM : ViewModel(), KoinComponent {
val selectedColors = uiSettings.colors.map {
when(it) {
ColorsDescriptor.Default -> DefaultThemeId
+ ColorsDescriptor.HighContrast -> HighContrastThemeId
ColorsDescriptor.BlackAndWhite -> BlackAndWhiteThemeId
is ColorsDescriptor.Custom -> UUID.fromString(it.id)
}
@@ -49,6 +51,7 @@ class ColorSchemesSettingsScreenVM : ViewModel(), KoinComponent {
fun selectTheme(colors: Colors) {
uiSettings.setColors(when(colors.id) {
DefaultThemeId -> ColorsDescriptor.Default
+ HighContrastThemeId -> ColorsDescriptor.HighContrast
BlackAndWhiteThemeId -> ColorsDescriptor.BlackAndWhite
else -> ColorsDescriptor.Custom(colors.id.toString())
})
diff --git a/core/i18n/src/main/res/values/strings.xml b/core/i18n/src/main/res/values/strings.xml
index a56f1d75..78d56013 100644
--- a/core/i18n/src/main/res/values/strings.xml
+++ b/core/i18n/src/main/res/values/strings.xml
@@ -428,6 +428,7 @@
Dark
Color scheme
Default
+ High contrast
Black and White
Color palette
Source for dynamic colors
diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt
index ff320950..6cb71319 100644
--- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt
+++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt
@@ -212,6 +212,10 @@ sealed interface ColorsDescriptor {
@SerialName("default")
data object Default : ColorsDescriptor
+ @Serializable
+ @SerialName("high_contrast")
+ data object HighContrast : ColorsDescriptor
+
@Serializable
@SerialName("bw")
data object BlackAndWhite : ColorsDescriptor
diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/DefaultThemes.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/DefaultThemes.kt
index ce694aab..ee2d9f11 100644
--- a/data/themes/src/main/java/de/mm20/launcher2/themes/DefaultThemes.kt
+++ b/data/themes/src/main/java/de/mm20/launcher2/themes/DefaultThemes.kt
@@ -4,6 +4,7 @@ import java.util.UUID
val DefaultThemeId = UUID(0L, 0L)
+val HighContrastThemeId = UUID(0L, 2L)
val BlackAndWhiteThemeId = UUID(0L, 1L)
val ExtraRoundShapesId = UUID(0L, 1L)
val CutShapesId = UUID(0L, 2L)
@@ -87,6 +88,83 @@ val DefaultDarkColorScheme = ColorScheme(
scrim = ColorRef(CorePaletteColor.Neutral, 0),
)
+val HighContrastLightColorScheme = ColorScheme(
+ primary = ColorRef(CorePaletteColor.Primary, 20),
+ onPrimary = ColorRef(CorePaletteColor.Primary, 100),
+ primaryContainer = ColorRef(CorePaletteColor.Primary, 30),
+ onPrimaryContainer = ColorRef(CorePaletteColor.Primary, 100),
+ secondary = ColorRef(CorePaletteColor.Secondary, 20),
+ onSecondary = ColorRef(CorePaletteColor.Secondary, 100),
+ secondaryContainer = ColorRef(CorePaletteColor.Secondary, 30),
+ onSecondaryContainer = ColorRef(CorePaletteColor.Secondary, 100),
+ tertiary = ColorRef(CorePaletteColor.Tertiary, 20),
+ onTertiary = ColorRef(CorePaletteColor.Tertiary, 100),
+ tertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 30),
+ onTertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 100),
+ error = ColorRef(CorePaletteColor.Error, 20),
+ onError = ColorRef(CorePaletteColor.Error, 100),
+ errorContainer = ColorRef(CorePaletteColor.Error, 30),
+ onErrorContainer = ColorRef(CorePaletteColor.Error, 100),
+ surfaceDim = ColorRef(CorePaletteColor.Neutral, 87),
+ surface = ColorRef(CorePaletteColor.Neutral, 98),
+ surfaceBright = ColorRef(CorePaletteColor.Neutral, 98),
+ surfaceContainerLowest = ColorRef(CorePaletteColor.Neutral, 100),
+ surfaceContainerLow = ColorRef(CorePaletteColor.Neutral, 96),
+ surfaceContainer = ColorRef(CorePaletteColor.Neutral, 94),
+ surfaceContainerHigh = ColorRef(CorePaletteColor.Neutral, 92),
+ surfaceContainerHighest = ColorRef(CorePaletteColor.Neutral, 90),
+ onSurface = ColorRef(CorePaletteColor.Neutral, 0),
+ onSurfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 0),
+ outline = ColorRef(CorePaletteColor.NeutralVariant, 20),
+ outlineVariant = ColorRef(CorePaletteColor.NeutralVariant, 30),
+ inverseSurface = ColorRef(CorePaletteColor.Neutral, 20),
+ inverseOnSurface = ColorRef(CorePaletteColor.Neutral, 100),
+ inversePrimary = ColorRef(CorePaletteColor.Primary, 80),
+ surfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 90),
+ surfaceTint = ColorRef(CorePaletteColor.Primary, 20),
+ background = ColorRef(CorePaletteColor.Neutral, 98),
+ onBackground = ColorRef(CorePaletteColor.Neutral, 0),
+ scrim = ColorRef(CorePaletteColor.Neutral, 0),
+)
+
+val HighContrastDarkColorScheme = ColorScheme(
+ primary = ColorRef(CorePaletteColor.Primary, 95),
+ onPrimary = ColorRef(CorePaletteColor.Primary, 0),
+ primaryContainer = ColorRef(CorePaletteColor.Primary, 80),
+ onPrimaryContainer = ColorRef(CorePaletteColor.Primary, 0),
+ secondary = ColorRef(CorePaletteColor.Secondary, 95),
+ onSecondary = ColorRef(CorePaletteColor.Secondary, 0),
+ secondaryContainer = ColorRef(CorePaletteColor.Secondary, 80),
+ onSecondaryContainer = ColorRef(CorePaletteColor.Secondary, 0),
+ tertiary = ColorRef(CorePaletteColor.Tertiary, 95),
+ onTertiary = ColorRef(CorePaletteColor.Tertiary, 0),
+ tertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 80),
+ onTertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 0),
+ error = ColorRef(CorePaletteColor.Error, 95),
+ onError = ColorRef(CorePaletteColor.Error, 0),
+ errorContainer = ColorRef(CorePaletteColor.Error, 80),
+ onErrorContainer = ColorRef(CorePaletteColor.Error, 0),
+ surfaceDim = ColorRef(CorePaletteColor.Neutral, 6),
+ surface = ColorRef(CorePaletteColor.Neutral, 6),
+ surfaceBright = ColorRef(CorePaletteColor.Neutral, 24),
+ surfaceContainerLowest = ColorRef(CorePaletteColor.Neutral, 4),
+ surfaceContainerLow = ColorRef(CorePaletteColor.Neutral, 10),
+ surfaceContainer = ColorRef(CorePaletteColor.Neutral, 12),
+ surfaceContainerHigh = ColorRef(CorePaletteColor.Neutral, 17),
+ surfaceContainerHighest = ColorRef(CorePaletteColor.Neutral, 22),
+ onSurface = ColorRef(CorePaletteColor.Neutral, 100),
+ onSurfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 100),
+ outline = ColorRef(CorePaletteColor.NeutralVariant, 95),
+ outlineVariant = ColorRef(CorePaletteColor.NeutralVariant, 80),
+ inverseSurface = ColorRef(CorePaletteColor.Neutral, 90),
+ inverseOnSurface = ColorRef(CorePaletteColor.Neutral, 0),
+ inversePrimary = ColorRef(CorePaletteColor.Primary, 20),
+ surfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 30),
+ surfaceTint = ColorRef(CorePaletteColor.Primary, 95),
+ background = ColorRef(CorePaletteColor.Neutral, 6),
+ onBackground = ColorRef(CorePaletteColor.Neutral, 100),
+ scrim = ColorRef(CorePaletteColor.Neutral, 0),
+)
val BlackAndWhiteLightColorScheme = ColorScheme(
primary = StaticColor(0xFF000000.toInt()),
diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeRepository.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeRepository.kt
index abc10fa9..793d1a7a 100644
--- a/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeRepository.kt
+++ b/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeRepository.kt
@@ -34,6 +34,7 @@ class ThemeRepository(
fun getColors(id: UUID): Flow {
if (id == DefaultThemeId) return flowOf(getDefaultColors())
+ if (id == HighContrastThemeId) return flowOf(getHighContrastColors())
if (id == BlackAndWhiteThemeId) return flowOf(getBlackAndWhiteColors())
return database.themeDao().getColors(id).map { it?.let { Colors(it) } }.flowOn(Dispatchers.Default)
}
@@ -52,6 +53,7 @@ class ThemeRepository(
fun getColorsOrDefault(theme: ColorsDescriptor?): Flow {
return when(theme) {
+ is ColorsDescriptor.HighContrast -> flowOf(getHighContrastColors())
is ColorsDescriptor.BlackAndWhite -> flowOf(getBlackAndWhiteColors())
is ColorsDescriptor.Custom -> {
val id = UUID.fromString(theme.id)
@@ -64,6 +66,7 @@ class ThemeRepository(
private fun getBuiltInColors(): List {
return listOf(
getDefaultColors(),
+ getHighContrastColors(),
getBlackAndWhiteColors(),
)
}
@@ -79,6 +82,17 @@ class ThemeRepository(
)
}
+ private fun getHighContrastColors(): Colors {
+ return Colors(
+ id = HighContrastThemeId,
+ builtIn = true,
+ name = context.getString(R.string.preference_colors_high_contrast),
+ corePalette = EmptyCorePalette,
+ lightColorScheme = HighContrastLightColorScheme,
+ darkColorScheme = HighContrastDarkColorScheme,
+ )
+ }
+
private fun getBlackAndWhiteColors(): Colors {
return Colors(
id = BlackAndWhiteThemeId,