Drop androidx.media3
This commit is contained in:
@@ -6,14 +6,15 @@ import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
@@ -25,33 +26,29 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.media3.common.Player
|
||||
import com.valentinilk.shimmer.ShimmerBounds
|
||||
import com.valentinilk.shimmer.rememberShimmer
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
|
||||
import it.vfsfitvnm.vimusic.ui.components.Error
|
||||
import it.vfsfitvnm.vimusic.ui.components.MusicBars
|
||||
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.LocalColorPalette
|
||||
import it.vfsfitvnm.vimusic.utils.LocalYoutubePlayer
|
||||
import it.vfsfitvnm.vimusic.utils.YoutubePlayer
|
||||
import it.vfsfitvnm.reordering.rememberReorderingState
|
||||
import it.vfsfitvnm.reordering.verticalDragAfterLongPressToReorder
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
|
||||
import it.vfsfitvnm.youtubemusic.Outcome
|
||||
import kotlinx.coroutines.launch
|
||||
import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
|
||||
import it.vfsfitvnm.vimusic.ui.components.MusicBars
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.QueuedMediaItemMenu
|
||||
import it.vfsfitvnm.vimusic.ui.styling.LightColorPalette
|
||||
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
|
||||
import it.vfsfitvnm.vimusic.utils.PlayerState
|
||||
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun CurrentPlaylistView(
|
||||
player: Player?,
|
||||
playerState: PlayerState?,
|
||||
layoutState: BottomSheetState,
|
||||
onGlobalRouteEmitted: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val hapticFeedback = LocalHapticFeedback.current
|
||||
val density = LocalDensity.current
|
||||
val player = LocalYoutubePlayer.current
|
||||
val colorPalette = LocalColorPalette.current
|
||||
|
||||
val thumbnailSize = remember {
|
||||
@@ -61,30 +58,26 @@ fun CurrentPlaylistView(
|
||||
}
|
||||
|
||||
val isPaused by derivedStateOf {
|
||||
player?.playbackState == Player.STATE_ENDED || player?.playWhenReady == false
|
||||
playerState?.playbackState == Player.STATE_ENDED || playerState?.playWhenReady == false
|
||||
}
|
||||
|
||||
val shimmer = rememberShimmer(shimmerBounds = ShimmerBounds.Window)
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val lazyListState =
|
||||
rememberLazyListState(initialFirstVisibleItemIndex = player?.mediaItemIndex ?: 0)
|
||||
rememberLazyListState(initialFirstVisibleItemIndex = playerState?.mediaItemIndex ?: 0)
|
||||
|
||||
val reorderingState = rememberReorderingState(player?.mediaItems ?: emptyList())
|
||||
val reorderingState = rememberReorderingState(playerState?.mediaItems ?: emptyList())
|
||||
|
||||
LazyColumn(
|
||||
state = lazyListState,
|
||||
modifier = modifier
|
||||
.nestedScroll(remember {
|
||||
layoutState.nestedScrollConnection(player?.mediaItemIndex == 0)
|
||||
layoutState.nestedScrollConnection(playerState?.mediaItemIndex == 0)
|
||||
})
|
||||
) {
|
||||
itemsIndexed(
|
||||
items = player?.mediaItems ?: emptyList()
|
||||
items = playerState?.mediaItems ?: emptyList()
|
||||
) { index, mediaItem ->
|
||||
val isPlayingThisMediaItem by derivedStateOf {
|
||||
player?.mediaItemIndex == index
|
||||
playerState?.mediaItemIndex == index
|
||||
}
|
||||
|
||||
SongItem(
|
||||
@@ -93,13 +86,13 @@ fun CurrentPlaylistView(
|
||||
onClick = {
|
||||
if (isPlayingThisMediaItem) {
|
||||
if (isPaused) {
|
||||
player?.mediaController?.play()
|
||||
player?.play()
|
||||
} else {
|
||||
player?.mediaController?.pause()
|
||||
player?.pause()
|
||||
}
|
||||
} else {
|
||||
player?.mediaController?.playWhenReady = true
|
||||
player?.mediaController?.seekToDefaultPosition(index)
|
||||
player?.playWhenReady = true
|
||||
player?.seekToDefaultPosition(index)
|
||||
}
|
||||
},
|
||||
menuContent = {
|
||||
@@ -151,7 +144,7 @@ fun CurrentPlaylistView(
|
||||
)
|
||||
},
|
||||
onDragEnd = { reachedIndex ->
|
||||
player?.mediaController?.moveMediaItem(index, reachedIndex)
|
||||
player?.moveMediaItem(index, reachedIndex)
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -165,7 +158,7 @@ fun CurrentPlaylistView(
|
||||
// SideEffect {
|
||||
// coroutineScope.launch {
|
||||
// YoutubePlayer.Radio.process(
|
||||
// player.mediaController,
|
||||
// playerState.mediaController,
|
||||
// force = true
|
||||
// )
|
||||
// }
|
||||
@@ -194,7 +187,7 @@ fun CurrentPlaylistView(
|
||||
// error = nextContinuation.error,
|
||||
// onRetry = {
|
||||
// coroutineScope.launch {
|
||||
// YoutubePlayer.Radio.process(player.mediaController, force = true)
|
||||
// YoutubePlayer.Radio.process(playerState.mediaController, force = true)
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
|
||||
@@ -20,6 +20,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.media3.common.Player
|
||||
import it.vfsfitvnm.route.Route
|
||||
import it.vfsfitvnm.route.RouteHandler
|
||||
import it.vfsfitvnm.route.empty
|
||||
@@ -44,13 +45,13 @@ import kotlinx.coroutines.withContext
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun PlayerBottomSheet(
|
||||
player: Player?,
|
||||
playerState: PlayerState?,
|
||||
layoutState: BottomSheetState,
|
||||
song: Song?,
|
||||
onGlobalRouteEmitted: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val player = LocalYoutubePlayer.current ?: return
|
||||
|
||||
val colorPalette = LocalColorPalette.current
|
||||
val typography = LocalTypography.current
|
||||
|
||||
@@ -60,7 +61,7 @@ fun PlayerBottomSheet(
|
||||
|
||||
var route by rememberRoute()
|
||||
|
||||
var nextOutcome by remember(player.mediaItem!!.mediaId) {
|
||||
var nextOutcome by remember(playerState?.mediaItem?.mediaId) {
|
||||
mutableStateOf<Outcome<YouTube.NextResult>>(Outcome.Initial)
|
||||
}
|
||||
|
||||
@@ -183,13 +184,19 @@ fun PlayerBottomSheet(
|
||||
coroutineScope.launch(Dispatchers.Main) {
|
||||
lyricsOutcome = Outcome.Loading
|
||||
|
||||
val mediaItem = player?.currentMediaItem!!
|
||||
|
||||
if (nextOutcome.isEvaluable) {
|
||||
nextOutcome = Outcome.Loading
|
||||
|
||||
|
||||
val mediaItemIndex = player.currentMediaItemIndex
|
||||
|
||||
nextOutcome = withContext(Dispatchers.IO) {
|
||||
YouTube.next(
|
||||
player.mediaItem!!.mediaId,
|
||||
player.mediaItem!!.mediaMetadata.extras?.getString("playlistId"),
|
||||
player.mediaItemIndex
|
||||
mediaItem.mediaId,
|
||||
mediaItem.mediaMetadata.extras?.getString("playlistId"),
|
||||
mediaItemIndex
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -200,7 +207,7 @@ fun PlayerBottomSheet(
|
||||
lyrics ?: ""
|
||||
}.map { lyrics ->
|
||||
withContext(Dispatchers.IO) {
|
||||
(song ?: player.mediaItem?.let(Database::insert))?.let {
|
||||
(song ?: mediaItem.let(Database::insert)).let {
|
||||
Database.update(it.copy(lyrics = lyrics))
|
||||
}
|
||||
}
|
||||
@@ -209,7 +216,7 @@ fun PlayerBottomSheet(
|
||||
}
|
||||
},
|
||||
onSearchOnline = {
|
||||
player.mediaMetadata.let {
|
||||
player?.mediaMetadata?.let {
|
||||
context.startActivity(Intent(Intent.ACTION_WEB_SEARCH).apply {
|
||||
putExtra(
|
||||
SearchManager.QUERY,
|
||||
@@ -219,8 +226,9 @@ fun PlayerBottomSheet(
|
||||
}
|
||||
},
|
||||
onLyricsUpdate = { lyrics ->
|
||||
val mediaItem = player?.currentMediaItem
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
(song ?: player.mediaItem?.let(Database::insert))?.let {
|
||||
(song ?: mediaItem?.let(Database::insert))?.let {
|
||||
Database.update(it.copy(lyrics = lyrics))
|
||||
}
|
||||
}
|
||||
@@ -230,6 +238,8 @@ fun PlayerBottomSheet(
|
||||
|
||||
host {
|
||||
CurrentPlaylistView(
|
||||
player = player,
|
||||
playerState = playerState,
|
||||
layoutState = layoutState,
|
||||
onGlobalRouteEmitted = onGlobalRouteEmitted,
|
||||
modifier = Modifier
|
||||
|
||||
@@ -30,15 +30,16 @@ import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.datasource.cache.Cache
|
||||
import androidx.media3.datasource.cache.CacheSpan
|
||||
import coil.compose.AsyncImage
|
||||
import it.vfsfitvnm.vimusic.Database
|
||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
|
||||
import it.vfsfitvnm.vimusic.services.GetSongCacheSizeCommand
|
||||
import it.vfsfitvnm.vimusic.ui.components.*
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.QueuedMediaItemMenu
|
||||
import it.vfsfitvnm.vimusic.ui.styling.BlackColorPalette
|
||||
@@ -67,12 +68,15 @@ fun PlayerView(
|
||||
val typography = LocalTypography.current
|
||||
val density = LocalDensity.current
|
||||
val configuration = LocalConfiguration.current
|
||||
val player = LocalYoutubePlayer.current
|
||||
val binder = LocalPlayerServiceBinder.current
|
||||
val context = LocalContext.current
|
||||
|
||||
val player = binder?.player
|
||||
val playerState = rememberYoutubePlayer(player)
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
player?.mediaItem ?: return
|
||||
playerState?.mediaItem ?: return
|
||||
|
||||
val smallThumbnailSize = remember {
|
||||
density.run { 64.dp.roundToPx() }
|
||||
@@ -108,7 +112,7 @@ fun PlayerView(
|
||||
y = 1.dp.toPx()
|
||||
),
|
||||
end = Offset(
|
||||
x = ((size.width - offset) * player.progress) + offset,
|
||||
x = ((size.width - offset) * playerState.progress) + offset,
|
||||
y = 1.dp.toPx()
|
||||
),
|
||||
strokeWidth = 2.dp.toPx()
|
||||
@@ -116,7 +120,7 @@ fun PlayerView(
|
||||
}
|
||||
) {
|
||||
AsyncImage(
|
||||
model = player.mediaMetadata.artworkUri.thumbnail(smallThumbnailSize),
|
||||
model = playerState.mediaMetadata.artworkUri.thumbnail(smallThumbnailSize),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
@@ -129,13 +133,13 @@ fun PlayerView(
|
||||
.weight(1f)
|
||||
) {
|
||||
BasicText(
|
||||
text = player.mediaMetadata.title?.toString() ?: "",
|
||||
text = playerState.mediaMetadata.title?.toString() ?: "",
|
||||
style = typography.xs.semiBold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
BasicText(
|
||||
text = player.mediaMetadata.artist?.toString() ?: "",
|
||||
text = playerState.mediaMetadata.artist?.toString() ?: "",
|
||||
style = typography.xs,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
@@ -143,16 +147,16 @@ fun PlayerView(
|
||||
}
|
||||
|
||||
when {
|
||||
player.playbackState == Player.STATE_ENDED || !player.playWhenReady -> Image(
|
||||
playerState.playbackState == Player.STATE_ENDED || !playerState.playWhenReady -> Image(
|
||||
painter = painterResource(R.drawable.play),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(colorPalette.text),
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
if (player.playbackState == Player.STATE_IDLE) {
|
||||
player.mediaController.prepare()
|
||||
if (playerState.playbackState == Player.STATE_IDLE) {
|
||||
player?.prepare()
|
||||
}
|
||||
player.mediaController.play()
|
||||
player?.play()
|
||||
}
|
||||
.padding(vertical = 8.dp)
|
||||
.padding(horizontal = 16.dp)
|
||||
@@ -164,7 +168,7 @@ fun PlayerView(
|
||||
colorFilter = ColorFilter.tint(colorPalette.text),
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
player.mediaController.pause()
|
||||
player?.pause()
|
||||
}
|
||||
.padding(vertical = 8.dp)
|
||||
.padding(horizontal = 16.dp)
|
||||
@@ -175,10 +179,11 @@ fun PlayerView(
|
||||
}
|
||||
}
|
||||
) {
|
||||
val song by remember(player.mediaItem?.mediaId) {
|
||||
player.mediaItem?.mediaId?.let(Database::songFlow)?.distinctUntilChanged() ?: flowOf(
|
||||
null
|
||||
)
|
||||
val song by remember(playerState.mediaItem?.mediaId) {
|
||||
playerState.mediaItem?.mediaId?.let(Database::songFlow)?.distinctUntilChanged()
|
||||
?: flowOf(
|
||||
null
|
||||
)
|
||||
}.collectAsState(initial = null, context = Dispatchers.IO)
|
||||
|
||||
var isShowingStatsForNerds by rememberSaveable {
|
||||
@@ -192,7 +197,7 @@ fun PlayerView(
|
||||
.padding(bottom = 72.dp)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
var scrubbingPosition by remember(player.mediaItemIndex) {
|
||||
var scrubbingPosition by remember(playerState.mediaItemIndex) {
|
||||
mutableStateOf<Long?>(null)
|
||||
}
|
||||
|
||||
@@ -211,8 +216,8 @@ fun PlayerView(
|
||||
.clickable {
|
||||
menuState.display {
|
||||
QueuedMediaItemMenu(
|
||||
mediaItem = player.mediaItem ?: MediaItem.EMPTY,
|
||||
indexInQueue = player.mediaItemIndex,
|
||||
mediaItem = playerState.mediaItem ?: MediaItem.EMPTY,
|
||||
indexInQueue = playerState.mediaItemIndex,
|
||||
onDismiss = menuState::hide,
|
||||
onGlobalRouteEmitted = layoutState.collapse
|
||||
)
|
||||
@@ -223,9 +228,9 @@ fun PlayerView(
|
||||
)
|
||||
}
|
||||
|
||||
if (player.error == null) {
|
||||
if (playerState.error == null) {
|
||||
AnimatedContent(
|
||||
targetState = player.mediaItemIndex,
|
||||
targetState = playerState.mediaItemIndex,
|
||||
transitionSpec = {
|
||||
val slideDirection =
|
||||
if (targetState > initialState) AnimatedContentScope.SlideDirection.Left else AnimatedContentScope.SlideDirection.Right
|
||||
@@ -240,7 +245,7 @@ fun PlayerView(
|
||||
.align(Alignment.CenterHorizontally)
|
||||
) {
|
||||
val artworkUri = remember(it) {
|
||||
player.mediaController.getMediaItemAt(it).mediaMetadata.artworkUri.thumbnail(
|
||||
player?.getMediaItemAt(it)?.mediaMetadata?.artworkUri.thumbnail(
|
||||
thumbnailSizePx
|
||||
)
|
||||
}
|
||||
@@ -273,13 +278,37 @@ fun PlayerView(
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
val cachedPercentage = remember(song?.contentLength) {
|
||||
song?.contentLength?.let { contentLength ->
|
||||
player.mediaController.syncCommand(
|
||||
GetSongCacheSizeCommand,
|
||||
bundleOf("videoId" to song?.id)
|
||||
).extras.getLong("cacheSize").toFloat() / contentLength * 100
|
||||
}?.roundToInt() ?: 0
|
||||
var cachedBytes by remember(song?.id) {
|
||||
mutableStateOf(binder?.cache?.getCachedBytes(song?.id ?: "", 0, -1) ?: 0L)
|
||||
}
|
||||
|
||||
DisposableEffect(song?.id) {
|
||||
val listener = object : Cache.Listener {
|
||||
override fun onSpanAdded(cache: Cache, span: CacheSpan) {
|
||||
cachedBytes += span.length
|
||||
}
|
||||
|
||||
override fun onSpanRemoved(cache: Cache, span: CacheSpan) {
|
||||
cachedBytes -= span.length
|
||||
}
|
||||
|
||||
override fun onSpanTouched(
|
||||
cache: Cache,
|
||||
oldSpan: CacheSpan,
|
||||
newSpan: CacheSpan
|
||||
) = Unit
|
||||
}
|
||||
|
||||
song?.id?.let { key ->
|
||||
binder?.cache?.addListener(key, listener)
|
||||
}
|
||||
|
||||
|
||||
onDispose {
|
||||
song?.id?.let { key ->
|
||||
binder?.cache?.removeListener(key, listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
@@ -321,7 +350,7 @@ fun PlayerView(
|
||||
|
||||
Column {
|
||||
BasicText(
|
||||
text = "${player.volume.times(100).roundToInt()}%",
|
||||
text = "${playerState.volume.times(100).roundToInt()}%",
|
||||
style = typography.xs.semiBold.color(BlackColorPalette.text)
|
||||
)
|
||||
BasicText(
|
||||
@@ -332,12 +361,21 @@ fun PlayerView(
|
||||
)
|
||||
BasicText(
|
||||
text = song?.contentLength?.let { contentLength ->
|
||||
Formatter.formatShortFileSize(context, contentLength)
|
||||
Formatter.formatShortFileSize(
|
||||
context,
|
||||
contentLength
|
||||
)
|
||||
} ?: "Unknown",
|
||||
style = typography.xs.semiBold.color(BlackColorPalette.text)
|
||||
)
|
||||
BasicText(
|
||||
text = "$cachedPercentage%",
|
||||
text = buildString {
|
||||
append(Formatter.formatShortFileSize(context, cachedBytes))
|
||||
|
||||
song?.contentLength?.let { contentLenght ->
|
||||
append(" (${(cachedBytes.toFloat() / contentLenght * 100).roundToInt()}%)")
|
||||
}
|
||||
},
|
||||
style = typography.xs.semiBold.color(BlackColorPalette.text)
|
||||
)
|
||||
}
|
||||
@@ -354,16 +392,20 @@ fun PlayerView(
|
||||
onClick = {
|
||||
song?.let { song ->
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
YouTube.player(song.id).map { body ->
|
||||
Database.update(
|
||||
song.copy(
|
||||
loudnessDb = body.playerConfig?.audioConfig?.loudnessDb?.toFloat(),
|
||||
contentLength = body.streamingData?.adaptiveFormats?.findLast { format ->
|
||||
format.itag == 251 || format.itag == 140
|
||||
}?.let(PlayerResponse.StreamingData.AdaptiveFormat::contentLength)
|
||||
YouTube
|
||||
.player(song.id)
|
||||
.map { body ->
|
||||
Database.update(
|
||||
song.copy(
|
||||
loudnessDb = body.playerConfig?.audioConfig?.loudnessDb?.toFloat(),
|
||||
contentLength = body.streamingData?.adaptiveFormats
|
||||
?.findLast { format ->
|
||||
format.itag == 251 || format.itag == 140
|
||||
}
|
||||
?.let(PlayerResponse.StreamingData.AdaptiveFormat::contentLength)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -387,18 +429,18 @@ fun PlayerView(
|
||||
.size(thumbnailSizeDp)
|
||||
) {
|
||||
Error(
|
||||
error = Outcome.Error.Unhandled(player.error!!),
|
||||
error = Outcome.Error.Unhandled(playerState.error!!),
|
||||
onRetry = {
|
||||
player.mediaController.playWhenReady = true
|
||||
player.mediaController.prepare()
|
||||
player.error = null
|
||||
player?.playWhenReady = true
|
||||
player?.prepare()
|
||||
playerState.error = null
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
BasicText(
|
||||
text = player.mediaMetadata.title?.toString() ?: "",
|
||||
text = playerState.mediaMetadata.title?.toString() ?: "",
|
||||
style = typography.l.bold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
@@ -407,7 +449,7 @@ fun PlayerView(
|
||||
)
|
||||
|
||||
BasicText(
|
||||
text = player.mediaMetadata.extras?.getStringArrayList("artistNames")
|
||||
text = playerState.mediaMetadata.extras?.getStringArrayList("artistNames")
|
||||
?.joinToString("") ?: "",
|
||||
style = typography.s.semiBold.secondary,
|
||||
maxLines = 1,
|
||||
@@ -417,24 +459,24 @@ fun PlayerView(
|
||||
)
|
||||
|
||||
SeekBar(
|
||||
value = scrubbingPosition ?: player.currentPosition,
|
||||
value = scrubbingPosition ?: playerState.currentPosition,
|
||||
minimumValue = 0,
|
||||
maximumValue = player.duration,
|
||||
maximumValue = playerState.duration,
|
||||
onDragStart = {
|
||||
scrubbingPosition = it
|
||||
},
|
||||
onDrag = { delta ->
|
||||
scrubbingPosition = if (player.duration != C.TIME_UNSET) {
|
||||
scrubbingPosition?.plus(delta)?.coerceIn(0, player.duration)
|
||||
scrubbingPosition = if (playerState.duration != C.TIME_UNSET) {
|
||||
scrubbingPosition?.plus(delta)?.coerceIn(0, playerState.duration)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
onDragEnd = {
|
||||
player.mediaController.seekTo(
|
||||
scrubbingPosition ?: player.mediaController.currentPosition
|
||||
)
|
||||
player.currentPosition = player.mediaController.currentPosition
|
||||
scrubbingPosition?.let { scrubbingPosition ->
|
||||
player?.seekTo(scrubbingPosition)
|
||||
playerState.currentPosition = scrubbingPosition
|
||||
}
|
||||
scrubbingPosition = null
|
||||
},
|
||||
color = colorPalette.text,
|
||||
@@ -456,16 +498,16 @@ fun PlayerView(
|
||||
) {
|
||||
BasicText(
|
||||
text = DateUtils.formatElapsedTime(
|
||||
(scrubbingPosition ?: player.currentPosition) / 1000
|
||||
(scrubbingPosition ?: playerState.currentPosition) / 1000
|
||||
),
|
||||
style = typography.xxs.semiBold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
if (player.duration != C.TIME_UNSET) {
|
||||
if (playerState.duration != C.TIME_UNSET) {
|
||||
BasicText(
|
||||
text = DateUtils.formatElapsedTime(player.duration / 1000),
|
||||
text = DateUtils.formatElapsedTime(playerState.duration / 1000),
|
||||
style = typography.xxs.semiBold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
@@ -488,7 +530,7 @@ fun PlayerView(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
(song ?: player.mediaItem?.let(Database::insert))?.let {
|
||||
(song ?: playerState.mediaItem?.let(Database::insert))?.let {
|
||||
Database.update(it.toggleLike())
|
||||
}
|
||||
}
|
||||
@@ -503,24 +545,24 @@ fun PlayerView(
|
||||
colorFilter = ColorFilter.tint(colorPalette.text),
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
player.mediaController.seekToPrevious()
|
||||
player?.seekToPrevious()
|
||||
}
|
||||
.padding(horizontal = 16.dp)
|
||||
.size(32.dp)
|
||||
)
|
||||
|
||||
when {
|
||||
player.playbackState == Player.STATE_ENDED || !player.playWhenReady -> Image(
|
||||
playerState.playbackState == Player.STATE_ENDED || !playerState.playWhenReady -> Image(
|
||||
painter = painterResource(R.drawable.play_circle),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(colorPalette.text),
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
if (player.playbackState == Player.STATE_IDLE) {
|
||||
player.mediaController.prepare()
|
||||
if (player?.playbackState == Player.STATE_IDLE) {
|
||||
player.prepare()
|
||||
}
|
||||
|
||||
player.mediaController.play()
|
||||
player?.play()
|
||||
}
|
||||
.size(64.dp)
|
||||
)
|
||||
@@ -530,7 +572,7 @@ fun PlayerView(
|
||||
colorFilter = ColorFilter.tint(colorPalette.text),
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
player.mediaController.pause()
|
||||
player?.pause()
|
||||
}
|
||||
.size(64.dp)
|
||||
)
|
||||
@@ -542,7 +584,7 @@ fun PlayerView(
|
||||
colorFilter = ColorFilter.tint(colorPalette.text),
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
player.mediaController.seekToNext()
|
||||
player?.seekToNext()
|
||||
}
|
||||
.padding(horizontal = 16.dp)
|
||||
.size(32.dp)
|
||||
@@ -551,7 +593,7 @@ fun PlayerView(
|
||||
|
||||
Image(
|
||||
painter = painterResource(
|
||||
if (player.repeatMode == Player.REPEAT_MODE_ONE) {
|
||||
if (playerState.repeatMode == Player.REPEAT_MODE_ONE) {
|
||||
R.drawable.repeat_one
|
||||
} else {
|
||||
R.drawable.repeat
|
||||
@@ -559,7 +601,7 @@ fun PlayerView(
|
||||
),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(
|
||||
if (player.repeatMode == Player.REPEAT_MODE_OFF) {
|
||||
if (playerState.repeatMode == Player.REPEAT_MODE_OFF) {
|
||||
colorPalette.textDisabled
|
||||
} else {
|
||||
colorPalette.text
|
||||
@@ -567,10 +609,13 @@ fun PlayerView(
|
||||
),
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
player.mediaController.repeatMode =
|
||||
(player.mediaController.repeatMode + 2) % 3
|
||||
|
||||
preferences.repeatMode = player.mediaController.repeatMode
|
||||
player?.repeatMode
|
||||
?.plus(2)
|
||||
?.mod(3)
|
||||
?.let { repeatMode ->
|
||||
player.repeatMode = repeatMode
|
||||
preferences.repeatMode = repeatMode
|
||||
}
|
||||
}
|
||||
.padding(horizontal = 16.dp)
|
||||
.size(28.dp)
|
||||
@@ -579,6 +624,8 @@ fun PlayerView(
|
||||
}
|
||||
|
||||
PlayerBottomSheet(
|
||||
player = player,
|
||||
playerState = playerState,
|
||||
layoutState = rememberBottomSheetState(64.dp, layoutState.upperBound - 128.dp),
|
||||
onGlobalRouteEmitted = layoutState.collapse,
|
||||
song = song,
|
||||
|
||||
Reference in New Issue
Block a user