From afc64cae74ee5d8983ab2755126858258279ea5b Mon Sep 17 00:00:00 2001 From: vfsfitvnm Date: Sat, 11 Jun 2022 18:57:32 +0200 Subject: [PATCH] Create NotificationScreen --- .../vimusic/ui/components/themed/Switch.kt | 51 ++++++++++ .../vimusic/ui/screens/HomeScreen.kt | 10 +- .../vimusic/ui/screens/SettingsScreen.kt | 80 +++++++++++++--- .../ui/screens/settings/NotificationScreen.kt | 92 +++++++++++++++++++ .../vimusic/ui/screens/settings/routes.kt | 7 ++ .../vimusic/ui/styling/ColorPalette.kt | 3 + .../it/vfsfitvnm/vimusic/utils/DrawScope.kt | 29 ++++++ .../it/vfsfitvnm/vimusic/utils/Preferences.kt | 1 + app/src/main/res/drawable/heart_outline.xml | 13 +++ app/src/main/res/drawable/notifications.xml | 12 +++ 10 files changed, 277 insertions(+), 21 deletions(-) create mode 100644 app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Switch.kt create mode 100644 app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/NotificationScreen.kt create mode 100644 app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/DrawScope.kt create mode 100644 app/src/main/res/drawable/heart_outline.xml create mode 100644 app/src/main/res/drawable/notifications.xml 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 new file mode 100644 index 0000000..baa8338 --- /dev/null +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/themed/Switch.kt @@ -0,0 +1,51 @@ +package it.vfsfitvnm.vimusic.ui.components.themed + +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.center +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shadow +import androidx.compose.ui.unit.dp +import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette +import it.vfsfitvnm.vimusic.utils.drawCircle + +@Composable +fun Switch( + isChecked: Boolean, + modifier: Modifier = Modifier, +) { + val colorPalette = LocalColorPalette.current + + val backgroundColor by animateColorAsState(if (isChecked) colorPalette.primaryContainer else colorPalette.lightBackground) + val color by animateColorAsState(if (isChecked) colorPalette.onPrimaryContainer else colorPalette.textDisabled) + val offset by animateDpAsState(if (isChecked) 36.dp else 12.dp) + + Spacer( + modifier = modifier + .width(48.dp) + .height(24.dp) + .background(color = backgroundColor, shape = CircleShape) + .drawBehind { + drawCircle( + color = color, + radius = 8.dp.toPx(), + center = size.center.copy(x = offset.toPx()), + shadow = Shadow( + color = Color.Black.copy(alpha = 0.4f), + blurRadius = 8.dp.toPx(), + offset = Offset(x = -1.dp.toPx(), y = 1.dp.toPx()) + ) + ) + } + ) +} \ No newline at end of file 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 83c9e67..b03c7fc 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 @@ -28,9 +28,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import androidx.media3.common.Player -import it.vfsfitvnm.route.Route import it.vfsfitvnm.route.RouteHandler -import it.vfsfitvnm.route.rememberRoute import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.enums.SongCollection @@ -86,13 +84,7 @@ fun HomeScreen( } }.collectAsState(initial = emptyList(), context = Dispatchers.IO) - RouteHandler( -// route = route, -// onRouteChanged = onRouteChanged, - listenToGlobalEmitter = true - ) { - println("route: ${this.route?.tag}") - + RouteHandler(listenToGlobalEmitter = true) { settingsRoute { SettingsScreen() } 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 b58829d..bb7738a 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 @@ -11,8 +11,7 @@ import androidx.compose.material.ripple.rememberRipple import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.* import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp @@ -20,13 +19,11 @@ import it.vfsfitvnm.route.* import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.themed.EnumValueSelectorDialog +import it.vfsfitvnm.vimusic.ui.components.themed.Switch import it.vfsfitvnm.vimusic.ui.screens.settings.* import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalTypography -import it.vfsfitvnm.vimusic.utils.disabled -import it.vfsfitvnm.vimusic.utils.medium -import it.vfsfitvnm.vimusic.utils.secondary -import it.vfsfitvnm.vimusic.utils.semiBold +import it.vfsfitvnm.vimusic.utils.* @ExperimentalAnimationApi @Composable @@ -34,6 +31,7 @@ fun SettingsScreen() { val albumRoute = rememberPlaylistOrAlbumRoute() val artistRoute = rememberArtistRoute() val appearanceRoute = rememberAppearanceRoute() + val notificationRoute = rememberNotificationRoute() val backupAndRestoreRoute = rememberBackupAndRestoreRoute() val otherRoute = rememberOtherRoute() val aboutRoute = rememberAboutRoute() @@ -44,10 +42,11 @@ fun SettingsScreen() { listenToGlobalEmitter = true, transitionSpec = { when (targetState.route) { - appearanceRoute, backupAndRestoreRoute, otherRoute, aboutRoute -> leftSlide + albumRoute, artistRoute -> fastFade else -> when (initialState.route) { - appearanceRoute, backupAndRestoreRoute, otherRoute, aboutRoute -> rightSlide - else -> fastFade + albumRoute, artistRoute -> fastFade + null -> leftSlide + else -> rightSlide } } } @@ -68,6 +67,10 @@ fun SettingsScreen() { AppearanceScreen() } + notificationRoute { + NotificationScreen() + } + backupAndRestoreRoute { BackupAndRestoreScreen() } @@ -177,6 +180,14 @@ fun SettingsScreen() { route = appearanceRoute, ) + Entry( + color = colorPalette.cyan, + icon = R.drawable.notifications, + title = "Notification", + description = "Customize the notification appearance", + route = notificationRoute + ) + Entry( color = colorPalette.orange, icon = R.drawable.server, @@ -213,8 +224,6 @@ inline fun >EnumValueSelectorEntry( modifier: Modifier = Modifier, crossinline valueText: (T) -> String = Enum::name ) { - val typography = LocalTypography.current - var isShowingDialog by remember { mutableStateOf(false) } @@ -241,6 +250,53 @@ inline fun >EnumValueSelectorEntry( ) } + +@Composable +fun SwitchSettingEntry( + title: String, + text: String, + isChecked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + isEnabled: Boolean = true +) { + val colorPalette = LocalColorPalette.current + val typography = LocalTypography.current + + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + .clickable( + indication = rememberRipple(bounded = true), + interactionSource = remember { MutableInteractionSource() }, + onClick = { onCheckedChange(!isChecked) }, + enabled = isEnabled + ) + .padding(start = 24.dp) + .padding(horizontal = 32.dp, vertical = 16.dp) + .fillMaxWidth() + ) { + + Column( + modifier = Modifier + .weight(1f) + ) { + BasicText( + text = title, + style = typography.xs.semiBold.copy(color = if (isEnabled) colorPalette.text else colorPalette.darkGray), + ) + + BasicText( + text = text, + style = typography.xs.semiBold.copy(color = if (isEnabled) colorPalette.textSecondary else colorPalette.darkGray), + ) + } + + Switch(isChecked = isChecked) + } +} + @Composable @NonRestartableComposable fun SettingsEntry( @@ -319,4 +375,4 @@ fun SettingsEntryGroupText( .padding(start = 24.dp, top = 24.dp) .padding(horizontal = 32.dp) ) -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/NotificationScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/NotificationScreen.kt new file mode 100644 index 0000000..ab52ae8 --- /dev/null +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/NotificationScreen.kt @@ -0,0 +1,92 @@ +package it.vfsfitvnm.vimusic.ui.screens.settings + +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.BasicText +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import it.vfsfitvnm.route.RouteHandler +import it.vfsfitvnm.vimusic.R +import it.vfsfitvnm.vimusic.ui.components.TopAppBar +import it.vfsfitvnm.vimusic.ui.screens.* +import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette +import it.vfsfitvnm.vimusic.ui.styling.LocalTypography +import it.vfsfitvnm.vimusic.utils.LocalPreferences +import it.vfsfitvnm.vimusic.utils.semiBold + +@ExperimentalAnimationApi +@Composable +fun NotificationScreen() { + val albumRoute = rememberPlaylistOrAlbumRoute() + val artistRoute = rememberArtistRoute() + + val scrollState = rememberScrollState() + + RouteHandler(listenToGlobalEmitter = true) { + albumRoute { browseId -> + PlaylistOrAlbumScreen( + browseId = browseId ?: error("browseId cannot be null") + ) + } + + artistRoute { browseId -> + ArtistScreen( + browseId = browseId ?: error("browseId cannot be null") + ) + } + + host { + val colorPalette = LocalColorPalette.current + val typography = LocalTypography.current + val preferences = LocalPreferences.current + + Column( + modifier = Modifier + .background(colorPalette.background) + .fillMaxSize() + .verticalScroll(scrollState) + .padding(bottom = 72.dp) + ) { + TopAppBar( + modifier = Modifier + .height(52.dp) + ) { + Image( + painter = painterResource(R.drawable.chevron_back), + contentDescription = null, + colorFilter = ColorFilter.tint(colorPalette.text), + modifier = Modifier + .clickable(onClick = pop) + .padding(horizontal = 16.dp, vertical = 8.dp) + .size(24.dp) + ) + + BasicText( + text = "Notification", + style = typography.m.semiBold + ) + + Spacer( + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 8.dp) + .size(24.dp) + ) + } + + SwitchSettingEntry( + title = "[SOON] Show Like button", + text = "Mark a song as favorite", + isChecked = preferences.displayLikeButtonInNotification, + onCheckedChange = { + preferences.displayLikeButtonInNotification = it + }, + isEnabled = false + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/routes.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/routes.kt index b2a1caa..b5a7d85 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/routes.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/routes.kt @@ -11,6 +11,13 @@ fun rememberAppearanceRoute(): Route0 { } } +@Composable +fun rememberNotificationRoute(): Route0 { + return remember { + Route0("NotificationRoute") + } +} + @Composable fun rememberBackupAndRestoreRoute(): Route0 { return remember { 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 b46ca3d..20979c8 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 @@ -19,6 +19,7 @@ data class ColorPalette( val green: Color, val orange: Color, val magenta: Color, + val cyan: Color, val primaryContainer: Color, val onPrimaryContainer: Color, @@ -40,6 +41,7 @@ val DarkColorPalette = ColorPalette( green = Color(0xff82b154), orange = Color(0xffe9a033), magenta = Color(0xffbb4da4), + cyan = Color(0xFF4DA5BB), primaryContainer = Color(0xff4046bf), onPrimaryContainer = Color.White, @@ -67,6 +69,7 @@ val LightColorPalette = ColorPalette( green = Color(0xff7fbf40), orange = Color(0xffe8730e), magenta = Color(0xffbb4da4), + cyan = Color(0xFF4DBBB2), primaryContainer = Color(0xff4046bf), onPrimaryContainer = Color.White, diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/DrawScope.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/DrawScope.kt new file mode 100644 index 0000000..1d7e9fb --- /dev/null +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/DrawScope.kt @@ -0,0 +1,29 @@ +package it.vfsfitvnm.vimusic.utils + +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.drawscope.DrawScope + +fun DrawScope.drawCircle( + color: Color, + shadow: Shadow, + radius: Float = size.minDimension / 2.0f, + center: Offset = this.center, + alpha: Float = 1.0f, + style: PaintingStyle = PaintingStyle.Fill, + colorFilter: ColorFilter? = null, + blendMode: BlendMode = DrawScope.DefaultBlendMode +) = drawContext.canvas.nativeCanvas.drawCircle( + center.x, + center.y, + radius, + Paint().also { + it.color = color + it.alpha = alpha + it.blendMode = blendMode + it.colorFilter = colorFilter + it.style = style + }.asFrameworkPaint().also { + it.setShadowLayer(shadow.blurRadius, shadow.offset.x, shadow.offset.y, shadow.color.toArgb()) + } +) \ No newline at end of file diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt index ff1b7a8..3b9ba5f 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt @@ -23,6 +23,7 @@ class Preferences(holder: SharedPreferences) : SharedPreferences by holder { var homePageSongCollection by preference("homePageSongCollection", SongCollection.MostPlayed) var thumbnailRoundness by preference("thumbnailRoundness", ThumbnailRoundness.Light) var coilDiskCacheMaxSizeBytes by preference("coilDiskCacheMaxSizeBytes", 512L * 1024 * 1024) + var displayLikeButtonInNotification by preference("displayLikeButtonInNotification", false) } val Context.preferences: Preferences diff --git a/app/src/main/res/drawable/heart_outline.xml b/app/src/main/res/drawable/heart_outline.xml new file mode 100644 index 0000000..66c3058 --- /dev/null +++ b/app/src/main/res/drawable/heart_outline.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/notifications.xml b/app/src/main/res/drawable/notifications.xml new file mode 100644 index 0000000..a06a9c2 --- /dev/null +++ b/app/src/main/res/drawable/notifications.xml @@ -0,0 +1,12 @@ + + + +