Implement color scheme preferences
This commit is contained in:
parent
a2cf713b56
commit
30e8af57ac
@ -390,6 +390,13 @@
|
||||
<string name="preference_card_background_default">Standard (weiß/dunkelgrau)</string>
|
||||
<string name="preference_card_background_black">Weiß/schwarz</string>
|
||||
<string name="preference_card_background_colored">Farbig (aus Hintergrundbild)</string>
|
||||
<string name="preference_screen_colors">Farbschema</string>
|
||||
<string name="preference_colors_default">Standard</string>
|
||||
<string name="preference_colors_mm20">MM20</string>
|
||||
<string name="preference_colors_wallpaper">Aus Hintergrundbild</string>
|
||||
<string name="preference_colors_mdyou">System / Material You</string>
|
||||
<string name="preference_colors_bw">Schwarz-Weiß</string>
|
||||
<string name="preference_colors_custom">Benutzerdefiniert</string>
|
||||
|
||||
<string name="date_today">Heute</string>
|
||||
<string name="date_tomorrow">Morgen</string>
|
||||
|
||||
@ -65,6 +65,13 @@
|
||||
<string name="preference_theme_light">Light</string>
|
||||
<string name="preference_theme_dark">Dark</string>
|
||||
<string name="preference_theme_black">Black</string>
|
||||
<string name="preference_screen_colors">Color scheme</string>
|
||||
<string name="preference_colors_default">Default</string>
|
||||
<string name="preference_colors_mm20">MM20</string>
|
||||
<string name="preference_colors_wallpaper">From wallpaper</string>
|
||||
<string name="preference_colors_mdyou">System / Material You</string>
|
||||
<string name="preference_colors_bw">Black and White</string>
|
||||
<string name="preference_colors_custom">Custom</string>
|
||||
<string name="preference_screen_about">About</string>
|
||||
<string name="preference_version">Version</string>
|
||||
<string name="preference_category_developer">Developer</string>
|
||||
|
||||
@ -11,6 +11,16 @@ message Settings {
|
||||
System = 2;
|
||||
}
|
||||
Theme theme = 1;
|
||||
enum ColorScheme {
|
||||
Default = 0;
|
||||
MM20 = 1;
|
||||
MaterialYou = 2;
|
||||
Wallpaper = 3;
|
||||
BlackAndWhite = 4;
|
||||
Custom = 5;
|
||||
}
|
||||
ColorScheme color_scheme = 6;
|
||||
|
||||
bool light_status_bar = 2;
|
||||
bool light_nav_bar = 3;
|
||||
bool dim_wallpaper = 4;
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
package de.mm20.launcher2.ui.activity
|
||||
|
||||
import android.app.WallpaperManager
|
||||
import android.appwidget.AppWidgetHost
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.View
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.google.accompanist.insets.ProvideWindowInsets
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.dataStore
|
||||
import de.mm20.launcher2.ui.LauncherTheme
|
||||
import de.mm20.launcher2.ui.locals.LocalAppWidgetHost
|
||||
import de.mm20.launcher2.ui.locals.LocalColorScheme
|
||||
@ -26,12 +26,9 @@ import de.mm20.launcher2.ui.locals.LocalNavController
|
||||
import de.mm20.launcher2.ui.locals.LocalWindowSize
|
||||
import de.mm20.launcher2.ui.screens.LauncherMainScreen
|
||||
import de.mm20.launcher2.ui.screens.settings.*
|
||||
import de.mm20.launcher2.ui.theme.WallpaperColors
|
||||
import de.mm20.launcher2.ui.theme.colors.DefaultColorScheme
|
||||
import de.mm20.launcher2.ui.theme.colors.WallpaperColorScheme
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import de.mm20.launcher2.ui.theme.colors.*
|
||||
import de.mm20.launcher2.ui.theme.wallpaperColorsAsState
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class ComposeActivity : AppCompatActivity() {
|
||||
|
||||
@ -46,43 +43,35 @@ class ComposeActivity : AppCompatActivity() {
|
||||
|
||||
setContent {
|
||||
val navController = rememberNavController()
|
||||
val context = LocalContext.current
|
||||
|
||||
var windowSize by remember { mutableStateOf(Size(0f, 0f)) }
|
||||
findViewById<View>(android.R.id.content).doOnLayout {
|
||||
windowSize = Size(it.width.toFloat(), it.height.toFloat())
|
||||
}
|
||||
|
||||
var wallpaperColors by remember { mutableStateOf<WallpaperColors?>(null) }
|
||||
|
||||
DisposableEffect(null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
val wallpaperManager = WallpaperManager.getInstance(this@ComposeActivity)
|
||||
val callback = { colors: android.app.WallpaperColors?, which: Int ->
|
||||
if (colors != null && which or WallpaperManager.FLAG_SYSTEM != 0) {
|
||||
wallpaperColors = WallpaperColors.fromPlatformType(colors)
|
||||
}
|
||||
}
|
||||
wallpaperManager.addOnColorsChangedListener(
|
||||
callback,
|
||||
Handler(Looper.getMainLooper())
|
||||
)
|
||||
|
||||
lifecycleScope.launch {
|
||||
val colors = withContext(Dispatchers.IO) {
|
||||
wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM)
|
||||
} ?: return@launch
|
||||
wallpaperColors = WallpaperColors.fromPlatformType(colors)
|
||||
}
|
||||
return@DisposableEffect onDispose {
|
||||
wallpaperManager.removeOnColorsChangedListener(callback)
|
||||
}
|
||||
}
|
||||
onDispose {}
|
||||
}
|
||||
|
||||
if (windowSize.height <= 0 || windowSize.width <= 0) return@setContent
|
||||
|
||||
val colorScheme = wallpaperColors?.let { WallpaperColorScheme(it) } ?: DefaultColorScheme()
|
||||
val colorSchemePreference by remember { dataStore.data.map { it.appearance.colorScheme } }
|
||||
.collectAsState(initial = Settings.AppearanceSettings.ColorScheme.Default)
|
||||
|
||||
val colorScheme = when (colorSchemePreference) {
|
||||
Settings.AppearanceSettings.ColorScheme.MM20 -> MM20ColorScheme()
|
||||
Settings.AppearanceSettings.ColorScheme.Wallpaper -> {
|
||||
if (isAtLeastApiLevel(Build.VERSION_CODES.O_MR1)) {
|
||||
val wallpaperColors by wallpaperColorsAsState()
|
||||
WallpaperColorScheme(wallpaperColors)
|
||||
} else DefaultColorScheme()
|
||||
}
|
||||
Settings.AppearanceSettings.ColorScheme.MaterialYou -> {
|
||||
if (isAtLeastApiLevel(Build.VERSION_CODES.S)) {
|
||||
SystemColorScheme(context)
|
||||
} else DefaultColorScheme()
|
||||
}
|
||||
Settings.AppearanceSettings.ColorScheme.BlackAndWhite -> BlackWhiteColorScheme()
|
||||
Settings.AppearanceSettings.ColorScheme.Custom -> TODO()
|
||||
else -> DefaultColorScheme()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -110,6 +99,9 @@ class ComposeActivity : AppCompatActivity() {
|
||||
composable("settings/appearance") {
|
||||
SettingsAppearanceScreen()
|
||||
}
|
||||
composable("settings/appearance/colors") {
|
||||
SettingsColorsScreen()
|
||||
}
|
||||
composable(
|
||||
"settings/license?library={libraryName}",
|
||||
arguments = listOf(navArgument("libraryName") {
|
||||
|
||||
@ -7,8 +7,10 @@ import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.dataStore
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.preferences.ListPreference
|
||||
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 kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ -16,6 +18,7 @@ import kotlinx.coroutines.launch
|
||||
fun SettingsAppearanceScreen() {
|
||||
val context = LocalContext.current
|
||||
val dataStore = context.dataStore
|
||||
val navController = LocalNavController.current
|
||||
val scope = rememberCoroutineScope()
|
||||
PreferenceScreen(title = stringResource(id = R.string.preference_screen_appearance)) {
|
||||
item {
|
||||
@ -41,6 +44,9 @@ fun SettingsAppearanceScreen() {
|
||||
}
|
||||
}
|
||||
)
|
||||
Preference(title = stringResource(id = R.string.preference_screen_colors), onClick = {
|
||||
navController?.navigate("settings/appearance/colors")
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,159 @@
|
||||
package de.mm20.launcher2.ui.screens.settings
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.RadioButtonChecked
|
||||
import androidx.compose.material.icons.rounded.RadioButtonUnchecked
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.preferences.dataStore
|
||||
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.colors.*
|
||||
import de.mm20.launcher2.ui.theme.wallpaperColorsAsState
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme as ColorSchemeOption
|
||||
|
||||
@Composable
|
||||
fun SettingsColorsScreen() {
|
||||
val context = LocalContext.current
|
||||
val dataStore = context.dataStore
|
||||
val scope = rememberCoroutineScope()
|
||||
PreferenceScreen(title = stringResource(id = R.string.preference_screen_colors)) {
|
||||
item {
|
||||
val colorScheme by remember {
|
||||
dataStore.data.map {
|
||||
it.appearance.colorScheme
|
||||
}
|
||||
}.collectAsState(initial = ColorSchemeOption.Default)
|
||||
val schemes = mutableListOf(
|
||||
ColorSchemeItem(
|
||||
ColorSchemeOption.Default,
|
||||
DefaultColorScheme(),
|
||||
stringResource(id = R.string.preference_colors_default)
|
||||
),
|
||||
ColorSchemeItem(
|
||||
ColorSchemeOption.MM20,
|
||||
MM20ColorScheme(),
|
||||
stringResource(id = R.string.preference_colors_mm20)
|
||||
),
|
||||
ColorSchemeItem(
|
||||
ColorSchemeOption.BlackAndWhite,
|
||||
BlackWhiteColorScheme(),
|
||||
stringResource(id = R.string.preference_colors_bw)
|
||||
)
|
||||
)
|
||||
if (isAtLeastApiLevel(Build.VERSION_CODES.S)) {
|
||||
schemes.add(
|
||||
ColorSchemeItem(
|
||||
ColorSchemeOption.MaterialYou,
|
||||
SystemColorScheme(context),
|
||||
stringResource(id = R.string.preference_colors_mdyou)
|
||||
)
|
||||
)
|
||||
}
|
||||
if (isAtLeastApiLevel(Build.VERSION_CODES.O_MR1)) {
|
||||
val wallpaperColors by wallpaperColorsAsState()
|
||||
schemes.add(
|
||||
ColorSchemeItem(
|
||||
ColorSchemeOption.Wallpaper,
|
||||
WallpaperColorScheme(wallpaperColors),
|
||||
stringResource(id = R.string.preference_colors_wallpaper)
|
||||
)
|
||||
)
|
||||
}
|
||||
PreferenceCategory {
|
||||
for (scheme in schemes) {
|
||||
Preference(
|
||||
title = scheme.label,
|
||||
icon = if (colorScheme == scheme.value) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
||||
controls = {
|
||||
ColorSchemePreview(scheme.colorScheme)
|
||||
},
|
||||
onClick = {
|
||||
scope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setAppearance(
|
||||
it.appearance.toBuilder().setColorScheme(scheme.value)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColorSchemePreview(colorScheme: ColorScheme) {
|
||||
val isDark = !MaterialTheme.colors.isLight
|
||||
val neutral1 = if (isDark) colorScheme.neutral1.shade800 else colorScheme.neutral1.shade100
|
||||
val neutral2 = if (isDark) colorScheme.neutral2.shade800 else colorScheme.neutral2.shade100
|
||||
val accent1 = if (isDark) colorScheme.accent1.shade300 else colorScheme.accent1.shade500
|
||||
val accent2 = if (isDark) colorScheme.accent2.shade300 else colorScheme.accent2.shade500
|
||||
val accent3 = if (isDark) colorScheme.accent3.shade300 else colorScheme.accent3.shade500
|
||||
Box(
|
||||
modifier = Modifier.height(48.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxHeight()
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.width(48.dp)
|
||||
.background(neutral1)
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.width(48.dp)
|
||||
.background(neutral2)
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.height(16.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.background(accent1)
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.size(16.dp)
|
||||
.background(accent2)
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.background(accent3)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private data class ColorSchemeItem(
|
||||
val value: ColorSchemeOption,
|
||||
val colorScheme: ColorScheme,
|
||||
val label: String,
|
||||
)
|
||||
@ -1,8 +1,17 @@
|
||||
package de.mm20.launcher2.ui.theme
|
||||
|
||||
import android.app.WallpaperManager
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
data class WallpaperColors(val primary: Color, val secondary: Color?, val tertiary: Color?) {
|
||||
companion object {
|
||||
@ -15,4 +24,37 @@ data class WallpaperColors(val primary: Color, val secondary: Color?, val tertia
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O_MR1)
|
||||
@Composable
|
||||
fun wallpaperColorsAsState(): State<WallpaperColors> {
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val state = remember { mutableStateOf(DefaultWallpaperColors) }
|
||||
DisposableEffect(null) {
|
||||
val wallpaperManager = WallpaperManager.getInstance(context)
|
||||
val callback = { colors: android.app.WallpaperColors?, which: Int ->
|
||||
if (colors != null && which or WallpaperManager.FLAG_SYSTEM != 0) {
|
||||
state.value = WallpaperColors.fromPlatformType(colors)
|
||||
}
|
||||
}
|
||||
wallpaperManager.addOnColorsChangedListener(
|
||||
callback,
|
||||
Handler(Looper.getMainLooper())
|
||||
)
|
||||
|
||||
scope.launch {
|
||||
val colors = withContext(Dispatchers.IO) {
|
||||
wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM)
|
||||
} ?: return@launch
|
||||
state.value = WallpaperColors.fromPlatformType(colors)
|
||||
}
|
||||
onDispose {
|
||||
wallpaperManager.removeOnColorsChangedListener(callback)
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
internal val DefaultWallpaperColors = WallpaperColors(Color.White, null, null)
|
||||
@ -0,0 +1,30 @@
|
||||
package de.mm20.launcher2.ui.theme.colors
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
class BlackWhiteColorScheme: ColorScheme() {
|
||||
override val neutral1: ColorSwatch
|
||||
get() = ColorSwatch(
|
||||
Color.White,
|
||||
Color.White,
|
||||
Color.White,
|
||||
Color.White,
|
||||
Color.White,
|
||||
Color.White,
|
||||
Color.White,
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
)
|
||||
override val neutral2: ColorSwatch
|
||||
get() = neutral1
|
||||
override val accent1: ColorSwatch
|
||||
get() = neutral1
|
||||
override val accent2: ColorSwatch
|
||||
get() = neutral1
|
||||
override val accent3: ColorSwatch
|
||||
get() = neutral1
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user