From 5e18d7e22a069ea12f25530735cd674597800dd4 Mon Sep 17 00:00:00 2001 From: vfsfitvnm Date: Sun, 5 Jun 2022 14:52:03 +0200 Subject: [PATCH] Add theme mode selector in SettingsScreen --- .../it/vfsfitvnm/vimusic/MainActivity.kt | 10 +- .../vimusic/enums/ColorPaletteMode.kt | 7 + .../vimusic/ui/components/themed/Dialog.kt | 128 ++++++++++++++++-- .../vimusic/ui/screens/SettingsScreen.kt | 101 +++++++++++++- .../it/vfsfitvnm/vimusic/utils/Preferences.kt | 2 + app/src/main/res/drawable/contrast.xml | 9 ++ 6 files changed, 245 insertions(+), 12 deletions(-) create mode 100644 app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/ColorPaletteMode.kt create mode 100644 app/src/main/res/drawable/contrast.xml diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt index f2e74b1..2c9a5fe 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt @@ -35,11 +35,13 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.common.util.concurrent.ListenableFuture import com.valentinilk.shimmer.LocalShimmerTheme import com.valentinilk.shimmer.defaultShimmerTheme +import it.vfsfitvnm.vimusic.enums.ColorPaletteMode import it.vfsfitvnm.vimusic.services.PlayerService import it.vfsfitvnm.vimusic.ui.components.BottomSheetMenu import it.vfsfitvnm.vimusic.ui.components.LocalMenuState import it.vfsfitvnm.vimusic.ui.components.rememberMenuState import it.vfsfitvnm.vimusic.ui.screens.HomeScreen +import it.vfsfitvnm.vimusic.ui.screens.SettingsScreen import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.ui.styling.rememberColorPalette @@ -64,7 +66,12 @@ class MainActivity : ComponentActivity() { setContent { val preferences = rememberPreferences() val systemUiController = rememberSystemUiController() - val isDarkTheme = isSystemInDarkTheme() + + val isDarkTheme = when (preferences.colorPaletteMode) { + ColorPaletteMode.Light -> false + ColorPaletteMode.Dark -> true + ColorPaletteMode.System -> isSystemInDarkTheme() + } val colorPalette = rememberColorPalette(isDarkTheme) @@ -126,6 +133,7 @@ class MainActivity : ComponentActivity() { .fillMaxSize() .background(LocalColorPalette.current.background) ) { +// SettingsScreen() HomeScreen(intentVideoId = intentVideoId) BottomSheetMenu( diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/ColorPaletteMode.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/ColorPaletteMode.kt new file mode 100644 index 0000000..63bcc3b --- /dev/null +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/ColorPaletteMode.kt @@ -0,0 +1,7 @@ +package it.vfsfitvnm.vimusic.enums + +enum class ColorPaletteMode { + Light, + Dark, + System +} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Dialog.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Dialog.kt index d41ef03..9369f85 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Dialog.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Dialog.kt @@ -3,17 +3,21 @@ package it.vfsfitvnm.vimusic.ui.components.themed import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut -import androidx.compose.foundation.background +import androidx.compose.foundation.* +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.ripple.rememberRipple import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.SolidColor @@ -26,10 +30,7 @@ import androidx.compose.ui.window.Dialog import it.vfsfitvnm.vimusic.ui.components.ChunkyButton import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalTypography -import it.vfsfitvnm.vimusic.utils.center -import it.vfsfitvnm.vimusic.utils.color -import it.vfsfitvnm.vimusic.utils.secondary -import it.vfsfitvnm.vimusic.utils.semiBold +import it.vfsfitvnm.vimusic.utils.* import kotlinx.coroutines.delay @Composable @@ -194,16 +195,15 @@ fun ConfirmationDialog( } @Composable -private inline fun DefaultDialog( +inline fun DefaultDialog( noinline onDismiss: () -> Unit, modifier: Modifier = Modifier, + horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, crossinline content: @Composable ColumnScope.() -> Unit ) { - Dialog( - onDismissRequest = onDismiss - ) { + Dialog(onDismissRequest = onDismiss) { Column( - horizontalAlignment = Alignment.CenterHorizontally, + horizontalAlignment = horizontalAlignment, modifier = modifier .padding(all = 48.dp) .background( @@ -215,3 +215,111 @@ private inline fun DefaultDialog( ) } } + +@Composable +inline fun > EnumValueSelectorDialog( + noinline onDismiss: () -> Unit, + title: String, + selectedValue: T, + crossinline onValueSelected: (T) -> Unit, + modifier: Modifier = Modifier, + crossinline valueText: (T) -> String = Enum::name +) { + val typography = LocalTypography.current + val colorPalette = LocalColorPalette.current + + Dialog(onDismissRequest = onDismiss) { + Column( + modifier = modifier + .padding(all = 48.dp) + .background( + color = LocalColorPalette.current.elevatedBackground, + shape = RoundedCornerShape(8.dp) + ) + .padding(vertical = 16.dp), + ) { + BasicText( + text = title, + style = typography.s.semiBold, + modifier = Modifier + .padding(vertical = 8.dp, horizontal = 24.dp) + ) + + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()) + ) { + enumValues().forEach { value -> + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier + .clickable( + indication = rememberRipple(bounded = true), + interactionSource = remember { MutableInteractionSource() }, + onClick = { + onDismiss() + onValueSelected(value) + } + ) + .padding(vertical = 8.dp, horizontal = 24.dp) + .fillMaxWidth() + ) { + if (selectedValue == value) { + Box(contentAlignment = Alignment.Center) { + Spacer( + modifier = Modifier + .size(18.dp) + .background( + color = colorPalette.primaryContainer, + shape = CircleShape + ) + ) + + Spacer( + modifier = Modifier + .size(6.dp) + .background( + color = colorPalette.onPrimaryContainer, + shape = CircleShape + ) + ) + } + + } else { + Spacer( + modifier = Modifier + .size(18.dp) + .border( + width = 1.dp, + color = colorPalette.textDisabled, + shape = CircleShape + ) + ) + } + + BasicText( + text = valueText(value), + style = typography.xs.medium + ) + } + } + } + + BasicText( + text = "Cancel", + style = typography.xs.semiBold, + modifier = Modifier + .padding(horizontal = 24.dp) + .clip(RoundedCornerShape(36.dp)) + .clickable( + indication = rememberRipple(bounded = true), + interactionSource = remember { MutableInteractionSource() }, + onClick = onDismiss + ) + .padding(horizontal = 24.dp, vertical = 16.dp) + .align(Alignment.End) + ) + } + } +} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SettingsScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SettingsScreen.kt index 97bbac7..eaf522c 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SettingsScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SettingsScreen.kt @@ -2,18 +2,26 @@ package it.vfsfitvnm.vimusic.ui.screens import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.* +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.Composable +import androidx.compose.material.ripple.rememberRipple +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.vimusic.R +import it.vfsfitvnm.vimusic.enums.ColorPaletteMode import it.vfsfitvnm.vimusic.ui.components.TopAppBar +import it.vfsfitvnm.vimusic.ui.components.themed.EnumValueSelectorDialog import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalTypography +import it.vfsfitvnm.vimusic.utils.LocalPreferences +import it.vfsfitvnm.vimusic.utils.secondary import it.vfsfitvnm.vimusic.utils.semiBold @ExperimentalAnimationApi @@ -40,6 +48,7 @@ fun SettingsScreen() { host { val colorPalette = LocalColorPalette.current val typography = LocalTypography.current + val preferences = LocalPreferences.current Column( modifier = Modifier @@ -73,7 +82,97 @@ fun SettingsScreen() { .size(24.dp) ) } + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 8.dp) + .background( + color = colorPalette.lightBackground, + shape = RoundedCornerShape(8.dp) + ) + .padding(vertical = 8.dp) + .fillMaxWidth() + ) { + Image( + painter = painterResource(R.drawable.contrast), + contentDescription = null, + colorFilter = ColorFilter.tint(colorPalette.text), + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 8.dp) + .size(20.dp) + ) + + BasicText( + text = "Appearance", + style = typography.m.semiBold, + modifier = Modifier + ) + } + + EnumValueSelectorEntry( + title = "Theme mode", + selectedValue = preferences.colorPaletteMode, + onValueSelected = { + preferences.colorPaletteMode = it + }, + valueText = { + when (it) { + ColorPaletteMode.Light -> "Light" + ColorPaletteMode.Dark -> "Dark" + ColorPaletteMode.System -> "System" + } + } + ) } } } +} + +@Composable +private inline fun >EnumValueSelectorEntry( + title: String, + selectedValue: T, + crossinline onValueSelected: (T) -> Unit, + modifier: Modifier = Modifier, + crossinline valueText: (T) -> String = Enum::name +) { + val typography = LocalTypography.current + + var isShowingDialog by remember { + mutableStateOf(false) + } + + if (isShowingDialog) { + EnumValueSelectorDialog( + onDismiss = { + isShowingDialog = false + }, + title = title, + selectedValue = selectedValue, + onValueSelected = onValueSelected, + valueText = valueText + ) + } + + Column( + modifier = modifier + .clickable( + indication = rememberRipple(bounded = true), + interactionSource = remember { MutableInteractionSource() }, + onClick = { isShowingDialog = true } + ) + .padding(horizontal = 32.dp, vertical = 8.dp) + .fillMaxWidth() + ) { + BasicText( + text = title, + style = typography.xs.semiBold, + ) + + BasicText( + text = valueText(selectedValue), + style = typography.xs.semiBold.secondary + ) + } } \ No newline at end of file diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt index f38b2f3..d37207d 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt @@ -10,11 +10,13 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.content.edit import androidx.media3.common.Player +import it.vfsfitvnm.vimusic.enums.ColorPaletteMode import it.vfsfitvnm.vimusic.enums.SongCollection import it.vfsfitvnm.youtubemusic.YouTube @Stable class Preferences(holder: SharedPreferences) : SharedPreferences by holder { + var colorPaletteMode by preference("colorPaletteMode", ColorPaletteMode.System) var searchFilter by preference("searchFilter", YouTube.Item.Song.Filter.value) var repeatMode by preference("repeatMode", Player.REPEAT_MODE_OFF) var homePageSongCollection by preference("homePageSongCollection", SongCollection.MostPlayed) diff --git a/app/src/main/res/drawable/contrast.xml b/app/src/main/res/drawable/contrast.xml new file mode 100644 index 0000000..fc24506 --- /dev/null +++ b/app/src/main/res/drawable/contrast.xml @@ -0,0 +1,9 @@ + + +