Custom color schemes v2 - part 3

Store preferences and apply color schemes
This commit is contained in:
MM20 2023-08-23 19:02:37 +02:00
parent f128469f68
commit 1a86aa018e
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
15 changed files with 93 additions and 1506 deletions

View File

@ -26,8 +26,6 @@ import de.mm20.launcher2.ui.settings.backup.BackupSettingsScreen
import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen
import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen
import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreen import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreen
import de.mm20.launcher2.ui.settings.colorscheme.ColorSchemeSettingsScreen
import de.mm20.launcher2.ui.settings.colorscheme.CustomColorSchemeSettingsScreen
import de.mm20.launcher2.ui.settings.colorscheme.ThemeSettingsScreen import de.mm20.launcher2.ui.settings.colorscheme.ThemeSettingsScreen
import de.mm20.launcher2.ui.settings.colorscheme.ThemesSettingsScreen import de.mm20.launcher2.ui.settings.colorscheme.ThemesSettingsScreen
import de.mm20.launcher2.ui.settings.crashreporter.CrashReportScreen import de.mm20.launcher2.ui.settings.crashreporter.CrashReportScreen
@ -98,12 +96,6 @@ class SettingsActivity : BaseActivity() {
composable("settings/icons") { composable("settings/icons") {
IconsSettingsScreen() IconsSettingsScreen()
} }
composable("settings/appearance/colorscheme") {
ColorSchemeSettingsScreen()
}
composable("settings/appearance/colorscheme/custom") {
CustomColorSchemeSettingsScreen()
}
composable("settings/appearance/themes") { composable("settings/appearance/themes") {
ThemesSettingsScreen() ThemesSettingsScreen()
} }

View File

@ -7,6 +7,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.preferences.Settings.AppearanceSettings import de.mm20.launcher2.preferences.Settings.AppearanceSettings
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme
@ -25,6 +26,7 @@ fun AppearanceSettingsScreen() {
val viewModel: AppearanceSettingsScreenVM = viewModel() val viewModel: AppearanceSettingsScreenVM = viewModel()
val context = LocalContext.current val context = LocalContext.current
val navController = LocalNavController.current val navController = LocalNavController.current
val themeName by viewModel.colorSchemeName.collectAsStateWithLifecycle(null)
PreferenceScreen(title = stringResource(id = R.string.preference_screen_appearance)) { PreferenceScreen(title = stringResource(id = R.string.preference_screen_appearance)) {
item { item {
PreferenceCategory { PreferenceCategory {
@ -42,27 +44,9 @@ fun AppearanceSettingsScreen() {
viewModel.setTheme(newValue) viewModel.setTheme(newValue)
} }
) )
val colorScheme by viewModel.colorScheme.collectAsState()
Preference( Preference(
title = stringResource(id = R.string.preference_screen_colors), title = stringResource(id = R.string.preference_screen_colors),
summary = when (colorScheme) { summary = themeName,
ColorScheme.Default -> stringResource(R.string.preference_colors_default)
ColorScheme.BlackAndWhite -> stringResource(R.string.preference_colors_bw)
ColorScheme.Custom -> stringResource(R.string.preference_colors_custom)
else -> null
},
onClick = {
navController?.navigate("settings/appearance/colorscheme")
}
)
Preference(
title = stringResource(id = R.string.preference_screen_colors),
summary = when (colorScheme) {
ColorScheme.Default -> stringResource(R.string.preference_colors_default)
ColorScheme.BlackAndWhite -> stringResource(R.string.preference_colors_bw)
ColorScheme.Custom -> stringResource(R.string.preference_colors_custom)
else -> null
},
onClick = { onClick = {
navController?.navigate("settings/appearance/themes") navController?.navigate("settings/appearance/themes")
} }

View File

@ -4,24 +4,27 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.icons.IconService import de.mm20.launcher2.icons.IconService
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Font import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Font
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme
import kotlinx.coroutines.flow.Flow import de.mm20.launcher2.themes.ThemeRepository
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
import java.util.UUID
class AppearanceSettingsScreenVM : ViewModel(), KoinComponent { class AppearanceSettingsScreenVM : ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject() private val dataStore: LauncherDataStore by inject()
private val iconService: IconService by inject() private val iconService: IconService by inject()
private val themeRepository: ThemeRepository by inject()
val theme = dataStore.data.map { it.appearance.theme } val theme = dataStore.data.map { it.appearance.theme }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setTheme(theme: Theme) { fun setTheme(theme: Theme) {
viewModelScope.launch { viewModelScope.launch {
dataStore.updateData { dataStore.updateData {
@ -32,20 +35,22 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent {
} }
} }
val colorScheme = dataStore.data.map { it.appearance.colorScheme } val colorSchemeName = dataStore.data.map {
it.appearance.themeId?.takeIf { it.isNotEmpty() }?.let {
UUID.fromString(it)
}
}
.flatMapLatest {
themeRepository.getThemeOrDefault(it)
}.map {
it.name
}
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setColorScheme(colorScheme: ColorScheme) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setAppearance(it.appearance.toBuilder().setColorScheme(colorScheme))
.build()
}
}
}
val font = dataStore.data.map { it.appearance.font } val font = dataStore.data.map { it.appearance.font }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFont(font: Font) { fun setFont(font: Font) {
viewModelScope.launch { viewModelScope.launch {
dataStore.updateData { dataStore.updateData {

View File

@ -1,122 +0,0 @@
package de.mm20.launcher2.ui.settings.colorscheme
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.RadioButtonChecked
import androidx.compose.material.icons.rounded.RadioButtonUnchecked
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.preferences.Settings.AppearanceSettings
import de.mm20.launcher2.ui.BuildConfig
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.preferences.Preference
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.locals.LocalDarkTheme
import de.mm20.launcher2.ui.locals.LocalNavController
import de.mm20.launcher2.ui.theme.colorSchemeAsState
@Composable
fun ColorSchemeSettingsScreen() {
val viewModel: ColorSchemeSettingsScreenVM = viewModel()
val navController = LocalNavController.current
PreferenceScreen(title = stringResource(R.string.preference_screen_colors)) {
item {
PreferenceCategory {
val colorScheme by viewModel.colorScheme.collectAsState()
val items = mutableListOf(
AppearanceSettings.ColorScheme.Default to stringResource(R.string.preference_colors_default),
AppearanceSettings.ColorScheme.BlackAndWhite to stringResource(R.string.preference_colors_bw),
AppearanceSettings.ColorScheme.Custom to stringResource(R.string.preference_colors_custom),
)
if (BuildConfig.DEBUG && isAtLeastApiLevel(27)) {
items.add(AppearanceSettings.ColorScheme.DebugMaterialYouCompat to "Material You Compat")
}
for (cs in items) {
val scheme by colorSchemeAsState(cs.first, LocalDarkTheme.current)
Preference(
title = cs.second,
icon = if (colorScheme == cs.first) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
onClick = {
viewModel.setColorScheme(cs.first)
if (cs.first == AppearanceSettings.ColorScheme.Custom) {
navController?.navigate("settings/appearance/colorscheme/custom")
}
},
controls = {
ColorSchemePreview(scheme)
}
)
}
}
}
}
}
@Composable
fun ColorSchemePreview(colorScheme: ColorScheme) {
MaterialTheme(colorScheme = colorScheme) {
Box(
modifier = Modifier
.padding(vertical = 12.dp)
.width(72.dp)
.height(36.dp),
contentAlignment = Alignment.Center
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Surface(
tonalElevation = 1.dp,
color = MaterialTheme.colorScheme.surface,
modifier = Modifier
.size(36.dp)
) {}
Surface(
tonalElevation = 1.dp,
color = MaterialTheme.colorScheme.surfaceVariant,
modifier = Modifier
.size(36.dp)
) {}
}
Row(
verticalAlignment = Alignment.CenterVertically
) {
Surface(
tonalElevation = 1.dp,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier
.size(16.dp)
) {}
Surface(
tonalElevation = 1.dp,
color = MaterialTheme.colorScheme.secondary,
modifier = Modifier
.padding(horizontal = 8.dp)
.size(16.dp)
) {}
Surface(
tonalElevation = 1.dp,
color = MaterialTheme.colorScheme.tertiary,
modifier = Modifier
.size(16.dp)
) {}
}
}
}
}

View File

@ -1,33 +0,0 @@
package de.mm20.launcher2.ui.settings.colorscheme
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings.AppearanceSettings
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class ColorSchemeSettingsScreenVM : ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val theme = dataStore.data.map { it.appearance.theme }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val colorScheme = dataStore.data.map { it.appearance.colorScheme }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setColorScheme(colorScheme: AppearanceSettings.ColorScheme) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setAppearance(
it.appearance.toBuilder()
.setColorScheme(colorScheme)
).build()
}
}
}
}

View File

@ -1,148 +0,0 @@
package de.mm20.launcher2.ui.settings.colorscheme
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.CustomColors
import de.mm20.launcher2.preferences.ktx.toSettingsColorsScheme
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import palettes.CorePalette
import palettes.TonalPalette
import scheme.Scheme
class CustomColorSchemeSettingsScreenVM : ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val advancedMode = dataStore.data.map { it.appearance.customColors.advancedMode }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setAdvancedMode(advancedMode: Boolean) {
viewModelScope.launch {
val lightScheme = dataStore.updateData {
val customColors = it.appearance.customColors.toBuilder()
.setAdvancedMode(advancedMode)
it.toBuilder()
.setAppearance(
it.appearance.toBuilder()
.setCustomColors(customColors)
)
.build()
}.appearance.customColors.lightScheme
if (!advancedMode) {
setBaseColors(CustomColors.BaseColors
.newBuilder()
.setAccent1(lightScheme.primary)
.setAccent2(lightScheme.secondary)
.setAccent3(lightScheme.tertiary)
.setNeutral1(lightScheme.surface)
.setNeutral2(lightScheme.surface)
.setError(lightScheme.error)
.build()
)
}
}
}
fun generateFromPrimaryColor() {
viewModelScope.launch {
val primary = dataStore.data.map { it.appearance.customColors.baseColors.accent1 }.first()
val scheme = Scheme.light(primary)
setBaseColors(
CustomColors.BaseColors.newBuilder()
.setAccent1(scheme.primary)
.setAccent2(scheme.secondary)
.setAccent3(scheme.tertiary)
.setNeutral1(scheme.surface)
.setNeutral2(scheme.outline)
.setError(scheme.error)
.build()
)
}
}
val baseColors = dataStore.data.map { it.appearance.customColors.baseColors }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setBaseColors(baseColors: CustomColors.BaseColors) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setAppearance(
it.appearance.toBuilder()
.setCustomColors(
it.appearance.customColors.toBuilder()
.setBaseColors(baseColors)
)
)
.build()
}
setDarkScheme(baseColorsToDarkScheme(baseColors))
setLightScheme(baseColorsToLightScheme(baseColors))
}
}
val darkScheme = dataStore.data.map { it.appearance.customColors.darkScheme }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setDarkScheme(darkScheme: CustomColors.Scheme) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setAppearance(
it.appearance.toBuilder()
.setCustomColors(
it.appearance.customColors.toBuilder()
.setDarkScheme(darkScheme)
)
)
.build()
}
}
}
val lightScheme = dataStore.data.map { it.appearance.customColors.lightScheme }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setLightScheme(lightScheme: CustomColors.Scheme) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setAppearance(
it.appearance.toBuilder()
.setCustomColors(
it.appearance.customColors.toBuilder()
.setLightScheme(lightScheme)
)
)
.build()
}
}
}
private fun baseColorsToDarkScheme(baseColors: CustomColors.BaseColors): CustomColors.Scheme {
val a1 = TonalPalette.fromInt(baseColors.accent1)
val a2 = TonalPalette.fromInt(baseColors.accent2)
val a3 = TonalPalette.fromInt(baseColors.accent3)
val n1 = TonalPalette.fromInt(baseColors.neutral1)
val n2 = TonalPalette.fromInt(baseColors.neutral2)
val error = TonalPalette.fromInt(baseColors.error)
val scheme = Scheme.darkFromCorePalette(CorePalette(a1, a2, a3, n1, n2, error))
return scheme.toSettingsColorsScheme()
}
private fun baseColorsToLightScheme(baseColors: CustomColors.BaseColors): CustomColors.Scheme {
val a1 = TonalPalette.fromInt(baseColors.accent1)
val a2 = TonalPalette.fromInt(baseColors.accent2)
val a3 = TonalPalette.fromInt(baseColors.accent3)
val n1 = TonalPalette.fromInt(baseColors.neutral1)
val n2 = TonalPalette.fromInt(baseColors.neutral2)
val error = TonalPalette.fromInt(baseColors.error)
val scheme = Scheme.lightFromCorePalette(CorePalette(a1, a2, a3, n1, n2, error))
return scheme.toSettingsColorsScheme()
}
}

View File

@ -77,6 +77,9 @@ fun ThemesSettingsScreen() {
) )
} }
} }
},
onClick = {
viewModel.selectTheme(theme)
} }
) )
Row( Row(

View File

@ -2,11 +2,17 @@ package de.mm20.launcher2.ui.settings.colorscheme
import android.util.Log import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.protobuf.ByteString
import de.mm20.launcher2.ktx.toBytes
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.themes.DefaultThemeId import de.mm20.launcher2.themes.DefaultThemeId
import de.mm20.launcher2.themes.Theme import de.mm20.launcher2.themes.Theme
import de.mm20.launcher2.themes.ThemeRepository import de.mm20.launcher2.themes.ThemeRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
import java.util.UUID import java.util.UUID
@ -14,8 +20,13 @@ import java.util.UUID
class ThemesSettingsScreenVM : ViewModel(), KoinComponent { class ThemesSettingsScreenVM : ViewModel(), KoinComponent {
private val themeRepository: ThemeRepository by inject() private val themeRepository: ThemeRepository by inject()
private val dataStore: LauncherDataStore by inject()
val selectedTheme: Flow<UUID?> = flowOf(DefaultThemeId) val selectedTheme: Flow<UUID?> = dataStore.data.map {
it.appearance.themeId?.takeIf { it.isNotEmpty() }?.let {
UUID.fromString(it)
} ?: DefaultThemeId
}
val themes: Flow<List<Theme>> = themeRepository.getThemes() val themes: Flow<List<Theme>> = themeRepository.getThemes()
fun getTheme(id: UUID): Flow<Theme?> { fun getTheme(id: UUID): Flow<Theme?> {
@ -23,7 +34,19 @@ class ThemesSettingsScreenVM: ViewModel(), KoinComponent {
} }
fun updateTheme(theme: Theme) { fun updateTheme(theme: Theme) {
Log.d("MM20", "updateTheme: $theme")
themeRepository.updateTheme(theme) themeRepository.updateTheme(theme)
} }
fun selectTheme(theme: Theme) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setAppearance(
it.appearance.toBuilder()
.setThemeId(theme.id.toString())
)
.build()
}
}
}
} }

View File

@ -18,12 +18,15 @@ import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.Settings.AppearanceSettings import de.mm20.launcher2.preferences.Settings.AppearanceSettings
import de.mm20.launcher2.themes.DefaultThemeId import de.mm20.launcher2.themes.DefaultThemeId
import de.mm20.launcher2.themes.Theme import de.mm20.launcher2.themes.Theme
import de.mm20.launcher2.themes.ThemeRepository
import de.mm20.launcher2.ui.locals.LocalDarkTheme import de.mm20.launcher2.ui.locals.LocalDarkTheme
import de.mm20.launcher2.ui.theme.colorscheme.* import de.mm20.launcher2.ui.theme.colorscheme.*
import de.mm20.launcher2.ui.theme.typography.DefaultTypography import de.mm20.launcher2.ui.theme.typography.DefaultTypography
import de.mm20.launcher2.ui.theme.typography.getDeviceDefaultTypography import de.mm20.launcher2.ui.theme.typography.getDeviceDefaultTypography
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import org.koin.androidx.compose.inject import org.koin.androidx.compose.inject
import java.util.UUID
@Composable @Composable
@ -33,15 +36,17 @@ fun LauncherTheme(
val context = LocalContext.current val context = LocalContext.current
val dataStore: LauncherDataStore by inject() val dataStore: LauncherDataStore by inject()
val themeRepository: ThemeRepository by inject()
val colorSchemePreference by remember { val theme by remember {
dataStore.data.map { dataStore.data.map {
if (it.easterEgg) Settings.AppearanceSettings.ColorScheme.EasterEgg it.appearance.themeId.takeIf { it.isNotEmpty() }?.let {
else it.appearance.colorScheme UUID.fromString(it)
} }
}.collectAsState( }.flatMapLatest {
AppearanceSettings.ColorScheme.Default themeRepository.getThemeOrDefault(it)
) }
}.collectAsState(themeRepository.getDefaultTheme())
val themePreference by remember { dataStore.data.map { it.appearance.theme } }.collectAsState( val themePreference by remember { dataStore.data.map { it.appearance.theme } }.collectAsState(
AppearanceSettings.Theme.System AppearanceSettings.Theme.System
@ -62,7 +67,11 @@ fun LauncherTheme(
} }
}.collectAsState(RoundedCornerShape(0f)) }.collectAsState(RoundedCornerShape(0f))
val colorScheme by colorSchemeAsState(colorSchemePreference, darkTheme) val colorScheme = if (darkTheme) {
darkColorSchemeOf(theme)
} else {
lightColorSchemeOf(theme)
}
val font by remember { dataStore.data.map { it.appearance.font } }.collectAsState( val font by remember { dataStore.data.map { it.appearance.font } }.collectAsState(
AppearanceSettings.Font.Outfit AppearanceSettings.Font.Outfit
@ -90,54 +99,6 @@ fun LauncherTheme(
} }
} }
@Composable
fun colorSchemeAsState(
colorScheme: AppearanceSettings.ColorScheme,
darkTheme: Boolean
): MutableState<ColorScheme> {
val dataStore: LauncherDataStore by inject()
when (colorScheme) {
AppearanceSettings.ColorScheme.BlackAndWhite -> {
return remember(darkTheme) {
mutableStateOf(
if (darkTheme) DarkBlackAndWhiteColorScheme else LightBlackAndWhiteColorScheme
)
}
}
AppearanceSettings.ColorScheme.EasterEgg -> {
return remember(darkTheme) {
mutableStateOf(
if (darkTheme) DarkEasterEggColorScheme else LightEasterEggColorScheme
)
}
}
AppearanceSettings.ColorScheme.Custom -> {
val colors by remember(darkTheme) {
dataStore.data.map { if (darkTheme) it.appearance.customColors.darkScheme else it.appearance.customColors.lightScheme }
}.collectAsState(null)
val state = remember(colors, darkTheme) {
mutableStateOf(
colors?.let { CustomColorScheme(it) }
?: if (darkTheme) DarkDefaultColorScheme else LightDefaultColorScheme
)
}
return state
}
else -> {
val scheme = if (darkTheme) {
darkColorSchemeOf(Theme(DefaultThemeId, name = ""))
} else {
lightColorSchemeOf(Theme(DefaultThemeId, name = ""))
}
return remember(scheme, darkTheme) {
mutableStateOf(scheme)
}
}
}
}
fun getTypography(context: Context, font: AppearanceSettings.Font?): Typography { fun getTypography(context: Context, font: AppearanceSettings.Font?): Typography {
return when (font) { return when (font) {
AppearanceSettings.Font.SystemDefault -> getDeviceDefaultTypography(context) AppearanceSettings.Font.SystemDefault -> getDeviceDefaultTypography(context)

View File

@ -22,7 +22,7 @@ internal val Context.dataStore: LauncherDataStore by dataStore(
} }
) )
internal const val SchemaVersion = 16 internal const val SchemaVersion = 17
internal fun getMigrations(context: Context): List<DataMigration<Settings>> { internal fun getMigrations(context: Context): List<DataMigration<Settings>> {
return listOf( return listOf(
@ -42,5 +42,6 @@ internal fun getMigrations(context: Context): List<DataMigration<Settings>> {
Migration_13_14(), Migration_13_14(),
Migration_14_15(), Migration_14_15(),
Migration_15_16(), Migration_15_16(),
Migration_16_17(),
) )
} }

View File

@ -5,6 +5,7 @@ import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.preferences.Settings.SearchBarSettings.SearchBarColors import de.mm20.launcher2.preferences.Settings.SearchBarSettings.SearchBarColors
import de.mm20.launcher2.preferences.ktx.toSettingsColorsScheme import de.mm20.launcher2.preferences.ktx.toSettingsColorsScheme
import scheme.Scheme import scheme.Scheme
import java.util.UUID
fun createFactorySettings(context: Context): Settings { fun createFactorySettings(context: Context): Settings {
return Settings.newBuilder() return Settings.newBuilder()
@ -12,16 +13,9 @@ fun createFactorySettings(context: Context): Settings {
Settings.AppearanceSettings Settings.AppearanceSettings
.newBuilder() .newBuilder()
.setTheme(Settings.AppearanceSettings.Theme.System) .setTheme(Settings.AppearanceSettings.Theme.System)
.setColorScheme(Settings.AppearanceSettings.ColorScheme.Default)
.setDimWallpaper(false) .setDimWallpaper(false)
.setBlurWallpaper(true) .setBlurWallpaper(true)
.setCustomColors( .setThemeId(UUID(0L, 0L).toString())
Settings.AppearanceSettings.CustomColors.newBuilder()
.setAdvancedMode(false)
.setBaseColors(DefaultCustomColorsBase)
.setLightScheme(DefaultLightCustomColorScheme)
.setDarkScheme(DefaultDarkCustomColorScheme)
)
.setFont(Settings.AppearanceSettings.Font.Outfit) .setFont(Settings.AppearanceSettings.Font.Outfit)
.build() .build()
) )

View File

@ -0,0 +1,19 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import java.util.UUID
class Migration_16_17: VersionedMigration(16, 17) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
return builder.setAppearance(
builder.appearance.toBuilder()
.setThemeId(
when(builder.appearance.colorScheme) {
Settings.AppearanceSettings.ColorScheme.BlackAndWhite -> UUID(0L, 1L)
Settings.AppearanceSettings.ColorScheme.Custom -> UUID(1L, 1L)
else -> UUID(0L, 0L)
}.toString()
)
)
}
}

View File

@ -19,7 +19,7 @@ message Settings {
Custom = 3; Custom = 3;
EasterEgg = 4; EasterEgg = 4;
} }
ColorScheme color_scheme = 6; ColorScheme color_scheme = 6 [deprecated = true];
message CustomColors { message CustomColors {
bool advanced_mode = 1; bool advanced_mode = 1;
message BaseColors { message BaseColors {
@ -72,7 +72,7 @@ message Settings {
Scheme light_scheme = 3; Scheme light_scheme = 3;
Scheme dark_scheme = 4; Scheme dark_scheme = 4;
} }
CustomColors custom_colors = 8; CustomColors custom_colors = 8 [deprecated = true];
bool dim_wallpaper = 7; bool dim_wallpaper = 7;
/** /**
@ -87,6 +87,8 @@ message Settings {
Font font = 10; Font font = 10;
bool blur_wallpaper = 11; bool blur_wallpaper = 11;
// UUID of the selected theme
string theme_id = 12;
} }
AppearanceSettings appearance = 2; AppearanceSettings appearance = 2;

View File

@ -44,7 +44,8 @@ class ThemeRepository(
} }
} }
fun getThemeOrDefault(id: UUID): Flow<Theme> { fun getThemeOrDefault(id: UUID?): Flow<Theme> {
if (id == null) return flowOf(getDefaultTheme())
return getTheme(id).map { it ?: getDefaultTheme() } return getTheme(id).map { it ?: getDefaultTheme() }
} }