From 2fd780d9bce285bbee79a540375afb20c855f37a Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 8 May 2026 22:39:06 +0200 Subject: [PATCH] Fix theme file deserialization --- .../colorscheme/CorePaletteColorPreference.kt | 6 +- .../colorscheme/ThemeColorPreference.kt | 6 +- .../ui/settings/icons/IconsSettingsScreen.kt | 136 ++++++++++-------- .../ui/theme/colorscheme/ColorScheme.kt | 7 +- .../de/mm20/launcher2/themes/ThemeBundle.kt | 6 +- .../de/mm20/launcher2/themes/colors/Colors.kt | 124 ++++++++-------- .../mm20/launcher2/themes/colors/Defaults.kt | 12 +- 7 files changed, 151 insertions(+), 146 deletions(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/CorePaletteColorPreference.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/CorePaletteColorPreference.kt index ea5054b7a..1b6736a1e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/CorePaletteColorPreference.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/CorePaletteColorPreference.kt @@ -45,7 +45,7 @@ fun CorePaletteColorPreference( title: String, value: Int?, onValueChange: (Int?) -> Unit, - defaultValue: Int, + defaultValue: Int?, modifier: Modifier = Modifier, autoGenerate: (() -> Int?)? = null, ) { @@ -63,7 +63,7 @@ fun CorePaletteColorPreference( verticalAlignment = Alignment.CenterVertically, ) { ColorSwatch( - color = Color(value ?: defaultValue), + color = Color(value ?: defaultValue ?: 0), modifier = Modifier .padding(end = 20.dp) .size(48.dp), @@ -114,7 +114,7 @@ fun CorePaletteColorPreference( modifier = Modifier.padding(bottom = 24.dp) ) val colorPickerState = rememberHctColorPickerState( - initialColor = Color(value ?: defaultValue), + initialColor = Color(value ?: defaultValue ?: 0), onColorChanged = { currentValue = it.toArgb() } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeColorPreference.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeColorPreference.kt index 898ecb1d1..a347df7ee 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeColorPreference.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeColorPreference.kt @@ -50,7 +50,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import de.mm20.launcher2.themes.colors.ColorRef import de.mm20.launcher2.themes.colors.CorePaletteColor -import de.mm20.launcher2.themes.colors.FullCorePalette +import de.mm20.launcher2.themes.colors.CorePalette import de.mm20.launcher2.themes.colors.StaticColor import de.mm20.launcher2.themes.colors.atTone import de.mm20.launcher2.themes.colors.get @@ -67,9 +67,9 @@ import de.mm20.launcher2.themes.colors.Color as ThemeColor fun ThemeColorPreference( title: String, value: ThemeColor?, - corePalette: FullCorePalette, + corePalette: CorePalette, onValueChange: (ThemeColor?) -> Unit, - defaultValue: ThemeColor, + defaultValue: ThemeColor?, modifier: Modifier = Modifier, ) { var showDialog by remember { mutableStateOf(false) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/icons/IconsSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/icons/IconsSettingsScreen.kt index 2a8939a68..cb9cd8729 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/icons/IconsSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/icons/IconsSettingsScreen.kt @@ -4,6 +4,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -19,7 +20,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -314,6 +314,9 @@ private fun IconPackSelectorSheet( onDismiss: () -> Unit, ) { DismissableBottomSheet(expanded = expanded, onDismissRequest = onDismiss) { + val xs = MaterialTheme.shapes.extraSmall + val md = MaterialTheme.shapes.medium + LazyColumn( contentPadding = PaddingValues( start = 16.dp, @@ -322,80 +325,87 @@ private fun IconPackSelectorSheet( bottom = 16.dp + WindowInsets.navigationBars.asPaddingValues() .calculateBottomPadding(), ), + verticalArrangement = Arrangement.spacedBy(2.dp) ) { items(installedIconPacks.size) { val pack = installedIconPacks[it] - if (it > 0) { - HorizontalDivider( - modifier = Modifier.padding(vertical = 8.dp) - ) - } - Column { - - - Column( - modifier = Modifier - .clip(MaterialTheme.shapes.medium) - .clickable { - onSelect(pack) - } - ) { - val icons by iconPackPreviewIcons[pack.packageName]!!.collectAsState(null) - - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(horizontal = 8.dp) - ) { - Text( - text = pack.name, - style = MaterialTheme.typography.titleMedium, - modifier = Modifier - .padding(end = 8.dp) + Column( + modifier = Modifier + .clip(when { + it == 0 -> md.copy( + bottomStart = xs.bottomStart, + bottomEnd = xs.bottomEnd, ) - if (pack.themed) { - Icon( - modifier = Modifier - .size(20.dp), - painter = painterResource(R.drawable.palette_20px), - contentDescription = null, - tint = MaterialTheme.colorScheme.primary + it == installedIconPacks.lastIndex -> md.copy( + topStart = xs.topStart, + topEnd = xs.topEnd, + ) + installedIconPacks.size == 1 -> md + else -> xs + }) + .background( + MaterialTheme.colorScheme.surfaceBright, + ) + .clickable { + onSelect(pack) + } + ) { + val icons by iconPackPreviewIcons[pack.packageName]!!.collectAsState(null) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp, bottom = 16.dp) + .height(48.dp) + ) { + if (icons == null) { + for (i in 0 until columns) { + Box( + modifier = Modifier.weight(1f), + contentAlignment = Alignment.Center, + ) { + ShapedLauncherIcon(size = 48.dp, icon = { null }) + } + } + } else { + for (icon in icons!!) { + Box( + modifier = Modifier.weight(1f), + contentAlignment = Alignment.Center, + ) { + ShapedLauncherIcon(size = 48.dp, icon = { icon }) + } + } + for (i in 0..<(columns - icons!!.size)) { + Box( + modifier = Modifier.weight(1f), ) } } + } - Row( + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(16.dp) + ) { + Text( + text = pack.name, + style = MaterialTheme.typography.titleMedium, modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp, bottom = 16.dp) - .height(48.dp) - ) { - if (icons == null) { - for (i in 0 until columns) { - Box( - modifier = Modifier.weight(1f), - contentAlignment = Alignment.Center, - ) { - ShapedLauncherIcon(size = 48.dp, icon = { null }) - } - } - } else { - for (icon in icons!!) { - Box( - modifier = Modifier.weight(1f), - contentAlignment = Alignment.Center, - ) { - ShapedLauncherIcon(size = 48.dp, icon = { icon }) - } - } - for (i in 0..<(columns - icons!!.size)) { - Box( - modifier = Modifier.weight(1f), - ) - } - } + .weight(1f) + .padding(end = 8.dp) + ) + if (pack.themed) { + Icon( + modifier = Modifier + .size(20.dp), + painter = painterResource(R.drawable.palette_20px), + contentDescription = null, + tint = MaterialTheme.colorScheme.primary + ) } } } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/ColorScheme.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/ColorScheme.kt index 7b3173790..152407e0d 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/ColorScheme.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/ColorScheme.kt @@ -14,8 +14,7 @@ import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.themes.colors.CorePalette import de.mm20.launcher2.themes.colors.DefaultDarkColorScheme import de.mm20.launcher2.themes.colors.DefaultLightColorScheme -import de.mm20.launcher2.themes.colors.FullColorScheme -import de.mm20.launcher2.themes.colors.PartialCorePalette +import de.mm20.launcher2.themes.colors.ColorScheme as ThemeColorScheme import de.mm20.launcher2.themes.colors.Colors as ThemeColors import de.mm20.launcher2.themes.colors.get import de.mm20.launcher2.themes.colors.merge @@ -39,7 +38,7 @@ fun darkColorSchemeOf(colors: ThemeColors): ColorScheme { } @Composable -fun colorSchemeOf(dark: Boolean, colorScheme: FullColorScheme, corePalette: PartialCorePalette): ColorScheme { +fun colorSchemeOf(dark: Boolean, colorScheme: ThemeColorScheme, corePalette: CorePalette): ColorScheme { val defaultPalette = systemCorePalette(dark) return remember(colorScheme, corePalette, defaultPalette) { val mergedCorePalette = corePalette.merge(defaultPalette) @@ -85,7 +84,7 @@ fun colorSchemeOf(dark: Boolean, colorScheme: FullColorScheme, corePalette: Part } @Composable -fun systemCorePalette(dark: Boolean): CorePalette { +fun systemCorePalette(dark: Boolean): CorePalette { val uiSettings: UiSettings = koinInject() val compatModeColors by remember { uiSettings.compatModeColors diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeBundle.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeBundle.kt index 1a9003ab9..7ed30fe1a 100644 --- a/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeBundle.kt +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeBundle.kt @@ -67,13 +67,13 @@ data class ThemeBundle( try { val name = (jsonElement["name"] as? JsonPrimitive)?.contentOrNull ?: return null val corePalette = (jsonElement["corePalette"] as? JsonObject)?.let { - LegacyThemeJson.decodeFromJsonElement>(it) + LegacyThemeJson.decodeFromJsonElement(it) } val lightColorScheme = (jsonElement["lightColorScheme"] as? JsonObject)?.let { - LegacyThemeJson.decodeFromJsonElement>(it) + LegacyThemeJson.decodeFromJsonElement(it) } val darkColorScheme = (jsonElement["darkColorScheme"] as? JsonObject)?.let { - LegacyThemeJson.decodeFromJsonElement>(it) + LegacyThemeJson.decodeFromJsonElement(it) } val colorScheme = Colors( diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Colors.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Colors.kt index d861c46f7..47c8c7b9f 100644 --- a/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Colors.kt +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Colors.kt @@ -13,9 +13,9 @@ data class Colors( @Serializable(with = UUIDSerializer::class) val id: UUID = UUID.randomUUID(), val builtIn: Boolean = false, val name: String, - val corePalette: PartialCorePalette = EmptyCorePalette, - val lightColorScheme: PartialColorScheme = DefaultLightColorScheme, - val darkColorScheme: PartialColorScheme = DefaultDarkColorScheme, + val corePalette: CorePalette = EmptyCorePalette, + val lightColorScheme: ColorScheme = DefaultLightColorScheme, + val darkColorScheme: ColorScheme = DefaultDarkColorScheme, ) { constructor(entity: ColorsEntity) : this( @@ -271,81 +271,77 @@ value class StaticColor(val color: Int) : Color { typealias CorePaletteColorValue = @Serializable(with = ColorIntAsHexSerializer::class) Int @Serializable -data class CorePalette( - val primary: T, - val secondary: T, - val tertiary: T, - val neutral: T, - val neutralVariant: T, - val error: T, +data class CorePalette( + val primary: CorePaletteColorValue? = null, + val secondary: CorePaletteColorValue? = null, + val tertiary: CorePaletteColorValue? = null, + val neutral: CorePaletteColorValue? = null, + val neutralVariant: CorePaletteColorValue? = null, + val error: CorePaletteColorValue? = null, ) -val EmptyCorePalette = CorePalette(null, null, null, null, null, null) - -typealias FullCorePalette = CorePalette -typealias PartialCorePalette = CorePalette +val EmptyCorePalette = CorePalette() @Serializable -data class ColorScheme( - @Contextual val primary: T, - @Contextual val onPrimary: T, - @Contextual val primaryContainer: T, - @Contextual val onPrimaryContainer: T, - @Contextual val secondary: T, - @Contextual val onSecondary: T, - @Contextual val secondaryContainer: T, - @Contextual val onSecondaryContainer: T, - @Contextual val tertiary: T, - @Contextual val onTertiary: T, - @Contextual val tertiaryContainer: T, - @Contextual val onTertiaryContainer: T, - @Contextual val error: T, - @Contextual val onError: T, - @Contextual val errorContainer: T, - @Contextual val onErrorContainer: T, - @Contextual val surface: T, - @Contextual val onSurface: T, - @Contextual val onSurfaceVariant: T, - @Contextual val outline: T, - @Contextual val outlineVariant: T, - @Contextual val inverseSurface: T, - @Contextual val inverseOnSurface: T, - @Contextual val inversePrimary: T, - @Contextual val surfaceDim: T, - @Contextual val surfaceBright: T, - @Contextual val surfaceContainerLowest: T, - @Contextual val surfaceContainerLow: T, - @Contextual val surfaceContainer: T, - @Contextual val surfaceContainerHigh: T, - @Contextual val surfaceContainerHighest: T, +data class ColorScheme( + @Contextual val primary:Color? = null, + @Contextual val onPrimary:Color? = null, + @Contextual val primaryContainer:Color? = null, + @Contextual val onPrimaryContainer:Color? = null, + @Contextual val secondary:Color? = null, + @Contextual val onSecondary:Color? = null, + @Contextual val secondaryContainer:Color? = null, + @Contextual val onSecondaryContainer:Color? = null, + @Contextual val tertiary:Color? = null, + @Contextual val onTertiary:Color? = null, + @Contextual val tertiaryContainer:Color? = null, + @Contextual val onTertiaryContainer:Color? = null, + @Contextual val error:Color? = null, + @Contextual val onError:Color? = null, + @Contextual val errorContainer:Color? = null, + @Contextual val onErrorContainer:Color? = null, + @Contextual val surface:Color? = null, + @Contextual val onSurface:Color? = null, + @Contextual val onSurfaceVariant:Color? = null, + @Contextual val outline:Color? = null, + @Contextual val outlineVariant:Color? = null, + @Contextual val inverseSurface:Color? = null, + @Contextual val inverseOnSurface:Color? = null, + @Contextual val inversePrimary:Color? = null, + @Contextual val surfaceDim:Color? = null, + @Contextual val surfaceBright:Color? = null, + @Contextual val surfaceContainerLowest:Color? = null, + @Contextual val surfaceContainerLow:Color? = null, + @Contextual val surfaceContainer:Color? = null, + @Contextual val surfaceContainerHigh:Color? = null, + @Contextual val surfaceContainerHighest:Color? = null, - @Contextual val background: T, - @Contextual val onBackground: T, - @Contextual val surfaceTint: T, - @Contextual val scrim: T, - @Contextual val surfaceVariant: T, + @Contextual val background:Color? = null, + @Contextual val onBackground:Color? = null, + @Contextual val surfaceTint:Color? = null, + @Contextual val scrim:Color? = null, + @Contextual val surfaceVariant:Color? = null, ) -typealias FullColorScheme = ColorScheme -typealias PartialColorScheme = ColorScheme -fun CorePalette.get(color: CorePaletteColor): T { +fun CorePalette.get(color: CorePaletteColor): CorePaletteColorValue { return when (color) { - CorePaletteColor.Primary -> primary - CorePaletteColor.Secondary -> secondary - CorePaletteColor.Tertiary -> tertiary - CorePaletteColor.Neutral -> neutral - CorePaletteColor.NeutralVariant -> neutralVariant - CorePaletteColor.Error -> error + CorePaletteColor.Primary -> primary ?: 0 + CorePaletteColor.Secondary -> secondary ?: 0 + CorePaletteColor.Tertiary -> tertiary ?: 0 + CorePaletteColor.Neutral -> neutral ?: 0 + CorePaletteColor.NeutralVariant -> neutralVariant ?: 0 + CorePaletteColor.Error -> error ?: 0 } } -fun Color.get(corePalette: FullCorePalette): Int { +fun Color?.get(corePalette: CorePalette): Int { return when (this) { is StaticColor -> color is ColorRef -> { - corePalette.get(this.color).atTone(this.tone) + corePalette.get(this.color).atTone(this.tone) ?: 0 } + null -> 0 } } @@ -355,7 +351,7 @@ fun Int.atTone(tone: Int): Int { }.toInt() } -fun PartialCorePalette.merge(other: FullCorePalette): FullCorePalette { +fun CorePalette.merge(other: CorePalette): CorePalette { return CorePalette( primary = this.primary ?: other.primary, secondary = this.secondary ?: other.secondary, @@ -366,7 +362,7 @@ fun PartialCorePalette.merge(other: FullCorePalette): FullCorePalette { ) } -fun PartialColorScheme.merge(other: FullColorScheme): FullColorScheme { +fun ColorScheme.merge(other: ColorScheme): ColorScheme { return ColorScheme( primary = this.primary ?: other.primary, onPrimary = this.onPrimary ?: other.onPrimary, diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Defaults.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Defaults.kt index e090ee1de..5be95c2b8 100644 --- a/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Defaults.kt +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Defaults.kt @@ -1,6 +1,6 @@ package de.mm20.launcher2.themes.colors -val DefaultLightColorScheme = ColorScheme( +val DefaultLightColorScheme = ColorScheme( primary = ColorRef(CorePaletteColor.Primary, 40), onPrimary = ColorRef(CorePaletteColor.Primary, 100), primaryContainer = ColorRef(CorePaletteColor.Primary, 90), @@ -39,7 +39,7 @@ val DefaultLightColorScheme = ColorScheme( scrim = ColorRef(CorePaletteColor.Neutral, 0), ) -val DefaultDarkColorScheme = ColorScheme( +val DefaultDarkColorScheme = ColorScheme( primary = ColorRef(CorePaletteColor.Primary, 80), onPrimary = ColorRef(CorePaletteColor.Primary, 20), primaryContainer = ColorRef(CorePaletteColor.Primary, 30), @@ -78,7 +78,7 @@ val DefaultDarkColorScheme = ColorScheme( scrim = ColorRef(CorePaletteColor.Neutral, 0), ) -val HighContrastLightColorScheme = ColorScheme( +val HighContrastLightColorScheme = ColorScheme( primary = ColorRef(CorePaletteColor.Primary, 20), onPrimary = ColorRef(CorePaletteColor.Primary, 100), primaryContainer = ColorRef(CorePaletteColor.Primary, 30), @@ -117,7 +117,7 @@ val HighContrastLightColorScheme = ColorScheme( scrim = ColorRef(CorePaletteColor.Neutral, 0), ) -val HighContrastDarkColorScheme = ColorScheme( +val HighContrastDarkColorScheme = ColorScheme( primary = ColorRef(CorePaletteColor.Primary, 95), onPrimary = ColorRef(CorePaletteColor.Primary, 0), primaryContainer = ColorRef(CorePaletteColor.Primary, 80), @@ -156,7 +156,7 @@ val HighContrastDarkColorScheme = ColorScheme( scrim = ColorRef(CorePaletteColor.Neutral, 0), ) -val BlackAndWhiteLightColorScheme = ColorScheme( +val BlackAndWhiteLightColorScheme = ColorScheme( primary = StaticColor(0xFF000000.toInt()), onPrimary = StaticColor(0xFFFFFFFF.toInt()), primaryContainer = StaticColor(0xFFFFFFFF.toInt()), @@ -195,7 +195,7 @@ val BlackAndWhiteLightColorScheme = ColorScheme( surfaceContainerLowest = StaticColor(0xFFFFFFFF.toInt()), ) -val BlackAndWhiteDarkColorScheme = ColorScheme( +val BlackAndWhiteDarkColorScheme = ColorScheme( primary = StaticColor(0xFFFFFFFF.toInt()), onPrimary = StaticColor(0xFF000000.toInt()), primaryContainer = StaticColor(0xFF000000.toInt()),