diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2494a35..1cc5b4a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -86,6 +86,7 @@ dependencies { implementation(libs.compose.coil) implementation(libs.accompanist.systemuicontroller) + implementation(libs.palette) implementation(libs.exoplayer) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt index d33dcdb..20d98b2 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.content.ServiceConnection import android.content.SharedPreferences +import android.graphics.Bitmap import android.net.Uri import android.os.Bundle import android.os.IBinder @@ -34,6 +35,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.neverEqualPolicy import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Alignment @@ -47,6 +49,7 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.valentinilk.shimmer.LocalShimmerTheme import com.valentinilk.shimmer.defaultShimmerTheme import it.vfsfitvnm.vimusic.enums.ColorPaletteMode +import it.vfsfitvnm.vimusic.enums.ColorPaletteName import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness import it.vfsfitvnm.vimusic.service.PlayerService import it.vfsfitvnm.vimusic.ui.components.BottomSheetMenu @@ -58,14 +61,21 @@ 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.LocalAppearance +import it.vfsfitvnm.vimusic.ui.styling.colorPaletteOf +import it.vfsfitvnm.vimusic.ui.styling.dynamicColorPaletteOf +import it.vfsfitvnm.vimusic.ui.styling.typographyOf import it.vfsfitvnm.vimusic.ui.views.PlayerView import it.vfsfitvnm.vimusic.utils.colorPaletteModeKey +import it.vfsfitvnm.vimusic.utils.colorPaletteNameKey import it.vfsfitvnm.vimusic.utils.getEnum import it.vfsfitvnm.vimusic.utils.intent import it.vfsfitvnm.vimusic.utils.listener import it.vfsfitvnm.vimusic.utils.preferences import it.vfsfitvnm.vimusic.utils.rememberHapticFeedback import it.vfsfitvnm.vimusic.utils.thumbnailRoundnessKey +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { private val serviceConnection = object : ServiceConnection { @@ -103,36 +113,84 @@ class MainActivity : ComponentActivity() { uri = intent?.data setContent { + val coroutineScope = rememberCoroutineScope() val isSystemInDarkTheme = isSystemInDarkTheme() var appearance by remember(isSystemInDarkTheme) { with(preferences) { + val colorPaletteName = getEnum(colorPaletteNameKey, ColorPaletteName.Dynamic) val colorPaletteMode = getEnum(colorPaletteModeKey, ColorPaletteMode.System) val thumbnailRoundness = getEnum(thumbnailRoundnessKey, ThumbnailRoundness.Light) + val colorPalette = + colorPaletteOf(colorPaletteName, colorPaletteMode, isSystemInDarkTheme) + mutableStateOf( Appearance( - colorPalette = colorPaletteMode.palette(isSystemInDarkTheme), - typography = colorPaletteMode.typography(isSystemInDarkTheme), + colorPalette = colorPalette, + typography = typographyOf(colorPalette.text), thumbnailShape = thumbnailRoundness.shape() ) ) } } - DisposableEffect(isSystemInDarkTheme) { + DisposableEffect(binder, isSystemInDarkTheme) { + var bitmapListenerJob: Job? = null + + fun setDynamicPalette(colorPaletteMode: ColorPaletteMode) { + val isDark = + colorPaletteMode == ColorPaletteMode.Dark || (colorPaletteMode == ColorPaletteMode.System && isSystemInDarkTheme) + + binder?.setBitmapListener { bitmap: Bitmap? -> + if (bitmap == null) { + val colorPalette = + colorPaletteOf(ColorPaletteName.Dynamic, colorPaletteMode, isSystemInDarkTheme) + + appearance = appearance.copy( + colorPalette = colorPalette, + typography = typographyOf(colorPalette.text) + ) + + return@setBitmapListener + } + + bitmapListenerJob = coroutineScope.launch(Dispatchers.IO) { + dynamicColorPaletteOf(bitmap, isDark)?.let { + appearance = appearance.copy(colorPalette = it, typography = typographyOf(it.text)) + } + } + } + } + val listener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key -> when (key) { - colorPaletteModeKey -> { - val colorPaletteMode = - sharedPreferences.getEnum(key, ColorPaletteMode.System) + colorPaletteNameKey, colorPaletteModeKey -> { + val colorPaletteName = + sharedPreferences.getEnum(colorPaletteNameKey, ColorPaletteName.Dynamic) - appearance = appearance.copy( - colorPalette = colorPaletteMode.palette(isSystemInDarkTheme), - typography = colorPaletteMode.typography(isSystemInDarkTheme), - ) + val colorPaletteMode = + sharedPreferences.getEnum(colorPaletteModeKey, ColorPaletteMode.System) + + if (colorPaletteName == ColorPaletteName.Dynamic) { + setDynamicPalette(colorPaletteMode) + } else { + bitmapListenerJob?.cancel() + binder?.setBitmapListener(null) + + val colorPalette = colorPaletteOf( + colorPaletteName, + colorPaletteMode, + isSystemInDarkTheme + ) + + appearance = appearance.copy( + colorPalette = colorPalette, + typography = typographyOf(colorPalette.text), + ) + } } thumbnailRoundnessKey -> { val thumbnailRoundness = @@ -148,7 +206,14 @@ class MainActivity : ComponentActivity() { with(preferences) { registerOnSharedPreferenceChangeListener(listener) + val colorPaletteName = getEnum(colorPaletteNameKey, ColorPaletteName.Dynamic) + if (colorPaletteName == ColorPaletteName.Dynamic) { + setDynamicPalette(getEnum(colorPaletteModeKey, ColorPaletteMode.System)) + } + onDispose { + bitmapListenerJob?.cancel() + binder?.setBitmapListener(null) unregisterOnSharedPreferenceChangeListener(listener) } } @@ -193,7 +258,7 @@ class MainActivity : ComponentActivity() { SideEffect { systemUiController.setSystemBarsColor( - appearance.colorPalette.background, + appearance.colorPalette.background1, !appearance.colorPalette.isDark ) } @@ -211,7 +276,7 @@ class MainActivity : ComponentActivity() { BoxWithConstraints( modifier = Modifier .fillMaxSize() - .background(appearance.colorPalette.background) + .background(appearance.colorPalette.background0) ) { when (val uri = uri) { null -> { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/ColorPaletteMode.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/ColorPaletteMode.kt index 1dc1af3..63bcc3b 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/ColorPaletteMode.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/ColorPaletteMode.kt @@ -1,81 +1,7 @@ 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.ColorPalette -import it.vfsfitvnm.vimusic.ui.styling.DarkColorPalette -import it.vfsfitvnm.vimusic.ui.styling.LightColorPalette -import it.vfsfitvnm.vimusic.ui.styling.Typography - enum class ColorPaletteMode { Light, Dark, - Black, - System; - - fun palette(isSystemInDarkMode: Boolean): ColorPalette { - return when (this) { - Light -> LightColorPalette - Dark -> DarkColorPalette - Black -> BlackColorPalette - System -> when (isSystemInDarkMode) { - true -> DarkColorPalette - false -> LightColorPalette - } - } - } - - @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), - ) - } + System } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/ColorPaletteName.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/ColorPaletteName.kt new file mode 100644 index 0000000..49ec459 --- /dev/null +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/ColorPaletteName.kt @@ -0,0 +1,7 @@ +package it.vfsfitvnm.vimusic.enums + +enum class ColorPaletteName { + Default, + Dynamic, + PureBlack +} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/BitmapProvider.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/BitmapProvider.kt index 1eb7b13..10262a4 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/BitmapProvider.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/BitmapProvider.kt @@ -17,7 +17,7 @@ class BitmapProvider( private val colorProvider: (isSystemInDarkMode: Boolean) -> Int ) { private var lastUri: Uri? = null - private var lastBitmap: Bitmap? = null + var lastBitmap: Bitmap? = null private var lastIsSystemInDarkMode = false private var lastEnqueued: Disposable? = null @@ -27,6 +27,12 @@ class BitmapProvider( val bitmap: Bitmap get() = lastBitmap ?: defaultBitmap + var listener: ((Bitmap?) -> Unit)? = null + set(value) { + field = value + value?.invoke(lastBitmap) + } + init { setDefaultBitmap() } @@ -56,14 +62,18 @@ class BitmapProvider( lastEnqueued = applicationContext.imageLoader.enqueue( ImageRequest.Builder(applicationContext) .data(uri.thumbnail(bitmapSize)) + .allowHardware(false) .listener( onError = { _, _ -> lastBitmap = null onDone(bitmap) + listener?.invoke(lastBitmap) }, onSuccess = { _, result -> lastBitmap = (result.drawable as BitmapDrawable).bitmap onDone(bitmap) + println("invoking listener") + listener?.invoke(lastBitmap) } ) .build() diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt index 5d8de1d..e05e34c 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt @@ -11,6 +11,7 @@ import android.content.Intent import android.content.IntentFilter import android.content.SharedPreferences import android.content.res.Configuration +import android.graphics.Bitmap import android.graphics.Color import android.media.MediaMetadata import android.media.session.MediaSession @@ -680,6 +681,13 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene var isLoadingRadio by mutableStateOf(false) private set + val lastBitmap: Bitmap? + get() = bitmapProvider.lastBitmap + + fun setBitmapListener(listener: ((Bitmap?) -> Unit)?) { + bitmapProvider.listener = listener + } + fun startSleepTimer(delayMillis: Long) { timerJob?.cancel() diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Dialog.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Dialog.kt index a29cd3e..40471e8 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Dialog.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Dialog.kt @@ -153,9 +153,9 @@ fun TextFieldDialog( ) ChunkyButton( - backgroundColor = colorPalette.primaryContainer, + backgroundColor = colorPalette.accent, text = doneText, - textStyle = typography.xs.semiBold.color(colorPalette.onPrimaryContainer), + textStyle = typography.xs.semiBold.color(colorPalette.onAccent), shape = RoundedCornerShape(36.dp), onClick = { if (isTextInputValid(textFieldValue.text)) { @@ -210,9 +210,9 @@ fun ConfirmationDialog( ) ChunkyButton( - backgroundColor = colorPalette.primaryContainer, + backgroundColor = colorPalette.accent, text = confirmText, - textStyle = typography.xs.semiBold.color(colorPalette.onPrimaryContainer), + textStyle = typography.xs.semiBold.color(colorPalette.onAccent), shape = RoundedCornerShape(36.dp), onClick = { onConfirm() @@ -242,7 +242,7 @@ inline fun DefaultDialog( modifier = modifier .padding(all = 48.dp) .background( - color = colorPalette.elevatedBackground, + color = colorPalette.background1, shape = RoundedCornerShape(8.dp) ) .padding(horizontal = 24.dp, vertical = 16.dp), @@ -268,7 +268,7 @@ inline fun ValueSelectorDialog( modifier = modifier .padding(all = 48.dp) .background( - color = colorPalette.elevatedBackground, + color = colorPalette.background1, shape = RoundedCornerShape(8.dp) ) .padding(vertical = 16.dp), @@ -305,12 +305,12 @@ inline fun ValueSelectorDialog( modifier = Modifier .size(18.dp) .background( - color = colorPalette.primaryContainer, + color = colorPalette.accent, shape = CircleShape ) ) { drawCircle( - color = colorPalette.onPrimaryContainer, + color = colorPalette.onAccent, radius = 4.dp.toPx(), center = size.center, shadow = Shadow( diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/DropDownSection.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/DropDownSection.kt index fcaa883..6ce771e 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/DropDownSection.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/DropDownSection.kt @@ -35,7 +35,7 @@ fun DropDownSection(content: @Composable ColumnScope.() -> Unit) { elevation = 2.dp, shape = RoundedCornerShape(16.dp) ) - .background(colorPalette.elevatedBackground) + .background(colorPalette.background1) .width(IntrinsicSize.Max), content = content ) @@ -60,14 +60,14 @@ fun DropDownTextItem( DropDownTextItem( text = text, textColor = if (isSelected) { - colorPalette.onPrimaryContainer + colorPalette.onAccent } else { colorPalette.textSecondary }, backgroundColor = if (isSelected) { - colorPalette.primaryContainer + colorPalette.accent } else { - colorPalette.elevatedBackground + colorPalette.background1 }, onClick = onClick ) @@ -95,7 +95,7 @@ fun DropDownTextItem( interactionSource = remember { MutableInteractionSource() }, onClick = onClick ) - .background(backgroundColor ?: colorPalette.elevatedBackground) + .background(backgroundColor ?: colorPalette.background1) .fillMaxWidth() .widthIn(min = 124.dp, max = 248.dp) .padding( diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/MediaItemMenu.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/MediaItemMenu.kt index a528d12..92aa38e 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/MediaItemMenu.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/MediaItemMenu.kt @@ -536,9 +536,9 @@ fun MediaItemMenu( ) ChunkyButton( - backgroundColor = colorPalette.primaryContainer, + backgroundColor = colorPalette.accent, text = "Set", - textStyle = typography.xs.semiBold.color(colorPalette.onPrimaryContainer), + textStyle = typography.xs.semiBold.color(colorPalette.onAccent), shape = RoundedCornerShape(36.dp), isEnabled = hours > 0 || minutes > 0, onClick = { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Menu.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Menu.kt index 2f7f8ca..f2f2a20 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Menu.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Menu.kt @@ -44,7 +44,7 @@ inline fun Menu( .verticalScroll(rememberScrollState()) .fillMaxWidth() .background( - color = colorPalette.elevatedBackground, + color = colorPalette.background1, shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp) ) .padding(top = 8.dp) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Switch.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Switch.kt index 2e5effb..5ba9df7 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Switch.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Switch.kt @@ -26,8 +26,8 @@ fun Switch( ) { val (colorPalette) = LocalAppearance.current - val backgroundColor by animateColorAsState(if (isChecked) colorPalette.primaryContainer else colorPalette.lightBackground) - val color by animateColorAsState(if (isChecked) colorPalette.onPrimaryContainer else colorPalette.textDisabled) + val backgroundColor by animateColorAsState(if (isChecked) colorPalette.accent else colorPalette.background1) + val color by animateColorAsState(if (isChecked) colorPalette.onAccent else colorPalette.textDisabled) val offset by animateDpAsState(if (isChecked) 36.dp else 12.dp) Spacer( diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/TextCard.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/TextCard.kt index ee24e61..d87311a 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/TextCard.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/TextCard.kt @@ -13,6 +13,7 @@ import androidx.compose.material.ripple.rememberRipple import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign @@ -41,14 +42,14 @@ fun TextCard( enabled = onClick != null, onClick = onClick ?: {} ) - .background(colorPalette.lightBackground) + .background(colorPalette.background1) .padding(horizontal = 16.dp, vertical = 16.dp) ) { icon?.let { Image( painter = painterResource(icon), contentDescription = null, - colorFilter = iconColor ?: ColorFilter.tint(colorPalette.red), + colorFilter = iconColor ?: ColorFilter.tint(Color.Red), modifier = Modifier .padding(bottom = 16.dp) .size(24.dp) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/TextPlaceholder.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/TextPlaceholder.kt index 9cb21e0..f747e91 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/TextPlaceholder.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/TextPlaceholder.kt @@ -5,23 +5,24 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance +import it.vfsfitvnm.vimusic.ui.styling.shimmer import kotlin.random.Random @Composable fun TextPlaceholder( - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + color: Color = LocalAppearance.current.colorPalette.shimmer ) { - val (colorPalette) = LocalAppearance.current Spacer( modifier = modifier .padding(vertical = 4.dp) - .background(color = colorPalette.darkGray, shape = RoundedCornerShape(0.dp)) + .background(color) .fillMaxWidth(remember { 0.25f + Random.nextFloat() * 0.5f }) .height(16.dp) ) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/AlbumScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/AlbumScreen.kt index d93eb9c..1135cc2 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/AlbumScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/AlbumScreen.kt @@ -59,6 +59,7 @@ import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.ui.styling.px +import it.vfsfitvnm.vimusic.ui.styling.shimmer import it.vfsfitvnm.vimusic.ui.views.SongItem import it.vfsfitvnm.vimusic.utils.asMediaItem import it.vfsfitvnm.vimusic.utils.bold @@ -110,7 +111,7 @@ fun AlbumScreen(browseId: String) { state = lazyListState, contentPadding = PaddingValues(bottom = 72.dp), modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() ) { item { @@ -377,7 +378,7 @@ private fun LoadingOrError( ) { Spacer( modifier = Modifier - .background(color = colorPalette.darkGray, shape = ThumbnailRoundness.shape) + .background(color = colorPalette.shimmer, shape = ThumbnailRoundness.shape) .size(Dimensions.thumbnails.album) ) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/ArtistScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/ArtistScreen.kt index 125fe73..f2e44e2 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/ArtistScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/ArtistScreen.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.center +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign @@ -53,6 +54,7 @@ import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.ui.styling.px +import it.vfsfitvnm.vimusic.ui.styling.shimmer import it.vfsfitvnm.vimusic.ui.views.SongItem import it.vfsfitvnm.vimusic.utils.asMediaItem import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex @@ -102,7 +104,7 @@ fun ArtistScreen(browseId: String) { contentPadding = PaddingValues(bottom = 72.dp), horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() ) { item { @@ -213,7 +215,7 @@ fun ArtistScreen(browseId: String) { verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .zIndex(1f) .fillMaxWidth() .padding(horizontal = 8.dp) @@ -271,7 +273,7 @@ fun ArtistScreen(browseId: String) { Column( verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxWidth() .padding(horizontal = 8.dp) .padding(top = 32.dp) @@ -295,14 +297,14 @@ fun ArtistScreen(browseId: String) { .width(48.dp) ) { drawLine( - color = colorPalette.backgroundContainer, + color = colorPalette.background2, start = size.center.copy(y = 0f), end = size.center.copy(y = size.height), strokeWidth = 2.dp.toPx() ) drawCircle( - color = colorPalette.backgroundContainer, + color = colorPalette.background2, center = size.center.copy(y = size.height), radius = 4.dp.toPx() ) @@ -340,7 +342,7 @@ private fun LoadingOrError( ) { Spacer( modifier = Modifier - .background(color = colorPalette.darkGray, shape = CircleShape) + .background(color = colorPalette.shimmer, shape = CircleShape) .size(Dimensions.thumbnails.artist) ) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/BuiltInPlaylistScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/BuiltInPlaylistScreen.kt index 1f30c89..8bd8389 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/BuiltInPlaylistScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/BuiltInPlaylistScreen.kt @@ -85,7 +85,7 @@ fun BuiltInPlaylistScreen(builtInPlaylist: BuiltInPlaylist) { state = lazyListState, contentPadding = PaddingValues(bottom = Dimensions.collapsedPlayer), modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() ) { item { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/HomeScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/HomeScreen.kt index c49ece5..f479e1a 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/HomeScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/HomeScreen.kt @@ -38,25 +38,15 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.center import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.ClipOp import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.Paint -import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.Shadow -import androidx.compose.ui.graphics.asAndroidPath -import androidx.compose.ui.graphics.drawscope.clipPath -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex -import androidx.core.content.res.ResourcesCompat import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder @@ -79,6 +69,8 @@ import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance +import it.vfsfitvnm.vimusic.ui.styling.onOverlay +import it.vfsfitvnm.vimusic.ui.styling.overlay import it.vfsfitvnm.vimusic.ui.styling.px import it.vfsfitvnm.vimusic.ui.views.BuiltInPlaylistItem import it.vfsfitvnm.vimusic.ui.views.PlaylistPreviewItem @@ -186,7 +178,6 @@ fun HomeScreen() { @Suppress("UNUSED_EXPRESSION") playlistPreviews @Suppress("UNUSED_EXPRESSION") songCollection - val context = LocalContext.current val binder = LocalPlayerServiceBinder.current val isFirstLaunch by rememberPreference(isFirstLaunchKey, true) @@ -215,7 +206,7 @@ fun HomeScreen() { state = lazyListState, contentPadding = PaddingValues(bottom = 72.dp), modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() ) { item("topAppBar") { @@ -228,22 +219,20 @@ fun HomeScreen() { contentDescription = null, colorFilter = ColorFilter.tint(colorPalette.text), modifier = Modifier - .clickable { - settingsRoute() - } + .clickable { settingsRoute() } .padding(horizontal = 16.dp, vertical = 8.dp) .run { if (isFirstLaunch) { drawBehind { drawCircle( - color = colorPalette.red, + color = colorPalette.accent, center = Offset( x = size.width, y = 0.dp.toPx() ), radius = 4.dp.toPx(), shadow = Shadow( - color = colorPalette.red, + color = colorPalette.accent, blurRadius = 4.dp.toPx() ) ) @@ -255,88 +244,12 @@ fun HomeScreen() { .size(24.dp) ) - BasicText( - text = "ViMusic", - style = typography.l.semiBold, - modifier = Modifier - .drawWithCache { - val decorationPath = Path().apply { - with(asAndroidPath()) { - addCircle( - 8.dp.toPx(), - size.center.y, - 16.dp.toPx(), - android.graphics.Path.Direction.CCW - ) - addCircle( - 32.dp.toPx(), - -2.dp.toPx(), - 8.dp.toPx(), - android.graphics.Path.Direction.CCW - ) - } - } - - if (colorPalette.isDark) { - return@drawWithCache onDrawBehind { - drawPath( - path = decorationPath, - color = colorPalette.primaryContainer - ) - } - } - - val textPaint = Paint() - .asFrameworkPaint() - .apply { - isAntiAlias = true - textSize = typography.l.fontSize.toPx() - color = colorPalette.text.toArgb() - typeface = ResourcesCompat.getFont( - context, - R.font.poppins_w500 - ) - textAlign = android.graphics.Paint.Align.CENTER - } - - val textY = - ((textPaint.fontMetrics.descent - textPaint.fontMetrics.ascent) / 2) - textPaint.fontMetrics.descent - - val textPath = Path().apply { - textPaint.getTextPath( - "ViMusic", - 0, - 7, - size.width / 2, - size.height / 2 + textY, - asAndroidPath() - ) - } - - onDrawWithContent { - clipPath(textPath, ClipOp.Difference) { - drawPath( - path = decorationPath, - color = colorPalette.primaryContainer - ) - } - - clipPath(decorationPath, ClipOp.Difference) { - this@onDrawWithContent.drawContent() - } - } - } - .padding(horizontal = 8.dp) - ) - Image( painter = painterResource(R.drawable.search), contentDescription = null, colorFilter = ColorFilter.tint(colorPalette.text), modifier = Modifier - .clickable { - searchRoute("") - } + .clickable { searchRoute("") } .padding(horizontal = 16.dp, vertical = 8.dp) .size(24.dp) ) @@ -364,9 +277,7 @@ fun HomeScreen() { contentDescription = null, colorFilter = ColorFilter.tint(colorPalette.text), modifier = Modifier - .clickable { - isCreatingANewPlaylist = true - } + .clickable { isCreatingANewPlaylist = true } .padding(all = 8.dp) .size(20.dp) ) @@ -381,18 +292,14 @@ fun HomeScreen() { contentDescription = null, colorFilter = ColorFilter.tint(colorPalette.text), modifier = Modifier - .clickable { - isSortMenuDisplayed = true - } + .clickable { isSortMenuDisplayed = true } .padding(horizontal = 8.dp, vertical = 8.dp) .size(20.dp) ) DropdownMenu( isDisplayed = isSortMenuDisplayed, - onDismissRequest = { - isSortMenuDisplayed = false - } + onDismissRequest = { isSortMenuDisplayed = false } ) { DropDownSection { DropDownTextItem( @@ -520,7 +427,7 @@ fun HomeScreen() { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .zIndex(1f) .padding(horizontal = 8.dp) .padding(top = 32.dp) @@ -649,17 +556,14 @@ fun HomeScreen() { ) { BasicText( text = song.formattedTotalPlayTime, - style = typography.xxs.semiBold.center.color(Color.White), + style = typography.xxs.semiBold.center.color(colorPalette.onOverlay), maxLines = 2, overflow = TextOverflow.Ellipsis, modifier = Modifier .fillMaxWidth() .background( brush = Brush.verticalGradient( - colors = listOf( - Color.Transparent, - Color.Black.copy(alpha = 0.75f) - ) + colors = listOf(Color.Transparent, colorPalette.overlay) ), shape = ThumbnailRoundness.shape ) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/IntentUriScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/IntentUriScreen.kt index 3d51e79..a0a645d 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/IntentUriScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/IntentUriScreen.kt @@ -146,7 +146,7 @@ fun IntentUriScreen(uri: Uri) { horizontalAlignment = Alignment.CenterHorizontally, contentPadding = PaddingValues(bottom = Dimensions.collapsedPlayer), modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() ) { item { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/LocalPlaylistScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/LocalPlaylistScreen.kt index 4b6b2c9..b8b8ab5 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/LocalPlaylistScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/LocalPlaylistScreen.kt @@ -128,7 +128,7 @@ fun LocalPlaylistScreen(playlistId: Long) { state = lazyListState, contentPadding = PaddingValues(bottom = Dimensions.collapsedPlayer), modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() ) { item { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/PlaylistScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/PlaylistScreen.kt index 105740f..a897e35 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/PlaylistScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/PlaylistScreen.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext @@ -59,6 +60,7 @@ import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.ui.styling.px +import it.vfsfitvnm.vimusic.ui.styling.shimmer import it.vfsfitvnm.vimusic.ui.views.SongItem import it.vfsfitvnm.vimusic.utils.bold import it.vfsfitvnm.vimusic.utils.center @@ -109,7 +111,7 @@ fun PlaylistScreen(browseId: String) { state = lazyListState, contentPadding = PaddingValues(bottom = 72.dp), modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() ) { item { @@ -396,7 +398,7 @@ private fun LoadingOrError( ) { Spacer( modifier = Modifier - .background(color = colorPalette.darkGray, shape = ThumbnailRoundness.shape) + .background(color = colorPalette.shimmer, shape = ThumbnailRoundness.shape) .size(Dimensions.thumbnails.playlist) ) @@ -434,7 +436,7 @@ private fun LoadingOrError( Spacer( modifier = Modifier .size(8.dp) - .background(color = colorPalette.darkGray, shape = CircleShape) + .background(color = Color.Black, shape = CircleShape) ) } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SearchResultScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SearchResultScreen.kt index 64fcf18..7affd5d 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SearchResultScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SearchResultScreen.kt @@ -54,6 +54,7 @@ import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.ui.styling.px +import it.vfsfitvnm.vimusic.ui.styling.shimmer import it.vfsfitvnm.vimusic.ui.views.SongItem import it.vfsfitvnm.vimusic.utils.asMediaItem import it.vfsfitvnm.vimusic.utils.center @@ -129,7 +130,7 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) { horizontalAlignment = Alignment.CenterHorizontally, contentPadding = PaddingValues(bottom = Dimensions.collapsedPlayer), modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() ) { item { @@ -200,9 +201,9 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) { ), ), value = searchFilter, - selectedBackgroundColor = colorPalette.primaryContainer, - unselectedBackgroundColor = colorPalette.lightBackground, - selectedTextStyle = typography.xs.medium.color(colorPalette.onPrimaryContainer), + selectedBackgroundColor = colorPalette.accent, + unselectedBackgroundColor = colorPalette.background1, + selectedTextStyle = typography.xs.medium.color(colorPalette.onAccent), unselectedTextStyle = typography.xs.medium, shape = RoundedCornerShape(36.dp), onValueChanged = { @@ -281,7 +282,7 @@ fun SmallSongItemShimmer( thumbnailSizeDp: Dp, modifier: Modifier = Modifier ) { - val (colorPalette) = LocalAppearance.current + val (colorPalette, _, thumbnailShape) = LocalAppearance.current Row( verticalAlignment = Alignment.CenterVertically, @@ -290,7 +291,7 @@ fun SmallSongItemShimmer( ) { Spacer( modifier = Modifier - .background(color = colorPalette.darkGray, shape = ThumbnailRoundness.shape) + .background(color = colorPalette.shimmer, shape = thumbnailShape) .size(thumbnailSizeDp) ) @@ -315,7 +316,7 @@ fun SmallArtistItemShimmer( ) { Spacer( modifier = Modifier - .background(color = colorPalette.darkGray, shape = CircleShape) + .background(color = colorPalette.shimmer, shape = CircleShape) .size(thumbnailSizeDp) ) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SearchScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SearchScreen.kt index 36544bd..3d96f20 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SearchScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SearchScreen.kt @@ -41,6 +41,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.res.painterResource @@ -194,7 +195,7 @@ fun SearchScreen(initialTextInput: String, onSearch: (String) -> Unit, onUri: (U } .padding(horizontal = 14.dp, vertical = 6.dp) .background( - color = colorPalette.lightBackground, + color = colorPalette.background1, shape = CircleShape ) .size(28.dp) @@ -228,13 +229,13 @@ fun SearchScreen(initialTextInput: String, onSearch: (String) -> Unit, onUri: (U } ) .fillMaxWidth() - .background(colorPalette.lightBackground) + .background(colorPalette.background1) .padding(vertical = 16.dp, horizontal = 8.dp) ) { Image( painter = painterResource(R.drawable.link), contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette.darkGray), + colorFilter = ColorFilter.tint( Color.Black), modifier = Modifier .padding(horizontal = 8.dp) .size(20.dp) @@ -262,17 +263,16 @@ fun SearchScreen(initialTextInput: String, onSearch: (String) -> Unit, onUri: (U modifier = Modifier .clickable( indication = rememberRipple(bounded = true), - interactionSource = remember { MutableInteractionSource() } - ) { - onSearch(searchQuery.query) - } + interactionSource = remember { MutableInteractionSource() }, + onClick = { onSearch(searchQuery.query) } + ) .fillMaxWidth() .padding(vertical = 16.dp, horizontal = 8.dp) ) { Image( painter = painterResource(R.drawable.time), contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette.darkGray), + colorFilter = ColorFilter.tint(colorPalette.textDisabled), modifier = Modifier .padding(horizontal = 8.dp) .size(20.dp) @@ -289,7 +289,7 @@ fun SearchScreen(initialTextInput: String, onSearch: (String) -> Unit, onUri: (U Image( painter = painterResource(R.drawable.close), contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette.darkGray), + colorFilter = ColorFilter.tint(colorPalette.textDisabled), modifier = Modifier .clickable { query { @@ -303,7 +303,7 @@ fun SearchScreen(initialTextInput: String, onSearch: (String) -> Unit, onUri: (U Image( painter = painterResource(R.drawable.arrow_forward), contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette.darkGray), + colorFilter = ColorFilter.tint(colorPalette.textDisabled), modifier = Modifier .clickable { textFieldValue = TextFieldValue( @@ -325,10 +325,9 @@ fun SearchScreen(initialTextInput: String, onSearch: (String) -> Unit, onUri: (U modifier = Modifier .clickable( indication = rememberRipple(bounded = true), - interactionSource = remember { MutableInteractionSource() } - ) { - onSearch(suggestion) - } + interactionSource = remember { MutableInteractionSource() }, + onClick = { onSearch(suggestion) } + ) .fillMaxWidth() .padding(vertical = 16.dp, horizontal = 8.dp) ) { @@ -346,11 +345,10 @@ fun SearchScreen(initialTextInput: String, onSearch: (String) -> Unit, onUri: (U .weight(1f) ) - Image( painter = painterResource(R.drawable.arrow_forward), contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette.darkGray), + colorFilter = ColorFilter.tint(Color.Black), modifier = Modifier .clickable { textFieldValue = TextFieldValue( diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SettingsScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SettingsScreen.kt index ecb7db8..c6088b0 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SettingsScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SettingsScreen.kt @@ -76,7 +76,7 @@ fun SettingsScreen() { Column( modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() .verticalScroll(scrollState) .padding(bottom = 72.dp) @@ -137,10 +137,10 @@ fun SettingsScreen() { Image( painter = painterResource(icon), contentDescription = null, - colorFilter = ColorFilter.tint(colorPalette.background), + colorFilter = ColorFilter.tint(colorPalette.text), modifier = Modifier .align(Alignment.Center) - .size(18.dp) + .size(16.dp) ) } @@ -166,11 +166,11 @@ fun SettingsScreen() { .size(8.dp) ) { drawCircle( - color = colorPalette.red, + color = colorPalette.accent, center = size.center.copy(x = size.width), radius = 4.dp.toPx(), shadow = Shadow( - color = colorPalette.red, + color = colorPalette.accent, blurRadius = 4.dp.toPx() ) ) @@ -180,7 +180,7 @@ fun SettingsScreen() { } Entry( - color = colorPalette.magenta, + color = colorPalette.background2, icon = R.drawable.color_palette, title = "Appearance", description = "Change the colors and shapes", @@ -188,7 +188,7 @@ fun SettingsScreen() { ) Entry( - color = colorPalette.blue, + color = colorPalette.background2, icon = R.drawable.play, title = "Player & Audio", description = "Player and audio settings", @@ -196,7 +196,7 @@ fun SettingsScreen() { ) Entry( - color = colorPalette.cyan, + color = colorPalette.background2, icon = R.drawable.server, title = "Cache", description = "Manage the used space", @@ -204,7 +204,7 @@ fun SettingsScreen() { ) Entry( - color = colorPalette.orange, + color = colorPalette.background2, icon = R.drawable.save, title = "Backup & Restore", description = "Backup and restore the database", @@ -212,7 +212,7 @@ fun SettingsScreen() { ) Entry( - color = colorPalette.green, + color = colorPalette.background2, icon = R.drawable.shapes, title = "Other", description = "Advanced settings", @@ -224,7 +224,7 @@ fun SettingsScreen() { ) Entry( - color = colorPalette.magenta, + color = colorPalette.background2, icon = R.drawable.information, title = "About", description = "App version and social links", @@ -241,6 +241,7 @@ inline fun > EnumValueSelectorSettingsEntry( selectedValue: T, crossinline onValueSelected: (T) -> Unit, modifier: Modifier = Modifier, + isEnabled: Boolean = true, crossinline valueText: (T) -> String = Enum::name ) { ValueSelectorSettingsEntry( @@ -249,6 +250,7 @@ inline fun > EnumValueSelectorSettingsEntry( values = enumValues().toList(), onValueSelected = onValueSelected, modifier = modifier, + isEnabled = isEnabled, valueText = valueText ) } @@ -260,6 +262,7 @@ inline fun ValueSelectorSettingsEntry( values: List, crossinline onValueSelected: (T) -> Unit, modifier: Modifier = Modifier, + isEnabled: Boolean = true, crossinline valueText: (T) -> String = { it.toString() } ) { var isShowingDialog by remember { @@ -283,6 +286,7 @@ inline fun ValueSelectorSettingsEntry( title = title, text = valueText(selectedValue), modifier = modifier, + isEnabled = isEnabled, onClick = { isShowingDialog = true } @@ -428,7 +432,7 @@ fun SettingsEntryGroupText( BasicText( text = title.uppercase(), - style = typography.xxs.semiBold.copy(colorPalette.blue), + style = typography.xxs.semiBold.copy(colorPalette.accent), modifier = modifier .padding(start = 24.dp, top = 24.dp) .padding(horizontal = 32.dp) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/AboutScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/AboutScreen.kt index ea5aa91..9f515cb 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/AboutScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/AboutScreen.kt @@ -42,7 +42,7 @@ fun AboutScreen() { Column( modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() .verticalScroll(scrollState) .padding(bottom = 72.dp) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/AppearanceSettingsScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/AppearanceSettingsScreen.kt index 138cd90..3b9cf87 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/AppearanceSettingsScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/AppearanceSettingsScreen.kt @@ -21,6 +21,7 @@ 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.enums.ColorPaletteName import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.screens.EnumValueSelectorSettingsEntry @@ -30,6 +31,7 @@ import it.vfsfitvnm.vimusic.ui.screens.SwitchSettingEntry import it.vfsfitvnm.vimusic.ui.screens.globalRoutes import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.utils.colorPaletteModeKey +import it.vfsfitvnm.vimusic.utils.colorPaletteNameKey import it.vfsfitvnm.vimusic.utils.isShowingThumbnailInLockscreenKey import it.vfsfitvnm.vimusic.utils.rememberPreference import it.vfsfitvnm.vimusic.utils.thumbnailRoundnessKey @@ -45,6 +47,7 @@ fun AppearanceSettingsScreen() { host { val (colorPalette) = LocalAppearance.current + var colorPaletteName by rememberPreference(colorPaletteNameKey, ColorPaletteName.Dynamic) var colorPaletteMode by rememberPreference(colorPaletteModeKey, ColorPaletteMode.System) var thumbnailRoundness by rememberPreference( thumbnailRoundnessKey, @@ -57,7 +60,7 @@ fun AppearanceSettingsScreen() { Column( modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() .verticalScroll(scrollState) .padding(bottom = 72.dp) @@ -81,9 +84,18 @@ fun AppearanceSettingsScreen() { SettingsEntryGroupText(title = "COLORS") + EnumValueSelectorSettingsEntry( + title = "Theme", + selectedValue = colorPaletteName, + onValueSelected = { + colorPaletteName = it + } + ) + EnumValueSelectorSettingsEntry( title = "Theme mode", selectedValue = colorPaletteMode, + isEnabled = colorPaletteName != ColorPaletteName.PureBlack, onValueSelected = { colorPaletteMode = it } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/BackupAndRestoreScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/BackupAndRestoreScreen.kt index 799c07e..f932dc9 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/BackupAndRestoreScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/BackupAndRestoreScreen.kt @@ -119,7 +119,7 @@ fun BackupAndRestoreScreen() { Column( modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() .verticalScroll(scrollState) .padding(bottom = 72.dp) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/CacheSettingsScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/CacheSettingsScreen.kt index e1ba6a8..c71d8ec 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/CacheSettingsScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/CacheSettingsScreen.kt @@ -67,7 +67,7 @@ fun CacheSettingsScreen() { Column( modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() .verticalScroll(scrollState) .padding(bottom = 72.dp) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherSettingsScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherSettingsScreen.kt index 76f34f5..d909366 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherSettingsScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherSettingsScreen.kt @@ -71,7 +71,7 @@ fun OtherSettingsScreen() { Column( modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() .verticalScroll(scrollState) .padding(bottom = 72.dp) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/PlayerSettingsScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/PlayerSettingsScreen.kt index 0939f9f..036f99c 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/PlayerSettingsScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/PlayerSettingsScreen.kt @@ -63,7 +63,7 @@ fun PlayerSettingsScreen() { Column( modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() .verticalScroll(scrollState) .padding(bottom = 72.dp) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/styling/ColorPalette.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/styling/ColorPalette.kt index 2533e1d..e09b44c 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/styling/ColorPalette.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/styling/ColorPalette.kt @@ -1,83 +1,142 @@ package it.vfsfitvnm.vimusic.ui.styling +import android.graphics.Bitmap import androidx.compose.runtime.Immutable import androidx.compose.ui.graphics.Color +import androidx.palette.graphics.Palette +import it.vfsfitvnm.vimusic.enums.ColorPaletteMode +import it.vfsfitvnm.vimusic.enums.ColorPaletteName @Immutable data class ColorPalette( - val background: Color, - val elevatedBackground: Color, - val lightBackground: Color, - val backgroundContainer: Color, + val background0: Color, + val background1: Color, + val background2: Color, + val accent: Color, + val onAccent: Color, + val red: Color = Color(0xffbf4040), + val blue: Color = Color(0xFF4472CF), val text: Color, val textSecondary: Color, val textDisabled: Color, - val lightGray: Color, - val gray: Color, - val darkGray: Color, - val blue: Color, - val red: Color, - val green: Color, - val orange: Color, - val magenta: Color, - val cyan: Color, - - val primaryContainer: Color, - val onPrimaryContainer: Color, - val iconOnPrimaryContainer: Color, - val isDark: Boolean ) -val DarkColorPalette = ColorPalette( - background = Color(0xff16171d), - lightBackground = Color(0xff1f2029), - elevatedBackground = Color(0xff1f2029), - backgroundContainer = Color(0xff2b2d3b), +val DefaultDarkColorPalette = ColorPalette( + background0 = Color(0xff16171d), + background1 = Color(0xff1f2029), + background2 = Color(0xff2b2d3b), text = Color(0xffe1e1e2), textSecondary = Color(0xffa3a4a6), textDisabled = Color(0xff6f6f73), - lightGray = Color(0xfff8f8f8), - gray = Color(0xFFE5E5E5), - darkGray = Color(0xFF838383), - blue = Color(0xff507fdd), - red = Color(0xffbf4040), - green = Color(0xff82b154), - orange = Color(0xffe9a033), - magenta = Color(0xffbb4da4), - cyan = Color(0xFF4DA5BB), - primaryContainer = Color(0xff4046bf), - onPrimaryContainer = Color.White, - iconOnPrimaryContainer = Color.White, + accent = Color(0xff4046bf), + onAccent = Color.White, isDark = true ) -val BlackColorPalette = DarkColorPalette.copy( - background = Color.Black, - lightBackground = Color(0xff0d0d12), - elevatedBackground = Color(0xff0d0d12), - backgroundContainer = Color(0xff0d0d12) -) - -val LightColorPalette = ColorPalette( - background = Color(0xfffdfdfe), - lightBackground = Color(0xfff8f8fc), - elevatedBackground = Color(0xfff8f8fc), - backgroundContainer = Color(0xffeaeaf5), - lightGray = Color(0xfff8f8f8), - gray = Color(0xFFE5E5E5), - darkGray = Color(0xFF838383), +val DefaultLightColorPalette = ColorPalette( + background0 = Color(0xfffdfdfe), + background1 = Color(0xfff8f8fc), + background2 = Color(0xffeaeaf5), text = Color(0xff212121), textSecondary = Color(0xFF656566), textDisabled = Color(0xFF9d9d9d), - blue = Color(0xff4059bf), - red = Color(0xffbf4040), - green = Color(0xff7fbf40), - orange = Color(0xffe8730e), - magenta = Color(0xffbb4da4), - cyan = Color(0xFF4DBBB2), - primaryContainer = Color(0xff4046bf), - onPrimaryContainer = Color.White, - iconOnPrimaryContainer = Color.White, + accent = Color(0xff4046bf), + onAccent = Color.White, isDark = false ) + +val PureBlackColorPalette = DefaultDarkColorPalette.copy( + background0 = Color.Black, + background1 = Color.Black, + background2 = Color.Black +) + +fun colorPaletteOf( + colorPaletteName: ColorPaletteName, + colorPaletteMode: ColorPaletteMode, + isSystemInDarkMode: Boolean +): ColorPalette { + return when (colorPaletteName) { + ColorPaletteName.Default, ColorPaletteName.Dynamic -> when (colorPaletteMode) { + ColorPaletteMode.Light -> DefaultLightColorPalette + ColorPaletteMode.Dark -> DefaultDarkColorPalette + ColorPaletteMode.System -> when (isSystemInDarkMode) { + true -> DefaultDarkColorPalette + false -> DefaultLightColorPalette + } + } + ColorPaletteName.PureBlack -> PureBlackColorPalette + } +} + +fun dynamicColorPaletteOf(bitmap: Bitmap, isDark: Boolean): ColorPalette? { + val palette = Palette + .from(bitmap) + .maximumColorCount(8) + .addFilter(if (isDark) ({ _, hsl -> hsl[0] !in 36f..100f }) else null) + .generate() + + val hsl = if (isDark) { + palette.dominantSwatch ?: Palette + .from(bitmap) + .maximumColorCount(8) + .generate() + .dominantSwatch + } else { + palette.dominantSwatch + }?.hsl ?: return null + + return if (hsl[1] < 0.08) { + val newHsl = palette.swatches + .map(Palette.Swatch::getHsl) + .sortedByDescending(FloatArray::component2) + .find { it[1] != 0f } + ?: hsl + + dynamicColorPaletteOf(newHsl, isDark) + } else { + dynamicColorPaletteOf(hsl, isDark) + } +} + +private fun dynamicColorPaletteOf(hsl: FloatArray, isDark: Boolean): ColorPalette { + return colorPaletteOf(ColorPaletteName.Dynamic, if (isDark) ColorPaletteMode.Dark else ColorPaletteMode.Light, false).copy( + background0 = Color.hsl(hsl[0], hsl[1].coerceAtMost(0.1f), if (isDark) 0.10f else 0.925f), + background1 = Color.hsl(hsl[0], hsl[1].coerceAtMost(0.3f), if (isDark) 0.15f else 0.90f), + background2 = Color.hsl(hsl[0], hsl[1].coerceAtMost(0.4f), if (isDark) 0.2f else 0.85f), + accent = Color.hsl(hsl[0], hsl[1].coerceAtMost(0.5f), 0.5f), +// background3 = Color.hsl(hue, saturation, if (isDark) 0.20f else 0.85f), +// background4 = Color.hsl(hue, saturation, if (isDark) 0.25f else 0.75f), + ) +} + +inline val ColorPalette.collapsedPlayerProgressBar: Color + get() = if (this === DefaultDarkColorPalette || this === DefaultLightColorPalette || this == PureBlackColorPalette) { + text + } else { + accent + } + +inline val ColorPalette.favoritesIcon: Color + get() = if (this === DefaultDarkColorPalette || this === DefaultLightColorPalette || this == PureBlackColorPalette) { + red + } else { + accent + } + +inline val ColorPalette.shimmer: Color + get() = if (this === DefaultDarkColorPalette || this === DefaultLightColorPalette || this == PureBlackColorPalette) { + Color(0xff838383) + } else { + accent + } + +inline val ColorPalette.overlay: Color + get() = PureBlackColorPalette.background0.copy(alpha = 0.75f) + +inline val ColorPalette.onOverlay: Color + get() = PureBlackColorPalette.text + +inline val ColorPalette.onOverlayShimmer: Color + get() = PureBlackColorPalette.shimmer diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/styling/Typography.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/styling/Typography.kt index 6c15340..57b987b 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/styling/Typography.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/styling/Typography.kt @@ -1,7 +1,16 @@ package it.vfsfitvnm.vimusic.ui.styling import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +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 @Immutable data class Typography( @@ -11,3 +20,47 @@ data class Typography( val m: TextStyle, val l: TextStyle, ) + +@OptIn(ExperimentalTextApi::class) +fun typographyOf(color: Color): Typography { + 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), + ) +} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/CurrentPlaylistView.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/CurrentPlaylistView.kt index 583cde6..17c0e0e 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/CurrentPlaylistView.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/CurrentPlaylistView.kt @@ -47,8 +47,8 @@ import it.vfsfitvnm.vimusic.ui.components.MusicBars import it.vfsfitvnm.vimusic.ui.components.themed.QueuedMediaItemMenu import it.vfsfitvnm.vimusic.ui.screens.SmallSongItemShimmer import it.vfsfitvnm.vimusic.ui.styling.Dimensions -import it.vfsfitvnm.vimusic.ui.styling.LightColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance +import it.vfsfitvnm.vimusic.ui.styling.onOverlay import it.vfsfitvnm.vimusic.ui.styling.px import it.vfsfitvnm.vimusic.utils.medium import it.vfsfitvnm.vimusic.utils.rememberMediaItemIndex @@ -91,6 +91,7 @@ fun CurrentPlaylistView( .nestedScroll(remember { layoutState.nestedScrollConnection(lazyListState.firstVisibleItemIndex == 0 && lazyListState.firstVisibleItemScrollOffset == 0) }) + .background(colorPalette.background1) ) { items( items = windows, @@ -137,7 +138,7 @@ fun CurrentPlaylistView( ) { if (shouldBePlaying) { MusicBars( - color = LightColorPalette.background, + color = colorPalette.onOverlay, modifier = Modifier .height(24.dp) ) @@ -145,7 +146,7 @@ fun CurrentPlaylistView( Image( painter = painterResource(R.drawable.play), contentDescription = null, - colorFilter = ColorFilter.tint(LightColorPalette.background), + colorFilter = ColorFilter.tint(colorPalette.onOverlay), modifier = Modifier .size(24.dp) ) @@ -164,7 +165,6 @@ fun CurrentPlaylistView( .size(20.dp) ) }, - backgroundColor = colorPalette.background, modifier = Modifier // .animateItemPlacement() .verticalDragAfterLongPressToReorder( @@ -211,7 +211,7 @@ fun CurrentPlaylistView( onClick = layoutState::collapseSoft ) .height(64.dp) - .background(colorPalette.elevatedBackground) + .background(colorPalette.background2) .fillMaxWidth() .padding(horizontal = 8.dp) .align(Alignment.BottomCenter) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerBottomSheet.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerBottomSheet.kt index aeaa929..238d948 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerBottomSheet.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerBottomSheet.kt @@ -7,6 +7,8 @@ import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp @@ -18,6 +20,7 @@ import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance @ExperimentalAnimationApi @Composable fun PlayerBottomSheet( + backgroundColorProvider: () -> Color, layoutState: BottomSheetState, onGlobalRouteEmitted: () -> Unit, modifier: Modifier = Modifier, @@ -31,7 +34,7 @@ fun PlayerBottomSheet( collapsedContent = { Box( modifier = Modifier - .background(colorPalette.background) + .drawBehind { drawRect(backgroundColorProvider()) } .fillMaxSize() ) { Image( @@ -40,7 +43,6 @@ fun PlayerBottomSheet( colorFilter = ColorFilter.tint(colorPalette.text), modifier = Modifier .align(Alignment.Center) - .padding(all = 8.dp) .size(18.dp) ) @@ -52,7 +54,7 @@ fun PlayerBottomSheet( layoutState = layoutState, onGlobalRouteEmitted = onGlobalRouteEmitted, modifier = Modifier - .background(colorPalette.background) + .background(colorPalette.background0) .fillMaxSize() ) } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt index f59d5c7..f973b16 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt @@ -29,6 +29,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.ColorFilter @@ -49,6 +50,7 @@ import it.vfsfitvnm.vimusic.ui.components.rememberBottomSheetState import it.vfsfitvnm.vimusic.ui.components.themed.BaseMediaItemMenu import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance +import it.vfsfitvnm.vimusic.ui.styling.collapsedPlayerProgressBar import it.vfsfitvnm.vimusic.ui.styling.px import it.vfsfitvnm.vimusic.ui.views.player.Controls import it.vfsfitvnm.vimusic.ui.views.player.Thumbnail @@ -70,7 +72,7 @@ fun PlayerView( ) { val menuState = LocalMenuState.current - val (colorPalette, typography) = LocalAppearance.current + val (colorPalette, typography, thumbnailShape) = LocalAppearance.current val binder = LocalPlayerServiceBinder.current val context = LocalContext.current val configuration = LocalConfiguration.current @@ -96,30 +98,32 @@ fun PlayerView( horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically, modifier = Modifier - .background(colorPalette.elevatedBackground) + .background(colorPalette.background1) .fillMaxSize() .drawBehind { val progress = positionAndDuration.first.toFloat() / positionAndDuration.second.absoluteValue - val offset = Dimensions.thumbnails.player.songPreview.toPx() drawLine( - color = colorPalette.text, - start = Offset(x = offset, y = 1.dp.toPx()), - end = Offset( - x = ((size.width - offset) * progress) + offset, - y = 1.dp.toPx() - ), + color = colorPalette.collapsedPlayerProgressBar, + start = Offset(x = 0f, y = 1.dp.toPx()), + end = Offset(x = size.width * progress, y = 1.dp.toPx()), strokeWidth = 2.dp.toPx() ) } ) { + Spacer( + modifier = Modifier + .width(2.dp) + ) + AsyncImage( model = mediaItem.mediaMetadata.artworkUri.thumbnail(Dimensions.thumbnails.player.songPreview.px), contentDescription = null, contentScale = ContentScale.Crop, modifier = Modifier - .size(Dimensions.thumbnails.player.songPreview) + .clip(thumbnailShape) + .size(48.dp) ) Column( @@ -206,7 +210,7 @@ fun PlayerView( verticalAlignment = Alignment.CenterVertically, modifier = Modifier .padding(bottom = 64.dp) - .background(colorPalette.background) + .background(colorPalette.background1) .padding(top = 16.dp) ) { Box( @@ -242,7 +246,7 @@ fun PlayerView( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .padding(bottom = 64.dp) - .background(colorPalette.background) + .background(colorPalette.background1) .padding(top = 32.dp) ) { Box( @@ -355,6 +359,7 @@ fun PlayerView( ) } }, + backgroundColorProvider = { colorPalette.background2 }, modifier = Modifier .align(Alignment.BottomCenter) ) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlaylistPreviewItem.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlaylistPreviewItem.kt index e02a937..ec4d012 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlaylistPreviewItem.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlaylistPreviewItem.kt @@ -91,7 +91,7 @@ fun PlaylistPreviewItem( contentScale = ContentScale.Crop, modifier = Modifier .clip(thumbnailShape) - .border(width = 1.dp, color = colorPalette.lightBackground) + .border(width = 1.dp, color = colorPalette.background1) .align(alignment) .size(thumbnailSize) ) @@ -143,7 +143,7 @@ fun PlaylistItem( Box( modifier = modifier .clip(thumbnailShape) - .background(colorPalette.lightBackground) + .background(colorPalette.background1) .size(thumbnailSize * 2) ) { Box( diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/SongItem.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/SongItem.kt index 9139f99..b67fd9f 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/SongItem.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/SongItem.kt @@ -2,7 +2,6 @@ package it.vfsfitvnm.vimusic.ui.views import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement @@ -22,7 +21,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextOverflow @@ -48,7 +46,6 @@ fun SongItem( onClick: () -> Unit, menuContent: @Composable () -> Unit, modifier: Modifier = Modifier, - backgroundColor: Color? = null, onThumbnailContent: (@Composable BoxScope.() -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null ) { @@ -64,7 +61,6 @@ fun SongItem( onClick = onClick, onThumbnailContent = onThumbnailContent, trailingContent = trailingContent, - backgroundColor = backgroundColor, modifier = modifier, ) } @@ -78,7 +74,6 @@ fun SongItem( onClick: () -> Unit, menuContent: @Composable () -> Unit, modifier: Modifier = Modifier, - backgroundColor: Color? = null, onThumbnailContent: (@Composable BoxScope.() -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null ) { @@ -90,7 +85,6 @@ fun SongItem( menuContent = menuContent, onClick = onClick, onThumbnailContent = onThumbnailContent, - backgroundColor = backgroundColor, trailingContent = trailingContent, modifier = modifier, ) @@ -107,7 +101,6 @@ fun SongItem( onClick: () -> Unit, menuContent: @Composable () -> Unit, modifier: Modifier = Modifier, - backgroundColor: Color? = null, onThumbnailContent: (@Composable BoxScope.() -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null ) { @@ -134,7 +127,6 @@ fun SongItem( } }, menuContent = menuContent, - backgroundColor = backgroundColor, trailingContent = trailingContent, modifier = modifier, ) @@ -151,11 +143,10 @@ fun SongItem( startContent: @Composable () -> Unit, menuContent: @Composable () -> Unit, modifier: Modifier = Modifier, - backgroundColor: Color? = null, trailingContent: (@Composable () -> Unit)? = null ) { val menuState = LocalMenuState.current - val (colorPalette, typography) = LocalAppearance.current + val (_, typography) = LocalAppearance.current Row( verticalAlignment = Alignment.CenterVertically, @@ -171,7 +162,6 @@ fun SongItem( ) .fillMaxWidth() .padding(vertical = Dimensions.itemsVerticalPadding) - .background(backgroundColor ?: colorPalette.background) .padding(start = 16.dp, end = if (trailingContent == null) 16.dp else 8.dp) ) { startContent() diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/Controls.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/Controls.kt index 86db7bd..f90e3d2 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/Controls.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/Controls.kt @@ -2,6 +2,7 @@ package it.vfsfitvnm.vimusic.ui.views.player import android.text.format.DateUtils import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.tween import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -40,6 +41,7 @@ import it.vfsfitvnm.vimusic.models.Song import it.vfsfitvnm.vimusic.query import it.vfsfitvnm.vimusic.ui.components.SeekBar import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance +import it.vfsfitvnm.vimusic.ui.styling.favoritesIcon import it.vfsfitvnm.vimusic.utils.bold import it.vfsfitvnm.vimusic.utils.rememberRepeatMode import it.vfsfitvnm.vimusic.utils.secondary @@ -70,7 +72,7 @@ fun Controls( Database.likedAt(mediaItem.mediaId).distinctUntilChanged() }.collectAsState(initial = null, context = Dispatchers.IO) - val playPauseRoundness by animateDpAsState(if (shouldBePlaying) 32.dp else 16.dp) + val playPauseRoundness by animateDpAsState(if (shouldBePlaying) 32.dp else 16.dp, tween()) Column( horizontalAlignment = Alignment.CenterHorizontally, @@ -121,7 +123,7 @@ fun Controls( scrubbingPosition = null }, color = colorPalette.text, - backgroundColor = colorPalette.backgroundContainer, + backgroundColor = colorPalette.background2, shape = RoundedCornerShape(8.dp) ) @@ -166,7 +168,7 @@ fun Controls( Image( painter = painterResource(R.drawable.heart), contentDescription = null, - colorFilter = ColorFilter.tint(if (likedAt != null) colorPalette.red else colorPalette.textDisabled), + colorFilter = ColorFilter.tint(if (likedAt == null) colorPalette.background2 else colorPalette.favoritesIcon), modifier = Modifier .clickable { query { @@ -211,7 +213,7 @@ fun Controls( binder.player.play() } } - .background(color = colorPalette.backgroundContainer) + .background(colorPalette.background2) .size(64.dp) ) { Image( diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/Lyrics.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/Lyrics.kt index 27035dc..3eee888 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/Lyrics.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/Lyrics.kt @@ -57,9 +57,10 @@ import it.vfsfitvnm.vimusic.ui.components.themed.Menu import it.vfsfitvnm.vimusic.ui.components.themed.MenuEntry import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder -import it.vfsfitvnm.vimusic.ui.styling.BlackColorPalette -import it.vfsfitvnm.vimusic.ui.styling.DarkColorPalette +import it.vfsfitvnm.vimusic.ui.styling.PureBlackColorPalette +import it.vfsfitvnm.vimusic.ui.styling.DefaultDarkColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance +import it.vfsfitvnm.vimusic.ui.styling.onOverlayShimmer import it.vfsfitvnm.vimusic.utils.SynchronizedLyrics import it.vfsfitvnm.vimusic.utils.center import it.vfsfitvnm.vimusic.utils.color @@ -88,7 +89,7 @@ fun Lyrics( nestedScrollConnectionProvider: () -> NestedScrollConnection, modifier: Modifier = Modifier ) { - val (_, typography) = LocalAppearance.current + val (colorPalette, typography) = LocalAppearance.current val context = LocalContext.current AnimatedVisibility( @@ -194,7 +195,7 @@ fun Lyrics( ) { BasicText( text = "An error has occurred while fetching the ${if (isShowingSynchronizedLyrics) "synchronized " else ""}lyrics", - style = typography.xs.center.medium.color(BlackColorPalette.text), + style = typography.xs.center.medium.color(PureBlackColorPalette.text), modifier = Modifier .background(Color.Black.copy(0.4f)) .padding(all = 8.dp) @@ -211,7 +212,7 @@ fun Lyrics( ) { BasicText( text = "${if (isShowingSynchronizedLyrics) "Synchronized l" else "L"}yrics are not available for this song", - style = typography.xs.center.medium.color(BlackColorPalette.text), + style = typography.xs.center.medium.color(PureBlackColorPalette.text), modifier = Modifier .background(Color.Black.copy(0.4f)) .padding(all = 8.dp) @@ -227,6 +228,7 @@ fun Lyrics( ) { repeat(4) { index -> TextPlaceholder( + color = colorPalette.onOverlayShimmer, modifier = Modifier .alpha(1f - index * 0.05f) ) @@ -269,7 +271,7 @@ fun Lyrics( itemsIndexed(items = synchronizedLyrics.sentences) { index, sentence -> BasicText( text = sentence.second, - style = typography.xs.center.medium.color(if (index == synchronizedLyrics.index) BlackColorPalette.text else BlackColorPalette.textDisabled), + style = typography.xs.center.medium.color(if (index == synchronizedLyrics.index) PureBlackColorPalette.text else PureBlackColorPalette.textDisabled), modifier = Modifier .padding(vertical = 4.dp, horizontal = 32.dp) ) @@ -278,7 +280,7 @@ fun Lyrics( } else { BasicText( text = lyrics, - style = typography.xs.center.medium.color(BlackColorPalette.text), + style = typography.xs.center.medium.color(PureBlackColorPalette.text), modifier = Modifier .nestedScroll(remember { nestedScrollConnectionProvider() }) .verticalFadingEdge() @@ -293,7 +295,7 @@ fun Lyrics( Image( painter = painterResource(R.drawable.ellipsis_horizontal), contentDescription = null, - colorFilter = ColorFilter.tint(DarkColorPalette.text), + colorFilter = ColorFilter.tint(DefaultDarkColorPalette.text), modifier = Modifier .padding(all = 4.dp) .clickable { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/PlaybackError.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/PlaybackError.kt index 9ace202..df9b2a7 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/PlaybackError.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/PlaybackError.kt @@ -20,7 +20,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.dp -import it.vfsfitvnm.vimusic.ui.styling.BlackColorPalette +import it.vfsfitvnm.vimusic.ui.styling.PureBlackColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.utils.center import it.vfsfitvnm.vimusic.utils.color @@ -64,7 +64,7 @@ fun PlaybackError( ) { BasicText( text = remember { messageProvider() }, - style = typography.xs.center.medium.color(BlackColorPalette.text), + style = typography.xs.center.medium.color(PureBlackColorPalette.text), modifier = Modifier .background(Color.Black.copy(0.4f)) .padding(all = 8.dp) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/StatsForNerds.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/StatsForNerds.kt index 43b6841..2aea50b 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/StatsForNerds.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/player/StatsForNerds.kt @@ -25,7 +25,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp @@ -35,8 +34,9 @@ import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder import it.vfsfitvnm.vimusic.models.Format import it.vfsfitvnm.vimusic.query -import it.vfsfitvnm.vimusic.ui.styling.BlackColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance +import it.vfsfitvnm.vimusic.ui.styling.onOverlay +import it.vfsfitvnm.vimusic.ui.styling.overlay import it.vfsfitvnm.vimusic.utils.color import it.vfsfitvnm.vimusic.utils.medium import it.vfsfitvnm.vimusic.utils.rememberVolume @@ -53,7 +53,7 @@ fun StatsForNerds( onDismiss: () -> Unit, modifier: Modifier = Modifier ) { - val (_, typography) = LocalAppearance.current + val (colorPalette, typography) = LocalAppearance.current val context = LocalContext.current val binder = LocalPlayerServiceBinder.current ?: return @@ -105,7 +105,7 @@ fun StatsForNerds( } ) } - .background(Color.Black.copy(alpha = 0.8f)) + .background(colorPalette.overlay) .fillMaxSize() ) { Row( @@ -117,50 +117,50 @@ fun StatsForNerds( Column(horizontalAlignment = Alignment.End) { BasicText( text = "Id", - style = typography.xs.medium.color(BlackColorPalette.text) + style = typography.xs.medium.color(colorPalette.onOverlay) ) BasicText( text = "Volume", - style = typography.xs.medium.color(BlackColorPalette.text) + style = typography.xs.medium.color(colorPalette.onOverlay) ) BasicText( text = "Loudness", - style = typography.xs.medium.color(BlackColorPalette.text) + style = typography.xs.medium.color(colorPalette.onOverlay) ) BasicText( text = "Bitrate", - style = typography.xs.medium.color(BlackColorPalette.text) + style = typography.xs.medium.color(colorPalette.onOverlay) ) BasicText( text = "Size", - style = typography.xs.medium.color(BlackColorPalette.text) + style = typography.xs.medium.color(colorPalette.onOverlay) ) BasicText( text = "Cached", - style = typography.xs.medium.color(BlackColorPalette.text) + style = typography.xs.medium.color(colorPalette.onOverlay) ) } Column { BasicText( text = mediaId, - style = typography.xs.medium.color(BlackColorPalette.text) + style = typography.xs.medium.color(colorPalette.onOverlay) ) BasicText( text = "${volume.times(100).roundToInt()}%", - style = typography.xs.medium.color(BlackColorPalette.text) + style = typography.xs.medium.color(colorPalette.onOverlay) ) BasicText( text = format?.loudnessDb?.let { loudnessDb -> "%.2f dB".format(loudnessDb) } ?: "Unknown", - style = typography.xs.medium.color(BlackColorPalette.text) + style = typography.xs.medium.color(colorPalette.onOverlay) ) BasicText( text = format?.bitrate?.let { bitrate -> "${bitrate / 1000} kbps" } ?: "Unknown", - style = typography.xs.medium.color(BlackColorPalette.text) + style = typography.xs.medium.color(colorPalette.onOverlay) ) BasicText( text = format?.contentLength?.let { contentLength -> @@ -169,7 +169,7 @@ fun StatsForNerds( contentLength ) } ?: "Unknown", - style = typography.xs.medium.color(BlackColorPalette.text) + style = typography.xs.medium.color(colorPalette.onOverlay) ) BasicText( text = buildString { @@ -179,7 +179,7 @@ fun StatsForNerds( append(" (${(cachedBytes.toFloat() / contentLength * 100).roundToInt()}%)") } }, - style = typography.xs.medium.color(BlackColorPalette.text) + style = typography.xs.medium.color(colorPalette.onOverlay) ) } } @@ -187,7 +187,7 @@ fun StatsForNerds( if (format != null && format?.itag == null) { BasicText( text = "FETCH MISSING DATA", - style = typography.xxs.medium.color(BlackColorPalette.text), + style = typography.xxs.medium.color(colorPalette.onOverlay), modifier = Modifier .clickable( indication = rememberRipple(bounded = true), diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt index e231b46..de2c103 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.core.content.edit +const val colorPaletteNameKey = "colorPaletteName" const val colorPaletteModeKey = "colorPaletteMode" const val thumbnailRoundnessKey = "thumbnailRoundness" const val coilDiskCacheMaxSizeKey = "coilDiskCacheMaxSize" diff --git a/settings.gradle.kts b/settings.gradle.kts index 757fc0d..ae86ad1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -49,6 +49,8 @@ dependencyResolutionManagement { library("brotli", "org.brotli", "dec").version("0.1.2") + library("palette", "androidx.palette", "palette").version("1.0.0") + library("desugaring", "com.android.tools", "desugar_jdk_libs").version("1.1.5") }