Custom color schemes v2 - part 3
Store preferences and apply color schemes
This commit is contained in:
parent
f128469f68
commit
1a86aa018e
@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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)
|
|
||||||
) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -77,6 +77,9 @@ fun ThemesSettingsScreen() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
viewModel.selectTheme(theme)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Row(
|
Row(
|
||||||
|
|||||||
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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)
|
||||||
|
|||||||
@ -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(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -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()
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user