Move sleep timer to PlayerView menu (#98)
This commit is contained in:
parent
d905fb8614
commit
78cbd9d129
|
@ -1,18 +1,22 @@
|
|||
package it.vfsfitvnm.vimusic.ui.components.themed
|
||||
|
||||
import android.text.format.DateUtils
|
||||
import androidx.compose.animation.AnimatedContentScope
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.with
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
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
|
||||
import androidx.media3.common.MediaItem
|
||||
import it.vfsfitvnm.route.RouteHandler
|
||||
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.Playlist
|
||||
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.Pager
|
||||
import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute
|
||||
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
|
||||
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.youtubemusic.models.NavigationEndpoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
|
@ -361,12 +370,128 @@ fun MediaItemMenu(
|
|||
}
|
||||
|
||||
onSetSleepTimer?.let { onSetSleepTimer ->
|
||||
val binder = LocalPlayerServiceBinder.current
|
||||
val typography = LocalTypography.current
|
||||
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 = {
|
||||
binder?.startSleepTimer((hours * 60 + minutes * 15) * 60 * 1000L)
|
||||
isShowingSleepTimerDialog = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuEntry(
|
||||
icon = R.drawable.time,
|
||||
icon = R.drawable.alarm,
|
||||
text = "Sleep timer",
|
||||
secondaryText = sleepTimerMillisLeft?.let { "${DateUtils.formatElapsedTime(it / 1000)} left" },
|
||||
onClick = {
|
||||
onDismiss()
|
||||
onSetSleepTimer()
|
||||
isShowingSleepTimerDialog = true
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,19 +2,15 @@ package it.vfsfitvnm.vimusic.ui.screens.settings
|
|||
|
||||
import android.content.Intent
|
||||
import android.media.audiofx.AudioEffect
|
||||
import android.text.format.DateUtils
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
|
@ -22,18 +18,12 @@ import androidx.compose.ui.unit.dp
|
|||
import it.vfsfitvnm.route.RouteHandler
|
||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||
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.themed.ConfirmationDialog
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.DefaultDialog
|
||||
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.color
|
||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
|
@ -68,118 +58,6 @@ fun PlayerSettingsScreen() {
|
|||
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(
|
||||
modifier = Modifier
|
||||
.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
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,6 @@ fun PlayerView(
|
|||
player ?: return
|
||||
playerState?.mediaItem ?: return
|
||||
|
||||
|
||||
BottomSheet(
|
||||
state = layoutState,
|
||||
modifier = modifier,
|
||||
|
@ -288,6 +287,7 @@ fun PlayerView(
|
|||
.show()
|
||||
}
|
||||
},
|
||||
onSetSleepTimer = {},
|
||||
onDismiss = menuState::hide,
|
||||
onGlobalRouteEmitted = layoutState.collapse,
|
||||
)
|
||||
|
|
15
app/src/main/res/drawable/alarm.xml
Normal file
15
app/src/main/res/drawable/alarm.xml
Normal 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>
|
Loading…
Reference in a new issue