Add indicator for queued songs

This commit is contained in:
Damontecres 2026-03-06 16:20:54 -05:00
parent 7f75da2493
commit dc58b31e2b
No known key found for this signature in database
4 changed files with 72 additions and 30 deletions

View file

@ -5,6 +5,8 @@ import androidx.annotation.OptIn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.media3.common.AudioAttributes
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.Timeline
@ -38,6 +40,7 @@ import org.jellyfin.sdk.api.client.extensions.instantMixApi
import org.jellyfin.sdk.api.client.extensions.universalAudioApi
import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.ImageType
import org.jellyfin.sdk.model.serializer.toUUID
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
import timber.log.Timber
import java.util.UUID
@ -67,6 +70,13 @@ class MusicService
),
).build()
.also {
it.setAudioAttributes(
AudioAttributes
.Builder()
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
.build(),
false,
)
it.addListener(MusicPlayerListener(it, _state))
}
}
@ -236,11 +246,24 @@ class MusicService
}
private suspend fun updateQueueSize() {
// val ids =
// withContext(Dispatchers.Default) {
// (0..<player.mediaItemCount).map { player.getMediaItemAt(it).mediaId.toUUID() }
// }
val timeline = onMain { player.currentTimeline }
val window = Timeline.Window()
val ids =
(0..<timeline.windowCount)
.map {
timeline.getWindow(it, window)
window.mediaItem.mediaId.toUUID()
}.toSet()
withContext(Dispatchers.Main) {
_state.update {
it.copy(
queueVersion = it.queueVersion + 1,
queueSize = player.mediaItemCount,
queuedIds = ids,
)
}
}
@ -298,9 +321,20 @@ data class MusicServiceState(
val status: NowPlayingStatus,
val currentItemTitle: String?,
val loadingState: LoadingState = LoadingState.Pending,
val queuedIds: Set<UUID>,
) {
companion object {
val EMPTY = MusicServiceState(0L, 0, 0, null, NowPlayingStatus.IDLE, null)
val EMPTY =
MusicServiceState(
0L,
0,
0,
null,
NowPlayingStatus.IDLE,
null,
LoadingState.Pending,
emptySet(),
)
}
}

View file

@ -421,7 +421,7 @@ fun AlbumDetailsPage(
}
item {
Text(
text = stringResource(R.string.songs),
text = stringResource(R.string.songs) + " (${state.songs.size})",
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(start = 8.dp),
@ -457,6 +457,7 @@ fun AlbumDetailsPage(
},
showArtist = state.isVariousArtists,
isPlaying = song != null && currentMusic.currentItemId == song.id,
isQueued = song != null && song.id in currentMusic.queuedIds,
modifier =
Modifier
.fillMaxWidth(.75f)

View file

@ -443,6 +443,7 @@ fun ArtistDetailsPage(
},
showArtist = false,
isPlaying = song != null && currentMusic.currentItemId == song.id,
isQueued = song != null && song.id in currentMusic.queuedIds,
modifier =
Modifier
.fillMaxWidth(.75f)

View file

@ -1,13 +1,15 @@
package com.github.damontecres.wholphin.ui.detail.music
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.runtime.Composable
@ -15,16 +17,17 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import androidx.tv.material3.Icon
import androidx.tv.material3.ListItem
import androidx.tv.material3.ListItemDefaults
import androidx.tv.material3.LocalContentColor
import androidx.tv.material3.Text
import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.enableMarquee
import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.roundMinutes
import com.github.damontecres.wholphin.ui.roundSeconds
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import org.jellyfin.sdk.model.extensions.ticks
@ -32,30 +35,6 @@ import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
@Composable
fun SongListDisplay(
songs: List<BaseItem?>,
showArtist: Boolean,
onClick: (Int, BaseItem) -> Unit,
onLongClick: (Int, BaseItem) -> Unit,
modifier: Modifier = Modifier,
) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier,
) {
itemsIndexed(songs) { index, song ->
SongListItem(
song = song,
onClick = { song?.let { onClick.invoke(index, song) } },
onLongClick = { song?.let { onLongClick.invoke(index, song) } },
showArtist = showArtist,
modifier = Modifier,
)
}
}
}
@Composable
fun SongListItem(
song: BaseItem?,
@ -64,6 +43,7 @@ fun SongListItem(
modifier: Modifier = Modifier,
showArtist: Boolean = false,
isPlaying: Boolean = false,
isQueued: Boolean = false,
) = SongListItem(
title = song?.title,
artist = if (showArtist) song?.data?.albumArtist else null,
@ -81,6 +61,7 @@ fun SongListItem(
modifier = modifier,
showArtist = showArtist,
isPlaying = isPlaying,
isQueued = isQueued,
)
@Composable
@ -94,6 +75,7 @@ fun SongListItem(
modifier: Modifier = Modifier,
showArtist: Boolean = false,
isPlaying: Boolean = false,
isQueued: Boolean = false,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
Row(
@ -113,6 +95,19 @@ fun SongListItem(
imageVector = Icons.Default.PlayArrow,
contentDescription = null,
)
} else if (isQueued) {
// Icon(
// imageVector = Icons.Default.Add,
// contentDescription = null,
// )
Box(
modifier =
Modifier
.padding(horizontal = 8.dp)
.clip(CircleShape)
.background(LocalContentColor.current)
.size(8.dp),
)
}
}
}
@ -181,6 +176,17 @@ fun SongListItemPreview() {
modifier = Modifier,
showArtist = false,
)
SongListItem(
title = "Song title",
artist = "Artists",
indexNumber = 1,
runtime = 2.minutes + 30.seconds,
onClick = {},
onLongClick = { },
modifier = Modifier,
showArtist = false,
isQueued = true,
)
SongListItem(
title = "Song title",
artist = "Artists",