Add custom color schemes

This commit is contained in:
MM20
2022-04-14 23:01:19 +02:00
parent 2ca05d34f0
commit 08731887cb
14 changed files with 1374 additions and 5 deletions
@@ -0,0 +1,95 @@
package de.mm20.launcher2.ui.component.preferences
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.godaddy.android.colorpicker.ClassicColorPicker
import de.mm20.launcher2.ui.ktx.toHexString
@Composable
fun ColorPreference(
title: String,
summary: String? = null,
value: Color?,
onValueChanged: (Color?) -> Unit = {}
) {
var showDialog by remember { mutableStateOf(false) }
Preference(
title = title,
summary = summary,
controls = {
value?.let {
Surface(
color = it,
shape = CircleShape,
modifier = Modifier
.padding(vertical = 12.dp)
.size(36.dp)
) {}
}
},
onClick = {
showDialog = true
}
)
if (showDialog) {
var color by remember(value) { mutableStateOf(value) }
AlertDialog(
onDismissRequest = { showDialog = false },
title = {
Text(
text = title,
style = MaterialTheme.typography.titleLarge
)
},
text = {
Column(
modifier = Modifier.fillMaxWidth()
) {
ClassicColorPicker(
color = value ?: Color.Black,
onColorChanged = {
color = it.toColor()
},
showAlphaBar = false,
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
)
var hexValue by remember(color) {
mutableStateOf(
color?.toHexString() ?: "#000000"
)
}
TextField(
modifier = Modifier.padding(top = 16.dp),
value = hexValue,
onValueChange = {
hexValue = it
if (Regex("#[0-9a-fA-F]{6}").matches(it)) {
val hex = it.substring(1).toIntOrNull(16) ?: return@TextField
color = Color(hex).copy(alpha = 1f)
}
}
)
}
},
confirmButton = {
TextButton(onClick = {
onValueChanged(color)
showDialog = false
}) {
Text(stringResource(android.R.string.ok))
}
}
)
}
}
@@ -28,6 +28,7 @@ import de.mm20.launcher2.ui.settings.calendarwidget.CalendarWidgetSettingsScreen
import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen
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.crashreporter.CrashReportScreen
import de.mm20.launcher2.ui.settings.crashreporter.CrashReporterScreen
import de.mm20.launcher2.ui.settings.debug.DebugSettingsScreen
@@ -88,6 +89,9 @@ class SettingsActivity : BaseActivity() {
composable("settings/appearance/colorscheme") {
ColorSchemeSettingsScreen()
}
composable("settings/appearance/colorscheme/custom") {
CustomColorSchemeSettingsScreen()
}
composable("settings/appearance/cards") {
CardsSettingsScreen()
}
@@ -71,6 +71,7 @@ fun AppearanceSettingsScreen() {
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 = {
@@ -22,11 +22,13 @@ 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.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 {
@@ -36,6 +38,7 @@ fun ColorSchemeSettingsScreen() {
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)) {
@@ -47,7 +50,12 @@ fun ColorSchemeSettingsScreen() {
Preference(
title = cs.second,
icon = if (colorScheme == cs.first) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
onClick = { viewModel.setColorScheme(cs.first) },
onClick = {
viewModel.setColorScheme(cs.first)
if (cs.first == AppearanceSettings.ColorScheme.Custom) {
navController?.navigate("settings/appearance/colorscheme/custom")
}
},
controls = {
ColorSchemePreview(scheme)
}
@@ -0,0 +1,852 @@
package de.mm20.launcher2.ui.settings.colorscheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.MoreVert
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.preferences.ColorPreference
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
@Composable
fun CustomColorSchemeSettingsScreen() {
val viewModel: CustomColorSchemeSettingsScreenVM = viewModel()
val advancedMode by viewModel.advancedMode.observeAsState()
PreferenceScreen(
title = stringResource(R.string.preference_screen_colors),
topBarActions = {
var showOverflowMenu by remember { mutableStateOf(false) }
IconButton(onClick = { showOverflowMenu = true }) {
Icon(imageVector = Icons.Rounded.MoreVert, contentDescription = null)
}
DropdownMenu(
expanded = showOverflowMenu,
onDismissRequest = { showOverflowMenu = false }) {
if (advancedMode == false) {
DropdownMenuItem(
text = { Text("Generate from primary color") },
onClick = {
viewModel.generateFromPrimaryColor()
showOverflowMenu = false
}
)
}
DropdownMenuItem(
text = {
Text(
stringResource(
if (advancedMode == true) {
R.string.preference_custom_colors_simple_mode
} else {
R.string.preference_custom_colors_advanced_mode
}
)
)
},
onClick = {
viewModel.setAdvancedMode(advancedMode?.not() ?: true)
showOverflowMenu = false
}
)
}
}
) {
if (advancedMode == false) {
item {
PreferenceCategory {
val baseColors by viewModel.baseColors.observeAsState()
ColorPreference(
title = stringResource(R.string.preference_custom_colors_a1),
value = baseColors?.let { Color(it.accent1) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = baseColors ?: return@ColorPreference
viewModel.setBaseColors(
colors.toBuilder()
.setAccent1(it.toArgb())
.build()
)
}
)
ColorPreference(
title = stringResource(R.string.preference_custom_colors_a2),
value = baseColors?.let { Color(it.accent2) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = baseColors ?: return@ColorPreference
viewModel.setBaseColors(
colors.toBuilder()
.setAccent2(it.toArgb())
.build()
)
}
)
ColorPreference(
title = stringResource(R.string.preference_custom_colors_a3),
value = baseColors?.let { Color(it.accent3) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = baseColors ?: return@ColorPreference
viewModel.setBaseColors(
colors.toBuilder()
.setAccent3(it.toArgb())
.build()
)
}
)
ColorPreference(
title = stringResource(R.string.preference_custom_colors_n1),
value = baseColors?.let { Color(it.neutral1) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = baseColors ?: return@ColorPreference
viewModel.setBaseColors(
colors.toBuilder()
.setNeutral1(it.toArgb())
.build()
)
}
)
ColorPreference(
title = stringResource(R.string.preference_custom_colors_n2),
value = baseColors?.let { Color(it.neutral2) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = baseColors ?: return@ColorPreference
viewModel.setBaseColors(
colors.toBuilder()
.setNeutral2(it.toArgb())
.build()
)
}
)
ColorPreference(
title = stringResource(R.string.preference_custom_colors_error),
value = baseColors?.let { Color(it.error) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = baseColors ?: return@ColorPreference
viewModel.setBaseColors(
colors.toBuilder()
.setError(it.toArgb())
.build()
)
}
)
}
}
}
if (advancedMode == true) {
item {
PreferenceCategory(stringResource(R.string.preference_category_custom_colors_light)) {
val lightScheme by viewModel.lightScheme.observeAsState()
ColorPreference(
title = "Primary",
value = lightScheme?.let { Color(it.primary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setPrimary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Primary",
value = lightScheme?.let { Color(it.onPrimary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setOnPrimary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Primary Container",
value = lightScheme?.let { Color(it.primaryContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setPrimaryContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Primary Container",
value = lightScheme?.let { Color(it.onPrimaryContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setOnPrimaryContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Secondary",
value = lightScheme?.let { Color(it.secondary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setSecondary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Secondary",
value = lightScheme?.let { Color(it.onSecondary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setOnSecondary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Secondary Container",
value = lightScheme?.let { Color(it.secondaryContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setSecondaryContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Secondary Container",
value = lightScheme?.let { Color(it.onSecondaryContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setOnSecondaryContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Tertiary",
value = lightScheme?.let { Color(it.tertiary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setTertiary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Tertiary",
value = lightScheme?.let { Color(it.onTertiary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setOnTertiary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Tertiary Container",
value = lightScheme?.let { Color(it.tertiaryContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setTertiaryContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Tertiary Container",
value = lightScheme?.let { Color(it.onTertiaryContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setOnTertiaryContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Background",
value = lightScheme?.let { Color(it.background) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setBackground(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Background",
value = lightScheme?.let { Color(it.onBackground) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setOnBackground(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Surface",
value = lightScheme?.let { Color(it.surface) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setSurface(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Surface",
value = lightScheme?.let { Color(it.onSurface) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setOnSurface(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Surface Variant",
value = lightScheme?.let { Color(it.surfaceVariant) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setSurfaceVariant(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Surface Variant",
value = lightScheme?.let { Color(it.onSurfaceVariant) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setOnSurfaceVariant(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Error",
value = lightScheme?.let { Color(it.error) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setError(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Error",
value = lightScheme?.let { Color(it.onError) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setOnError(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Error Container",
value = lightScheme?.let { Color(it.errorContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setErrorContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Error Container",
value = lightScheme?.let { Color(it.onErrorContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setOnErrorContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Inverse Primary",
value = lightScheme?.let { Color(it.inversePrimary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setInversePrimary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Inverse Surface",
value = lightScheme?.let { Color(it.inverseSurface) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setInverseSurface(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Inverse On Surface",
value = lightScheme?.let { Color(it.inverseOnSurface) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setInverseOnSurface(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Outline",
value = lightScheme?.let { Color(it.outline) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = lightScheme ?: return@ColorPreference
viewModel.setLightScheme(
colors.toBuilder()
.setOutline(it.toArgb())
.build()
)
}
)
}
PreferenceCategory(stringResource(R.string.preference_category_custom_colors_light)) {
val darkScheme by viewModel.darkScheme.observeAsState()
ColorPreference(
title = "Primary",
value = darkScheme?.let { Color(it.primary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setPrimary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Primary",
value = darkScheme?.let { Color(it.onPrimary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setOnPrimary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Primary Container",
value = darkScheme?.let { Color(it.primaryContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setPrimaryContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Primary Container",
value = darkScheme?.let { Color(it.onPrimaryContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setOnPrimaryContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Secondary",
value = darkScheme?.let { Color(it.secondary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setSecondary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Secondary",
value = darkScheme?.let { Color(it.onSecondary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setOnSecondary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Secondary Container",
value = darkScheme?.let { Color(it.secondaryContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setSecondaryContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Secondary Container",
value = darkScheme?.let { Color(it.onSecondaryContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setOnSecondaryContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Tertiary",
value = darkScheme?.let { Color(it.tertiary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setTertiary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Tertiary",
value = darkScheme?.let { Color(it.onTertiary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setOnTertiary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Tertiary Container",
value = darkScheme?.let { Color(it.tertiaryContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setTertiaryContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Tertiary Container",
value = darkScheme?.let { Color(it.onTertiaryContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setOnTertiaryContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Background",
value = darkScheme?.let { Color(it.background) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setBackground(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Background",
value = darkScheme?.let { Color(it.onBackground) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setOnBackground(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Surface",
value = darkScheme?.let { Color(it.surface) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setSurface(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Surface",
value = darkScheme?.let { Color(it.onSurface) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setOnSurface(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Surface Variant",
value = darkScheme?.let { Color(it.surfaceVariant) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setSurfaceVariant(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Surface Variant",
value = darkScheme?.let { Color(it.onSurfaceVariant) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setOnSurfaceVariant(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Error",
value = darkScheme?.let { Color(it.error) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setError(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Error",
value = darkScheme?.let { Color(it.onError) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setOnError(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Error Container",
value = darkScheme?.let { Color(it.errorContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setErrorContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "On Error Container",
value = darkScheme?.let { Color(it.onErrorContainer) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setOnErrorContainer(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Inverse Primary",
value = darkScheme?.let { Color(it.inversePrimary) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setInversePrimary(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Inverse Surface",
value = darkScheme?.let { Color(it.inverseSurface) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setInverseSurface(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Inverse On Surface",
value = darkScheme?.let { Color(it.inverseOnSurface) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setInverseOnSurface(it.toArgb())
.build()
)
}
)
ColorPreference(
title = "Outline",
value = darkScheme?.let { Color(it.outline) },
onValueChanged = {
if (it == null) return@ColorPreference
val colors = darkScheme ?: return@ColorPreference
viewModel.setDarkScheme(
colors.toBuilder()
.setOutline(it.toArgb())
.build()
)
}
)
}
}
}
}
}
@@ -0,0 +1,194 @@
package de.mm20.launcher2.ui.settings.colorscheme
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.CustomColors
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
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 }.asLiveData()
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.surfaceVariant)
.setError(scheme.error)
.build()
)
}
}
val baseColors = dataStore.data.map { it.appearance.customColors.baseColors }.asLiveData()
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 }.asLiveData()
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 }.asLiveData()
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)
return CustomColors.Scheme.newBuilder()
.setPrimary(a1.tone(80))
.setOnPrimary(a1.tone(20))
.setPrimaryContainer(a1.tone(30))
.setOnPrimaryContainer(a1.tone(90))
.setSecondary(a2.tone(80))
.setOnSecondary(a2.tone(20))
.setSecondaryContainer(a2.tone(30))
.setOnSecondaryContainer(a2.tone(90))
.setTertiary(a3.tone(80))
.setOnTertiary(a3.tone(20))
.setTertiaryContainer(a3.tone(30))
.setOnTertiaryContainer(a3.tone(90))
.setError(error.tone(80))
.setOnError(error.tone(20))
.setErrorContainer(error.tone(30))
.setOnErrorContainer(error.tone(80))
.setBackground(n1.tone(10))
.setOnBackground(n1.tone(90))
.setSurface(n1.tone(10))
.setOnSurface(n1.tone(90))
.setSurfaceVariant(n2.tone(30))
.setOnSurfaceVariant(n2.tone(80))
.setOutline(n2.tone(60))
.setInverseSurface(n1.tone(90))
.setInverseOnSurface(n1.tone(20))
.setInversePrimary(a1.tone(40))
.build()
}
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)
return CustomColors.Scheme.newBuilder()
.setPrimary(a1.tone(40))
.setOnPrimary(a1.tone(100))
.setPrimaryContainer(a1.tone(90))
.setOnPrimaryContainer(a1.tone(10))
.setSecondary(a2.tone(40))
.setOnSecondary(a2.tone(100))
.setSecondaryContainer(a2.tone(90))
.setOnSecondaryContainer(a2.tone(10))
.setTertiary(a3.tone(40))
.setOnTertiary(a3.tone(100))
.setTertiaryContainer(a3.tone(90))
.setOnTertiaryContainer(a3.tone(10))
.setError(error.tone(40))
.setOnError(error.tone(100))
.setErrorContainer(error.tone(90))
.setOnErrorContainer(error.tone(10))
.setBackground(n1.tone(99))
.setOnBackground(n1.tone(10))
.setSurface(n1.tone(99))
.setOnSurface(n1.tone(10))
.setSurfaceVariant(n2.tone(90))
.setOnSurfaceVariant(n2.tone(30))
.setOutline(n2.tone(50))
.setInverseSurface(n1.tone(20))
.setInverseOnSurface(n1.tone(95))
.setInversePrimary(a1.tone(80))
.build()
}
}
@@ -57,6 +57,18 @@ fun colorSchemeAsState(colorScheme: AppearanceSettings.ColorScheme): MutableStat
)
}
}
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 -> {
if (Build.VERSION.SDK_INT >= 27 && (Build.VERSION.SDK_INT < 31 || colorScheme == AppearanceSettings.ColorScheme.DebugMaterialYouCompat)) {
val wallpaperColors by wallpaperColorsAsState()
@@ -71,7 +83,9 @@ fun colorSchemeAsState(colorScheme: AppearanceSettings.ColorScheme): MutableStat
if (Build.VERSION.SDK_INT >= 31) {
return remember(darkTheme) {
mutableStateOf(
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(
context
)
)
}
}
@@ -0,0 +1,37 @@
package de.mm20.launcher2.ui.theme.colorscheme
import androidx.compose.material3.ColorScheme
import androidx.compose.ui.graphics.Color
import de.mm20.launcher2.preferences.Settings
fun CustomColorScheme(colors: Settings.AppearanceSettings.CustomColors.Scheme) : ColorScheme {
return ColorScheme(
primary = Color(colors.primary),
onPrimary = Color(colors.onPrimary),
primaryContainer = Color(colors.primaryContainer),
onPrimaryContainer = Color(colors.onPrimaryContainer),
secondary = Color(colors.secondary),
onSecondary = Color(colors.onSecondary),
secondaryContainer = Color(colors.secondaryContainer),
onSecondaryContainer = Color(colors.onSecondaryContainer),
tertiary = Color(colors.tertiary),
onTertiary = Color(colors.onTertiary),
tertiaryContainer = Color(colors.tertiaryContainer),
onTertiaryContainer = Color(colors.onTertiaryContainer),
background = Color(colors.background),
onBackground = Color(colors.onBackground),
surface = Color(colors.surface),
onSurface = Color(colors.onSurface),
surfaceVariant = Color(colors.surfaceVariant),
onSurfaceVariant = Color(colors.onSurfaceVariant),
outline = Color(colors.outline),
inverseSurface = Color(colors.inverseSurface),
inverseOnSurface = Color(colors.inverseOnSurface),
inversePrimary = Color(colors.inversePrimary),
surfaceTint = Color(colors.primary),
error = Color(colors.error),
onError = Color(colors.onError),
errorContainer = Color(colors.errorContainer),
onErrorContainer = Color(colors.onErrorContainer),
)
}