diff --git a/app/src/main/java/app/suhasdissa/vibeyou/backend/viewmodel/PlayerViewModel.kt b/app/src/main/java/app/suhasdissa/vibeyou/backend/viewmodel/PlayerViewModel.kt
index c2b00e32..6ef18fed 100644
--- a/app/src/main/java/app/suhasdissa/vibeyou/backend/viewmodel/PlayerViewModel.kt
+++ b/app/src/main/java/app/suhasdissa/vibeyou/backend/viewmodel/PlayerViewModel.kt
@@ -13,6 +13,7 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import androidx.media3.common.MediaItem
+import androidx.media3.common.PlaybackParameters
import androidx.media3.session.MediaController
import app.suhasdissa.vibeyou.MellowMusicApplication
import app.suhasdissa.vibeyou.backend.data.Song
@@ -102,6 +103,12 @@ class PlayerViewModel(
controller!!.playGracefully(song.asMediaItem)
}
+ fun setPlaybackParams(speed: Float, pitch: Float) {
+ controller!!.playbackParameters = PlaybackParameters(speed, pitch)
+ }
+
+ fun getPlaybackParams() = controller!!.playbackParameters
+
fun toggleFavourite(id: String) {
viewModelScope.launch {
val song = songDatabaseRepository.getSongById(id)
diff --git a/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/player/FullScreenPlayer.kt b/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/player/FullScreenPlayer.kt
index 3104d01c..8cfe8b31 100644
--- a/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/player/FullScreenPlayer.kt
+++ b/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/player/FullScreenPlayer.kt
@@ -27,6 +27,7 @@ import androidx.compose.material.icons.filled.RepeatOneOn
import androidx.compose.material.icons.filled.SkipNext
import androidx.compose.material.icons.filled.SkipPrevious
import androidx.compose.material.icons.rounded.ExpandMore
+import androidx.compose.material.icons.rounded.MoreVert
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.CircularProgressIndicator
@@ -77,6 +78,8 @@ fun FullScreenPlayer(
playerViewModel: PlayerViewModel = viewModel(factory = PlayerViewModel.Factory)
) {
var showQueueSheet by remember { mutableStateOf(false) }
+ var showSongOptions by remember { mutableStateOf(false) }
+
val view = LocalView.current
CenterAlignedTopAppBar(navigationIcon = {
IconButton({
@@ -89,9 +92,9 @@ fun FullScreenPlayer(
)
}
}, title = { Text(stringResource(R.string.now_playing)) }, actions = {
-// IconButton(onClick = { }) {
-// Icon(Icons.Rounded.MoreVert, contentDescription = stringResource(R.string.song_options))
-// }
+ IconButton(onClick = { showSongOptions = true }) {
+ Icon(Icons.Rounded.MoreVert, contentDescription = stringResource(R.string.song_options))
+ }
})
Divider(Modifier.fillMaxWidth())
Column(
@@ -167,6 +170,7 @@ fun FullScreenPlayer(
}
}
if (showQueueSheet) QueueSheet(onDismissRequest = { showQueueSheet = false })
+ if (showSongOptions) SongOptionsSheet(onDismissRequest = { showSongOptions = false })
}
@Composable
diff --git a/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/player/SongOptionsSheet.kt b/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/player/SongOptionsSheet.kt
new file mode 100644
index 00000000..cf3c7dde
--- /dev/null
+++ b/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/player/SongOptionsSheet.kt
@@ -0,0 +1,109 @@
+package app.suhasdissa.vibeyou.ui.screens.player
+
+import android.view.SoundEffectConstants
+import androidx.compose.foundation.layout.Column
+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.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.ExpandMore
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.Divider
+import androidx.compose.material3.ElevatedCard
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.Slider
+import androidx.compose.material3.Text
+import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import app.suhasdissa.vibeyou.R
+import app.suhasdissa.vibeyou.backend.viewmodel.PlayerViewModel
+import kotlinx.coroutines.launch
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SongOptionsSheet(
+ onDismissRequest: () -> Unit,
+ playerViewModel: PlayerViewModel = viewModel(factory = PlayerViewModel.Factory)
+) {
+ val playerSheetState = rememberModalBottomSheetState(
+ skipPartiallyExpanded = true
+ )
+ val scope = rememberCoroutineScope()
+ val view = LocalView.current
+ ModalBottomSheet(
+ onDismissRequest = onDismissRequest,
+ sheetState = playerSheetState,
+ shape = RoundedCornerShape(8.dp),
+ tonalElevation = 0.dp,
+ dragHandle = null
+ ) {
+ CenterAlignedTopAppBar(navigationIcon = {
+ IconButton({
+ view.playSoundEffect(SoundEffectConstants.CLICK)
+ scope.launch {
+ playerSheetState.hide()
+ }.invokeOnCompletion {
+ onDismissRequest()
+ }
+ }) {
+ Icon(
+ Icons.Rounded.ExpandMore,
+ contentDescription = null
+ )
+ }
+ }, title = { Text(stringResource(R.string.song_options)) })
+ Divider(Modifier.fillMaxWidth())
+
+ val scrollState = rememberScrollState()
+ Column(
+ modifier = Modifier
+ .verticalScroll(scrollState)
+ .padding(horizontal = 12.dp, vertical = 10.dp)
+ ) {
+ var speed by remember {
+ mutableFloatStateOf(playerViewModel.getPlaybackParams().speed)
+ }
+ var pitch by remember {
+ mutableFloatStateOf(playerViewModel.getPlaybackParams().pitch)
+ }
+ fun updatePlaybackParams() = playerViewModel.setPlaybackParams(speed, pitch)
+
+ Text(text = stringResource(R.string.playback_speed), fontSize = 16.sp)
+ Slider(value = speed, onValueChange = { speed = it; updatePlaybackParams() }, valueRange = 0.25f..4f, steps = 14)
+ ElevatedCard(modifier = Modifier.align(Alignment.CenterHorizontally)) {
+ Text(
+ modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp),
+ text = "%.2f".format(speed)
+ )
+ }
+ Text(text = stringResource(R.string.pitch), fontSize = 16.sp)
+ Slider(value = pitch, onValueChange = { pitch = it; updatePlaybackParams() }, valueRange = 0.5f..2f, steps = 5)
+ ElevatedCard(modifier = Modifier.align(Alignment.CenterHorizontally)) {
+ Text(
+ modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp),
+ text = "%.2f".format(pitch)
+ )
+ }
+ }
+ Spacer(modifier = Modifier.height(20.dp))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7dbece54..00fede2e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -62,4 +62,6 @@
Change music cache size
Music cache limit
Unlimited
+ Playback speed
+ Pitch
\ No newline at end of file