Move sleep timer to PlayerView menu (#98)

This commit is contained in:
vfsfitvnm
2022-07-13 23:21:58 +02:00
parent d905fb8614
commit 78cbd9d129
4 changed files with 149 additions and 142 deletions

View File

@@ -1,18 +1,22 @@
package it.vfsfitvnm.vimusic.ui.components.themed package it.vfsfitvnm.vimusic.ui.components.themed
import android.text.format.DateUtils
import androidx.compose.animation.AnimatedContentScope import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.with import androidx.compose.animation.with
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
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.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.route.empty import it.vfsfitvnm.route.empty
@@ -21,13 +25,18 @@ import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.models.DetailedSong import it.vfsfitvnm.vimusic.models.DetailedSong
import it.vfsfitvnm.vimusic.models.Playlist import it.vfsfitvnm.vimusic.models.Playlist
import it.vfsfitvnm.vimusic.models.SongPlaylistMap import it.vfsfitvnm.vimusic.models.SongPlaylistMap
import it.vfsfitvnm.vimusic.ui.components.ChunkyButton
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.Pager
import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
import it.vfsfitvnm.vimusic.ui.screens.rememberCreatePlaylistRoute import it.vfsfitvnm.vimusic.ui.screens.rememberCreatePlaylistRoute
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.* import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOf
@ExperimentalAnimationApi @ExperimentalAnimationApi
@@ -361,12 +370,128 @@ fun MediaItemMenu(
} }
onSetSleepTimer?.let { onSetSleepTimer -> onSetSleepTimer?.let { onSetSleepTimer ->
MenuEntry( val binder = LocalPlayerServiceBinder.current
icon = R.drawable.time, val typography = LocalTypography.current
text = "Sleep timer", val colorPalette = LocalColorPalette.current
var isShowingSleepTimerDialog by remember {
mutableStateOf(false)
}
val sleepTimerMillisLeft by (binder?.sleepTimerMillisLeft ?: flowOf(null))
.collectAsState(initial = null)
if (isShowingSleepTimerDialog) {
if (sleepTimerMillisLeft != null) {
ConfirmationDialog(
text = "Do you want to stop the sleep timer?",
cancelText = "No",
confirmText = "Stop",
onDismiss = {
isShowingSleepTimerDialog = false
},
onConfirm = {
binder?.cancelSleepTimer()
}
)
} else {
DefaultDialog(
onDismiss = {
isShowingSleepTimerDialog = false
}
) {
var hours by remember {
mutableStateOf(0)
}
var minutes by remember {
mutableStateOf(0)
}
BasicText(
text = "Set sleep timer",
style = typography.s.semiBold,
modifier = Modifier
.padding(vertical = 8.dp, horizontal = 24.dp)
)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(vertical = 16.dp)
) {
Pager(
selectedIndex = hours,
onSelectedIndex = {
hours = it
},
orientation = Orientation.Vertical,
modifier = Modifier
.padding(horizontal = 8.dp)
.height(92.dp)
) {
repeat(12) {
BasicText(
text = "$it h",
style = typography.xs.semiBold
)
}
}
Pager(
selectedIndex = minutes,
onSelectedIndex = {
minutes = it
},
orientation = Orientation.Vertical,
modifier = Modifier
.padding(horizontal = 8.dp)
.height(72.dp)
) {
repeat(4) {
BasicText(
text = "${it * 15} m",
style = typography.xs.semiBold
)
}
}
}
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier
.fillMaxWidth()
) {
ChunkyButton(
backgroundColor = Color.Transparent,
text = "Cancel",
textStyle = typography.xs.semiBold,
shape = RoundedCornerShape(36.dp),
onClick = { isShowingSleepTimerDialog = false }
)
ChunkyButton(
backgroundColor = colorPalette.primaryContainer,
text = "Set",
textStyle = typography.xs.semiBold.color(colorPalette.onPrimaryContainer),
shape = RoundedCornerShape(36.dp),
isEnabled = hours > 0 || minutes > 0,
onClick = { onClick = {
onDismiss() binder?.startSleepTimer((hours * 60 + minutes * 15) * 60 * 1000L)
onSetSleepTimer() isShowingSleepTimerDialog = false
}
)
}
}
}
}
MenuEntry(
icon = R.drawable.alarm,
text = "Sleep timer",
secondaryText = sleepTimerMillisLeft?.let { "${DateUtils.formatElapsedTime(it / 1000)} left" },
onClick = {
isShowingSleepTimerDialog = true
} }
) )
} }

View File

@@ -2,19 +2,15 @@ package it.vfsfitvnm.vimusic.ui.screens.settings
import android.content.Intent import android.content.Intent
import android.media.audiofx.AudioEffect import android.media.audiofx.AudioEffect
import android.text.format.DateUtils
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.* import androidx.compose.foundation.*
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.* import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@@ -22,18 +18,12 @@ import androidx.compose.ui.unit.dp
import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.components.ChunkyButton
import it.vfsfitvnm.vimusic.ui.components.Pager
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog
import it.vfsfitvnm.vimusic.ui.components.themed.DefaultDialog
import it.vfsfitvnm.vimusic.ui.screens.* import it.vfsfitvnm.vimusic.ui.screens.*
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.LocalPreferences import it.vfsfitvnm.vimusic.utils.LocalPreferences
import it.vfsfitvnm.vimusic.utils.color
import it.vfsfitvnm.vimusic.utils.semiBold import it.vfsfitvnm.vimusic.utils.semiBold
import kotlinx.coroutines.flow.flowOf
@ExperimentalAnimationApi @ExperimentalAnimationApi
@@ -68,118 +58,6 @@ fun PlayerSettingsScreen() {
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
} }
val sleepTimerMillisLeft by (binder?.sleepTimerMillisLeft
?: flowOf(null)).collectAsState(initial = null)
var isShowingSleepTimerDialog by remember {
mutableStateOf(false)
}
if (isShowingSleepTimerDialog) {
if (sleepTimerMillisLeft != null) {
ConfirmationDialog(
text = "Do you want to stop the sleep timer?",
cancelText = "No",
confirmText = "Stop",
onDismiss = {
isShowingSleepTimerDialog = false
},
onConfirm = {
binder?.cancelSleepTimer()
}
)
} else {
DefaultDialog(
onDismiss = {
isShowingSleepTimerDialog = false
},
modifier = Modifier
) {
var hours by remember {
mutableStateOf(0)
}
var minutes by remember {
mutableStateOf(0)
}
BasicText(
text = "Set sleep timer",
style = typography.s.semiBold,
modifier = Modifier
.padding(vertical = 8.dp, horizontal = 24.dp)
)
Row(
modifier = Modifier
.padding(vertical = 16.dp)
) {
Pager(
selectedIndex = hours,
onSelectedIndex = {
hours = it
},
orientation = Orientation.Vertical,
modifier = Modifier
.padding(horizontal = 8.dp)
.height(72.dp)
) {
repeat(12) {
BasicText(
text = "$it h",
style = typography.xs.semiBold
)
}
}
Pager(
selectedIndex = minutes,
onSelectedIndex = {
minutes = it
},
orientation = Orientation.Vertical,
modifier = Modifier
.padding(horizontal = 8.dp)
.height(72.dp)
) {
repeat(4) {
BasicText(
text = "${it * 15} m",
style = typography.xs.semiBold
)
}
}
}
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier
.fillMaxWidth()
) {
ChunkyButton(
backgroundColor = Color.Transparent,
text = "Cancel",
textStyle = typography.xs.semiBold,
shape = RoundedCornerShape(36.dp),
onClick = { isShowingSleepTimerDialog = false }
)
ChunkyButton(
backgroundColor = colorPalette.primaryContainer,
text = "Set",
textStyle = typography.xs.semiBold.color(colorPalette.onPrimaryContainer),
shape = RoundedCornerShape(36.dp),
isEnabled = hours > 0 || minutes > 0,
onClick = {
binder?.startSleepTimer((hours * 60 + minutes * 15) * 60 * 1000L)
isShowingSleepTimerDialog = false
}
)
}
}
}
}
Column( Column(
modifier = Modifier modifier = Modifier
.background(colorPalette.background) .background(colorPalette.background)
@@ -262,17 +140,6 @@ fun PlayerSettingsScreen() {
} }
} }
) )
SettingsEntryGroupText(title = "OTHER")
SettingsEntry(
title = "Sleep timer",
text = sleepTimerMillisLeft?.let { "${DateUtils.formatElapsedTime(it / 1000)} left" }
?: "Stop the music after a period of time",
onClick = {
isShowingSleepTimerDialog = true
}
)
} }
} }
} }

View File

@@ -75,7 +75,6 @@ fun PlayerView(
player ?: return player ?: return
playerState?.mediaItem ?: return playerState?.mediaItem ?: return
BottomSheet( BottomSheet(
state = layoutState, state = layoutState,
modifier = modifier, modifier = modifier,
@@ -288,6 +287,7 @@ fun PlayerView(
.show() .show()
} }
}, },
onSetSleepTimer = {},
onDismiss = menuState::hide, onDismiss = menuState::hide,
onGlobalRouteEmitted = layoutState.collapse, onGlobalRouteEmitted = layoutState.collapse,
) )

View File

@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#FF000000"
android:pathData="M153.59,110.46A21.41,21.41 0,0 0,152.48 79h0A62.67,62.67 0,0 0,112 64l-3.27,0.09 -0.48,0C74.4,66.15 48,95.55 48.07,131c0,19 8,29.06 14.32,37.11a20.61,20.61 0,0 0,14.7 7.8c0.26,0 0.7,0.05 2,0.05a19.06,19.06 0,0 0,13.75 -5.89Z"/>
<path
android:fillColor="#FF000000"
android:pathData="M403.79,64.11l-3.27,-0.1H400a62.67,62.67 0,0 0,-40.52 15,21.41 21.41,0 0,0 -1.11,31.44l60.77,59.65A19.06,19.06 0,0 0,432.93 176c1.28,0 1.72,0 2,-0.05a20.61,20.61 0,0 0,14.69 -7.8c6.36,-8.05 14.28,-18.08 14.32,-37.11C464,95.55 437.6,66.15 403.79,64.11Z"/>
<path
android:fillColor="#FF000000"
android:pathData="M256.07,96c-97,0 -176,78.95 -176,176a175.23,175.23 0,0 0,40.81 112.56L84.76,420.69a16,16 0,1 0,22.63 22.62l36.12,-36.12a175.63,175.63 0,0 0,225.12 0l36.13,36.12a16,16 0,1 0,22.63 -22.62l-36.13,-36.13A175.17,175.17 0,0 0,432.07 272C432.07,175 353.12,96 256.07,96ZM272.07,272a16,16 0,0 1,-16 16h-80a16,16 0,0 1,0 -32h64L240.07,160a16,16 0,0 1,32 0Z"/>
</vector>