Create NotificationScreen

This commit is contained in:
vfsfitvnm
2022-06-11 18:57:32 +02:00
parent baea3d252c
commit afc64cae74
10 changed files with 277 additions and 21 deletions

View File

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

View File

@@ -28,9 +28,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import androidx.media3.common.Player import androidx.media3.common.Player
import it.vfsfitvnm.route.Route
import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.route.rememberRoute
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.SongCollection import it.vfsfitvnm.vimusic.enums.SongCollection
@@ -86,13 +84,7 @@ fun HomeScreen(
} }
}.collectAsState(initial = emptyList(), context = Dispatchers.IO) }.collectAsState(initial = emptyList(), context = Dispatchers.IO)
RouteHandler( RouteHandler(listenToGlobalEmitter = true) {
// route = route,
// onRouteChanged = onRouteChanged,
listenToGlobalEmitter = true
) {
println("route: ${this.route?.tag}")
settingsRoute { settingsRoute {
SettingsScreen() SettingsScreen()
} }

View File

@@ -11,8 +11,7 @@ import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -20,13 +19,11 @@ import it.vfsfitvnm.route.*
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.EnumValueSelectorDialog 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.screens.settings.*
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.disabled import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.vimusic.utils.medium
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
@ExperimentalAnimationApi @ExperimentalAnimationApi
@Composable @Composable
@@ -34,6 +31,7 @@ fun SettingsScreen() {
val albumRoute = rememberPlaylistOrAlbumRoute() val albumRoute = rememberPlaylistOrAlbumRoute()
val artistRoute = rememberArtistRoute() val artistRoute = rememberArtistRoute()
val appearanceRoute = rememberAppearanceRoute() val appearanceRoute = rememberAppearanceRoute()
val notificationRoute = rememberNotificationRoute()
val backupAndRestoreRoute = rememberBackupAndRestoreRoute() val backupAndRestoreRoute = rememberBackupAndRestoreRoute()
val otherRoute = rememberOtherRoute() val otherRoute = rememberOtherRoute()
val aboutRoute = rememberAboutRoute() val aboutRoute = rememberAboutRoute()
@@ -44,10 +42,11 @@ fun SettingsScreen() {
listenToGlobalEmitter = true, listenToGlobalEmitter = true,
transitionSpec = { transitionSpec = {
when (targetState.route) { when (targetState.route) {
appearanceRoute, backupAndRestoreRoute, otherRoute, aboutRoute -> leftSlide albumRoute, artistRoute -> fastFade
else -> when (initialState.route) { else -> when (initialState.route) {
appearanceRoute, backupAndRestoreRoute, otherRoute, aboutRoute -> rightSlide albumRoute, artistRoute -> fastFade
else -> fastFade null -> leftSlide
else -> rightSlide
} }
} }
} }
@@ -68,6 +67,10 @@ fun SettingsScreen() {
AppearanceScreen() AppearanceScreen()
} }
notificationRoute {
NotificationScreen()
}
backupAndRestoreRoute { backupAndRestoreRoute {
BackupAndRestoreScreen() BackupAndRestoreScreen()
} }
@@ -177,6 +180,14 @@ fun SettingsScreen() {
route = appearanceRoute, route = appearanceRoute,
) )
Entry(
color = colorPalette.cyan,
icon = R.drawable.notifications,
title = "Notification",
description = "Customize the notification appearance",
route = notificationRoute
)
Entry( Entry(
color = colorPalette.orange, color = colorPalette.orange,
icon = R.drawable.server, icon = R.drawable.server,
@@ -213,8 +224,6 @@ inline fun <reified T: Enum<T>>EnumValueSelectorEntry(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
crossinline valueText: (T) -> String = Enum<T>::name crossinline valueText: (T) -> String = Enum<T>::name
) { ) {
val typography = LocalTypography.current
var isShowingDialog by remember { var isShowingDialog by remember {
mutableStateOf(false) mutableStateOf(false)
} }
@@ -241,6 +250,53 @@ inline fun <reified T: Enum<T>>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 @Composable
@NonRestartableComposable @NonRestartableComposable
fun SettingsEntry( fun SettingsEntry(
@@ -319,4 +375,4 @@ fun SettingsEntryGroupText(
.padding(start = 24.dp, top = 24.dp) .padding(start = 24.dp, top = 24.dp)
.padding(horizontal = 32.dp) .padding(horizontal = 32.dp)
) )
} }

View File

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

View File

@@ -11,6 +11,13 @@ fun rememberAppearanceRoute(): Route0 {
} }
} }
@Composable
fun rememberNotificationRoute(): Route0 {
return remember {
Route0("NotificationRoute")
}
}
@Composable @Composable
fun rememberBackupAndRestoreRoute(): Route0 { fun rememberBackupAndRestoreRoute(): Route0 {
return remember { return remember {

View File

@@ -19,6 +19,7 @@ data class ColorPalette(
val green: Color, val green: Color,
val orange: Color, val orange: Color,
val magenta: Color, val magenta: Color,
val cyan: Color,
val primaryContainer: Color, val primaryContainer: Color,
val onPrimaryContainer: Color, val onPrimaryContainer: Color,
@@ -40,6 +41,7 @@ val DarkColorPalette = ColorPalette(
green = Color(0xff82b154), green = Color(0xff82b154),
orange = Color(0xffe9a033), orange = Color(0xffe9a033),
magenta = Color(0xffbb4da4), magenta = Color(0xffbb4da4),
cyan = Color(0xFF4DA5BB),
primaryContainer = Color(0xff4046bf), primaryContainer = Color(0xff4046bf),
onPrimaryContainer = Color.White, onPrimaryContainer = Color.White,
@@ -67,6 +69,7 @@ val LightColorPalette = ColorPalette(
green = Color(0xff7fbf40), green = Color(0xff7fbf40),
orange = Color(0xffe8730e), orange = Color(0xffe8730e),
magenta = Color(0xffbb4da4), magenta = Color(0xffbb4da4),
cyan = Color(0xFF4DBBB2),
primaryContainer = Color(0xff4046bf), primaryContainer = Color(0xff4046bf),
onPrimaryContainer = Color.White, onPrimaryContainer = Color.White,

View File

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

View File

@@ -23,6 +23,7 @@ class Preferences(holder: SharedPreferences) : SharedPreferences by holder {
var homePageSongCollection by preference("homePageSongCollection", SongCollection.MostPlayed) var homePageSongCollection by preference("homePageSongCollection", SongCollection.MostPlayed)
var thumbnailRoundness by preference("thumbnailRoundness", ThumbnailRoundness.Light) var thumbnailRoundness by preference("thumbnailRoundness", ThumbnailRoundness.Light)
var coilDiskCacheMaxSizeBytes by preference("coilDiskCacheMaxSizeBytes", 512L * 1024 * 1024) var coilDiskCacheMaxSizeBytes by preference("coilDiskCacheMaxSizeBytes", 512L * 1024 * 1024)
var displayLikeButtonInNotification by preference("displayLikeButtonInNotification", false)
} }
val Context.preferences: Preferences val Context.preferences: Preferences

View File

@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M352.92,80C288,80 256,144 256,144s-32,-64 -96.92,-64c-52.76,0 -94.54,44.14 -95.08,96.81 -1.1,109.33 86.73,187.08 183,252.42a16,16 0,0 0,18 0c96.26,-65.34 184.09,-143.09 183,-252.42 -0.54,-52.67 -42.32,-96.81 -95.08,-96.81z"
android:strokeLineJoin="round"
android:strokeWidth="32"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#FF000000"
android:pathData="M440.08,341.31c-1.66,-2 -3.29,-4 -4.89,-5.93 -22,-26.61 -35.31,-42.67 -35.31,-118 0,-39 -9.33,-71 -27.72,-95 -13.56,-17.73 -31.89,-31.18 -56.05,-41.12a3,3 0,0 1,-0.82 -0.67C306.6,51.49 282.82,32 256,32s-50.59,19.49 -59.28,48.56a3.13,3.13 0,0 1,-0.81 0.65c-56.38,23.21 -83.78,67.74 -83.78,136.14 0,75.36 -13.29,91.42 -35.31,118 -1.6,1.93 -3.23,3.89 -4.89,5.93a35.16,35.16 0,0 0,-4.65 37.62c6.17,13 19.32,21.07 34.33,21.07H410.5c14.94,0 28,-8.06 34.19,-21A35.17,35.17 0,0 0,440.08 341.31Z"/>
<path
android:fillColor="#FF000000"
android:pathData="M256,480a80.06,80.06 0,0 0,70.44 -42.13,4 4,0 0,0 -3.54,-5.87H189.12a4,4 0,0 0,-3.55 5.87A80.06,80.06 0,0 0,256 480Z"/>
</vector>