Add wallpaper color scheme
This commit is contained in:
@@ -104,6 +104,8 @@ dependencies {
|
||||
implementation(libs.coil.core)
|
||||
implementation(libs.coil.compose)
|
||||
|
||||
implementation(project(":material-color-utilities"))
|
||||
|
||||
implementation(project(":base"))
|
||||
implementation(project(":i18n"))
|
||||
implementation(project(":compat"))
|
||||
|
||||
@@ -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.Wallpaper -> stringResource(R.string.preference_colors_wallpaper)
|
||||
else -> null
|
||||
},
|
||||
onClick = {
|
||||
|
||||
+61
-58
@@ -1,5 +1,6 @@
|
||||
package de.mm20.launcher2.ui.settings.colorscheme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.*
|
||||
@@ -7,6 +8,7 @@ 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.getValue
|
||||
@@ -17,44 +19,42 @@ import androidx.compose.ui.platform.LocalContext
|
||||
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.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.theme.getColorScheme
|
||||
import de.mm20.launcher2.ui.theme.colorSchemeAsState
|
||||
|
||||
@Composable
|
||||
fun ColorSchemeSettingsScreen() {
|
||||
val viewModel: ColorSchemeSettingsScreenVM = viewModel()
|
||||
val context = LocalContext.current
|
||||
|
||||
PreferenceScreen(title = stringResource(R.string.preference_screen_colors)) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
val theme by viewModel.theme.observeAsState()
|
||||
val darkTheme =
|
||||
theme == AppearanceSettings.Theme.Dark || theme == AppearanceSettings.Theme.System && isSystemInDarkTheme()
|
||||
val colorScheme by viewModel.colorScheme.observeAsState()
|
||||
|
||||
val items = listOf(
|
||||
val items = mutableListOf(
|
||||
AppearanceSettings.ColorScheme.Default to R.string.preference_colors_default,
|
||||
AppearanceSettings.ColorScheme.BlackAndWhite to R.string.preference_colors_bw
|
||||
AppearanceSettings.ColorScheme.BlackAndWhite to R.string.preference_colors_bw,
|
||||
)
|
||||
|
||||
if (isAtLeastApiLevel(Build.VERSION_CODES.O_MR1)) {
|
||||
items.add(
|
||||
AppearanceSettings.ColorScheme.Wallpaper to R.string.preference_colors_wallpaper
|
||||
)
|
||||
}
|
||||
|
||||
for (cs in items) {
|
||||
val scheme by colorSchemeAsState(cs.first)
|
||||
Preference(
|
||||
title = stringResource(cs.second),
|
||||
icon = if (colorScheme == cs.first) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
||||
onClick = { viewModel.setColorScheme(cs.first) },
|
||||
controls = {
|
||||
ColorSchemePreview(
|
||||
getColorScheme(
|
||||
LocalContext.current,
|
||||
cs.first,
|
||||
darkTheme,
|
||||
)
|
||||
)
|
||||
ColorSchemePreview(scheme)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -65,51 +65,54 @@ fun ColorSchemeSettingsScreen() {
|
||||
|
||||
@Composable
|
||||
fun ColorSchemePreview(colorScheme: ColorScheme) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 12.dp)
|
||||
.width(72.dp)
|
||||
.height(36.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
MaterialTheme(colorScheme = colorScheme) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 12.dp)
|
||||
.width(72.dp)
|
||||
.height(36.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Surface(
|
||||
tonalElevation = 1.dp,
|
||||
color = colorScheme.surface,
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
) {}
|
||||
Surface(
|
||||
tonalElevation = 1.dp,
|
||||
color = colorScheme.surfaceVariant,
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
) {}
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Surface(
|
||||
tonalElevation = 1.dp,
|
||||
color = colorScheme.primary,
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
) {}
|
||||
Surface(
|
||||
tonalElevation = 1.dp,
|
||||
color = colorScheme.secondary,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.size(16.dp)
|
||||
) {}
|
||||
Surface(
|
||||
tonalElevation = 1.dp,
|
||||
color = colorScheme.tertiary,
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
) {}
|
||||
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,24 +1,18 @@
|
||||
package de.mm20.launcher2.ui.theme
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings.AppearanceSettings
|
||||
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme
|
||||
import de.mm20.launcher2.ui.theme.colorscheme.DarkBlackAndWhiteColorScheme
|
||||
import de.mm20.launcher2.ui.theme.colorscheme.DarkPre31DefaultColorScheme
|
||||
import de.mm20.launcher2.ui.theme.colorscheme.LightBlackAndWhiteColorScheme
|
||||
import de.mm20.launcher2.ui.theme.colorscheme.LightPre31DefaultColorScheme
|
||||
import de.mm20.launcher2.ui.theme.colorscheme.*
|
||||
import de.mm20.launcher2.ui.theme.typography.DefaultTypography
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.koin.androidx.compose.inject
|
||||
@@ -34,34 +28,52 @@ fun LauncherTheme(
|
||||
val colorSchemePreference by remember { dataStore.data.map { it.appearance.colorScheme } }.collectAsState(
|
||||
AppearanceSettings.ColorScheme.Default
|
||||
)
|
||||
val themePreference by remember { dataStore.data.map { it.appearance.theme } }.collectAsState(
|
||||
Theme.System
|
||||
)
|
||||
|
||||
val darkTheme =
|
||||
themePreference == Theme.Dark || themePreference == Theme.System && isSystemInDarkTheme()
|
||||
val colorScheme by colorSchemeAsState(colorSchemePreference)
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = getColorScheme(LocalContext.current, colorSchemePreference, darkTheme),
|
||||
colorScheme = colorScheme,
|
||||
typography = DefaultTypography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
fun getColorScheme(context: Context, colorScheme: AppearanceSettings.ColorScheme, darkTheme: Boolean): ColorScheme {
|
||||
return when (colorScheme) {
|
||||
AppearanceSettings.ColorScheme.BlackAndWhite -> {
|
||||
if (darkTheme) DarkBlackAndWhiteColorScheme
|
||||
else LightBlackAndWhiteColorScheme
|
||||
}
|
||||
else -> {
|
||||
if (darkTheme) {
|
||||
if (isAtLeastApiLevel(31)) dynamicDarkColorScheme(context)
|
||||
else DarkPre31DefaultColorScheme
|
||||
} else {
|
||||
if (isAtLeastApiLevel(31)) dynamicLightColorScheme(context)
|
||||
else LightPre31DefaultColorScheme
|
||||
@Composable
|
||||
fun colorSchemeAsState(colorScheme: AppearanceSettings.ColorScheme): MutableState<ColorScheme> {
|
||||
val context = LocalContext.current
|
||||
val dataStore: LauncherDataStore by inject()
|
||||
|
||||
val themePreference by remember { dataStore.data.map { it.appearance.theme } }.collectAsState(
|
||||
Theme.System
|
||||
)
|
||||
val darkTheme =
|
||||
themePreference == Theme.Dark || themePreference == Theme.System && isSystemInDarkTheme()
|
||||
|
||||
val state = remember(colorScheme, darkTheme) {
|
||||
mutableStateOf(
|
||||
when (colorScheme) {
|
||||
AppearanceSettings.ColorScheme.BlackAndWhite -> {
|
||||
if (darkTheme) DarkBlackAndWhiteColorScheme else LightBlackAndWhiteColorScheme
|
||||
}
|
||||
else -> {
|
||||
if (darkTheme) {
|
||||
if (isAtLeastApiLevel(31)) dynamicDarkColorScheme(context)
|
||||
else DarkPre31DefaultColorScheme
|
||||
} else {
|
||||
if (isAtLeastApiLevel(31)) dynamicLightColorScheme(context)
|
||||
else LightPre31DefaultColorScheme
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (colorScheme == AppearanceSettings.ColorScheme.Wallpaper && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
val wallpaperColors by wallpaperColorsAsState()
|
||||
LaunchedEffect(wallpaperColors, darkTheme) {
|
||||
state.value = WallpaperColorScheme(wallpaperColors, darkTheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.WallpaperManager
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package de.mm20.launcher2.ui.theme.colorscheme
|
||||
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import de.mm20.launcher2.ui.theme.WallpaperColors
|
||||
import palettes.TonalPalette
|
||||
|
||||
fun WallpaperColorScheme(wallpaperColors: WallpaperColors, darkTheme: Boolean): ColorScheme {
|
||||
val primary = TonalPalette.fromInt(wallpaperColors.primary.toArgb())
|
||||
val secondary = TonalPalette.fromInt((wallpaperColors.secondary ?: wallpaperColors.primary).toArgb())
|
||||
val tertiary = TonalPalette.fromInt((wallpaperColors.tertiary ?: wallpaperColors.primary).toArgb())
|
||||
|
||||
val neutral1 = TonalPalette.fromInt(Color.Black.toArgb())
|
||||
val neutral2 = TonalPalette.fromInt(Color.Black.toArgb())
|
||||
|
||||
return if(darkTheme) {
|
||||
darkColorScheme(
|
||||
primary = Color(primary.tone(80)),
|
||||
onPrimary = Color(primary.tone(20)),
|
||||
primaryContainer = Color(primary.tone(30)),
|
||||
onPrimaryContainer = Color(primary.tone(90)),
|
||||
secondary = Color(secondary.tone(80)),
|
||||
onSecondary = Color(secondary.tone(20)),
|
||||
secondaryContainer = Color(secondary.tone(30)),
|
||||
onSecondaryContainer = Color(secondary.tone(90)),
|
||||
tertiary = Color(tertiary.tone(80)),
|
||||
onTertiary = Color(tertiary.tone(20)),
|
||||
tertiaryContainer = Color(tertiary.tone(30)),
|
||||
onTertiaryContainer = Color(tertiary.tone(90)),
|
||||
background = Color(neutral1.tone(10)),
|
||||
onBackground = Color(neutral1.tone(90)),
|
||||
surface = Color(neutral1.tone(10)),
|
||||
onSurface = Color(neutral1.tone(90)),
|
||||
surfaceVariant = Color(neutral2.tone(30)),
|
||||
onSurfaceVariant = Color(neutral2.tone(80)),
|
||||
outline = Color(neutral2.tone(60)),
|
||||
inverseSurface = Color(neutral1.tone(90)),
|
||||
inverseOnSurface = Color(neutral1.tone(20)),
|
||||
inversePrimary = Color(primary.tone(40)),
|
||||
)
|
||||
} else {
|
||||
lightColorScheme(
|
||||
primary = Color(primary.tone(40)),
|
||||
onPrimary = Color(primary.tone(100)),
|
||||
primaryContainer = Color(primary.tone(90)),
|
||||
onPrimaryContainer = Color(primary.tone(10)),
|
||||
secondary = Color(secondary.tone(40)),
|
||||
onSecondary = Color(secondary.tone(100)),
|
||||
secondaryContainer = Color(secondary.tone(90)),
|
||||
onSecondaryContainer = Color(secondary.tone(10)),
|
||||
tertiary = Color(tertiary.tone(40)),
|
||||
onTertiary = Color(tertiary.tone(100)),
|
||||
tertiaryContainer = Color(tertiary.tone(90)),
|
||||
onTertiaryContainer = Color(tertiary.tone(10)),
|
||||
background = Color(neutral1.tone(99)),
|
||||
onBackground = Color(neutral1.tone(10)),
|
||||
surface = Color(neutral1.tone(99)),
|
||||
onSurface = Color(neutral1.tone(10)),
|
||||
surfaceVariant = Color(neutral2.tone(90)),
|
||||
onSurfaceVariant = Color(neutral2.tone(30)),
|
||||
outline = Color(neutral2.tone(50)),
|
||||
inverseSurface = Color(neutral1.tone(20)),
|
||||
inverseOnSurface = Color(neutral1.tone(95)),
|
||||
inversePrimary = Color(primary.tone(80)),)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user