mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Add indicator for queued songs
This commit is contained in:
parent
7f75da2493
commit
dc58b31e2b
4 changed files with 72 additions and 30 deletions
|
|
@ -5,6 +5,8 @@ import androidx.annotation.OptIn
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.media3.common.AudioAttributes
|
||||||
|
import androidx.media3.common.C
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import androidx.media3.common.Timeline
|
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.api.client.extensions.universalAudioApi
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.ImageType
|
import org.jellyfin.sdk.model.api.ImageType
|
||||||
|
import org.jellyfin.sdk.model.serializer.toUUID
|
||||||
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
@ -67,6 +70,13 @@ class MusicService
|
||||||
),
|
),
|
||||||
).build()
|
).build()
|
||||||
.also {
|
.also {
|
||||||
|
it.setAudioAttributes(
|
||||||
|
AudioAttributes
|
||||||
|
.Builder()
|
||||||
|
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
|
||||||
|
.build(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
it.addListener(MusicPlayerListener(it, _state))
|
it.addListener(MusicPlayerListener(it, _state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -236,11 +246,24 @@ class MusicService
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updateQueueSize() {
|
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) {
|
withContext(Dispatchers.Main) {
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
queueVersion = it.queueVersion + 1,
|
queueVersion = it.queueVersion + 1,
|
||||||
queueSize = player.mediaItemCount,
|
queueSize = player.mediaItemCount,
|
||||||
|
queuedIds = ids,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -298,9 +321,20 @@ data class MusicServiceState(
|
||||||
val status: NowPlayingStatus,
|
val status: NowPlayingStatus,
|
||||||
val currentItemTitle: String?,
|
val currentItemTitle: String?,
|
||||||
val loadingState: LoadingState = LoadingState.Pending,
|
val loadingState: LoadingState = LoadingState.Pending,
|
||||||
|
val queuedIds: Set<UUID>,
|
||||||
) {
|
) {
|
||||||
companion object {
|
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(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -421,7 +421,7 @@ fun AlbumDetailsPage(
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.songs),
|
text = stringResource(R.string.songs) + " (${state.songs.size})",
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
color = MaterialTheme.colorScheme.onBackground,
|
color = MaterialTheme.colorScheme.onBackground,
|
||||||
modifier = Modifier.padding(start = 8.dp),
|
modifier = Modifier.padding(start = 8.dp),
|
||||||
|
|
@ -457,6 +457,7 @@ fun AlbumDetailsPage(
|
||||||
},
|
},
|
||||||
showArtist = state.isVariousArtists,
|
showArtist = state.isVariousArtists,
|
||||||
isPlaying = song != null && currentMusic.currentItemId == song.id,
|
isPlaying = song != null && currentMusic.currentItemId == song.id,
|
||||||
|
isQueued = song != null && song.id in currentMusic.queuedIds,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth(.75f)
|
.fillMaxWidth(.75f)
|
||||||
|
|
|
||||||
|
|
@ -443,6 +443,7 @@ fun ArtistDetailsPage(
|
||||||
},
|
},
|
||||||
showArtist = false,
|
showArtist = false,
|
||||||
isPlaying = song != null && currentMusic.currentItemId == song.id,
|
isPlaying = song != null && currentMusic.currentItemId == song.id,
|
||||||
|
isQueued = song != null && song.id in currentMusic.queuedIds,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth(.75f)
|
.fillMaxWidth(.75f)
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
package com.github.damontecres.wholphin.ui.detail.music
|
package com.github.damontecres.wholphin.ui.detail.music
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
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.BoxScope
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.PlayArrow
|
import androidx.compose.material.icons.filled.PlayArrow
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -15,16 +17,17 @@ 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.clip
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.tv.material3.Icon
|
import androidx.tv.material3.Icon
|
||||||
import androidx.tv.material3.ListItem
|
import androidx.tv.material3.ListItem
|
||||||
import androidx.tv.material3.ListItemDefaults
|
import androidx.tv.material3.ListItemDefaults
|
||||||
|
import androidx.tv.material3.LocalContentColor
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||||
import com.github.damontecres.wholphin.ui.enableMarquee
|
import com.github.damontecres.wholphin.ui.enableMarquee
|
||||||
import com.github.damontecres.wholphin.ui.letNotEmpty
|
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.roundSeconds
|
||||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||||
import org.jellyfin.sdk.model.extensions.ticks
|
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.minutes
|
||||||
import kotlin.time.Duration.Companion.seconds
|
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
|
@Composable
|
||||||
fun SongListItem(
|
fun SongListItem(
|
||||||
song: BaseItem?,
|
song: BaseItem?,
|
||||||
|
|
@ -64,6 +43,7 @@ fun SongListItem(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
showArtist: Boolean = false,
|
showArtist: Boolean = false,
|
||||||
isPlaying: Boolean = false,
|
isPlaying: Boolean = false,
|
||||||
|
isQueued: Boolean = false,
|
||||||
) = SongListItem(
|
) = SongListItem(
|
||||||
title = song?.title,
|
title = song?.title,
|
||||||
artist = if (showArtist) song?.data?.albumArtist else null,
|
artist = if (showArtist) song?.data?.albumArtist else null,
|
||||||
|
|
@ -81,6 +61,7 @@ fun SongListItem(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
showArtist = showArtist,
|
showArtist = showArtist,
|
||||||
isPlaying = isPlaying,
|
isPlaying = isPlaying,
|
||||||
|
isQueued = isQueued,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -94,6 +75,7 @@ fun SongListItem(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
showArtist: Boolean = false,
|
showArtist: Boolean = false,
|
||||||
isPlaying: Boolean = false,
|
isPlaying: Boolean = false,
|
||||||
|
isQueued: Boolean = false,
|
||||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
|
|
@ -113,6 +95,19 @@ fun SongListItem(
|
||||||
imageVector = Icons.Default.PlayArrow,
|
imageVector = Icons.Default.PlayArrow,
|
||||||
contentDescription = null,
|
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,
|
modifier = Modifier,
|
||||||
showArtist = false,
|
showArtist = false,
|
||||||
)
|
)
|
||||||
|
SongListItem(
|
||||||
|
title = "Song title",
|
||||||
|
artist = "Artists",
|
||||||
|
indexNumber = 1,
|
||||||
|
runtime = 2.minutes + 30.seconds,
|
||||||
|
onClick = {},
|
||||||
|
onLongClick = { },
|
||||||
|
modifier = Modifier,
|
||||||
|
showArtist = false,
|
||||||
|
isQueued = true,
|
||||||
|
)
|
||||||
SongListItem(
|
SongListItem(
|
||||||
title = "Song title",
|
title = "Song title",
|
||||||
artist = "Artists",
|
artist = "Artists",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue