From ea243975ad4129a0bc39253718f476334f1e2de6 Mon Sep 17 00:00:00 2001 From: Damontecres Date: Sat, 28 Feb 2026 23:30:26 -0500 Subject: [PATCH] WIP --- .../damontecres/wholphin/MainActivity.kt | 4 +- .../wholphin/services/MusicService.kt | 9 ++ .../ui/detail/music/AlbumDetailsPage.kt | 12 ++- .../ui/detail/music/ArtistDetailsPage.kt | 91 +++++++++++++--- .../ui/detail/music/NowPlayingOverlay.kt | 62 ++++++++--- .../ui/detail/music/NowPlayingPage.kt | 7 +- .../ui/detail/music/NowPlayingViewModel.kt | 6 ++ .../ui/detail/music/SongListDisplay.kt | 101 ++++++++++-------- .../ui/preferences/NavDrawerPreference.kt | 8 +- .../damontecres/wholphin/util/BlockingList.kt | 17 +++ app/src/main/res/values/strings.xml | 1 + 11 files changed, 234 insertions(+), 84 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt index 48cc9fff..ed9642e0 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt @@ -325,8 +325,8 @@ class MainActivity : AppCompatActivity() { // ?: Destination.Home(), // TODO undo ?: Destination.MediaItem( - itemId = "bbc3e562-6045-5521-dfda-7effa56e14f2".toUUID(), - type = BaseItemKind.MUSIC_ARTIST, + itemId = "011ef0c7ca45684f2cd9dd3b020ca5f6".toUUID(), + type = BaseItemKind.MUSIC_ALBUM, ), navigationManager = navigationManager, preferences = preferences, diff --git a/app/src/main/java/com/github/damontecres/wholphin/services/MusicService.kt b/app/src/main/java/com/github/damontecres/wholphin/services/MusicService.kt index e17e6bbf..feaa2aae 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/services/MusicService.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/MusicService.kt @@ -17,6 +17,7 @@ import com.github.damontecres.wholphin.data.model.AudioItem import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient import com.github.damontecres.wholphin.ui.DefaultItemFields +import com.github.damontecres.wholphin.ui.main.settings.MoveDirection import com.github.damontecres.wholphin.ui.onMain import com.github.damontecres.wholphin.ui.toServerString import com.github.damontecres.wholphin.util.BlockingList @@ -200,6 +201,14 @@ class MusicService } } } + + suspend fun moveQueue( + index: Int, + direction: MoveDirection, + ) = withContext(Dispatchers.Main) { + player.moveMediaItem(index, if (direction == MoveDirection.UP) index - 1 else index + 1) + updateQueueSize() + } } @Stable diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/AlbumDetailsPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/AlbumDetailsPage.kt index fce887b8..88b4c58a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/AlbumDetailsPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/AlbumDetailsPage.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.relocation.BringIntoViewRequester import androidx.compose.foundation.relocation.bringIntoViewRequester +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -21,9 +22,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -330,6 +333,9 @@ fun AlbumDetailsPage( item { Text( text = stringResource(R.string.songs), + style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.onBackground, + modifier = Modifier.padding(start = 8.dp), ) } itemsIndexed(state.songs) { index, song -> @@ -389,7 +395,11 @@ fun AlbumHeader( AsyncImage( model = imageUrl, contentDescription = null, - modifier = Modifier.fillMaxWidth(.25f), + contentScale = ContentScale.FillWidth, + modifier = + Modifier + .fillMaxWidth(.20f) + .clip(RoundedCornerShape(16.dp)), ) Column( verticalArrangement = Arrangement.spacedBy(4.dp), diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/ArtistDetailsPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/ArtistDetailsPage.kt index c6441487..bd67bbcd 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/ArtistDetailsPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/ArtistDetailsPage.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.relocation.BringIntoViewRequester import androidx.compose.foundation.relocation.bringIntoViewRequester +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -22,9 +23,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -58,9 +61,11 @@ import com.github.damontecres.wholphin.ui.components.GenreText import com.github.damontecres.wholphin.ui.components.LoadingPage import com.github.damontecres.wholphin.ui.components.OverviewText import com.github.damontecres.wholphin.ui.components.QuickDetails +import com.github.damontecres.wholphin.ui.launchDefault import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.nav.Destination +import com.github.damontecres.wholphin.ui.toBaseItems import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.GetItemsRequestHandler @@ -149,13 +154,44 @@ class ArtistViewModel loading = LoadingState.Success, ) } - backdropService.submit(artist) + + viewModelScope.launchIO { + val request = + GetItemsRequest( + parentId = itemId, + fields = DefaultItemFields, + recursive = true, + includeItemTypes = listOf(BaseItemKind.AUDIO), + minCommunityRating = 1.0, + sortBy = + listOf( + ItemSortBy.COMMUNITY_RATING, + ), + sortOrder = listOf(SortOrder.DESCENDING), + limit = 10, + ) + val topSongs = + GetItemsRequestHandler.execute(api, request).toBaseItems(api, false) + if (topSongs.isNotEmpty()) { + _state.update { + it.copy(topSongs = topSongs) + } + } + } } catch (ex: Exception) { _state.update { it.copy(loading = LoadingState.Error(ex)) } } } } + fun init() { + viewModelScope.launchDefault { + state.value.artist?.let { + backdropService.submit(it) + } + } + } + fun setFavorite( itemId: UUID, favorite: Boolean, @@ -169,14 +205,25 @@ class ArtistViewModel _state.update { it.copy(artist = artist) } } - fun play( - shuffled: Boolean, - startIndex: Int = 0, - ) { + fun play(shuffled: Boolean) { viewModelScope.launchIO { - Timber.v("Playing artist %s from %s", itemId, startIndex) - val songs = state.value.topSongs as ApiRequestPager<*> - musicService.setQueue(songs, startIndex, shuffled) + Timber.v("Playing artist %s", itemId) + val request = + GetItemsRequest( + parentId = itemId, + fields = DefaultItemFields, + recursive = true, + includeItemTypes = listOf(BaseItemKind.AUDIO), + sortBy = + listOf( + ItemSortBy.PREMIERE_DATE, + ItemSortBy.INDEX_NUMBER, + ), + sortOrder = listOf(SortOrder.DESCENDING, SortOrder.ASCENDING), + ) + val pager = + ApiRequestPager(api, request, GetItemsRequestHandler, viewModelScope).init() + musicService.setQueue(pager, 0, shuffled) } } @@ -199,6 +246,12 @@ class ArtistViewModel navigationManager.navigateTo(Destination.NowPlaying) } } + + fun playSong(song: BaseItem) { + viewModelScope.launchDefault { + musicService.setQueue(listOf(song), false) + } + } } data class ArtistState( @@ -233,7 +286,7 @@ fun ArtistDetailsPage( remember { MusicMoreDialogActions( onNavigate = { viewModel.navigationManager.navigateTo(it) }, - onClickPlay = { index, _ -> viewModel.play(false, index) }, + onClickPlay = { index, song -> TODO() }, onClickAddToQueue = { index, itemId -> viewModel.addToQueue(itemId, index) }, onClickFavorite = { itemId, favorite -> viewModel.setFavorite(itemId, favorite) }, onClickAddPlaylist = {}, @@ -254,7 +307,10 @@ fun ArtistDetailsPage( LoadingState.Success -> { val artist = state.artist!! val focusRequester = remember { FocusRequester() } - LaunchedEffect(Unit) { focusRequester.requestFocus() } + LaunchedEffect(Unit) { + focusRequester.requestFocus() + viewModel.init() + } val bringIntoViewRequester = remember { BringIntoViewRequester() } Box(modifier = modifier) { LazyColumn( @@ -284,7 +340,7 @@ fun ArtistDetailsPage( actions = remember { MusicButtonActions( - onClickPlay = { viewModel.play(it, 0) }, + onClickPlay = { viewModel.play(it) }, onClickInstantMix = viewModel::startInstantMix, onClickFavorite = { viewModel.setFavorite( @@ -320,13 +376,16 @@ fun ArtistDetailsPage( if (state.topSongs.isNotEmpty()) { item { Text( - text = stringResource(R.string.songs), + text = stringResource(R.string.popular_songs), + style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.onBackground, + modifier = Modifier.padding(start = 8.dp), ) } itemsIndexed(state.topSongs) { index, song -> SongListItem( song = song, - onClick = { viewModel.play(false, index) }, + onClick = { song?.let { viewModel.playSong(it) } }, onLongClick = { if (song != null) { moreDialog = @@ -403,7 +462,11 @@ fun ArtistHeader( AsyncImage( model = imageUrl, contentDescription = null, - modifier = Modifier.fillMaxWidth(.25f), + contentScale = ContentScale.FillWidth, + modifier = + Modifier + .fillMaxWidth(.20f) + .clip(RoundedCornerShape(16.dp)), ) Column( verticalArrangement = Arrangement.spacedBy(4.dp), diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingOverlay.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingOverlay.kt index de76501d..ec08367b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingOverlay.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingOverlay.kt @@ -8,6 +8,7 @@ import androidx.compose.animation.core.tween import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -15,6 +16,7 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState @@ -46,11 +48,13 @@ import androidx.tv.material3.Text import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.AudioItem import com.github.damontecres.wholphin.ui.ifElse +import com.github.damontecres.wholphin.ui.main.settings.MoveDirection import com.github.damontecres.wholphin.ui.playback.ControllerViewState import com.github.damontecres.wholphin.ui.playback.PlaybackAction import com.github.damontecres.wholphin.ui.playback.PlaybackButtons import com.github.damontecres.wholphin.ui.playback.PlaybackFaButton import com.github.damontecres.wholphin.ui.playback.SeekBar +import com.github.damontecres.wholphin.ui.preferences.MoveButton import com.github.damontecres.wholphin.ui.roundSeconds import com.github.damontecres.wholphin.ui.tryRequestFocus import kotlinx.coroutines.launch @@ -65,6 +69,7 @@ fun NowPlayingOverlay( current: AudioItem?, queue: List, controllerViewState: ControllerViewState, + onMoveQueue: (Int, MoveDirection) -> Unit, modifier: Modifier = Modifier, ) { val scope = rememberCoroutineScope() @@ -228,23 +233,46 @@ fun NowPlayingOverlay( }, ) { itemsIndexed(queue) { index, song -> - SongListItem( - title = song.title, - artist = song.artistNames, - indexNumber = index + 1, - runtime = song.runtime?.roundSeconds, - showArtist = true, - isPlaying = current?.id == song.id, - onClick = { - player.seekTo(index, 0L) - }, - onLongClick = {}, - modifier = - Modifier.ifElse( - index == 0, - Modifier.focusRequester(firstFocusRequester), - ), - ) + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth(), + ) { + SongListItem( + title = song.title, + artist = song.artistNames, + indexNumber = index + 1, + runtime = song.runtime?.roundSeconds, + showArtist = true, + isPlaying = current?.id == song.id, + onClick = { + player.seekTo(index, 0L) + }, + onLongClick = {}, + modifier = + Modifier + .weight(1f) + .ifElse( + index == 0, + Modifier.focusRequester(firstFocusRequester), + ), + ) + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.wrapContentWidth(), + ) { + MoveButton( + icon = R.string.fa_caret_up, + enabled = index > 0, + onClick = { onMoveQueue.invoke(index, MoveDirection.UP) }, + ) + MoveButton( + icon = R.string.fa_caret_down, + enabled = index < queue.lastIndex, + onClick = { onMoveQueue.invoke(index, MoveDirection.DOWN) }, + ) + } + } } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingPage.kt index 9be7e4f9..168a58e4 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingPage.kt @@ -5,6 +5,8 @@ import androidx.annotation.OptIn import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandHorizontally import androidx.compose.animation.shrinkHorizontally +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.background import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.Arrangement @@ -131,7 +133,9 @@ fun NowPlayingPage( controllerViewState.hideControls() } AnimatedVisibility( - controllerViewState.controlsVisible, + visible = controllerViewState.controlsVisible, + enter = slideInVertically { it }, + exit = slideOutVertically { it }, modifier = Modifier .align(Alignment.BottomCenter), @@ -142,6 +146,7 @@ fun NowPlayingPage( current = current, queue = queue, controllerViewState = controllerViewState, + onMoveQueue = { index, direction -> viewModel.moveQueue(index, direction) }, modifier = Modifier .background(AppColors.TransparentBlack50) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingViewModel.kt index fbaba251..d78ca31f 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingViewModel.kt @@ -14,6 +14,7 @@ import com.github.damontecres.wholphin.services.MusicService import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.services.UserPreferencesService import com.github.damontecres.wholphin.ui.launchDefault +import com.github.damontecres.wholphin.ui.main.settings.MoveDirection import com.github.damontecres.wholphin.ui.onMain import com.github.damontecres.wholphin.ui.playback.ControllerViewState import com.mayakapps.kache.InMemoryKache @@ -164,4 +165,9 @@ class NowPlayingViewModel } } } + + fun moveQueue( + index: Int, + direction: MoveDirection, + ) = viewModelScope.launchDefault { musicService.moveQueue(index, direction) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/SongListDisplay.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/SongListDisplay.kt index 8eba55c4..9b92d257 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/SongListDisplay.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/SongListDisplay.kt @@ -3,6 +3,7 @@ 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.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.lazy.LazyColumn @@ -22,7 +23,6 @@ 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.isNotNullOrBlank import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.roundMinutes import com.github.damontecres.wholphin.ui.theme.WholphinTheme @@ -98,51 +98,66 @@ fun SongListItem( modifier = modifier, ) { val focused by interactionSource.collectIsFocusedAsState() - ListItem( - selected = isPlaying, - onClick = onClick, - onLongClick = onLongClick, - interactionSource = interactionSource, - leadingContent = { - Row( - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = indexNumber?.toString() ?: "", + val leadingContent: @Composable (BoxScope.() -> Unit) = { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = indexNumber?.toString() ?: "", + ) + if (isPlaying) { + Icon( + imageVector = Icons.Default.PlayArrow, + contentDescription = null, ) - if (isPlaying) { - Icon( - imageVector = Icons.Default.PlayArrow, - contentDescription = null, - ) - } } - }, - headlineContent = { - Text( - text = title ?: "", - maxLines = 1, - modifier = Modifier.enableMarquee(focused), - ) - }, - supportingContent = - if (showArtist && artist.isNotNullOrBlank()) { - { - Text( - text = artist, - ) - } - } else { - null + } + } + val headlineContent = @Composable { + Text( + text = title ?: "", + maxLines = 1, + modifier = Modifier.enableMarquee(focused), + ) + } + val trailingContent = @Composable { + Text( + text = runtime.toString(), + ) + } + + if (showArtist) { + // TODO use dense? + ListItem( + selected = isPlaying, + onClick = onClick, + onLongClick = onLongClick, + interactionSource = interactionSource, + leadingContent = leadingContent, + headlineContent = headlineContent, + supportingContent = { + Text( + text = artist ?: "", + ) }, - trailingContent = { - Text( - text = runtime.toString(), - ) - }, - scale = ListItemDefaults.scale(1f, 1f, .95f), - modifier = Modifier.weight(1f), - ) + trailingContent = trailingContent, + scale = ListItemDefaults.scale(1f, 1f, .95f), + modifier = Modifier, + ) + } else { + ListItem( + selected = isPlaying, + onClick = onClick, + onLongClick = onLongClick, + interactionSource = interactionSource, + leadingContent = leadingContent, + headlineContent = headlineContent, + supportingContent = null, + trailingContent = trailingContent, + scale = ListItemDefaults.scale(1f, 1f, .95f), + modifier = Modifier, + ) + } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/NavDrawerPreference.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/NavDrawerPreference.kt index afdc93a1..89fce8fb 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/NavDrawerPreference.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/NavDrawerPreference.kt @@ -53,6 +53,7 @@ import com.github.damontecres.wholphin.ui.components.BasicDialog import com.github.damontecres.wholphin.ui.components.Button import com.github.damontecres.wholphin.ui.launchDefault import com.github.damontecres.wholphin.ui.launchIO +import com.github.damontecres.wholphin.ui.main.settings.MoveDirection import com.github.damontecres.wholphin.ui.nav.NavDrawerItem import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.util.ExceptionHandler @@ -85,11 +86,6 @@ data class NavDrawerPin( } } -enum class MoveDirection { - UP, - DOWN, -} - private fun List.move( direction: MoveDirection, index: Int, @@ -246,7 +242,7 @@ fun NavDrawerPreferenceListItem( } @Composable -private fun MoveButton( +fun MoveButton( @StringRes icon: Int, enabled: Boolean, onClick: () -> Unit, diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/BlockingList.kt b/app/src/main/java/com/github/damontecres/wholphin/util/BlockingList.kt index 75ccc38e..8a6fc939 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/BlockingList.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/BlockingList.kt @@ -1,9 +1,26 @@ package com.github.damontecres.wholphin.util +import java.util.function.IntFunction import java.util.function.Predicate interface BlockingList : List { suspend fun getBlocking(index: Int): T suspend fun indexOfBlocking(predicate: Predicate): Int + + companion object { + fun of(list: List): BlockingList = BlockingListWrapper(list) + } +} + +private class BlockingListWrapper( + private val list: List, +) : BlockingList, + List by list { + override suspend fun getBlocking(index: Int): T = get(index) + + override suspend fun indexOfBlocking(predicate: Predicate): Int = indexOfFirst { predicate.test(it) } + + @Deprecated("Deprecated") + override fun toArray(generator: IntFunction?>): Array = super.toArray(generator) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c5910249..1014f59c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -726,5 +726,6 @@ Artist Album artist Album + Popular songs