More button & queue management

This commit is contained in:
Damontecres 2026-02-11 11:25:10 -05:00
parent 11e28a605c
commit b48eb9898c
No known key found for this signature in database
6 changed files with 265 additions and 116 deletions

View file

@ -15,6 +15,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient
import com.github.damontecres.wholphin.ui.DefaultItemFields import com.github.damontecres.wholphin.ui.DefaultItemFields
import com.github.damontecres.wholphin.ui.toServerString import com.github.damontecres.wholphin.ui.toServerString
import com.github.damontecres.wholphin.util.BlockingList
import com.github.damontecres.wholphin.util.profile.Codec import com.github.damontecres.wholphin.util.profile.Codec
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -75,6 +76,33 @@ class MusicService
setQueue(items, false) setQueue(items, false)
} }
/**
* Replace the queue with the given list and starting playing the song as startIndex as soon as its ready
*
* Fetches each item in a blocking way and adds to the queue
*/
suspend fun setQueue(
items: BlockingList<BaseItem?>,
startIndex: Int,
shuffled: Boolean,
) = withContext(Dispatchers.IO) {
Timber.d("setQueue: %s items, startIndex=%s, shuffled=%s", items.size, startIndex, shuffled)
withContext(Dispatchers.Main) {
player.setMediaItems(emptyList())
player.shuffleModeEnabled = shuffled
player.play()
}
(startIndex..items.lastIndex).forEach {
val item = items.getBlocking(it)
if (item != null && item.type == BaseItemKind.AUDIO) {
val mediaItem = convert(item)
withContext(Dispatchers.Main) {
player.addMediaItem(mediaItem)
}
}
}
}
suspend fun setQueue( suspend fun setQueue(
items: List<BaseItem>, items: List<BaseItem>,
shuffled: Boolean, shuffled: Boolean,
@ -136,6 +164,8 @@ data class MusicServiceState(
companion object { companion object {
val EMPTY = MusicServiceState(emptyList(), 0, false) val EMPTY = MusicServiceState(emptyList(), 0, false)
} }
val currentItemId: UUID? get() = if (isPlaying) queue.getOrNull(currentIndex)?.id else null
} }
/** /**

View file

@ -1,35 +1,30 @@
package com.github.damontecres.wholphin.ui.detail.music package com.github.damontecres.wholphin.ui.detail.music
import android.content.Context import android.content.Context
import androidx.compose.foundation.focusGroup
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.relocation.BringIntoViewRequester import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.FocusState
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@ -52,9 +47,9 @@ import com.github.damontecres.wholphin.services.MusicService
import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.services.NavigationManager
import com.github.damontecres.wholphin.services.UserPreferencesService import com.github.damontecres.wholphin.services.UserPreferencesService
import com.github.damontecres.wholphin.ui.DefaultItemFields import com.github.damontecres.wholphin.ui.DefaultItemFields
import com.github.damontecres.wholphin.ui.components.DialogParams
import com.github.damontecres.wholphin.ui.components.DialogPopup
import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.components.ErrorMessage
import com.github.damontecres.wholphin.ui.components.ExpandableFaButton
import com.github.damontecres.wholphin.ui.components.ExpandablePlayButton
import com.github.damontecres.wholphin.ui.components.GenreText import com.github.damontecres.wholphin.ui.components.GenreText
import com.github.damontecres.wholphin.ui.components.LoadingPage import com.github.damontecres.wholphin.ui.components.LoadingPage
import com.github.damontecres.wholphin.ui.components.OverviewText import com.github.damontecres.wholphin.ui.components.OverviewText
@ -85,7 +80,6 @@ import org.jellyfin.sdk.model.api.ItemSortBy
import org.jellyfin.sdk.model.api.request.GetItemsRequest import org.jellyfin.sdk.model.api.request.GetItemsRequest
import timber.log.Timber import timber.log.Timber
import java.util.UUID import java.util.UUID
import kotlin.time.Duration
@HiltViewModel(assistedFactory = AlbumViewModel.Factory::class) @HiltViewModel(assistedFactory = AlbumViewModel.Factory::class)
class AlbumViewModel class AlbumViewModel
@ -93,7 +87,7 @@ class AlbumViewModel
constructor( constructor(
private val api: ApiClient, private val api: ApiClient,
@param:ApplicationContext private val context: Context, @param:ApplicationContext private val context: Context,
private val navigationManager: NavigationManager, val navigationManager: NavigationManager,
val serverRepository: ServerRepository, val serverRepository: ServerRepository,
val mediaReportService: MediaReportService, val mediaReportService: MediaReportService,
private val favoriteWatchManager: FavoriteWatchManager, private val favoriteWatchManager: FavoriteWatchManager,
@ -111,6 +105,8 @@ class AlbumViewModel
private val _state = MutableStateFlow(AlbumState.EMPTY) private val _state = MutableStateFlow(AlbumState.EMPTY)
val state: StateFlow<AlbumState> = _state val state: StateFlow<AlbumState> = _state
val currentMusic = musicService.state
init { init {
viewModelScope.launchIO { viewModelScope.launchIO {
try { try {
@ -169,8 +165,10 @@ class AlbumViewModel
} }
} }
fun setFavorite(favorite: Boolean) = fun setFavorite(
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) { itemId: UUID,
favorite: Boolean,
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
favoriteWatchManager.setFavorite(itemId, favorite) favoriteWatchManager.setFavorite(itemId, favorite)
val album = val album =
api.userLibraryApi api.userLibraryApi
@ -187,12 +185,27 @@ class AlbumViewModel
viewModelScope.launchIO { viewModelScope.launchIO {
Timber.v("Playing album %s from %s", itemId, startIndex) Timber.v("Playing album %s from %s", itemId, startIndex)
val songs = state.value.songs as ApiRequestPager<*> val songs = state.value.songs as ApiRequestPager<*>
val songsToAdd = musicService.setQueue(songs, startIndex, shuffled)
(startIndex..songs.lastIndex) }
.mapNotNull { idx -> }
songs.getBlocking(idx)
fun addToQueue(
itemId: UUID,
index: Int,
) {
viewModelScope.launchIO {
val songs = state.value.songs as ApiRequestPager<*>
if (itemId == this@AlbumViewModel.itemId) {
songs.indices.forEach {
songs.getBlocking(it)?.let {
musicService.addToQueue(it)
}
}
} else {
songs.getBlocking(index)?.let {
musicService.addToQueue(it)
}
} }
musicService.setQueue(songsToAdd, shuffled)
} }
} }
@ -225,8 +238,22 @@ fun AlbumDetailsPage(
creationCallback = { it.create(itemId) }, creationCallback = { it.create(itemId) },
), ),
) { ) {
val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
val currentMusic by viewModel.currentMusic.collectAsState()
var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
val moreDialogActions =
remember {
MusicMoreDialogActions(
onNavigate = { viewModel.navigationManager.navigateTo(it) },
onClickPlay = { index, _ -> viewModel.play(false, index) },
onClickAddToQueue = { index, itemId -> viewModel.addToQueue(itemId, index) },
onClickFavorite = { itemId, favorite -> viewModel.setFavorite(itemId, favorite) },
onClickAddPlaylist = {},
)
}
when (val loading = state.loading) { when (val loading = state.loading) {
is LoadingState.Error -> { is LoadingState.Error -> {
@ -271,9 +298,25 @@ fun AlbumDetailsPage(
MusicButtonActions( MusicButtonActions(
onClickPlay = { viewModel.play(it, 0) }, onClickPlay = { viewModel.play(it, 0) },
onClickInstantMix = viewModel::startInstantMix, onClickInstantMix = viewModel::startInstantMix,
onClickFavorite = { viewModel.setFavorite(!album.favorite) }, onClickFavorite = {
viewModel.setFavorite(
album.id,
!album.favorite,
)
},
onClickMore = { onClickMore = {
// TODO moreDialog =
DialogParams(
fromLongClick = false,
title = album.name + " (${album.data.productionYear ?: ""})",
items =
buildMoreDialogForMusic(
context = context,
actions = moreDialogActions,
item = album,
index = 0,
),
)
}, },
) )
}, },
@ -295,16 +338,41 @@ fun AlbumDetailsPage(
SongListItem( SongListItem(
song = song, song = song,
onClick = { viewModel.play(false, index) }, onClick = { viewModel.play(false, index) },
onClickAddToQueue = {}, onLongClick = {
onClickAddToPlaylist = {}, if (song != null) {
moreDialog =
DialogParams(
fromLongClick = true,
title = song.name ?: "",
items =
buildMoreDialogForMusic(
context = context,
actions = moreDialogActions,
item = song,
index = index,
),
)
}
},
modifier = Modifier.padding(horizontal = 16.dp), modifier = Modifier.padding(horizontal = 16.dp),
showArtist = false, showArtist = false,
isPlaying = song != null && currentMusic.currentItemId == song.id,
) )
} }
} }
} }
} }
} }
moreDialog?.let { params ->
DialogPopup(
showDialog = true,
title = params.title,
dialogItems = params.items,
onDismissRequest = { moreDialog = null },
dismissOnClick = true,
waitToLoad = params.fromLongClick,
)
}
} }
@Composable @Composable
@ -388,57 +456,3 @@ fun AlbumHeader(
} }
} }
} }
@Composable
fun AlbumButtons(
onClickPlay: (Boolean) -> Unit,
onClickAddToPlaylist: () -> Unit,
onClickGoToArtist: () -> Unit,
onClickMore: () -> Unit,
buttonOnFocusChanged: (FocusState) -> Unit,
modifier: Modifier = Modifier,
) {
val firstFocus = remember { FocusRequester() }
LazyRow(
horizontalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(8.dp),
modifier =
modifier
.focusGroup()
.focusRestorer(firstFocus),
) {
item {
ExpandablePlayButton(
title = R.string.play,
resume = Duration.ZERO,
icon = Icons.Default.PlayArrow,
onClick = { onClickPlay.invoke(false) },
modifier =
Modifier
.onFocusChanged(buttonOnFocusChanged)
.focusRequester(firstFocus),
)
}
item {
ExpandableFaButton(
title = R.string.shuffle,
iconStringRes = R.string.fa_shuffle,
onClick = { onClickPlay.invoke(true) },
modifier =
Modifier
.onFocusChanged(buttonOnFocusChanged),
)
}
item {
ExpandablePlayButton(
title = R.string.go_to_artist,
resume = Duration.ZERO,
icon = Icons.Default.AccountCircle,
onClick = { onClickGoToArtist.invoke() },
modifier =
Modifier
.onFocusChanged(buttonOnFocusChanged),
)
}
}
}

View file

@ -113,8 +113,7 @@ fun NowPlayingPage(
onClick = { onClick = {
player.seekTo(index, 0L) player.seekTo(index, 0L)
}, },
onClickAddToQueue = {}, onLongClick = {},
onClickAddToPlaylist = {},
modifier = Modifier, modifier = Modifier,
) )
} }

View file

@ -1,22 +1,27 @@
package com.github.damontecres.wholphin.ui.detail.music package com.github.damontecres.wholphin.ui.detail.music
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
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.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
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.unit.dp import androidx.compose.ui.unit.dp
import androidx.tv.material3.Button import androidx.tv.material3.Icon
import androidx.tv.material3.ButtonDefaults
import androidx.tv.material3.ListItem import androidx.tv.material3.ListItem
import androidx.tv.material3.ListItemDefaults import androidx.tv.material3.ListItemDefaults
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.isNotNullOrBlank import com.github.damontecres.wholphin.ui.isNotNullOrBlank
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.roundMinutes
@ -31,8 +36,7 @@ fun SongListDisplay(
songs: List<BaseItem?>, songs: List<BaseItem?>,
showArtist: Boolean, showArtist: Boolean,
onClick: (Int, BaseItem) -> Unit, onClick: (Int, BaseItem) -> Unit,
onClickAddToQueue: (Int, BaseItem) -> Unit, onLongClick: (Int, BaseItem) -> Unit,
onClickAddToPlaylist: (Int, BaseItem) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
LazyColumn( LazyColumn(
@ -43,8 +47,7 @@ fun SongListDisplay(
SongListItem( SongListItem(
song = song, song = song,
onClick = { song?.let { onClick.invoke(index, song) } }, onClick = { song?.let { onClick.invoke(index, song) } },
onClickAddToQueue = { song?.let { onClick.invoke(index, song) } }, onLongClick = { song?.let { onLongClick.invoke(index, song) } },
onClickAddToPlaylist = { song?.let { onClick.invoke(index, song) } },
showArtist = showArtist, showArtist = showArtist,
modifier = Modifier, modifier = Modifier,
) )
@ -56,10 +59,10 @@ fun SongListDisplay(
fun SongListItem( fun SongListItem(
song: BaseItem?, song: BaseItem?,
onClick: () -> Unit, onClick: () -> Unit,
onClickAddToQueue: () -> Unit, onLongClick: () -> Unit,
onClickAddToPlaylist: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
showArtist: Boolean = false, showArtist: Boolean = false,
isPlaying: 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,
@ -71,10 +74,10 @@ fun SongListItem(
?.ticks ?.ticks
?.roundMinutes, ?.roundMinutes,
onClick = onClick, onClick = onClick,
onClickAddToQueue = onClickAddToQueue, onLongClick = onLongClick,
onClickAddToPlaylist = onClickAddToPlaylist,
modifier = modifier, modifier = modifier,
showArtist = showArtist, showArtist = showArtist,
isPlaying = isPlaying,
) )
@Composable @Composable
@ -84,27 +87,42 @@ fun SongListItem(
indexNumber: Int?, indexNumber: Int?,
runtime: Duration?, runtime: Duration?,
onClick: () -> Unit, onClick: () -> Unit,
onClickAddToQueue: () -> Unit, onLongClick: () -> Unit,
onClickAddToPlaylist: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
showArtist: Boolean = false, showArtist: Boolean = false,
isPlaying: Boolean = false, isPlaying: Boolean = false,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = modifier, modifier = modifier,
) { ) {
val focused by interactionSource.collectIsFocusedAsState()
ListItem( ListItem(
selected = isPlaying, selected = isPlaying,
onClick = onClick, onClick = onClick,
onLongClick = onLongClick,
interactionSource = interactionSource,
leadingContent = { leadingContent = {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Text( Text(
text = indexNumber?.toString() ?: "", text = indexNumber?.toString() ?: "",
) )
if (isPlaying) {
Icon(
imageVector = Icons.Default.PlayArrow,
contentDescription = null,
)
}
}
}, },
headlineContent = { headlineContent = {
Text( Text(
text = title ?: "", text = title ?: "",
maxLines = 1,
modifier = Modifier.enableMarquee(focused),
) )
}, },
supportingContent = supportingContent =
@ -125,14 +143,6 @@ fun SongListItem(
scale = ListItemDefaults.scale(1f, 1f, .95f), scale = ListItemDefaults.scale(1f, 1f, .95f),
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
) )
Button(
onClick = onClickAddToQueue,
shape = ButtonDefaults.shape(shape = RoundedCornerShape(8.dp)),
) {
Text(
text = "Add to queue",
)
}
} }
} }
@ -149,21 +159,20 @@ fun SongListItemPreview() {
indexNumber = 1, indexNumber = 1,
runtime = 2.minutes + 30.seconds, runtime = 2.minutes + 30.seconds,
onClick = {}, onClick = {},
onClickAddToQueue = { }, onLongClick = { },
onClickAddToPlaylist = {},
modifier = Modifier, modifier = Modifier,
showArtist = false, showArtist = false,
) )
SongListItem( SongListItem(
title = "Song title", title = "Song title",
artist = "Artists", artist = "Artists",
indexNumber = 1, indexNumber = 2,
runtime = 2.minutes + 30.seconds, runtime = 2.minutes + 30.seconds,
onClick = {}, onClick = {},
onClickAddToQueue = { }, onLongClick = { },
onClickAddToPlaylist = {},
modifier = Modifier, modifier = Modifier,
showArtist = true, showArtist = true,
isPlaying = true,
) )
} }
} }

View file

@ -0,0 +1,95 @@
package com.github.damontecres.wholphin.ui.detail.music
import android.content.Context
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.ui.graphics.Color
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.ui.components.DialogItem
import com.github.damontecres.wholphin.ui.nav.Destination
import org.jellyfin.sdk.model.UUID
import org.jellyfin.sdk.model.api.BaseItemKind
data class MusicMoreDialogActions(
val onNavigate: (Destination) -> Unit,
val onClickPlay: (Int, UUID) -> Unit,
val onClickAddToQueue: (Int, UUID) -> Unit,
val onClickFavorite: (UUID, Boolean) -> Unit,
val onClickAddPlaylist: (UUID) -> Unit,
val onClickGoToAlbum: (UUID) -> Unit = {
onNavigate.invoke(Destination.MediaItem(itemId = it, type = BaseItemKind.MUSIC_ALBUM))
},
val onClickGoToArtist: (UUID) -> Unit = {
onNavigate.invoke(Destination.MediaItem(itemId = it, type = BaseItemKind.MUSIC_ARTIST))
},
)
fun buildMoreDialogForMusic(
context: Context,
actions: MusicMoreDialogActions,
item: BaseItem,
index: Int,
): List<DialogItem> =
buildList {
add(
DialogItem(
context.getString(R.string.play),
Icons.Default.PlayArrow,
iconColor = Color.Green.copy(alpha = .8f),
) {
actions.onClickPlay(index, item.id)
},
)
add(
DialogItem(
context.getString(R.string.add_to_queue),
Icons.Default.Add,
) {
actions.onClickAddToQueue(index, item.id)
},
)
add(
DialogItem(
text = R.string.add_to_playlist,
iconStringRes = R.string.fa_list_ul,
) {
actions.onClickAddPlaylist.invoke(item.id)
},
)
add(
DialogItem(
text = if (item.favorite) R.string.remove_favorite else R.string.add_favorite,
iconStringRes = R.string.fa_heart,
iconColor = if (item.favorite) Color.Red else Color.Unspecified,
) {
actions.onClickFavorite.invoke(item.id, !item.favorite)
},
)
if (item.type == BaseItemKind.AUDIO && item.data.albumId != null) {
add(
DialogItem(
context.getString(R.string.go_to_album),
Icons.Default.ArrowForward,
) {
actions.onClickGoToAlbum.invoke(item.data.albumId!!)
},
)
}
if ((item.type == BaseItemKind.AUDIO || item.type == BaseItemKind.MUSIC_ALBUM) && item.data.artistItems?.isNotEmpty() == true) {
add(
DialogItem(
context.getString(R.string.go_to_artist),
Icons.Default.ArrowForward,
) {
actions.onClickGoToArtist.invoke(
item.data.artistItems!!
.first()
.id,
)
},
)
}
}

View file

@ -498,6 +498,8 @@
<string name="go_to_artist">Go to artist</string> <string name="go_to_artist">Go to artist</string>
<string name="now_playing">Now playing</string> <string name="now_playing">Now playing</string>
<string name="instant_mix">Instant mix</string> <string name="instant_mix">Instant mix</string>
<string name="go_to_album">Go to album</string>
<string name="add_to_queue">Add to queue</string>
<string-array name="theme_song_volume"> <string-array name="theme_song_volume">
<item>Disabled</item> <item>Disabled</item>