Add theme mode selector in SettingsScreen
This commit is contained in:
parent
116e8889ac
commit
5e18d7e22a
6 changed files with 245 additions and 12 deletions
|
@ -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(
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package it.vfsfitvnm.vimusic.enums
|
||||
|
||||
enum class ColorPaletteMode {
|
||||
Light,
|
||||
Dark,
|
||||
System
|
||||
}
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
9
app/src/main/res/drawable/contrast.xml
Normal file
9
app/src/main/res/drawable/contrast.xml
Normal 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>
|
Loading…
Reference in a new issue