Add theme mode selector in SettingsScreen

This commit is contained in:
vfsfitvnm 2022-06-05 14:52:03 +02:00
parent 116e8889ac
commit 5e18d7e22a
6 changed files with 245 additions and 12 deletions

View file

@ -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(

View file

@ -0,0 +1,7 @@
package it.vfsfitvnm.vimusic.enums
enum class ColorPaletteMode {
Light,
Dark,
System
}

View file

@ -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 <reified T : Enum<T>> EnumValueSelectorDialog(
noinline onDismiss: () -> Unit,
title: String,
selectedValue: T,
crossinline onValueSelected: (T) -> Unit,
modifier: Modifier = Modifier,
crossinline valueText: (T) -> String = Enum<T>::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<T>().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)
)
}
}
}

View file

@ -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 <reified T: Enum<T>>EnumValueSelectorEntry(
title: String,
selectedValue: T,
crossinline onValueSelected: (T) -> Unit,
modifier: Modifier = Modifier,
crossinline valueText: (T) -> String = Enum<T>::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
)
}
}

View file

@ -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)

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#FF000000"
android:pathData="M256,32A224,224 0,0 0,97.61 414.39,224 224,0 1,0 414.39,97.61 222.53,222.53 0,0 0,256 32ZM64,256C64,150.13 150.13,64 256,64V448C150.13,448 64,361.87 64,256Z"/>
</vector>