Refactor preferences to avoid a global state when it's not necessary

This commit is contained in:
vfsfitvnm 2022-07-16 13:51:38 +02:00
parent aa8e065412
commit 1e719b33ed
38 changed files with 415 additions and 452 deletions

View file

@ -1,10 +1,7 @@
package it.vfsfitvnm.vimusic package it.vfsfitvnm.vimusic
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.ComponentName import android.content.*
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.IBinder import android.os.IBinder
@ -27,9 +24,12 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.text.ExperimentalTextApi
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.valentinilk.shimmer.LocalShimmerTheme import com.valentinilk.shimmer.LocalShimmerTheme
import com.valentinilk.shimmer.defaultShimmerTheme import com.valentinilk.shimmer.defaultShimmerTheme
import it.vfsfitvnm.vimusic.enums.ColorPaletteMode
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.service.PlayerService import it.vfsfitvnm.vimusic.service.PlayerService
import it.vfsfitvnm.vimusic.ui.components.BottomSheetMenu import it.vfsfitvnm.vimusic.ui.components.BottomSheetMenu
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
@ -37,15 +37,11 @@ import it.vfsfitvnm.vimusic.ui.components.rememberBottomSheetState
import it.vfsfitvnm.vimusic.ui.components.rememberMenuState import it.vfsfitvnm.vimusic.ui.components.rememberMenuState
import it.vfsfitvnm.vimusic.ui.screens.HomeScreen import it.vfsfitvnm.vimusic.ui.screens.HomeScreen
import it.vfsfitvnm.vimusic.ui.screens.IntentUriScreen import it.vfsfitvnm.vimusic.ui.screens.IntentUriScreen
import it.vfsfitvnm.vimusic.ui.styling.Appearance
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.ui.styling.rememberTypography
import it.vfsfitvnm.vimusic.ui.views.PlayerView import it.vfsfitvnm.vimusic.ui.views.PlayerView
import it.vfsfitvnm.vimusic.utils.LocalPreferences import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.intent
import it.vfsfitvnm.vimusic.utils.rememberHapticFeedback
import it.vfsfitvnm.vimusic.utils.rememberPreferences
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@ -75,30 +71,77 @@ class MainActivity : ComponentActivity() {
} }
@SuppressLint("BatteryLife") @SuppressLint("BatteryLife")
@OptIn(ExperimentalFoundationApi::class, ExperimentalAnimationApi::class) @OptIn(ExperimentalFoundationApi::class, ExperimentalAnimationApi::class,
ExperimentalTextApi::class
)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
uri = intent?.data uri = intent?.data
setContent { setContent {
val preferences = rememberPreferences() val isSystemInDarkTheme = isSystemInDarkTheme()
var appearance by remember(isSystemInDarkTheme) {
with(preferences) {
val colorPaletteMode = getEnum(colorPaletteModeKey, ColorPaletteMode.System)
val thumbnailRoundness = getEnum(thumbnailRoundnessKey, ThumbnailRoundness.Light)
mutableStateOf(
Appearance(
colorPalette = colorPaletteMode.palette(isSystemInDarkTheme),
typography = colorPaletteMode.typography(isSystemInDarkTheme),
thumbnailShape = thumbnailRoundness.shape()
)
)
}
}
DisposableEffect(isSystemInDarkTheme) {
val listener =
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
when (key) {
colorPaletteModeKey -> {
val colorPaletteMode = sharedPreferences.getEnum(key, ColorPaletteMode.System)
appearance = appearance.copy(
colorPalette = colorPaletteMode.palette(isSystemInDarkTheme),
typography = colorPaletteMode.typography(isSystemInDarkTheme),
)
}
thumbnailRoundnessKey -> {
val thumbnailRoundness = sharedPreferences.getEnum(key, ThumbnailRoundness.Light)
appearance = appearance.copy(
thumbnailShape = thumbnailRoundness.shape()
)
}
}
}
with(preferences) {
registerOnSharedPreferenceChangeListener(listener)
onDispose {
unregisterOnSharedPreferenceChangeListener(listener)
}
}
}
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
val colorPalette = preferences.colorPaletteMode.palette(isSystemInDarkTheme()) val rippleTheme = remember(appearance.colorPalette.text, appearance.colorPalette.isDark) {
val rippleTheme = remember(colorPalette.text, colorPalette.isDark) {
object : RippleTheme { object : RippleTheme {
@Composable @Composable
override fun defaultColor(): Color = RippleTheme.defaultRippleColor( override fun defaultColor(): Color = RippleTheme.defaultRippleColor(
contentColor = colorPalette.text, contentColor = appearance.colorPalette.text,
lightTheme = !colorPalette.isDark lightTheme = !appearance.colorPalette.isDark
) )
@Composable @Composable
override fun rippleAlpha(): RippleAlpha = RippleTheme.defaultRippleAlpha( override fun rippleAlpha(): RippleAlpha = RippleTheme.defaultRippleAlpha(
contentColor = colorPalette.text, contentColor = appearance.colorPalette.text,
lightTheme = !colorPalette.isDark lightTheme = !appearance.colorPalette.isDark
) )
} }
} }
@ -122,17 +165,15 @@ class MainActivity : ComponentActivity() {
} }
SideEffect { SideEffect {
systemUiController.setSystemBarsColor(colorPalette.background, !colorPalette.isDark) systemUiController.setSystemBarsColor(appearance.colorPalette.background, !appearance.colorPalette.isDark)
} }
CompositionLocalProvider( CompositionLocalProvider(
LocalAppearance provides appearance,
LocalOverscrollConfiguration provides null, LocalOverscrollConfiguration provides null,
LocalIndication provides rememberRipple(bounded = false), LocalIndication provides rememberRipple(bounded = false),
LocalRippleTheme provides rippleTheme, LocalRippleTheme provides rippleTheme,
LocalPreferences provides preferences,
LocalColorPalette provides colorPalette,
LocalShimmerTheme provides shimmerTheme, LocalShimmerTheme provides shimmerTheme,
LocalTypography provides rememberTypography(colorPalette.text),
LocalPlayerServiceBinder provides binder, LocalPlayerServiceBinder provides binder,
LocalMenuState provides rememberMenuState(), LocalMenuState provides rememberMenuState(),
LocalHapticFeedback provides rememberHapticFeedback() LocalHapticFeedback provides rememberHapticFeedback()
@ -140,7 +181,7 @@ class MainActivity : ComponentActivity() {
BoxWithConstraints( BoxWithConstraints(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(colorPalette.background) .background(appearance.colorPalette.background)
) { ) {
when (val uri = uri) { when (val uri = uri) {
null -> { null -> {

View file

@ -4,7 +4,10 @@ import android.app.Application
import coil.ImageLoader import coil.ImageLoader
import coil.ImageLoaderFactory import coil.ImageLoaderFactory
import coil.disk.DiskCache import coil.disk.DiskCache
import it.vfsfitvnm.vimusic.utils.Preferences import it.vfsfitvnm.vimusic.enums.CoilDiskCacheMaxSize
import it.vfsfitvnm.vimusic.utils.coilDiskCacheMaxSizeKey
import it.vfsfitvnm.vimusic.utils.getEnum
import it.vfsfitvnm.vimusic.utils.preferences
class MainApplication : Application(), ImageLoaderFactory { class MainApplication : Application(), ImageLoaderFactory {
@ -19,7 +22,7 @@ class MainApplication : Application(), ImageLoaderFactory {
.diskCache( .diskCache(
DiskCache.Builder() DiskCache.Builder()
.directory(filesDir.resolve("coil")) .directory(filesDir.resolve("coil"))
.maxSizeBytes(Preferences().coilDiskCacheMaxSize.bytes) .maxSizeBytes(preferences.getEnum(coilDiskCacheMaxSizeKey, CoilDiskCacheMaxSize.`128MB`).bytes)
.build() .build()
) )
.build() .build()

View file

@ -1,9 +1,20 @@
package it.vfsfitvnm.vimusic.enums package it.vfsfitvnm.vimusic.enums
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.styling.BlackColorPalette import it.vfsfitvnm.vimusic.ui.styling.BlackColorPalette
import it.vfsfitvnm.vimusic.ui.styling.ColorPalette import it.vfsfitvnm.vimusic.ui.styling.ColorPalette
import it.vfsfitvnm.vimusic.ui.styling.DarkColorPalette import it.vfsfitvnm.vimusic.ui.styling.DarkColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LightColorPalette import it.vfsfitvnm.vimusic.ui.styling.LightColorPalette
import it.vfsfitvnm.vimusic.ui.styling.Typography
enum class ColorPaletteMode { enum class ColorPaletteMode {
Light, Light,
@ -22,4 +33,50 @@ enum class ColorPaletteMode {
} }
} }
} }
@OptIn(ExperimentalTextApi::class)
fun typography(isSystemInDarkMode: Boolean): Typography {
val color = palette(isSystemInDarkMode).text
val textStyle = TextStyle(
fontFamily = FontFamily(
Font(
resId = R.font.poppins_w300,
weight = FontWeight.Light
),
Font(
resId = R.font.poppins_w400,
weight = FontWeight.Normal
),
Font(
resId = R.font.poppins_w400_italic,
weight = FontWeight.Normal,
style = FontStyle.Italic
),
Font(
resId = R.font.poppins_w500,
weight = FontWeight.Medium
),
Font(
resId = R.font.poppins_w600,
weight = FontWeight.SemiBold
),
Font(
resId = R.font.poppins_w700,
weight = FontWeight.Bold
),
),
fontWeight = FontWeight.Normal,
color = color,
platformStyle = @Suppress("DEPRECATION") PlatformTextStyle(includeFontPadding = false)
)
return Typography(
xxs = textStyle.copy(fontSize = 12.sp),
xs = textStyle.copy(fontSize = 14.sp),
s = textStyle.copy(fontSize = 16.sp),
m = textStyle.copy(fontSize = 18.sp),
l = textStyle.copy(fontSize = 20.sp),
)
}
} }

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.utils.LocalPreferences import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
enum class ThumbnailRoundness { enum class ThumbnailRoundness {
None, None,
@ -14,15 +14,19 @@ enum class ThumbnailRoundness {
Medium, Medium,
Heavy; Heavy;
companion object { fun shape(): Shape {
val shape: Shape return when (this) {
@Composable
@ReadOnlyComposable
get() = when (LocalPreferences.current.thumbnailRoundness) {
None -> RectangleShape None -> RectangleShape
Light -> RoundedCornerShape(2.dp) Light -> RoundedCornerShape(2.dp)
Medium -> RoundedCornerShape(4.dp) Medium -> RoundedCornerShape(4.dp)
Heavy -> RoundedCornerShape(8.dp) Heavy -> RoundedCornerShape(8.dp)
} }
} }
companion object {
val shape: Shape
@Composable
@ReadOnlyComposable
get() = LocalAppearance.current.thumbnailShape
}
} }

View file

@ -18,6 +18,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat.startForegroundService import androidx.core.content.ContextCompat.startForegroundService
import androidx.core.content.edit
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.media3.common.* import androidx.media3.common.*
@ -116,17 +117,14 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
createNotificationChannel() createNotificationChannel()
getSharedPreferences( preferences.registerOnSharedPreferenceChangeListener(this)
Preferences.fileName,
Context.MODE_PRIVATE
).registerOnSharedPreferenceChangeListener(this)
val preferences = Preferences() val preferences = preferences
isPersistentQueueEnabled = preferences.persistentQueue isPersistentQueueEnabled = preferences.getBoolean(persistentQueueKey, false)
isVolumeNormalizationEnabled = preferences.volumeNormalization isVolumeNormalizationEnabled = preferences.getBoolean(volumeNormalizationKey, false)
isInvincibilityEnabled = preferences.isInvincibilityEnabled isInvincibilityEnabled = preferences.getBoolean(isInvincibilityEnabledKey, false)
val cacheEvictor = when (val size = preferences.exoPlayerDiskCacheMaxSize) { val cacheEvictor = when (val size = preferences.getEnum(exoPlayerDiskCacheMaxSizeKey, ExoPlayerDiskCacheMaxSize.`2GB`)) {
ExoPlayerDiskCacheMaxSize.Unlimited -> NoOpCacheEvictor() ExoPlayerDiskCacheMaxSize.Unlimited -> NoOpCacheEvictor()
else -> LeastRecentlyUsedCacheEvictor(size.bytes) else -> LeastRecentlyUsedCacheEvictor(size.bytes)
} }
@ -146,8 +144,8 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
.setUsePlatformDiagnostics(false) .setUsePlatformDiagnostics(false)
.build() .build()
player.repeatMode = preferences.repeatMode player.repeatMode = preferences.getInt(repeatModeKey, Player.REPEAT_MODE_OFF)
player.skipSilenceEnabled = preferences.skipSilence player.skipSilenceEnabled = preferences.getBoolean(skipSilenceKey, false)
player.addListener(this) player.addListener(this)
player.addAnalyticsListener(PlaybackStatsListener(false, this)) player.addAnalyticsListener(PlaybackStatsListener(false, this))
@ -184,10 +182,7 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
override fun onDestroy() { override fun onDestroy() {
maybeSavePlayerQueue() maybeSavePlayerQueue()
getSharedPreferences( preferences.unregisterOnSharedPreferenceChangeListener(this)
Preferences.fileName,
Context.MODE_PRIVATE
).unregisterOnSharedPreferenceChangeListener(this)
player.removeListener(this) player.removeListener(this)
player.stop() player.stop()
@ -289,14 +284,16 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
} }
private fun normalizeVolume() { private fun normalizeVolume() {
if (isVolumeNormalizationEnabled) { player.volume = if (isVolumeNormalizationEnabled) {
player.volume = player.currentMediaItem?.let { mediaItem -> player.currentMediaItem?.let { mediaItem ->
songPendingLoudnessDb.getOrElse(mediaItem.mediaId) { songPendingLoudnessDb.getOrElse(mediaItem.mediaId) {
mediaItem.mediaMetadata.extras?.getFloatOrNull("loudnessDb") mediaItem.mediaMetadata.extras?.getFloatOrNull("loudnessDb")
}?.takeIf { it > 0 }?.let { loudnessDb -> }?.takeIf { it > 0 }?.let { loudnessDb ->
(1f - (0.01f + loudnessDb / 14)).coerceIn(0.1f, 1f) (1f - (0.01f + loudnessDb / 14)).coerceIn(0.1f, 1f)
} }
} ?: 1f } ?: 1f
} else {
1f
} }
} }
@ -309,6 +306,10 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
else -> PlaybackState.STATE_NONE else -> PlaybackState.STATE_NONE
} }
override fun onRepeatModeChanged(repeatMode: Int) {
preferences.edit { putInt(repeatModeKey, repeatMode) }
}
override fun onEvents(player: Player, events: Player.Events) { override fun onEvents(player: Player, events: Player.Events) {
if (player.duration != C.TIME_UNSET) { if (player.duration != C.TIME_UNSET) {
metadataBuilder metadataBuilder
@ -362,12 +363,16 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
when (key) { when (key) {
Preferences.Keys.persistentQueue -> isPersistentQueueEnabled = persistentQueueKey -> isPersistentQueueEnabled =
sharedPreferences.getBoolean(key, isPersistentQueueEnabled) sharedPreferences.getBoolean(key, isPersistentQueueEnabled)
Preferences.Keys.volumeNormalization -> isVolumeNormalizationEnabled = volumeNormalizationKey -> {
isVolumeNormalizationEnabled =
sharedPreferences.getBoolean(key, isVolumeNormalizationEnabled) sharedPreferences.getBoolean(key, isVolumeNormalizationEnabled)
Preferences.Keys.isInvincibilityEnabled -> isInvincibilityEnabled = normalizeVolume()
}
isInvincibilityEnabledKey -> isInvincibilityEnabled =
sharedPreferences.getBoolean(key, isInvincibilityEnabled) sharedPreferences.getBoolean(key, isInvincibilityEnabled)
skipSilenceKey -> player.skipSilenceEnabled = sharedPreferences.getBoolean(key, false)
} }
} }

View file

@ -34,8 +34,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import it.vfsfitvnm.vimusic.ui.components.ChunkyButton import it.vfsfitvnm.vimusic.ui.components.ChunkyButton
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.* import it.vfsfitvnm.vimusic.utils.*
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -56,8 +55,7 @@ fun TextFieldDialog(
val focusRequester = remember { val focusRequester = remember {
FocusRequester() FocusRequester()
} }
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
var textFieldValue by rememberSaveable(initialTextInput, stateSaver = TextFieldValue.Saver) { var textFieldValue by rememberSaveable(initialTextInput, stateSaver = TextFieldValue.Saver) {
mutableStateOf( mutableStateOf(
@ -162,8 +160,7 @@ fun ConfirmationDialog(
confirmText: String = "Confirm", confirmText: String = "Confirm",
onCancel: () -> Unit = onDismiss onCancel: () -> Unit = onDismiss
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
DefaultDialog( DefaultDialog(
onDismiss = onDismiss, onDismiss = onDismiss,
@ -211,6 +208,8 @@ inline fun DefaultDialog(
horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
crossinline content: @Composable ColumnScope.() -> Unit crossinline content: @Composable ColumnScope.() -> Unit
) { ) {
val (colorPalette) = LocalAppearance.current
Dialog( Dialog(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
properties = DialogProperties(usePlatformDefaultWidth = false) properties = DialogProperties(usePlatformDefaultWidth = false)
@ -220,7 +219,7 @@ inline fun DefaultDialog(
modifier = modifier modifier = modifier
.padding(all = 48.dp) .padding(all = 48.dp)
.background( .background(
color = LocalColorPalette.current.elevatedBackground, color = colorPalette.elevatedBackground,
shape = RoundedCornerShape(8.dp) shape = RoundedCornerShape(8.dp)
) )
.padding(horizontal = 24.dp, vertical = 16.dp), .padding(horizontal = 24.dp, vertical = 16.dp),
@ -240,15 +239,14 @@ inline fun <T> ValueSelectorDialog(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
crossinline valueText: (T) -> String = { it.toString() } crossinline valueText: (T) -> String = { it.toString() }
) { ) {
val typography = LocalTypography.current val (colorPalette, typography) = LocalAppearance.current
val colorPalette = LocalColorPalette.current
Dialog(onDismissRequest = onDismiss) { Dialog(onDismissRequest = onDismiss) {
Column( Column(
modifier = modifier modifier = modifier
.padding(all = 48.dp) .padding(all = 48.dp)
.background( .background(
color = LocalColorPalette.current.elevatedBackground, color = colorPalette.elevatedBackground,
shape = RoundedCornerShape(8.dp) shape = RoundedCornerShape(8.dp)
) )
.padding(vertical = 16.dp), .padding(vertical = 16.dp),

View file

@ -31,8 +31,7 @@ import it.vfsfitvnm.vimusic.ui.components.Pager
import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
import it.vfsfitvnm.vimusic.ui.screens.rememberCreatePlaylistRoute import it.vfsfitvnm.vimusic.ui.screens.rememberCreatePlaylistRoute
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.* import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -371,8 +370,7 @@ fun MediaItemMenu(
onSetSleepTimer?.let { onSetSleepTimer -> onSetSleepTimer?.let { onSetSleepTimer ->
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
val typography = LocalTypography.current val (colorPalette, typography) = LocalAppearance.current
val colorPalette = LocalColorPalette.current
var isShowingSleepTimerDialog by remember { var isShowingSleepTimerDialog by remember {
mutableStateOf(false) mutableStateOf(false)

View file

@ -16,8 +16,7 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.medium import it.vfsfitvnm.vimusic.utils.medium
import it.vfsfitvnm.vimusic.utils.secondary import it.vfsfitvnm.vimusic.utils.secondary
@ -27,7 +26,7 @@ inline fun Menu(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit content: @Composable ColumnScope.() -> Unit
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette) = LocalAppearance.current
Column( Column(
modifier = modifier modifier = modifier
@ -52,8 +51,7 @@ fun MenuEntry(
secondaryText: String? = null, secondaryText: String? = null,
isEnabled: Boolean = true, isEnabled: Boolean = true,
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@ -99,7 +97,7 @@ fun MenuIconButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette) = LocalAppearance.current
Box( Box(
modifier = modifier modifier = modifier

View file

@ -16,7 +16,7 @@ import androidx.compose.ui.geometry.center
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.drawCircle import it.vfsfitvnm.vimusic.utils.drawCircle
@ -25,7 +25,7 @@ fun Switch(
isChecked: Boolean, isChecked: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette) = LocalAppearance.current
val backgroundColor by animateColorAsState(if (isChecked) colorPalette.primaryContainer else colorPalette.lightBackground) val backgroundColor by animateColorAsState(if (isChecked) colorPalette.primaryContainer else colorPalette.lightBackground)
val color by animateColorAsState(if (isChecked) colorPalette.onPrimaryContainer else colorPalette.textDisabled) val color by animateColorAsState(if (isChecked) colorPalette.onPrimaryContainer else colorPalette.textDisabled)

View file

@ -17,8 +17,7 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.align import it.vfsfitvnm.vimusic.utils.align
import it.vfsfitvnm.vimusic.utils.secondary import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold import it.vfsfitvnm.vimusic.utils.semiBold
@ -32,7 +31,7 @@ fun TextCard(
onClick: (() -> Unit)? = null, onClick: (() -> Unit)? = null,
content: @Composable TextCardScope.() -> Unit, content: @Composable TextCardScope.() -> Unit,
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette) = LocalAppearance.current
Column( Column(
modifier = modifier modifier = modifier
@ -72,17 +71,19 @@ interface TextCardScope {
private object TextCardScopeImpl : TextCardScope { private object TextCardScopeImpl : TextCardScope {
@Composable @Composable
override fun Title(text: String) { override fun Title(text: String) {
val (_, typography) = LocalAppearance.current
BasicText( BasicText(
text = text, text = text,
style = LocalTypography.current.xxs.semiBold, style = typography.xxs.semiBold,
) )
} }
@Composable @Composable
override fun Text(text: String) { override fun Text(text: String) {
val (_, typography) = LocalAppearance.current
BasicText( BasicText(
text = text, text = text,
style = LocalTypography.current.xxs.secondary.align(TextAlign.Justify), style = typography.xxs.secondary.align(TextAlign.Justify),
) )
} }
} }
@ -90,9 +91,10 @@ private object TextCardScopeImpl : TextCardScope {
private object IconTextCardScopeImpl : TextCardScope { private object IconTextCardScopeImpl : TextCardScope {
@Composable @Composable
override fun Title(text: String) { override fun Title(text: String) {
val (_, typography) = LocalAppearance.current
BasicText( BasicText(
text = text, text = text,
style = LocalTypography.current.xxs.semiBold, style = typography.xxs.semiBold,
modifier = Modifier modifier = Modifier
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
) )
@ -100,9 +102,10 @@ private object IconTextCardScopeImpl : TextCardScope {
@Composable @Composable
override fun Text(text: String) { override fun Text(text: String) {
val (_, typography) = LocalAppearance.current
BasicText( BasicText(
text = text, text = text,
style = LocalTypography.current.xxs.secondary, style = typography.xxs.secondary,
modifier = Modifier modifier = Modifier
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
) )

View file

@ -10,20 +10,18 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import kotlin.random.Random import kotlin.random.Random
@Composable @Composable
fun TextPlaceholder( fun TextPlaceholder(
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val (colorPalette) = LocalAppearance.current
Spacer( Spacer(
modifier = modifier modifier = modifier
.padding(vertical = 4.dp) .padding(vertical = 4.dp)
.background( .background(color = colorPalette.darkGray, shape = RoundedCornerShape(0.dp))
color = LocalColorPalette.current.darkGray,
shape = RoundedCornerShape(0.dp)
)
.fillMaxWidth(remember { 0.25f + Random.nextFloat() * 0.5f }) .fillMaxWidth(remember { 0.25f + Random.nextFloat() * 0.5f })
.height(16.dp) .height(16.dp)
) )

View file

@ -28,16 +28,17 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage import coil.compose.AsyncImage
import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.* import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.models.* import it.vfsfitvnm.vimusic.models.*
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.* import it.vfsfitvnm.vimusic.ui.components.themed.*
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.ui.styling.px import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SongItem import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.* import it.vfsfitvnm.vimusic.utils.*
@ -90,8 +91,7 @@ fun AlbumScreen(
val context = LocalContext.current val context = LocalContext.current
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val menuState = LocalMenuState.current val menuState = LocalMenuState.current
@ -356,6 +356,8 @@ private fun LoadingOrError(
errorMessage: String? = null, errorMessage: String? = null,
onRetry: (() -> Unit)? = null onRetry: (() -> Unit)? = null
) { ) {
val (colorPalette) = LocalAppearance.current
LoadingOrError( LoadingOrError(
errorMessage = errorMessage, errorMessage = errorMessage,
onRetry = onRetry onRetry = onRetry
@ -369,7 +371,7 @@ private fun LoadingOrError(
) { ) {
Spacer( Spacer(
modifier = Modifier modifier = Modifier
.background(color = LocalColorPalette.current.darkGray, shape = ThumbnailRoundness.shape) .background(color = colorPalette.darkGray, shape = ThumbnailRoundness.shape)
.size(Dimensions.thumbnails.album) .size(Dimensions.thumbnails.album)
) )

View file

@ -36,10 +36,7 @@ import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.*
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SongItem import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.* import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.youtubemusic.YouTube import it.vfsfitvnm.youtubemusic.YouTube
@ -77,8 +74,7 @@ fun ArtistScreen(
host { host {
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val artistResult by remember(browseId) { val artistResult by remember(browseId) {
Database.artist(browseId).map { artist -> Database.artist(browseId).map { artist ->
@ -294,7 +290,7 @@ private fun LoadingOrError(
errorMessage: String? = null, errorMessage: String? = null,
onRetry: (() -> Unit)? = null onRetry: (() -> Unit)? = null
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette) = LocalAppearance.current
LoadingOrError( LoadingOrError(
errorMessage = errorMessage, errorMessage = errorMessage,

View file

@ -29,10 +29,7 @@ import it.vfsfitvnm.vimusic.models.DetailedSong
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.* import it.vfsfitvnm.vimusic.ui.components.themed.*
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.*
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SongItem import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.* import it.vfsfitvnm.vimusic.utils.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -66,8 +63,7 @@ fun BuiltInPlaylistScreen(
val menuState = LocalMenuState.current val menuState = LocalMenuState.current
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val thumbnailSize = Dimensions.thumbnails.song.px val thumbnailSize = Dimensions.thumbnails.song.px

View file

@ -50,10 +50,7 @@ import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.DropdownMenu import it.vfsfitvnm.vimusic.ui.components.themed.DropdownMenu
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.*
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.PlaylistPreviewItem import it.vfsfitvnm.vimusic.ui.views.PlaylistPreviewItem
import it.vfsfitvnm.vimusic.ui.views.SongItem import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.* import it.vfsfitvnm.vimusic.utils.*
@ -64,8 +61,7 @@ import kotlinx.coroutines.Dispatchers
@ExperimentalAnimationApi @ExperimentalAnimationApi
@Composable @Composable
fun HomeScreen() { fun HomeScreen() {
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val lazyListState = rememberLazyListState() val lazyListState = rememberLazyListState()
@ -82,10 +78,11 @@ fun HomeScreen() {
Database.playlistPreviews() Database.playlistPreviews()
}.collectAsState(initial = emptyList(), context = Dispatchers.IO) }.collectAsState(initial = emptyList(), context = Dispatchers.IO)
val preferences = LocalPreferences.current var songSortBy by rememberPreference(songSortByKey, SongSortBy.DateAdded)
var songSortOrder by rememberPreference(songSortOrderKey, SortOrder.Descending)
val songCollection by remember(preferences.songSortBy, preferences.songSortOrder) { val songCollection by remember(songSortBy, songSortOrder) {
Database.songs(preferences.songSortBy, preferences.songSortOrder) Database.songs(songSortBy, songSortOrder)
}.collectAsState(initial = emptyList(), context = Dispatchers.IO) }.collectAsState(initial = emptyList(), context = Dispatchers.IO)
RouteHandler(listenToGlobalEmitter = true) { RouteHandler(listenToGlobalEmitter = true) {
@ -153,6 +150,9 @@ fun HomeScreen() {
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
val isFirstLaunch by rememberPreference(isFirstLaunchKey, true)
val isCachedPlaylistShown by rememberPreference(isCachedPlaylistShownKey, false)
val thumbnailSize = Dimensions.thumbnails.song.px val thumbnailSize = Dimensions.thumbnails.song.px
var isGridExpanded by remember { var isGridExpanded by remember {
@ -199,7 +199,7 @@ fun HomeScreen() {
} }
.padding(horizontal = 16.dp, vertical = 8.dp) .padding(horizontal = 16.dp, vertical = 8.dp)
.run { .run {
if (preferences.isFirstLaunch) { if (isFirstLaunch) {
drawBehind { drawBehind {
drawCircle( drawCircle(
color = colorPalette.red, color = colorPalette.red,
@ -321,7 +321,7 @@ fun HomeScreen() {
} }
} }
if (preferences.isCachedPlaylistShown) { if (isCachedPlaylistShown) {
item { item {
Box( Box(
modifier = Modifier modifier = Modifier
@ -492,16 +492,16 @@ fun HomeScreen() {
) { ) {
Item( Item(
text = "PLAY TIME", text = "PLAY TIME",
isSelected = preferences.songSortBy == SongSortBy.PlayTime, isSelected = songSortBy == SongSortBy.PlayTime,
onClick = { onClick = {
preferences.songSortBy = SongSortBy.PlayTime songSortBy = SongSortBy.PlayTime
} }
) )
Item( Item(
text = "DATE ADDED", text = "DATE ADDED",
isSelected = preferences.songSortBy == SongSortBy.DateAdded, isSelected = songSortBy == SongSortBy.DateAdded,
onClick = { onClick = {
preferences.songSortBy = SongSortBy.DateAdded songSortBy = SongSortBy.DateAdded
} }
) )
} }
@ -518,14 +518,14 @@ fun HomeScreen() {
.width(IntrinsicSize.Max), .width(IntrinsicSize.Max),
) { ) {
Item( Item(
text = when (preferences.songSortOrder) { text = when (songSortOrder) {
SortOrder.Ascending -> "ASCENDING" SortOrder.Ascending -> "ASCENDING"
SortOrder.Descending -> "DESCENDING" SortOrder.Descending -> "DESCENDING"
}, },
textColor = colorPalette.text, textColor = colorPalette.text,
backgroundColor = colorPalette.elevatedBackground, backgroundColor = colorPalette.elevatedBackground,
onClick = { onClick = {
preferences.songSortOrder = !preferences.songSortOrder songSortOrder = !songSortOrder
} }
) )
} }
@ -556,7 +556,7 @@ fun HomeScreen() {
}, },
onThumbnailContent = { onThumbnailContent = {
AnimatedVisibility( AnimatedVisibility(
visible = preferences.songSortBy == SongSortBy.PlayTime, visible = songSortBy == SongSortBy.PlayTime,
enter = fadeIn(), enter = fadeIn(),
exit = fadeOut(), exit = fadeOut(),
modifier = Modifier modifier = Modifier

View file

@ -27,7 +27,7 @@ import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.* import it.vfsfitvnm.vimusic.ui.components.themed.*
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.px import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.utils.asMediaItem import it.vfsfitvnm.vimusic.utils.asMediaItem
import it.vfsfitvnm.vimusic.utils.enqueue import it.vfsfitvnm.vimusic.utils.enqueue
@ -61,7 +61,7 @@ fun IntentUriScreen(uri: Uri) {
host { host {
val menuState = LocalMenuState.current val menuState = LocalMenuState.current
val colorPalette = LocalColorPalette.current val (colorPalette) = LocalAppearance.current
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
val thumbnailSizePx = Dimensions.thumbnails.song.px val thumbnailSizePx = Dimensions.thumbnails.song.px

View file

@ -32,8 +32,7 @@ import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.* import it.vfsfitvnm.vimusic.ui.components.themed.*
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.ui.styling.px import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SongItem import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.* import it.vfsfitvnm.vimusic.utils.*
@ -73,8 +72,7 @@ fun LocalPlaylistScreen(
val menuState = LocalMenuState.current val menuState = LocalMenuState.current
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val thumbnailSize = Dimensions.thumbnails.song.px val thumbnailSize = Dimensions.thumbnails.song.px

View file

@ -36,8 +36,7 @@ import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.* import it.vfsfitvnm.vimusic.ui.components.themed.*
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.ui.styling.px import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SongItem import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.* import it.vfsfitvnm.vimusic.utils.*
@ -73,8 +72,7 @@ fun PlaylistScreen(
val context = LocalContext.current val context = LocalContext.current
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val menuState = LocalMenuState.current val menuState = LocalMenuState.current
val thumbnailSizePx = Dimensions.thumbnails.playlist.px val thumbnailSizePx = Dimensions.thumbnails.playlist.px
@ -387,7 +385,7 @@ private fun LoadingOrError(
errorMessage: String? = null, errorMessage: String? = null,
onRetry: (() -> Unit)? = null onRetry: (() -> Unit)? = null
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette) = LocalAppearance.current
LoadingOrError( LoadingOrError(
errorMessage = errorMessage, errorMessage = errorMessage,

View file

@ -37,8 +37,7 @@ import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.ui.styling.px import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SongItem import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.* import it.vfsfitvnm.vimusic.utils.*
@ -53,29 +52,29 @@ fun SearchResultScreen(
query: String, query: String,
onSearchAgain: () -> Unit, onSearchAgain: () -> Unit,
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val preferences = LocalPreferences.current
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
var searchFilter by rememberPreference(searchFilterKey, YouTube.Item.Song.Filter.value)
val lazyListState = rememberLazyListState() val lazyListState = rememberLazyListState()
val items = remember(preferences.searchFilter) { val items = remember(searchFilter) {
mutableStateListOf<YouTube.Item>() mutableStateListOf<YouTube.Item>()
} }
var continuationResult by remember(preferences.searchFilter) { var continuationResult by remember(searchFilter) {
mutableStateOf<Result<String?>?>(null) mutableStateOf<Result<String?>?>(null)
} }
val onLoad = relaunchableEffect(preferences.searchFilter) { val onLoad = relaunchableEffect(searchFilter) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val token = continuationResult?.getOrNull() val token = continuationResult?.getOrNull()
continuationResult = null continuationResult = null
continuationResult = withContext(Dispatchers.IO) { continuationResult = withContext(Dispatchers.IO) {
YouTube.search(query, preferences.searchFilter, token) YouTube.search(query, searchFilter, token)
}?.map { searchResult -> }?.map { searchResult ->
items.addAll(searchResult.items) items.addAll(searchResult.items)
searchResult.continuation searchResult.continuation
@ -186,14 +185,14 @@ fun SearchResultScreen(
value = YouTube.Item.FeaturedPlaylist.Filter.value value = YouTube.Item.FeaturedPlaylist.Filter.value
), ),
), ),
value = preferences.searchFilter, value = searchFilter,
selectedBackgroundColor = colorPalette.primaryContainer, selectedBackgroundColor = colorPalette.primaryContainer,
unselectedBackgroundColor = colorPalette.lightBackground, unselectedBackgroundColor = colorPalette.lightBackground,
selectedTextStyle = typography.xs.medium.color(colorPalette.onPrimaryContainer), selectedTextStyle = typography.xs.medium.color(colorPalette.onPrimaryContainer),
unselectedTextStyle = typography.xs.medium, unselectedTextStyle = typography.xs.medium,
shape = RoundedCornerShape(36.dp), shape = RoundedCornerShape(36.dp),
onValueChanged = { onValueChanged = {
preferences.searchFilter = it searchFilter = it
}, },
modifier = Modifier modifier = Modifier
.padding(vertical = 8.dp) .padding(vertical = 8.dp)
@ -255,7 +254,7 @@ fun SearchResultScreen(
} ?: item(key = "loading") { } ?: item(key = "loading") {
LoadingOrError( LoadingOrError(
itemCount = if (items.isEmpty()) 8 else 3, itemCount = if (items.isEmpty()) 8 else 3,
isLoadingArtists = preferences.searchFilter == YouTube.Item.Artist.Filter.value isLoadingArtists = searchFilter == YouTube.Item.Artist.Filter.value
) )
} }
} }
@ -268,7 +267,7 @@ fun SmallSongItemShimmer(
thumbnailSizeDp: Dp, thumbnailSizeDp: Dp,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette) = LocalAppearance.current
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@ -293,7 +292,7 @@ fun SmallArtistItemShimmer(
thumbnailSizeDp: Dp, thumbnailSizeDp: Dp,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette) = LocalAppearance.current
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@ -422,7 +421,7 @@ fun SmallPlaylistItem(
thumbnailSizePx: Int, thumbnailSizePx: Int,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val typography = LocalTypography.current val (_, typography) = LocalAppearance.current
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@ -474,7 +473,7 @@ fun SmallAlbumItem(
thumbnailSizePx: Int, thumbnailSizePx: Int,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val typography = LocalTypography.current val (_, typography) = LocalAppearance.current
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@ -526,7 +525,7 @@ fun SmallArtistItem(
thumbnailSizePx: Int, thumbnailSizePx: Int,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val typography = LocalTypography.current val (_, typography) = LocalAppearance.current
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,

View file

@ -37,8 +37,7 @@ import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.medium import it.vfsfitvnm.vimusic.utils.medium
import it.vfsfitvnm.vimusic.utils.secondary import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.youtubemusic.YouTube import it.vfsfitvnm.youtubemusic.YouTube
@ -104,8 +103,7 @@ fun SearchScreen(
} }
host { host {
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val isOpenableUrl = remember(textFieldValue.text) { val isOpenableUrl = remember(textFieldValue.text) {
listOf( listOf(

View file

@ -23,8 +23,7 @@ import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.Switch import it.vfsfitvnm.vimusic.ui.components.themed.Switch
import it.vfsfitvnm.vimusic.ui.components.themed.ValueSelectorDialog import it.vfsfitvnm.vimusic.ui.components.themed.ValueSelectorDialog
import it.vfsfitvnm.vimusic.ui.screens.settings.* import it.vfsfitvnm.vimusic.ui.screens.settings.*
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.* import it.vfsfitvnm.vimusic.utils.*
@ -92,9 +91,9 @@ fun SettingsScreen() {
} }
host { host {
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val preferences = LocalPreferences.current var isFirstLaunch by rememberPreference(isFirstLaunchKey, true)
Column( Column(
modifier = Modifier modifier = Modifier
@ -242,9 +241,9 @@ fun SettingsScreen() {
title = "Other", title = "Other",
description = "Advanced settings", description = "Advanced settings",
route = otherSettingsRoute, route = otherSettingsRoute,
withAlert = LocalPreferences.current.isFirstLaunch, withAlert = isFirstLaunch,
onClick = { onClick = {
preferences.isFirstLaunch = false isFirstLaunch = false
} }
) )
@ -324,8 +323,7 @@ fun SwitchSettingEntry(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
isEnabled: Boolean = true isEnabled: Boolean = true
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
Row( Row(
horizontalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp),
@ -370,8 +368,8 @@ fun SettingsEntry(
onClick: () -> Unit, onClick: () -> Unit,
isEnabled: Boolean = true isEnabled: Boolean = true
) { ) {
val typography = LocalTypography.current val (_, typography) = LocalAppearance.current
val colorPalette = LocalColorPalette.current val (colorPalette) = LocalAppearance.current
Column( Column(
modifier = modifier modifier = modifier
@ -422,7 +420,7 @@ fun BaseSettingsEntry(
titleTextStyle: @Composable TextStyle.() -> TextStyle = { this }, titleTextStyle: @Composable TextStyle.() -> TextStyle = { this },
textStyle: @Composable TextStyle.() -> TextStyle = { this }, textStyle: @Composable TextStyle.() -> TextStyle = { this },
) { ) {
val typography = LocalTypography.current val (_, typography) = LocalAppearance.current
Column( Column(
modifier = modifier modifier = modifier
@ -447,11 +445,11 @@ fun SettingsEntryGroupText(
title: String, title: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val typography = LocalTypography.current val (colorPalette, typography) = LocalAppearance.current
BasicText( BasicText(
text = title.uppercase(), text = title.uppercase(),
style = typography.xxs.semiBold.copy(LocalColorPalette.current.blue), style = typography.xxs.semiBold.copy(colorPalette.blue),
modifier = modifier modifier = modifier
.padding(start = 24.dp, top = 24.dp) .padding(start = 24.dp, top = 24.dp)
.padding(horizontal = 32.dp) .padding(horizontal = 32.dp)

View file

@ -16,12 +16,11 @@ import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.BuildConfig import it.vfsfitvnm.vimusic.BuildConfig
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.screens.ArtistScreen
import it.vfsfitvnm.vimusic.ui.screens.AlbumScreen import it.vfsfitvnm.vimusic.ui.screens.AlbumScreen
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute import it.vfsfitvnm.vimusic.ui.screens.ArtistScreen
import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.bold import it.vfsfitvnm.vimusic.utils.bold
import it.vfsfitvnm.vimusic.utils.secondary import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold import it.vfsfitvnm.vimusic.utils.semiBold
@ -48,8 +47,7 @@ fun AboutScreen() {
} }
host { host {
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
Column( Column(

View file

@ -5,18 +5,20 @@ import androidx.compose.foundation.*
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.ColorPaletteMode
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.screens.* import it.vfsfitvnm.vimusic.ui.screens.*
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.LocalPreferences
import it.vfsfitvnm.vimusic.utils.semiBold
@ExperimentalAnimationApi @ExperimentalAnimationApi
@Composable @Composable
@ -40,9 +42,11 @@ fun AppearanceSettingsScreen() {
} }
host { host {
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val preferences = LocalPreferences.current var colorPaletteMode by rememberPreference(colorPaletteModeKey, ColorPaletteMode.System)
var thumbnailRoundness by rememberPreference(thumbnailRoundnessKey, ThumbnailRoundness.Light)
var isCachedPlaylistShown by rememberPreference(isCachedPlaylistShownKey, false)
Column( Column(
modifier = Modifier modifier = Modifier
@ -81,9 +85,9 @@ fun AppearanceSettingsScreen() {
EnumValueSelectorSettingsEntry( EnumValueSelectorSettingsEntry(
title = "Theme mode", title = "Theme mode",
selectedValue = preferences.colorPaletteMode, selectedValue = colorPaletteMode,
onValueSelected = { onValueSelected = {
preferences.colorPaletteMode = it colorPaletteMode = it
} }
) )
@ -91,9 +95,9 @@ fun AppearanceSettingsScreen() {
EnumValueSelectorSettingsEntry( EnumValueSelectorSettingsEntry(
title = "Thumbnail roundness", title = "Thumbnail roundness",
selectedValue = preferences.thumbnailRoundness, selectedValue = thumbnailRoundness,
onValueSelected = { onValueSelected = {
preferences.thumbnailRoundness = it thumbnailRoundness = it
} }
) )
@ -102,9 +106,9 @@ fun AppearanceSettingsScreen() {
SwitchSettingEntry( SwitchSettingEntry(
title = "Cached playlist", title = "Cached playlist",
text = "Display a playlist whose songs can be played offline", text = "Display a playlist whose songs can be played offline",
isChecked = preferences.isCachedPlaylistShown, isChecked = isCachedPlaylistShown,
onCheckedChange = { onCheckedChange = {
preferences.isCachedPlaylistShown = it isCachedPlaylistShown = it
} }
) )
} }

View file

@ -31,8 +31,7 @@ import it.vfsfitvnm.vimusic.ui.screens.AlbumScreen
import it.vfsfitvnm.vimusic.ui.screens.ArtistScreen import it.vfsfitvnm.vimusic.ui.screens.ArtistScreen
import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.intent import it.vfsfitvnm.vimusic.utils.intent
import it.vfsfitvnm.vimusic.utils.semiBold import it.vfsfitvnm.vimusic.utils.semiBold
import java.io.FileInputStream import java.io.FileInputStream
@ -64,8 +63,7 @@ fun BackupAndRestoreScreen() {
} }
host { host {
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val context = LocalContext.current val context = LocalContext.current
val backupLauncher = val backupLauncher =

View file

@ -16,13 +16,15 @@ import coil.annotation.ExperimentalCoilApi
import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.CoilDiskCacheMaxSize
import it.vfsfitvnm.vimusic.enums.ExoPlayerDiskCacheMaxSize import it.vfsfitvnm.vimusic.enums.ExoPlayerDiskCacheMaxSize
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.screens.* import it.vfsfitvnm.vimusic.ui.screens.*
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.utils.coilDiskCacheMaxSizeKey
import it.vfsfitvnm.vimusic.utils.LocalPreferences import it.vfsfitvnm.vimusic.utils.exoPlayerDiskCacheMaxSizeKey
import it.vfsfitvnm.vimusic.utils.rememberPreference
import it.vfsfitvnm.vimusic.utils.semiBold import it.vfsfitvnm.vimusic.utils.semiBold
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -51,11 +53,12 @@ fun CacheSettingsScreen() {
host { host {
val context = LocalContext.current val context = LocalContext.current
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val preferences = LocalPreferences.current
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
var coilDiskCacheMaxSize by rememberPreference(coilDiskCacheMaxSizeKey, CoilDiskCacheMaxSize.`128MB`)
var exoPlayerDiskCacheMaxSize by rememberPreference(exoPlayerDiskCacheMaxSizeKey, ExoPlayerDiskCacheMaxSize.`2GB`)
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
Column( Column(
@ -100,9 +103,9 @@ fun CacheSettingsScreen() {
EnumValueSelectorSettingsEntry( EnumValueSelectorSettingsEntry(
title = "Max size", title = "Max size",
selectedValue = preferences.coilDiskCacheMaxSize, selectedValue = coilDiskCacheMaxSize,
onValueSelected = { onValueSelected = {
preferences.coilDiskCacheMaxSize = it coilDiskCacheMaxSize = it
} }
) )
@ -114,9 +117,7 @@ fun CacheSettingsScreen() {
diskCacheSize diskCacheSize
) )
} (${ } (${
diskCacheSize * 100 / preferences.coilDiskCacheMaxSize.bytes.coerceAtLeast( diskCacheSize * 100 / coilDiskCacheMaxSize.bytes.coerceAtLeast(1)
1
)
}%)", }%)",
) )
@ -143,9 +144,9 @@ fun CacheSettingsScreen() {
EnumValueSelectorSettingsEntry( EnumValueSelectorSettingsEntry(
title = "Max size", title = "Max size",
selectedValue = preferences.exoPlayerDiskCacheMaxSize, selectedValue = exoPlayerDiskCacheMaxSize,
onValueSelected = { onValueSelected = {
preferences.exoPlayerDiskCacheMaxSize = it exoPlayerDiskCacheMaxSize = it
} }
) )
@ -154,7 +155,7 @@ fun CacheSettingsScreen() {
text = buildString { text = buildString {
append(Formatter.formatShortFileSize(context, diskCacheSize)) append(Formatter.formatShortFileSize(context, diskCacheSize))
when (val size = preferences.exoPlayerDiskCacheMaxSize) { when (val size = exoPlayerDiskCacheMaxSize) {
ExoPlayerDiskCacheMaxSize.Unlimited -> {} ExoPlayerDiskCacheMaxSize.Unlimited -> {}
else -> append("(${diskCacheSize * 100 / size.bytes}%)") else -> append("(${diskCacheSize * 100 / size.bytes}%)")
} }

View file

@ -23,10 +23,10 @@ import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.screens.* import it.vfsfitvnm.vimusic.ui.screens.*
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.LocalPreferences
import it.vfsfitvnm.vimusic.utils.isIgnoringBatteryOptimizations import it.vfsfitvnm.vimusic.utils.isIgnoringBatteryOptimizations
import it.vfsfitvnm.vimusic.utils.isInvincibilityEnabledKey
import it.vfsfitvnm.vimusic.utils.rememberPreference
import it.vfsfitvnm.vimusic.utils.semiBold import it.vfsfitvnm.vimusic.utils.semiBold
@ -53,9 +53,9 @@ fun OtherSettingsScreen() {
host { host {
val context = LocalContext.current val context = LocalContext.current
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val preferences = LocalPreferences.current var isInvincibilityEnabled by rememberPreference(isInvincibilityEnabledKey, false)
var isIgnoringBatteryOptimizations by remember { var isIgnoringBatteryOptimizations by remember {
mutableStateOf(context.isIgnoringBatteryOptimizations) mutableStateOf(context.isIgnoringBatteryOptimizations)
@ -140,9 +140,9 @@ fun OtherSettingsScreen() {
SwitchSettingEntry( SwitchSettingEntry(
title = "Invincible service", title = "Invincible service",
text = "When turning off battery optimizations is not enough", text = "When turning off battery optimizations is not enough",
isChecked = preferences.isInvincibilityEnabled, isChecked = isInvincibilityEnabled,
onCheckedChange = { onCheckedChange = {
preferences.isInvincibilityEnabled = it isInvincibilityEnabled = it
} }
) )

View file

@ -10,6 +10,8 @@ import androidx.compose.foundation.*
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -20,10 +22,8 @@ import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.screens.* import it.vfsfitvnm.vimusic.ui.screens.*
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.LocalPreferences
import it.vfsfitvnm.vimusic.utils.semiBold
@ExperimentalAnimationApi @ExperimentalAnimationApi
@ -49,11 +49,13 @@ fun PlayerSettingsScreen() {
host { host {
val context = LocalContext.current val context = LocalContext.current
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val preferences = LocalPreferences.current
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
var persistentQueue by rememberPreference(persistentQueueKey, false)
var skipSilence by rememberPreference(skipSilenceKey, false)
var volumeNormalization by rememberPreference(volumeNormalizationKey, false)
val activityResultLauncher = val activityResultLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
} }
@ -96,9 +98,9 @@ fun PlayerSettingsScreen() {
SwitchSettingEntry( SwitchSettingEntry(
title = "Persistent queue", title = "Persistent queue",
text = "Save and restore playing songs", text = "Save and restore playing songs",
isChecked = preferences.persistentQueue, isChecked = persistentQueue,
onCheckedChange = { onCheckedChange = {
preferences.persistentQueue = it persistentQueue = it
} }
) )
@ -107,19 +109,18 @@ fun PlayerSettingsScreen() {
SwitchSettingEntry( SwitchSettingEntry(
title = "Skip silence", title = "Skip silence",
text = "Skip silent parts during playback", text = "Skip silent parts during playback",
isChecked = preferences.skipSilence, isChecked = skipSilence,
onCheckedChange = { onCheckedChange = {
binder?.player?.skipSilenceEnabled = it skipSilence = it
preferences.skipSilence = it
} }
) )
SwitchSettingEntry( SwitchSettingEntry(
title = "Loudness normalization", title = "Loudness normalization",
text = "Lower the volume to a standard level", text = "Lower the volume to a standard level",
isChecked = preferences.volumeNormalization, isChecked = volumeNormalization,
onCheckedChange = { onCheckedChange = {
preferences.volumeNormalization = it volumeNormalization = it
} }
) )

View file

@ -0,0 +1,12 @@
package it.vfsfitvnm.vimusic.ui.styling
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Shape
data class Appearance(
val colorPalette: ColorPalette,
val typography: Typography,
val thumbnailShape: Shape
)
val LocalAppearance = staticCompositionLocalOf<Appearance> { TODO() }

View file

@ -82,5 +82,3 @@ val LightColorPalette = ColorPalette(
// iconOnPrimaryContainer = Color(0xff2e30b8), // iconOnPrimaryContainer = Color(0xff2e30b8),
isDark = false isDark = false
) )
val LocalColorPalette = staticCompositionLocalOf { LightColorPalette }

View file

@ -23,53 +23,3 @@ data class Typography(
val m: TextStyle, val m: TextStyle,
val l: TextStyle, val l: TextStyle,
) )
val LocalTypography = staticCompositionLocalOf<Typography> { TODO() }
@Suppress("DEPRECATION")
@OptIn(ExperimentalTextApi::class)
@Composable
fun rememberTypography(color: Color): Typography {
return remember(color) {
TextStyle(
fontFamily = FontFamily(
Font(
resId = R.font.poppins_w300,
weight = FontWeight.Light
),
Font(
resId = R.font.poppins_w400,
weight = FontWeight.Normal
),
Font(
resId = R.font.poppins_w400_italic,
weight = FontWeight.Normal,
style = FontStyle.Italic
),
Font(
resId = R.font.poppins_w500,
weight = FontWeight.Medium
),
Font(
resId = R.font.poppins_w600,
weight = FontWeight.SemiBold
),
Font(
resId = R.font.poppins_w700,
weight = FontWeight.Bold
),
),
fontWeight = FontWeight.Normal,
color = color,
platformStyle = PlatformTextStyle(includeFontPadding = false)
).run {
Typography(
xxs = copy(fontSize = 12.sp),
xs = copy(fontSize = 14.sp),
s = copy(fontSize = 16.sp),
m = copy(fontSize = 18.sp),
l = copy(fontSize = 20.sp),
)
}
}
}

View file

@ -36,10 +36,7 @@ import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
import it.vfsfitvnm.vimusic.ui.components.MusicBars import it.vfsfitvnm.vimusic.ui.components.MusicBars
import it.vfsfitvnm.vimusic.ui.components.themed.QueuedMediaItemMenu import it.vfsfitvnm.vimusic.ui.components.themed.QueuedMediaItemMenu
import it.vfsfitvnm.vimusic.ui.screens.SmallSongItemShimmer import it.vfsfitvnm.vimusic.ui.screens.SmallSongItemShimmer
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.*
import it.vfsfitvnm.vimusic.ui.styling.LightColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.utils.PlayerState import it.vfsfitvnm.vimusic.utils.PlayerState
@ -53,7 +50,7 @@ fun CurrentPlaylistView(
) { ) {
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
val hapticFeedback = LocalHapticFeedback.current val hapticFeedback = LocalHapticFeedback.current
val colorPalette = LocalColorPalette.current val (colorPalette) = LocalAppearance.current
val thumbnailSize = Dimensions.thumbnails.song.px val thumbnailSize = Dimensions.thumbnails.song.px

View file

@ -19,7 +19,7 @@ import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.center import it.vfsfitvnm.vimusic.utils.center
import it.vfsfitvnm.vimusic.utils.secondary import it.vfsfitvnm.vimusic.utils.secondary
@ -32,7 +32,7 @@ fun LyricsView(
onLyricsUpdate: (String) -> Unit, onLyricsUpdate: (String) -> Unit,
nestedScrollConnectionProvider: () -> NestedScrollConnection, nestedScrollConnectionProvider: () -> NestedScrollConnection,
) { ) {
val typography = LocalTypography.current val (_, typography) = LocalAppearance.current
var isEditingLyrics by remember { var isEditingLyrics by remember {
mutableStateOf(false) mutableStateOf(false)

View file

@ -28,8 +28,7 @@ import it.vfsfitvnm.vimusic.ui.components.BottomSheet
import it.vfsfitvnm.vimusic.ui.components.BottomSheetState import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
import it.vfsfitvnm.vimusic.ui.components.HorizontalTabPager import it.vfsfitvnm.vimusic.ui.components.HorizontalTabPager
import it.vfsfitvnm.vimusic.ui.components.rememberTabPagerState import it.vfsfitvnm.vimusic.ui.components.rememberTabPagerState
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.PlayerState import it.vfsfitvnm.vimusic.utils.PlayerState
import it.vfsfitvnm.vimusic.utils.center import it.vfsfitvnm.vimusic.utils.center
import it.vfsfitvnm.vimusic.utils.color import it.vfsfitvnm.vimusic.utils.color
@ -50,8 +49,7 @@ fun PlayerBottomSheet(
onGlobalRouteEmitted: () -> Unit, onGlobalRouteEmitted: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()

View file

@ -63,8 +63,7 @@ fun PlayerView(
) { ) {
val menuState = LocalMenuState.current val menuState = LocalMenuState.current
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
val context = LocalContext.current val context = LocalContext.current
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
@ -318,7 +317,7 @@ private fun Thumbnail(
song: Song?, song: Song?,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val typography = LocalTypography.current val (_, typography) = LocalAppearance.current
val context = LocalContext.current val context = LocalContext.current
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
val player = binder?.player ?: return val player = binder?.player ?: return
@ -527,9 +526,7 @@ private fun Controls(
song: Song?, song: Song?,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val typography = LocalTypography.current val (colorPalette, typography) = LocalAppearance.current
val colorPalette = LocalColorPalette.current
val preferences = LocalPreferences.current
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
val player = binder?.player ?: return val player = binder?.player ?: return
@ -730,9 +727,8 @@ private fun Controls(
player.repeatMode player.repeatMode
.plus(2) .plus(2)
.mod(3) .mod(3)
.let { repeatMode -> .let {
player.repeatMode = repeatMode player.repeatMode = it
preferences.repeatMode = repeatMode
} }
} }
.weight(1f) .weight(1f)

View file

@ -23,8 +23,7 @@ import coil.compose.AsyncImage
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.models.PlaylistPreview import it.vfsfitvnm.vimusic.models.PlaylistPreview
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.color import it.vfsfitvnm.vimusic.utils.color
import it.vfsfitvnm.vimusic.utils.semiBold import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.thumbnail import it.vfsfitvnm.vimusic.utils.thumbnail
@ -38,8 +37,7 @@ fun PlaylistPreviewItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
thumbnailSize: Dp = Dimensions.thumbnails.song, thumbnailSize: Dp = Dimensions.thumbnails.song,
) { ) {
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
val density = LocalDensity.current val density = LocalDensity.current
val thumbnailSizePx = density.run { val thumbnailSizePx = density.run {

View file

@ -26,8 +26,7 @@ import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.models.DetailedSong import it.vfsfitvnm.vimusic.models.DetailedSong
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.secondary import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.thumbnail import it.vfsfitvnm.vimusic.utils.thumbnail
@ -150,8 +149,7 @@ fun SongItem(
trailingContent: (@Composable () -> Unit)? = null trailingContent: (@Composable () -> Unit)? = null
) { ) {
val menuState = LocalMenuState.current val menuState = LocalMenuState.current
val colorPalette = LocalColorPalette.current val (colorPalette, typography) = LocalAppearance.current
val typography = LocalTypography.current
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,

View file

@ -5,145 +5,24 @@ import android.content.SharedPreferences
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.core.content.edit import androidx.core.content.edit
import androidx.media3.common.Player
import it.vfsfitvnm.vimusic.enums.*
import it.vfsfitvnm.youtubemusic.YouTube
@Stable const val colorPaletteModeKey = "colorPaletteMode"
class Preferences( const val thumbnailRoundnessKey = "thumbnailRoundness"
private val edit: (action: SharedPreferences.Editor.() -> Unit) -> Unit, const val isCachedPlaylistShownKey = "isCachedPlaylistShown"
initialIsFirstLaunch: Boolean, const val coilDiskCacheMaxSizeKey = "coilDiskCacheMaxSize"
initialSongSortBy: SongSortBy, const val exoPlayerDiskCacheMaxSizeKey = "exoPlayerDiskCacheMaxSize"
initialSongSortOrder: SortOrder, const val isInvincibilityEnabledKey = "isInvincibilityEnabled"
initialColorPaletteMode: ColorPaletteMode, const val isFirstLaunchKey = "isFirstLaunch"
initialSearchFilter: String, const val songSortOrderKey = "songSortOrder"
initialRepeatMode: Int, const val songSortByKey = "songSortBy"
initialThumbnailRoundness: ThumbnailRoundness, const val searchFilterKey = "searchFilter"
initialCoilDiskCacheMaxSize: CoilDiskCacheMaxSize, const val repeatModeKey = "repeatMode"
initialExoPlayerDiskCacheMaxSize: ExoPlayerDiskCacheMaxSize, const val skipSilenceKey = "skipSilence"
initialSkipSilence: Boolean, const val volumeNormalizationKey = "volumeNormalization"
initialVolumeNormalization: Boolean, const val persistentQueueKey = "persistentQueue"
initialPersistentQueue: Boolean,
initialIsInvincibilityEnabled: Boolean,
initialIsCachedPlaylistShown: Boolean,
) {
constructor(preferences: SharedPreferences) : this(
edit = { action: SharedPreferences.Editor.() -> Unit ->
preferences.edit(action = action)
},
initialIsFirstLaunch = preferences.getBoolean(Keys.isFirstLaunch, true),
initialSongSortBy = preferences.getEnum(Keys.songSortBy, SongSortBy.DateAdded),
initialSongSortOrder = preferences.getEnum(Keys.songSortOrder, SortOrder.Descending),
initialColorPaletteMode = preferences.getEnum(Keys.colorPaletteMode, ColorPaletteMode.System),
initialSearchFilter = preferences.getString(Keys.searchFilter, YouTube.Item.Song.Filter.value)!!,
initialRepeatMode = preferences.getInt(Keys.repeatMode, Player.REPEAT_MODE_OFF),
initialThumbnailRoundness = preferences.getEnum(Keys.thumbnailRoundness, ThumbnailRoundness.Light),
initialCoilDiskCacheMaxSize = preferences.getEnum(Keys.coilDiskCacheMaxSize, CoilDiskCacheMaxSize.`128MB`),
initialExoPlayerDiskCacheMaxSize = preferences.getEnum(Keys.exoPlayerDiskCacheMaxSize, ExoPlayerDiskCacheMaxSize.`2GB`),
initialSkipSilence = preferences.getBoolean(Keys.skipSilence, false),
initialVolumeNormalization = preferences.getBoolean(Keys.volumeNormalization, false),
initialPersistentQueue = preferences.getBoolean(Keys.persistentQueue, false),
initialIsInvincibilityEnabled = preferences.getBoolean(Keys.isInvincibilityEnabled, false),
initialIsCachedPlaylistShown = preferences.getBoolean(Keys.isCachedPlaylistShown, false),
)
var isFirstLaunch = initialIsFirstLaunch inline fun <reified T : Enum<T>> SharedPreferences.getEnum(
set(value) = edit { putBoolean(Keys.isFirstLaunch, value) }
var songSortBy = initialSongSortBy
set(value) = edit { putEnum(Keys.songSortBy, value) }
var songSortOrder = initialSongSortOrder
set(value) = edit { putEnum(Keys.songSortOrder, value) }
var colorPaletteMode = initialColorPaletteMode
set(value) = edit { putEnum(Keys.colorPaletteMode, value) }
var searchFilter = initialSearchFilter
set(value) = edit { putString(Keys.searchFilter, value) }
var repeatMode = initialRepeatMode
set(value) = edit { putInt(Keys.repeatMode, value) }
var thumbnailRoundness = initialThumbnailRoundness
set(value) = edit { putEnum(Keys.thumbnailRoundness, value) }
var coilDiskCacheMaxSize = initialCoilDiskCacheMaxSize
set(value) = edit { putEnum(Keys.coilDiskCacheMaxSize, value) }
var exoPlayerDiskCacheMaxSize = initialExoPlayerDiskCacheMaxSize
set(value) = edit { putEnum(Keys.exoPlayerDiskCacheMaxSize, value) }
var skipSilence = initialSkipSilence
set(value) = edit { putBoolean(Keys.skipSilence, value) }
var volumeNormalization = initialVolumeNormalization
set(value) = edit { putBoolean(Keys.volumeNormalization, value) }
var persistentQueue = initialPersistentQueue
set(value) = edit { putBoolean(Keys.persistentQueue, value) }
var isInvincibilityEnabled = initialIsInvincibilityEnabled
set(value) = edit { putBoolean(Keys.isInvincibilityEnabled, value) }
var isCachedPlaylistShown = initialIsCachedPlaylistShown
set(value) = edit { putBoolean(Keys.isCachedPlaylistShown, value) }
object Keys {
const val isFirstLaunch = "isFirstLaunch"
const val songSortOrder = "songSortOrder"
const val songSortBy = "songSortBy"
const val colorPaletteMode = "colorPaletteMode"
const val searchFilter = "searchFilter"
const val repeatMode = "repeatMode"
const val thumbnailRoundness = "thumbnailRoundness"
const val coilDiskCacheMaxSize = "coilDiskCacheMaxSize"
const val exoPlayerDiskCacheMaxSize = "exoPlayerDiskCacheMaxSize"
const val skipSilence = "skipSilence"
const val volumeNormalization = "volumeNormalization"
const val persistentQueue = "persistentQueue"
const val isInvincibilityEnabled = "isInvincibilityEnabled"
const val isCachedPlaylistShown = "isCachedPlaylistShown"
}
companion object {
const val fileName = "preferences"
context(Context)
operator fun invoke() =
Preferences(getSharedPreferences(fileName, Context.MODE_PRIVATE))
}
}
val LocalPreferences = staticCompositionLocalOf<Preferences> { TODO() }
@Composable
fun rememberPreferences(): Preferences {
val context = LocalContext.current
var preferences by remember {
mutableStateOf(context.run { Preferences() })
}
DisposableEffect(Unit) {
val holder = context.getSharedPreferences(Preferences.fileName, Context.MODE_PRIVATE)
val listener =
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, _ ->
preferences = Preferences(sharedPreferences)
}
holder.registerOnSharedPreferenceChangeListener(listener)
onDispose {
holder.unregisterOnSharedPreferenceChangeListener(listener)
}
}
return preferences
}
private inline fun <reified T : Enum<T>> SharedPreferences.getEnum(
key: String, key: String,
defaultValue: T defaultValue: T
): T = ): T =
@ -155,6 +34,51 @@ private inline fun <reified T : Enum<T>> SharedPreferences.getEnum(
} }
} ?: defaultValue } ?: defaultValue
private inline fun <reified T : Enum<T>> SharedPreferences.Editor.putEnum(key: String, value: T) = inline fun <reified T : Enum<T>> SharedPreferences.Editor.putEnum(key: String, value: T) =
putString(key, value.name) putString(key, value.name)
val Context.preferences: SharedPreferences
get() = getSharedPreferences("preferences", Context.MODE_PRIVATE)
@Composable
fun rememberPreference(key: String, defaultValue: Boolean): MutableState<Boolean> {
val context = LocalContext.current
return remember {
mutableStatePreferenceOf(context.preferences.getBoolean(key, defaultValue)) {
context.preferences.edit { putBoolean(key, it) }
}
}
}
@Composable
fun rememberPreference(key: String, defaultValue: String): MutableState<String> {
val context = LocalContext.current
return remember {
mutableStatePreferenceOf(context.preferences.getString(key, null) ?: defaultValue) {
context.preferences.edit { putString(key, it) }
}
}
}
@Composable
inline fun <reified T: Enum<T>> rememberPreference(key: String, defaultValue: T): MutableState<T> {
val context = LocalContext.current
return remember {
mutableStatePreferenceOf(context.preferences.getEnum(key, defaultValue)) {
context.preferences.edit { putEnum(key, it) }
}
}
}
inline fun <T> mutableStatePreferenceOf(value: T, crossinline onStructuralInequality: (newValue: T) -> Unit) =
mutableStateOf(
value = value,
policy = object : SnapshotMutationPolicy<T> {
override fun equivalent(a: T, b: T): Boolean {
val areEquals = a == b
if (!areEquals) onStructuralInequality(b)
return areEquals
}
})

View file

@ -7,7 +7,7 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
fun TextStyle.style(style: FontStyle) = copy(fontStyle = style) fun TextStyle.style(style: FontStyle) = copy(fontStyle = style)
@ -35,9 +35,9 @@ inline val TextStyle.center: TextStyle
inline val TextStyle.secondary: TextStyle inline val TextStyle.secondary: TextStyle
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
get() = color(LocalColorPalette.current.textSecondary) get() = color(LocalAppearance.current.colorPalette.textSecondary)
inline val TextStyle.disabled: TextStyle inline val TextStyle.disabled: TextStyle
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
get() = color(LocalColorPalette.current.textDisabled) get() = color(LocalAppearance.current.colorPalette.textDisabled)