mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Updates to playlists, add more button on album/artist pages
This commit is contained in:
parent
8ddd4bd747
commit
5f5b07e861
6 changed files with 182 additions and 28 deletions
|
|
@ -186,7 +186,7 @@ class MusicService
|
|||
updateQueueSize()
|
||||
if (player.mediaItemCount == 1) {
|
||||
// Start playing if this was the first time added
|
||||
player.play()
|
||||
start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -295,7 +295,12 @@ class MusicService
|
|||
|
||||
suspend fun playNext(song: BaseItem) {
|
||||
val mediaItem = convert(song)
|
||||
onMain { player.addMediaItem(state.value.currentIndex + 1, mediaItem) }
|
||||
onMain {
|
||||
player.addMediaItem(state.value.currentIndex + 1, mediaItem)
|
||||
if (player.mediaItemCount == 1) {
|
||||
start()
|
||||
}
|
||||
}
|
||||
updateQueueSize()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
|
|
@ -44,6 +45,7 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
|
@ -67,6 +69,7 @@ import com.github.damontecres.wholphin.services.NavigationManager
|
|||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
||||
import com.github.damontecres.wholphin.ui.TimeFormatter
|
||||
import com.github.damontecres.wholphin.ui.cards.ItemCardImage
|
||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||
import com.github.damontecres.wholphin.ui.components.DialogItem
|
||||
import com.github.damontecres.wholphin.ui.components.DialogParams
|
||||
import com.github.damontecres.wholphin.ui.components.DialogPopup
|
||||
|
|
@ -77,9 +80,12 @@ import com.github.damontecres.wholphin.ui.components.FilterByButton
|
|||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||
import com.github.damontecres.wholphin.ui.components.OverviewText
|
||||
import com.github.damontecres.wholphin.ui.components.SortByButton
|
||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||
import com.github.damontecres.wholphin.ui.data.BoxSetSortOptions
|
||||
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
||||
import com.github.damontecres.wholphin.ui.detail.music.MusicQueueMarker
|
||||
import com.github.damontecres.wholphin.ui.enableMarquee
|
||||
import com.github.damontecres.wholphin.ui.equalsNotNull
|
||||
import com.github.damontecres.wholphin.ui.formatDateTime
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.launchDefault
|
||||
|
|
@ -224,17 +230,24 @@ class PlaylistViewModel
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method tries to determine the [MediaType] of a playlist
|
||||
*
|
||||
* In theory, the server will set the type, but sometime it doesn't
|
||||
*/
|
||||
private suspend fun determineMediaType() {
|
||||
// Use the type the server says
|
||||
var mediaType =
|
||||
super.item.value
|
||||
?.data
|
||||
?.mediaType ?: MediaType.UNKNOWN
|
||||
mediaType =
|
||||
if (mediaType == MediaType.UNKNOWN) {
|
||||
// Otherwise, if a most of the list is one type, we can assume that type
|
||||
val pager = (this@PlaylistViewModel.items.value as? ApiRequestPager<*>)
|
||||
if (pager != null && pager.size < 50) {
|
||||
if (pager != null && pager.size <= 50) {
|
||||
val types =
|
||||
(0..<50).groupBy { index ->
|
||||
(0..<50.coerceAtMost(pager.size)).groupBy { index ->
|
||||
val pagerItem = pager.getBlocking(index)
|
||||
when (pagerItem?.type) {
|
||||
BaseItemKind.AUDIO -> MediaType.AUDIO
|
||||
|
|
@ -259,6 +272,7 @@ class PlaylistViewModel
|
|||
} else {
|
||||
mediaType
|
||||
}
|
||||
Timber.d("mediaType=%s", mediaType)
|
||||
playlistMediaType.update { mediaType }
|
||||
}
|
||||
|
||||
|
|
@ -312,7 +326,7 @@ fun PlaylistDetails(
|
|||
val playlistMediaType by viewModel.playlistMediaType.collectAsState()
|
||||
|
||||
var longClickDialog by remember { mutableStateOf<DialogParams?>(null) }
|
||||
var showConfirmTypeDialog by remember { mutableStateOf(false) }
|
||||
var showConfirmTypeDialog by remember { mutableStateOf<Pair<Int, Boolean>?>(null) }
|
||||
|
||||
val goToString = stringResource(R.string.go_to)
|
||||
val playFromHereString = stringResource(R.string.play_from_here)
|
||||
|
|
@ -340,7 +354,7 @@ fun PlaylistDetails(
|
|||
}
|
||||
|
||||
else -> {
|
||||
showConfirmTypeDialog = true
|
||||
showConfirmTypeDialog = Pair(index, shuffle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -390,6 +404,12 @@ fun PlaylistDetails(
|
|||
onDismissRequest = { longClickDialog = null },
|
||||
)
|
||||
}
|
||||
showConfirmTypeDialog?.let { (index, shuffle) ->
|
||||
ConfirmMediaTypeDialog(
|
||||
onConfirm = { mediaType -> play(index, shuffle, mediaType) },
|
||||
onCancel = { showConfirmTypeDialog = null },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -513,10 +533,18 @@ fun PlaylistDetailsContent(
|
|||
onLongClickIndex.invoke(index, item)
|
||||
}
|
||||
},
|
||||
isPlaying =
|
||||
equalsNotNull(
|
||||
musicState.currentItemId,
|
||||
item?.id,
|
||||
),
|
||||
isQueued = item?.id in musicState.queuedIds,
|
||||
modifier =
|
||||
Modifier
|
||||
.height(80.dp)
|
||||
.ifElse(
|
||||
item?.type != BaseItemKind.AUDIO,
|
||||
Modifier.height(80.dp),
|
||||
).ifElse(
|
||||
index == savedIndex,
|
||||
Modifier.focusRequester(focus),
|
||||
).onFocusChanged {
|
||||
|
|
@ -646,6 +674,8 @@ fun PlaylistItem(
|
|||
onLongClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
isPlaying: Boolean = false,
|
||||
isQueued: Boolean = false,
|
||||
) {
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
ListItem(
|
||||
|
|
@ -658,6 +688,12 @@ fun PlaylistItem(
|
|||
text = item?.title ?: "",
|
||||
modifier = Modifier.enableMarquee(focused),
|
||||
)
|
||||
if (item?.type == BaseItemKind.AUDIO) {
|
||||
MusicQueueMarker(
|
||||
isPlaying = isPlaying,
|
||||
isQueued = isQueued,
|
||||
)
|
||||
}
|
||||
},
|
||||
supportingContent = {
|
||||
Text(
|
||||
|
|
@ -714,3 +750,47 @@ fun PlaylistItem(
|
|||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConfirmMediaTypeDialog(
|
||||
onConfirm: (MediaType) -> Unit,
|
||||
onCancel: () -> Unit,
|
||||
) {
|
||||
BasicDialog(
|
||||
onDismissRequest = onCancel,
|
||||
properties = DialogProperties(),
|
||||
elevation = 3.dp,
|
||||
) {
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
modifier = Modifier.wrapContentSize(),
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(R.string.play_as_type),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillParentMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
TextButton(
|
||||
stringRes = R.string.audio,
|
||||
onClick = { onConfirm.invoke(MediaType.AUDIO) },
|
||||
)
|
||||
TextButton(
|
||||
stringRes = R.string.video,
|
||||
onClick = { onConfirm.invoke(MediaType.VIDEO) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -455,6 +455,23 @@ fun AlbumDetailsPage(
|
|||
)
|
||||
}
|
||||
},
|
||||
onClickMore = {
|
||||
if (song != null) {
|
||||
moreDialog =
|
||||
DialogParams(
|
||||
fromLongClick = false,
|
||||
title = song.name ?: "",
|
||||
items =
|
||||
buildMoreDialogForMusic(
|
||||
context = context,
|
||||
actions = moreDialogActions,
|
||||
item = song,
|
||||
index = index,
|
||||
canRemove = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
showArtist = state.isVariousArtists,
|
||||
isPlaying = song != null && currentMusic.currentItemId == song.id,
|
||||
isQueued = song != null && song.id in currentMusic.queuedIds,
|
||||
|
|
|
|||
|
|
@ -441,6 +441,23 @@ fun ArtistDetailsPage(
|
|||
)
|
||||
}
|
||||
},
|
||||
onClickMore = {
|
||||
if (song != null) {
|
||||
moreDialog =
|
||||
DialogParams(
|
||||
fromLongClick = false,
|
||||
title = song.name ?: "",
|
||||
items =
|
||||
buildMoreDialogForMusic(
|
||||
context = context,
|
||||
actions = moreDialogActions,
|
||||
item = song,
|
||||
index = index,
|
||||
canRemove = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
showArtist = false,
|
||||
isPlaying = song != null && currentMusic.currentItemId == song.id,
|
||||
isQueued = song != null && song.id in currentMusic.queuedIds,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ 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.MoreVert
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -18,14 +19,17 @@ 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.res.stringResource
|
||||
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.R
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||
import com.github.damontecres.wholphin.ui.components.Button
|
||||
import com.github.damontecres.wholphin.ui.enableMarquee
|
||||
import com.github.damontecres.wholphin.ui.letNotEmpty
|
||||
import com.github.damontecres.wholphin.ui.roundSeconds
|
||||
|
|
@ -40,6 +44,7 @@ fun SongListItem(
|
|||
song: BaseItem?,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
onClickMore: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
showArtist: Boolean = false,
|
||||
isPlaying: Boolean = false,
|
||||
|
|
@ -62,6 +67,8 @@ fun SongListItem(
|
|||
showArtist = showArtist,
|
||||
isPlaying = isPlaying,
|
||||
isQueued = isQueued,
|
||||
showMoreButton = true,
|
||||
onClickMore = onClickMore,
|
||||
)
|
||||
|
||||
@Composable
|
||||
|
|
@ -76,6 +83,8 @@ fun SongListItem(
|
|||
showArtist: Boolean = false,
|
||||
isPlaying: Boolean = false,
|
||||
isQueued: Boolean = false,
|
||||
showMoreButton: Boolean = false,
|
||||
onClickMore: () -> Unit = {},
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
) {
|
||||
Row(
|
||||
|
|
@ -90,25 +99,10 @@ fun SongListItem(
|
|||
Text(
|
||||
text = indexNumber?.toString() ?: "",
|
||||
)
|
||||
if (isPlaying) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.PlayArrow,
|
||||
contentDescription = null,
|
||||
MusicQueueMarker(
|
||||
isPlaying = isPlaying,
|
||||
isQueued = isQueued,
|
||||
)
|
||||
} 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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
val headlineContent = @Composable {
|
||||
|
|
@ -140,7 +134,7 @@ fun SongListItem(
|
|||
},
|
||||
trailingContent = trailingContent,
|
||||
scale = ListItemDefaults.scale(1f, 1f, .95f),
|
||||
modifier = Modifier,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
} else {
|
||||
ListItem(
|
||||
|
|
@ -153,14 +147,53 @@ fun SongListItem(
|
|||
supportingContent = null,
|
||||
trailingContent = trailingContent,
|
||||
scale = ListItemDefaults.scale(1f, 1f, .95f),
|
||||
modifier = Modifier,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
if (showMoreButton) {
|
||||
Button(
|
||||
onClick = onClickMore,
|
||||
enabled = true,
|
||||
modifier = Modifier.size(32.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = stringResource(R.string.more),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val BaseItem.artistsString: String? get() = data.artists?.letNotEmpty { it.joinToString(", ") }
|
||||
|
||||
/**
|
||||
* Add an indicator for if the item is currently playing or queued
|
||||
*/
|
||||
@Composable
|
||||
fun MusicQueueMarker(
|
||||
isPlaying: Boolean,
|
||||
isQueued: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (isPlaying) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.PlayArrow,
|
||||
contentDescription = null,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else if (isQueued) {
|
||||
Box(
|
||||
modifier =
|
||||
modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.clip(CircleShape)
|
||||
.background(LocalContentColor.current)
|
||||
.size(8.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewTvSpec
|
||||
@Composable
|
||||
fun SongListItemPreview() {
|
||||
|
|
@ -186,6 +219,7 @@ fun SongListItemPreview() {
|
|||
modifier = Modifier,
|
||||
showArtist = false,
|
||||
isQueued = true,
|
||||
showMoreButton = true,
|
||||
)
|
||||
SongListItem(
|
||||
title = "Song title",
|
||||
|
|
|
|||
|
|
@ -741,6 +741,7 @@
|
|||
<string name="show_album_cover">Show album cover</string>
|
||||
<string name="show_visualizer">Show visualizer</string>
|
||||
<string name="show_backdrop">Show backdrop</string>
|
||||
<string name="play_as_type">Play as?</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue