Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MOBILE-209: Handle mini music player like spotify #511

Merged
merged 13 commits into from
Dec 27, 2024
Merged
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ dependencies {
implementation(libs.androidx.work.runtime.ktx)
implementation(libs.androidx.paging.runtime)
implementation(libs.androidx.paging.compose)
implementation(libs.androidx.palette.ktx)

//Room DB
implementation(libs.androidx.room.runtime)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
package org.listenbrainz.android.ui.components

import androidx.annotation.FloatRange
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material.Slider
import androidx.compose.material.SliderDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.progressBarRangeInfo
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import org.listenbrainz.android.R

@Composable
Expand All @@ -25,6 +38,61 @@ fun SeekBar(
thumbColor = colorResource(id = R.color.app_bg),
activeTrackColor = colorResource(id = R.color.bp_color_primary)
)

)
}
}

@Composable
fun CustomSeekBar(
modifier: Modifier = Modifier,
@FloatRange(from = 0.0, to = 1.0)
progress: Float,
onValueChange: (Float) -> Unit,
remainingProgressColor: Color = Color.Transparent
) {
val range = 0f..1f

Box(
modifier = modifier
.fillMaxWidth()
.semantics(mergeDescendants = true) {
progressBarRangeInfo = ProgressBarRangeInfo(progress, range)
}
.pointerInput(Unit) {
detectDragGestures(
onDragStart = { offset ->
val width = size.width.toFloat()
val newProgress = (offset.x / width).coerceIn(range)
onValueChange(newProgress)
},
onDrag = { change, _ ->
val width = size.width.toFloat()
val newProgress = (change.position.x / width).coerceIn(range)
onValueChange(newProgress)
}
)
}
.pointerInput(Unit) {
detectTapGestures { offset ->
val width = size.width.toFloat()
val newProgress = (offset.x / width).coerceIn(range)
onValueChange(newProgress)
}
}
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(4.dp)
.graphicsLayer {
alpha = 0.2f
}
.background(remainingProgressColor)
)
Box(
modifier = Modifier
.fillMaxWidth(progress)
.height(4.dp)
.background(colorResource(id = R.color.bp_color_primary))
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
Expand All @@ -76,6 +77,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.lerp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import kotlinx.coroutines.launch
Expand All @@ -86,6 +88,7 @@ import org.listenbrainz.android.model.Playlist.Companion.recentlyPlayed
import org.listenbrainz.android.model.RepeatMode
import org.listenbrainz.android.model.Song
import org.listenbrainz.android.model.feed.FeedListenArtist
import org.listenbrainz.android.ui.components.CustomSeekBar
import org.listenbrainz.android.ui.components.ListenCardSmall
import org.listenbrainz.android.ui.components.PlayPauseIcon
import org.listenbrainz.android.ui.components.SeekBar
Expand All @@ -108,24 +111,24 @@ fun BrainzPlayerBackDropScreen(
paddingValues: PaddingValues,
backLayerContent: @Composable () -> Unit
) {
val isShuffled by brainzPlayerViewModel.isShuffled.collectAsState()
val isShuffled by brainzPlayerViewModel.isShuffled.collectAsStateWithLifecycle()
val currentlyPlayingSong =
brainzPlayerViewModel.currentlyPlayingSong.collectAsState().value.toSong
brainzPlayerViewModel.currentlyPlayingSong.collectAsStateWithLifecycle().value.toSong
var maxDelta by rememberSaveable {
mutableFloatStateOf(0F)
}
val repeatMode by brainzPlayerViewModel.repeatMode.collectAsState()
val repeatMode by brainzPlayerViewModel.repeatMode.collectAsStateWithLifecycle()

/** 56.dp is default bottom navigation height. 80.dp is our mini player's height. */
val headerHeight by animateDpAsState(targetValue = if (currentlyPlayingSong.title == "null" && currentlyPlayingSong.artist == "null") 56.dp else 136.dp)
/** 56.dp is default bottom navigation height. 70.dp is our mini player's height. */
val headerHeight by animateDpAsState(targetValue = if (currentlyPlayingSong.title == "null" && currentlyPlayingSong.artist == "null") 56.dp else 126.dp)
val isPlaying = brainzPlayerViewModel.isPlaying.collectAsState().value

BackdropScaffold(
modifier = Modifier.padding(top = paddingValues.calculateTopPadding()),
frontLayerShape = RectangleShape,
backLayerBackgroundColor = MaterialTheme.colorScheme.background,
frontLayerScrimColor = Color.Unspecified,
headerHeight = if (isPlaying) headerHeight else 56.dp, // 136.dp is optimal header height.
headerHeight = headerHeight, // 126.dp is optimal header height.
peekHeight = 0.dp,
scaffoldState = backdropScaffoldState,
backLayerContent = {
Expand Down Expand Up @@ -257,14 +260,17 @@ fun PlayerScreen(
item {
Box {
val progress by brainzPlayerViewModel.progress.collectAsState()
SeekBar(
CustomSeekBar(
modifier = Modifier
.height(10.dp)
.fillMaxWidth(0.98F)
.padding(horizontal = 20.dp),
progress = progress,
onValueChange = brainzPlayerViewModel::onSeek,
onValueChanged = brainzPlayerViewModel::onSeeked
onValueChange = { newProgress ->
brainzPlayerViewModel.onSeek(newProgress)
brainzPlayerViewModel.onSeeked()
},
remainingProgressColor = colorResource(id = R.color.bp_color_primary)
)
}
Row(
Expand Down Expand Up @@ -457,8 +463,7 @@ fun PlayerScreen(
) { index, song ->
val isChecked = checkedSongs.contains(song)
BoxWithConstraints {
val maxWidth =
(maxWidth - 70.dp)
val maxWidth = ([email protected] - 70.dp)
Row(
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
Expand Down Expand Up @@ -530,14 +535,6 @@ fun PlayerScreen(
Spacer(modifier = Modifier.height(56.dp))
}
}

// TODO: fix this
val cache = App.context?.let { CacheService<Song>(it, RECENTLY_PLAYED_KEY) }
cache?.saveData(currentlyPlayingSong, Song::class.java)
val data = cache?.getData(Song::class.java)
if (data != null) {
recentlyPlayed.items = data.filter { it.title != "null" }.toList().reversed()
}
}

@OptIn(ExperimentalFoundationApi::class)
Expand Down Expand Up @@ -624,3 +621,4 @@ fun BrainzPlayerBackDropScreenPreview() {
) {}
}


Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ fun BrainzPlayerScreen() {
topAlbums.add(Album())

Column(
modifier = Modifier
.fillMaxSize()
modifier = Modifier.fillMaxSize()
) {
Navigation(albums = albums, previewAlbums = topAlbums, artists = artists, previewArtists = topArtists, playlists, songsPlayedToday, songsPlayedThisWeek ,topRecents ,songs, albumSongsMap)
}
Expand All @@ -108,7 +107,8 @@ fun BrainzPlayerHomeScreen(
brainzPlayerViewModel.currentlyPlayingSong.collectAsState().value.toSong
val isPlaying = brainzPlayerViewModel.isPlaying
Column {
Row(modifier = Modifier
Row(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState())
.background(
Expand All @@ -118,7 +118,8 @@ fun BrainzPlayerHomeScreen(
Color.Transparent
)
)
)) {
)
) {
Spacer(modifier = Modifier.width(ListenBrainzTheme.paddings.chipsHorizontal / 2))
repeat(5) { position ->
ElevatedSuggestionChip(
Expand Down
Loading
Loading