Show radio loading state
This commit is contained in:
@@ -17,6 +17,9 @@ import android.support.v4.media.session.MediaSessionCompat
|
|||||||
import android.support.v4.media.session.PlaybackStateCompat
|
import android.support.v4.media.session.PlaybackStateCompat
|
||||||
import android.support.v4.media.session.PlaybackStateCompat.*
|
import android.support.v4.media.session.PlaybackStateCompat.*
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat.startForegroundService
|
import androidx.core.content.ContextCompat.startForegroundService
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
@@ -541,6 +544,9 @@ class PlayerService : Service(), Player.Listener, PlaybackStatsListener.Callback
|
|||||||
|
|
||||||
private var radioJob: Job? = null
|
private var radioJob: Job? = null
|
||||||
|
|
||||||
|
var isLoadingRadio by mutableStateOf(false)
|
||||||
|
private set
|
||||||
|
|
||||||
fun startSleepTimer(delayMillis: Long) {
|
fun startSleepTimer(delayMillis: Long) {
|
||||||
timerJob?.cancel()
|
timerJob?.cancel()
|
||||||
|
|
||||||
@@ -581,6 +587,7 @@ class PlayerService : Service(), Player.Listener, PlaybackStatsListener.Callback
|
|||||||
endpoint?.playlistSetVideoId,
|
endpoint?.playlistSetVideoId,
|
||||||
endpoint?.params
|
endpoint?.params
|
||||||
).let {
|
).let {
|
||||||
|
isLoadingRadio = true
|
||||||
radioJob = coroutineScope.launch(Dispatchers.Main) {
|
radioJob = coroutineScope.launch(Dispatchers.Main) {
|
||||||
if (justAdd) {
|
if (justAdd) {
|
||||||
player.addMediaItems(it.process().drop(1))
|
player.addMediaItems(it.process().drop(1))
|
||||||
@@ -588,11 +595,13 @@ class PlayerService : Service(), Player.Listener, PlaybackStatsListener.Callback
|
|||||||
player.forcePlayFromBeginning(it.process())
|
player.forcePlayFromBeginning(it.process())
|
||||||
}
|
}
|
||||||
radio = it
|
radio = it
|
||||||
|
isLoadingRadio = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopRadio() {
|
fun stopRadio() {
|
||||||
|
isLoadingRadio = false
|
||||||
radioJob?.cancel()
|
radioJob?.cancel()
|
||||||
radio = null
|
radio = null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import androidx.compose.animation.fadeIn
|
|||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
@@ -18,6 +16,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
@@ -27,6 +26,8 @@ import androidx.compose.ui.platform.LocalHapticFeedback
|
|||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
|
import com.valentinilk.shimmer.ShimmerBounds
|
||||||
|
import com.valentinilk.shimmer.rememberShimmer
|
||||||
import it.vfsfitvnm.reordering.rememberReorderingState
|
import it.vfsfitvnm.reordering.rememberReorderingState
|
||||||
import it.vfsfitvnm.reordering.verticalDragAfterLongPressToReorder
|
import it.vfsfitvnm.reordering.verticalDragAfterLongPressToReorder
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
@@ -35,6 +36,7 @@ import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
|
|||||||
import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
|
import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.MusicBars
|
import it.vfsfitvnm.vimusic.ui.components.MusicBars
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.QueuedMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.QueuedMediaItemMenu
|
||||||
|
import it.vfsfitvnm.vimusic.ui.screens.SmallSongItemShimmer
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LightColorPalette
|
import it.vfsfitvnm.vimusic.ui.styling.LightColorPalette
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
|
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
|
||||||
import it.vfsfitvnm.vimusic.utils.PlayerState
|
import it.vfsfitvnm.vimusic.utils.PlayerState
|
||||||
@@ -48,7 +50,7 @@ fun CurrentPlaylistView(
|
|||||||
onGlobalRouteEmitted: () -> Unit,
|
onGlobalRouteEmitted: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val player = LocalPlayerServiceBinder.current?.player
|
val binder = LocalPlayerServiceBinder.current
|
||||||
val hapticFeedback = LocalHapticFeedback.current
|
val hapticFeedback = LocalHapticFeedback.current
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
val colorPalette = LocalColorPalette.current
|
val colorPalette = LocalColorPalette.current
|
||||||
@@ -88,13 +90,13 @@ fun CurrentPlaylistView(
|
|||||||
onClick = {
|
onClick = {
|
||||||
if (isPlayingThisMediaItem) {
|
if (isPlayingThisMediaItem) {
|
||||||
if (isPaused) {
|
if (isPaused) {
|
||||||
player?.play()
|
binder?.player?.play()
|
||||||
} else {
|
} else {
|
||||||
player?.pause()
|
binder?.player?.pause()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
player?.playWhenReady = true
|
binder?.player?.playWhenReady = true
|
||||||
player?.seekToDefaultPosition(index)
|
binder?.player?.seekToDefaultPosition(index)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
menuContent = {
|
menuContent = {
|
||||||
@@ -113,7 +115,10 @@ fun CurrentPlaylistView(
|
|||||||
Box(
|
Box(
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(color = Color.Black.copy(alpha = 0.25f), shape = ThumbnailRoundness.shape)
|
.background(
|
||||||
|
color = Color.Black.copy(alpha = 0.25f),
|
||||||
|
shape = ThumbnailRoundness.shape
|
||||||
|
)
|
||||||
.size(54.dp)
|
.size(54.dp)
|
||||||
) {
|
) {
|
||||||
if (isPaused) {
|
if (isPaused) {
|
||||||
@@ -146,56 +151,29 @@ fun CurrentPlaylistView(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
onDragEnd = { reachedIndex ->
|
onDragEnd = { reachedIndex ->
|
||||||
player?.moveMediaItem(index, reachedIndex)
|
binder?.player?.moveMediaItem(index, reachedIndex)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (YoutubePlayer.Radio.isActive && player != null) {
|
item {
|
||||||
// when (val nextContinuation = YoutubePlayer.Radio.nextContinuation) {
|
if (binder?.isLoadingRadio == true) {
|
||||||
// is Outcome.Loading, is Outcome.Success<*> -> {
|
val shimmer = rememberShimmer(shimmerBounds = ShimmerBounds.Window)
|
||||||
// if (nextContinuation is Outcome.Success<*>) {
|
|
||||||
// item {
|
Column {
|
||||||
// SideEffect {
|
repeat(3) { index ->
|
||||||
// coroutineScope.launch {
|
SmallSongItemShimmer(
|
||||||
// YoutubePlayer.Radio.process(
|
shimmer = shimmer,
|
||||||
// playerState.mediaController,
|
thumbnailSizeDp = 54.dp,
|
||||||
// force = true
|
modifier = Modifier
|
||||||
// )
|
.alpha(1f - index * 0.125f)
|
||||||
// }
|
.fillMaxWidth()
|
||||||
// }
|
.padding(vertical = 4.dp, horizontal = 16.dp)
|
||||||
// }
|
)
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
// items(count = 3, key = { it }) { index ->
|
}
|
||||||
// SmallSongItemShimmer(
|
}
|
||||||
// shimmer = shimmer,
|
|
||||||
// thumbnailSizeDp = 54.dp,
|
|
||||||
// modifier = Modifier
|
|
||||||
// .alpha(1f - index * 0.125f)
|
|
||||||
// .fillMaxWidth()
|
|
||||||
// .padding(vertical = 4.dp, horizontal = 16.dp)
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// is Outcome.Error -> item {
|
|
||||||
// Error(
|
|
||||||
// error = nextContinuation
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// is Outcome.Recovered<*> -> item {
|
|
||||||
// Error(
|
|
||||||
// error = nextContinuation.error,
|
|
||||||
// onRetry = {
|
|
||||||
// coroutineScope.launch {
|
|
||||||
// YoutubePlayer.Radio.process(playerState.mediaController, force = true)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// else -> {}
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package it.vfsfitvnm.vimusic.utils
|
package it.vfsfitvnm.vimusic.utils
|
||||||
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import it.vfsfitvnm.youtubemusic.Outcome
|
|
||||||
import it.vfsfitvnm.youtubemusic.YouTube
|
import it.vfsfitvnm.youtubemusic.YouTube
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -13,13 +11,9 @@ data class YouTubeRadio(
|
|||||||
private val playlistSetVideoId: String? = null,
|
private val playlistSetVideoId: String? = null,
|
||||||
private val parameters: String? = null
|
private val parameters: String? = null
|
||||||
) {
|
) {
|
||||||
var nextContinuation by mutableStateOf<Outcome<String?>>(Outcome.Initial)
|
private var nextContinuation: String? = null
|
||||||
|
|
||||||
suspend fun process(): List<MediaItem> {
|
suspend fun process(): List<MediaItem> {
|
||||||
val token = nextContinuation.valueOrNull
|
|
||||||
|
|
||||||
nextContinuation = Outcome.Loading
|
|
||||||
|
|
||||||
var mediaItems: List<MediaItem>? = null
|
var mediaItems: List<MediaItem>? = null
|
||||||
|
|
||||||
nextContinuation = withContext(Dispatchers.IO) {
|
nextContinuation = withContext(Dispatchers.IO) {
|
||||||
@@ -28,13 +22,12 @@ data class YouTubeRadio(
|
|||||||
playlistId = playlistId,
|
playlistId = playlistId,
|
||||||
params = parameters,
|
params = parameters,
|
||||||
playlistSetVideoId = playlistSetVideoId,
|
playlistSetVideoId = playlistSetVideoId,
|
||||||
continuation = token
|
continuation = nextContinuation
|
||||||
)
|
)
|
||||||
}.map { nextResult ->
|
}.map { nextResult ->
|
||||||
mediaItems = nextResult.items?.map(YouTube.Item.Song::asMediaItem)
|
mediaItems = nextResult.items?.map(YouTube.Item.Song::asMediaItem)
|
||||||
|
nextResult.continuation?.takeUnless { nextContinuation == nextResult.continuation }
|
||||||
nextResult.continuation?.takeUnless { token == nextResult.continuation }
|
}.recoverWith(nextContinuation).valueOrNull
|
||||||
}.recoverWith(token)
|
|
||||||
|
|
||||||
return mediaItems ?: emptyList()
|
return mediaItems ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user