Use dependency injection to access dataStore, not the context.dataStore extension

This commit is contained in:
MM20 2022-01-02 21:58:53 +01:00
parent 9d6ca3d613
commit 11e6a3ceb3
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
9 changed files with 10 additions and 597 deletions

View File

@ -10,7 +10,7 @@ import de.mm20.launcher2.preferences.migrations.FactorySettingsMigration
typealias LauncherDataStore = DataStore<Settings>
val Context.dataStore: LauncherDataStore by dataStore(
internal val Context.dataStore: LauncherDataStore by dataStore(
fileName = "settings.pb",
serializer = SettingsSerializer,
produceMigrations = {

View File

@ -3,14 +3,9 @@ package de.mm20.launcher2.ui
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Typography
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
@ -18,11 +13,9 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme
import de.mm20.launcher2.preferences.dataStore
import de.mm20.launcher2.ui.locals.LocalColorScheme
import de.mm20.launcher2.ui.theme.colors.toDarkColorScheme
import de.mm20.launcher2.ui.theme.colors.toLightColorScheme
import kotlinx.coroutines.flow.map
val Poppins = FontFamily(
Font(R.font.poppins100, FontWeight.Thin, FontStyle.Normal),
@ -124,11 +117,7 @@ val typography = Typography(
@Composable
fun LauncherTheme(content: @Composable () -> Unit) {
val dataStore = LocalContext.current.dataStore
val theme by remember {
dataStore.data.map { it.appearance.theme }
}.collectAsState(initial = Theme.System)
val theme = Theme.System
val darkTheme = theme == Theme.Dark || theme == Theme.System && isSystemInDarkTheme()

View File

@ -20,7 +20,6 @@ import com.google.accompanist.navigation.animation.composable
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
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
@ -28,6 +27,7 @@ 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.settings.appearance.AppearanceScreen
import de.mm20.launcher2.ui.theme.colors.*
import de.mm20.launcher2.ui.theme.wallpaperColorsAsState
import kotlinx.coroutines.flow.map
@ -55,8 +55,7 @@ class ComposeActivity : AppCompatActivity() {
if (windowSize.height <= 0 || windowSize.width <= 0) return@setContent
val colorSchemePreference by remember { dataStore.data.map { it.appearance.colorScheme } }
.collectAsState(initial = Settings.AppearanceSettings.ColorScheme.Default)
val colorSchemePreference = Settings.AppearanceSettings.ColorScheme.Default
val colorScheme = when (colorSchemePreference) {
Settings.AppearanceSettings.ColorScheme.MM20 -> MM20ColorPalette()
@ -72,10 +71,6 @@ class ComposeActivity : AppCompatActivity() {
} else DefaultColorPalette()
}
Settings.AppearanceSettings.ColorScheme.BlackAndWhite -> BlackWhiteColorPalette()
Settings.AppearanceSettings.ColorScheme.Custom -> {
val customColors by customColorsAsState()
CustomColorPalette(customColors)
}
else -> DefaultColorPalette()
}
@ -113,13 +108,7 @@ class ComposeActivity : AppCompatActivity() {
SettingsAccountScreen()
}
composable("settings/appearance") {
SettingsAppearanceScreen()
}
composable("settings/appearance/colors") {
SettingsColorsScreen()
}
composable("settings/appearance/clock") {
SettingsClockScreen()
AppearanceScreen()
}
composable(
"settings/license?library={libraryName}",

View File

@ -19,7 +19,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.google.accompanist.insets.LocalWindowInsets
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ClockStyle
import de.mm20.launcher2.preferences.dataStore
import de.mm20.launcher2.ui.component.AnalogClock
import de.mm20.launcher2.ui.component.BinaryClock
import de.mm20.launcher2.ui.component.DigitalClock
@ -55,10 +54,7 @@ fun ClockWidget(
fun Clock() {
var time by remember { mutableStateOf(System.currentTimeMillis()) }
val context = LocalContext.current
val dataStore = context.dataStore
val clockStyle by remember { dataStore.data.map { it.appearance.clockStyle } }.collectAsState(
initial = ClockStyle.Digital
)
val clockStyle = ClockStyle.Digital
DisposableEffect(null) {
val receiver = object : BroadcastReceiver() {

View File

@ -1,65 +0,0 @@
package de.mm20.launcher2.ui.screens.settings
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
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
@Composable
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 {
PreferenceCategory {
val theme by remember {
dataStore.data.map { it.appearance.theme }
}.collectAsState(initial = Settings.AppearanceSettings.Theme.System)
ListPreference(
title = stringResource(id = R.string.preference_theme),
items = listOf(
stringResource(id = R.string.preference_theme_system) to Settings.AppearanceSettings.Theme.System,
stringResource(id = R.string.preference_theme_light) to Settings.AppearanceSettings.Theme.Light,
stringResource(id = R.string.preference_theme_dark) to Settings.AppearanceSettings.Theme.Dark,
),
value = theme,
onValueChanged = { newValue ->
scope.launch {
dataStore.updateData {
it.toBuilder()
.setAppearance(it.appearance.toBuilder().setTheme(newValue))
.build()
}
}
}
)
Preference(
title = stringResource(id = R.string.preference_screen_colors),
onClick = {
navController?.navigate("settings/appearance/colors")
})
}
}
item {
PreferenceCategory(title = stringResource(id = R.string.preference_category_clock_widget)) {
Preference(
title = stringResource(id = R.string.preference_clock_widget_style),
onClick = {
navController?.navigate("settings/appearance/clock")
}
)
}
}
}
}

View File

@ -1,102 +1,17 @@
package de.mm20.launcher2.ui.screens.settings
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.res.stringResource
import de.mm20.launcher2.preferences.dataStore
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@Composable
fun SettingsBadgesScreen() {
val dataStore = LocalContext.current.dataStore
val scope = rememberCoroutineScope()
PreferenceScreen(
title = stringResource(id = R.string.preference_screen_badges)
) {
item {
PreferenceCategory {
val notificationBadges by remember {
dataStore.data.map { it.badges.notificationBadges }
}.collectAsState(initial = false)
SwitchPreference(
title = stringResource(id = R.string.preference_notification_badges),
summary = stringResource(id = R.string.preference_notification_badges_summary),
value = notificationBadges,
onValueChanged = { newValue ->
scope.launch {
dataStore.updateData {
it.toBuilder()
.setBadges(
it.badges.toBuilder().setNotificationBadges(newValue)
)
.build()
}
}
}
)
val suspendedBadges by remember {
dataStore.data.map { it.badges.suspendBadges }
}.collectAsState(initial = false)
SwitchPreference(
title = stringResource(id = R.string.preference_suspended_badges),
summary = stringResource(id = R.string.preference_suspended_badges_summary),
value = suspendedBadges,
onValueChanged = { newValue ->
scope.launch {
dataStore.updateData {
it.toBuilder()
.setBadges(
it.badges.toBuilder().setSuspendBadges(newValue)
)
.build()
}
}
}
)
val cloudBadges by remember {
dataStore.data.map { it.badges.cloudBadges }
}.collectAsState(initial = false)
SwitchPreference(
title = stringResource(id = R.string.preference_cloud_badges),
summary = stringResource(id = R.string.preference_cloud_badges_summary),
value = cloudBadges,
onValueChanged = { newValue ->
scope.launch {
dataStore.updateData {
it.toBuilder()
.setBadges(
it.badges.toBuilder().setCloudBadges(newValue)
)
.build()
}
}
}
)
val shortcutBadges by remember {
dataStore.data.map { it.badges.shortcutBadges }
}.collectAsState(initial = false)
SwitchPreference(
title = stringResource(id = R.string.preference_shortcut_badges),
summary = stringResource(id = R.string.preference_shortcut_badges_summary),
value = shortcutBadges,
onValueChanged = { newValue ->
scope.launch {
dataStore.updateData {
it.toBuilder()
.setBadges(
it.badges.toBuilder().setShortcutBadges(newValue)
)
.build()
}
}
}
)
}
}
}
}

View File

@ -1,97 +0,0 @@
package de.mm20.launcher2.ui.screens.settings
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
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.datastore.core.DataStore
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ClockStyle
import de.mm20.launcher2.preferences.dataStore
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.AnalogClock
import de.mm20.launcher2.ui.component.BinaryClock
import de.mm20.launcher2.ui.component.DigitalClock
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 kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@Composable
fun SettingsClockScreen() {
val dataStore = LocalContext.current.dataStore
val scope = rememberCoroutineScope()
val selectedClock by remember { dataStore.data.map { it.appearance.clockStyle } }.collectAsState(
initial = ClockStyle.Digital
)
val time = System.currentTimeMillis()
PreferenceScreen(title = stringResource(id = R.string.preference_clock_widget_style)) {
item {
ClockPreview(selected = selectedClock == ClockStyle.Digital, onClick = {
scope.launch {
updateClock(dataStore, ClockStyle.Digital)
}
}) {
DigitalClock(time)
}
}
item {
ClockPreview(selected = selectedClock == ClockStyle.Analog, onClick = {
scope.launch {
updateClock(dataStore, ClockStyle.Analog)
}
}) {
AnalogClock(time)
}
}
item {
ClockPreview(selected = selectedClock == ClockStyle.Binary, onClick = {
scope.launch {
updateClock(dataStore, ClockStyle.Binary)
}
}) {
BinaryClock(time)
}
}
}
}
private suspend fun updateClock(dataStore: DataStore<Settings>, clockStyle: ClockStyle) {
dataStore.updateData {
it.toBuilder()
.setAppearance(it.appearance.toBuilder().setClockStyle(clockStyle))
.build()
}
}
@Composable
private fun ClockPreview(
selected: Boolean,
onClick: () -> Unit,
clock: @Composable () -> Unit
) {
PreferenceCategory {
Preference(
title = "",
icon = if (selected) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
controls = {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
clock()
}
},
onClick = onClick
)
}
}

View File

@ -1,255 +0,0 @@
package de.mm20.launcher2.ui.screens.settings
import android.os.Build
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.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.graphics.toArgb
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.ColorPreference
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()
val customColors by customColorsAsState()
val colorScheme by remember {
dataStore.data.map {
it.appearance.colorScheme
}
}.collectAsState(initial = ColorSchemeOption.Default)
PreferenceScreen(title = stringResource(id = R.string.preference_screen_colors)) {
item {
val schemes = mutableListOf(
ColorSchemeItem(
ColorSchemeOption.Default,
DefaultColorPalette(),
stringResource(id = R.string.preference_colors_default)
),
ColorSchemeItem(
ColorSchemeOption.MM20,
MM20ColorPalette(),
stringResource(id = R.string.preference_colors_mm20)
),
ColorSchemeItem(
ColorSchemeOption.BlackAndWhite,
BlackWhiteColorPalette(),
stringResource(id = R.string.preference_colors_bw)
)
)
if (isAtLeastApiLevel(Build.VERSION_CODES.S)) {
schemes.add(
ColorSchemeItem(
ColorSchemeOption.MaterialYou,
SystemColorPalette(context),
stringResource(id = R.string.preference_colors_mdyou)
)
)
}
if (isAtLeastApiLevel(Build.VERSION_CODES.O_MR1)) {
val wallpaperColors by wallpaperColorsAsState()
schemes.add(
ColorSchemeItem(
ColorSchemeOption.Wallpaper,
WallpaperColorPalette(wallpaperColors),
stringResource(id = R.string.preference_colors_wallpaper)
)
)
}
schemes.add(
ColorSchemeItem(
ColorSchemeOption.Custom,
CustomColorPalette(customColors),
stringResource(id = R.string.preference_colors_custom)
)
)
PreferenceCategory {
for (scheme in schemes) {
Preference(
title = scheme.label,
icon = if (colorScheme == scheme.value) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
controls = {
ColorSchemePreview(scheme.colorPalette)
},
onClick = {
scope.launch {
dataStore.updateData {
it.toBuilder()
.setAppearance(
it.appearance.toBuilder().setColorScheme(scheme.value)
)
.build()
}
}
}
)
}
}
}
if (colorScheme == ColorSchemeOption.Custom) {
item {
PreferenceCategory(title = stringResource(R.string.preference_category_custom_colors)) {
ColorPreference(
title = "Neutral1",
value = customColors.neutral1,
onValueChanged = { newValue ->
scope.launch {
dataStore.updateData {
it.toBuilder().setAppearance(
it.appearance.toBuilder().setCustomColors(
it.appearance.customColors.toBuilder()
.setNeutral1(newValue.toArgb())
)
).build()
}
}
}
)
ColorPreference(
title = "Neutral2",
value = customColors.neutral2,
onValueChanged = { newValue ->
scope.launch {
dataStore.updateData {
it.toBuilder().setAppearance(
it.appearance.toBuilder().setCustomColors(
it.appearance.customColors.toBuilder()
.setNeutral2(newValue.toArgb())
)
).build()
}
}
}
)
ColorPreference(
title = "Accent1",
value = customColors.accent1,
onValueChanged = { newValue ->
scope.launch {
dataStore.updateData {
it.toBuilder().setAppearance(
it.appearance.toBuilder().setCustomColors(
it.appearance.customColors.toBuilder()
.setAccent1(newValue.toArgb())
)
).build()
}
}
}
)
ColorPreference(
title = "Accent2",
value = customColors.accent2,
onValueChanged = { newValue ->
scope.launch {
dataStore.updateData {
it.toBuilder().setAppearance(
it.appearance.toBuilder().setCustomColors(
it.appearance.customColors.toBuilder()
.setAccent2(newValue.toArgb())
)
).build()
}
}
}
)
ColorPreference(
title = "Accent3",
value = customColors.accent3,
onValueChanged = { newValue ->
scope.launch {
dataStore.updateData {
it.toBuilder().setAppearance(
it.appearance.toBuilder().setCustomColors(
it.appearance.customColors.toBuilder()
.setAccent3(newValue.toArgb())
)
).build()
}
}
}
)
}
}
}
}
}
@Composable
private fun ColorSchemePreview(colorPalette: ColorPalette) {
val isDark = !androidx.compose.material.MaterialTheme.colors.isLight
val neutral1 = if (isDark) colorPalette.neutral.shade20 else colorPalette.neutral.shade90
val neutral2 = if (isDark) colorPalette.neutralVariant.shade20 else colorPalette.neutralVariant.shade90
val accent1 = if (isDark) colorPalette.primary.shade70 else colorPalette.primary.shade50
val accent2 = if (isDark) colorPalette.secondary.shade70 else colorPalette.secondary.shade50
val accent3 = if (isDark) colorPalette.tertiary.shade70 else colorPalette.tertiary.shade50
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 colorPalette: ColorPalette,
val label: String,
)

View File

@ -1,59 +0,0 @@
package de.mm20.launcher2.ui.theme.colors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import de.mm20.launcher2.preferences.dataStore
import kotlinx.coroutines.flow.map
class CustomColorPalette(val colors: CustomColors) : ColorPalette() {
override val neutral: ColorSwatch
get() = colorSwatch(colors.neutral1)
override val neutralVariant: ColorSwatch
get() = colorSwatch(colors.neutral2)
override val primary: ColorSwatch
get() = colorSwatch(colors.accent1)
override val secondary: ColorSwatch
get() = colorSwatch(colors.accent2)
override val tertiary: ColorSwatch
get() = colorSwatch(colors.accent3)
}
data class CustomColors(
val neutral1: Color,
val neutral2: Color,
val accent1: Color,
val accent2: Color,
val accent3: Color,
)
@Composable
fun customColorsAsState(): State<CustomColors> {
val dataStore = LocalContext.current.dataStore
return remember {
dataStore.data.map {
val colors = it.appearance.customColors
CustomColors(
neutral1 = Color(colors.neutral1).copy(alpha = 1f),
neutral2 = Color(colors.neutral2).copy(alpha = 1f),
accent1 = Color(colors.accent1).copy(alpha = 1f),
accent2 = Color(colors.accent2).copy(alpha = 1f),
accent3 = Color(colors.accent3).copy(alpha = 1f),
)
}
}.collectAsState(
initial = DefaultCustomColors
)
}
val DefaultCustomColors = CustomColors(
Color.Black,
Color.Black,
Color.Black,
Color.Black,
Color.Black,
)