This commit is contained in:
Damontecres 2026-02-28 23:30:26 -05:00
parent 6a1725fd3d
commit ea243975ad
No known key found for this signature in database
11 changed files with 234 additions and 84 deletions

View file

@ -325,8 +325,8 @@ class MainActivity : AppCompatActivity() {
// ?: Destination.Home(), // ?: Destination.Home(),
// TODO undo // TODO undo
?: Destination.MediaItem( ?: Destination.MediaItem(
itemId = "bbc3e562-6045-5521-dfda-7effa56e14f2".toUUID(), itemId = "011ef0c7ca45684f2cd9dd3b020ca5f6".toUUID(),
type = BaseItemKind.MUSIC_ARTIST, type = BaseItemKind.MUSIC_ALBUM,
), ),
navigationManager = navigationManager, navigationManager = navigationManager,
preferences = preferences, preferences = preferences,

View file

@ -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.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.main.settings.MoveDirection
import com.github.damontecres.wholphin.ui.onMain import com.github.damontecres.wholphin.ui.onMain
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.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 @Stable

View file

@ -12,6 +12,7 @@ import androidx.compose.foundation.lazy.LazyColumn
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.foundation.shape.RoundedCornerShape
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
@ -21,9 +22,11 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier 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.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext 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
@ -330,6 +333,9 @@ fun AlbumDetailsPage(
item { item {
Text( Text(
text = stringResource(R.string.songs), text = stringResource(R.string.songs),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(start = 8.dp),
) )
} }
itemsIndexed(state.songs) { index, song -> itemsIndexed(state.songs) { index, song ->
@ -389,7 +395,11 @@ fun AlbumHeader(
AsyncImage( AsyncImage(
model = imageUrl, model = imageUrl,
contentDescription = null, contentDescription = null,
modifier = Modifier.fillMaxWidth(.25f), contentScale = ContentScale.FillWidth,
modifier =
Modifier
.fillMaxWidth(.20f)
.clip(RoundedCornerShape(16.dp)),
) )
Column( Column(
verticalArrangement = Arrangement.spacedBy(4.dp), verticalArrangement = Arrangement.spacedBy(4.dp),

View file

@ -13,6 +13,7 @@ import androidx.compose.foundation.lazy.LazyColumn
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.foundation.shape.RoundedCornerShape
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
@ -22,9 +23,11 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier 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.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext 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
@ -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.LoadingPage
import com.github.damontecres.wholphin.ui.components.OverviewText import com.github.damontecres.wholphin.ui.components.OverviewText
import com.github.damontecres.wholphin.ui.components.QuickDetails 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.launchIO
import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.nav.Destination 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.ApiRequestPager
import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.GetItemsRequestHandler import com.github.damontecres.wholphin.util.GetItemsRequestHandler
@ -149,13 +154,44 @@ class ArtistViewModel
loading = LoadingState.Success, 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) { } catch (ex: Exception) {
_state.update { it.copy(loading = LoadingState.Error(ex)) } _state.update { it.copy(loading = LoadingState.Error(ex)) }
} }
} }
} }
fun init() {
viewModelScope.launchDefault {
state.value.artist?.let {
backdropService.submit(it)
}
}
}
fun setFavorite( fun setFavorite(
itemId: UUID, itemId: UUID,
favorite: Boolean, favorite: Boolean,
@ -169,14 +205,25 @@ class ArtistViewModel
_state.update { it.copy(artist = artist) } _state.update { it.copy(artist = artist) }
} }
fun play( fun play(shuffled: Boolean) {
shuffled: Boolean,
startIndex: Int = 0,
) {
viewModelScope.launchIO { viewModelScope.launchIO {
Timber.v("Playing artist %s from %s", itemId, startIndex) Timber.v("Playing artist %s", itemId)
val songs = state.value.topSongs as ApiRequestPager<*> val request =
musicService.setQueue(songs, startIndex, shuffled) 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) navigationManager.navigateTo(Destination.NowPlaying)
} }
} }
fun playSong(song: BaseItem) {
viewModelScope.launchDefault {
musicService.setQueue(listOf(song), false)
}
}
} }
data class ArtistState( data class ArtistState(
@ -233,7 +286,7 @@ fun ArtistDetailsPage(
remember { remember {
MusicMoreDialogActions( MusicMoreDialogActions(
onNavigate = { viewModel.navigationManager.navigateTo(it) }, onNavigate = { viewModel.navigationManager.navigateTo(it) },
onClickPlay = { index, _ -> viewModel.play(false, index) }, onClickPlay = { index, song -> TODO() },
onClickAddToQueue = { index, itemId -> viewModel.addToQueue(itemId, index) }, onClickAddToQueue = { index, itemId -> viewModel.addToQueue(itemId, index) },
onClickFavorite = { itemId, favorite -> viewModel.setFavorite(itemId, favorite) }, onClickFavorite = { itemId, favorite -> viewModel.setFavorite(itemId, favorite) },
onClickAddPlaylist = {}, onClickAddPlaylist = {},
@ -254,7 +307,10 @@ fun ArtistDetailsPage(
LoadingState.Success -> { LoadingState.Success -> {
val artist = state.artist!! val artist = state.artist!!
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) { focusRequester.requestFocus() } LaunchedEffect(Unit) {
focusRequester.requestFocus()
viewModel.init()
}
val bringIntoViewRequester = remember { BringIntoViewRequester() } val bringIntoViewRequester = remember { BringIntoViewRequester() }
Box(modifier = modifier) { Box(modifier = modifier) {
LazyColumn( LazyColumn(
@ -284,7 +340,7 @@ fun ArtistDetailsPage(
actions = actions =
remember { remember {
MusicButtonActions( MusicButtonActions(
onClickPlay = { viewModel.play(it, 0) }, onClickPlay = { viewModel.play(it) },
onClickInstantMix = viewModel::startInstantMix, onClickInstantMix = viewModel::startInstantMix,
onClickFavorite = { onClickFavorite = {
viewModel.setFavorite( viewModel.setFavorite(
@ -320,13 +376,16 @@ fun ArtistDetailsPage(
if (state.topSongs.isNotEmpty()) { if (state.topSongs.isNotEmpty()) {
item { item {
Text( 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 -> itemsIndexed(state.topSongs) { index, song ->
SongListItem( SongListItem(
song = song, song = song,
onClick = { viewModel.play(false, index) }, onClick = { song?.let { viewModel.playSong(it) } },
onLongClick = { onLongClick = {
if (song != null) { if (song != null) {
moreDialog = moreDialog =
@ -403,7 +462,11 @@ fun ArtistHeader(
AsyncImage( AsyncImage(
model = imageUrl, model = imageUrl,
contentDescription = null, contentDescription = null,
modifier = Modifier.fillMaxWidth(.25f), contentScale = ContentScale.FillWidth,
modifier =
Modifier
.fillMaxWidth(.20f)
.clip(RoundedCornerShape(16.dp)),
) )
Column( Column(
verticalArrangement = Arrangement.spacedBy(4.dp), verticalArrangement = Arrangement.spacedBy(4.dp),

View file

@ -8,6 +8,7 @@ import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row 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.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.layout.wrapContentWidth
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.lazy.rememberLazyListState 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.R
import com.github.damontecres.wholphin.data.model.AudioItem import com.github.damontecres.wholphin.data.model.AudioItem
import com.github.damontecres.wholphin.ui.ifElse 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.ControllerViewState
import com.github.damontecres.wholphin.ui.playback.PlaybackAction import com.github.damontecres.wholphin.ui.playback.PlaybackAction
import com.github.damontecres.wholphin.ui.playback.PlaybackButtons import com.github.damontecres.wholphin.ui.playback.PlaybackButtons
import com.github.damontecres.wholphin.ui.playback.PlaybackFaButton import com.github.damontecres.wholphin.ui.playback.PlaybackFaButton
import com.github.damontecres.wholphin.ui.playback.SeekBar 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.roundSeconds
import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.ui.tryRequestFocus
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -65,6 +69,7 @@ fun NowPlayingOverlay(
current: AudioItem?, current: AudioItem?,
queue: List<AudioItem>, queue: List<AudioItem>,
controllerViewState: ControllerViewState, controllerViewState: ControllerViewState,
onMoveQueue: (Int, MoveDirection) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
@ -228,23 +233,46 @@ fun NowPlayingOverlay(
}, },
) { ) {
itemsIndexed(queue) { index, song -> itemsIndexed(queue) { index, song ->
SongListItem( Row(
title = song.title, verticalAlignment = Alignment.CenterVertically,
artist = song.artistNames, modifier = Modifier.fillMaxWidth(),
indexNumber = index + 1, ) {
runtime = song.runtime?.roundSeconds, SongListItem(
showArtist = true, title = song.title,
isPlaying = current?.id == song.id, artist = song.artistNames,
onClick = { indexNumber = index + 1,
player.seekTo(index, 0L) runtime = song.runtime?.roundSeconds,
}, showArtist = true,
onLongClick = {}, isPlaying = current?.id == song.id,
modifier = onClick = {
Modifier.ifElse( player.seekTo(index, 0L)
index == 0, },
Modifier.focusRequester(firstFocusRequester), 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) },
)
}
}
} }
} }
} }

View file

@ -5,6 +5,8 @@ import androidx.annotation.OptIn
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandHorizontally import androidx.compose.animation.expandHorizontally
import androidx.compose.animation.shrinkHorizontally import androidx.compose.animation.shrinkHorizontally
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.focusable import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -131,7 +133,9 @@ fun NowPlayingPage(
controllerViewState.hideControls() controllerViewState.hideControls()
} }
AnimatedVisibility( AnimatedVisibility(
controllerViewState.controlsVisible, visible = controllerViewState.controlsVisible,
enter = slideInVertically { it },
exit = slideOutVertically { it },
modifier = modifier =
Modifier Modifier
.align(Alignment.BottomCenter), .align(Alignment.BottomCenter),
@ -142,6 +146,7 @@ fun NowPlayingPage(
current = current, current = current,
queue = queue, queue = queue,
controllerViewState = controllerViewState, controllerViewState = controllerViewState,
onMoveQueue = { index, direction -> viewModel.moveQueue(index, direction) },
modifier = modifier =
Modifier Modifier
.background(AppColors.TransparentBlack50) .background(AppColors.TransparentBlack50)

View file

@ -14,6 +14,7 @@ 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.launchDefault 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.onMain
import com.github.damontecres.wholphin.ui.playback.ControllerViewState import com.github.damontecres.wholphin.ui.playback.ControllerViewState
import com.mayakapps.kache.InMemoryKache import com.mayakapps.kache.InMemoryKache
@ -164,4 +165,9 @@ class NowPlayingViewModel
} }
} }
} }
fun moveQueue(
index: Int,
direction: MoveDirection,
) = viewModelScope.launchDefault { musicService.moveQueue(index, direction) }
} }

View file

@ -3,6 +3,7 @@ package com.github.damontecres.wholphin.ui.detail.music
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.Arrangement
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.lazy.LazyColumn
@ -22,7 +23,6 @@ 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.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
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
@ -98,51 +98,66 @@ fun SongListItem(
modifier = modifier, modifier = modifier,
) { ) {
val focused by interactionSource.collectIsFocusedAsState() val focused by interactionSource.collectIsFocusedAsState()
ListItem( val leadingContent: @Composable (BoxScope.() -> Unit) = {
selected = isPlaying, Row(
onClick = onClick, verticalAlignment = Alignment.CenterVertically,
onLongClick = onLongClick, ) {
interactionSource = interactionSource, Text(
leadingContent = { text = indexNumber?.toString() ?: "",
Row( )
verticalAlignment = Alignment.CenterVertically, if (isPlaying) {
) { Icon(
Text( imageVector = Icons.Default.PlayArrow,
text = indexNumber?.toString() ?: "", contentDescription = null,
) )
if (isPlaying) {
Icon(
imageVector = Icons.Default.PlayArrow,
contentDescription = null,
)
}
} }
}, }
headlineContent = { }
Text( val headlineContent = @Composable {
text = title ?: "", Text(
maxLines = 1, text = title ?: "",
modifier = Modifier.enableMarquee(focused), maxLines = 1,
) modifier = Modifier.enableMarquee(focused),
}, )
supportingContent = }
if (showArtist && artist.isNotNullOrBlank()) { val trailingContent = @Composable {
{ Text(
Text( text = runtime.toString(),
text = artist, )
) }
}
} else { if (showArtist) {
null // TODO use dense?
ListItem(
selected = isPlaying,
onClick = onClick,
onLongClick = onLongClick,
interactionSource = interactionSource,
leadingContent = leadingContent,
headlineContent = headlineContent,
supportingContent = {
Text(
text = artist ?: "",
)
}, },
trailingContent = { trailingContent = trailingContent,
Text( scale = ListItemDefaults.scale(1f, 1f, .95f),
text = runtime.toString(), modifier = Modifier,
) )
}, } else {
scale = ListItemDefaults.scale(1f, 1f, .95f), ListItem(
modifier = Modifier.weight(1f), selected = isPlaying,
) onClick = onClick,
onLongClick = onLongClick,
interactionSource = interactionSource,
leadingContent = leadingContent,
headlineContent = headlineContent,
supportingContent = null,
trailingContent = trailingContent,
scale = ListItemDefaults.scale(1f, 1f, .95f),
modifier = Modifier,
)
}
} }
} }

View file

@ -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.components.Button
import com.github.damontecres.wholphin.ui.launchDefault import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.ui.launchIO 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.nav.NavDrawerItem
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.ExceptionHandler
@ -85,11 +86,6 @@ data class NavDrawerPin(
} }
} }
enum class MoveDirection {
UP,
DOWN,
}
private fun <T> List<T>.move( private fun <T> List<T>.move(
direction: MoveDirection, direction: MoveDirection,
index: Int, index: Int,
@ -246,7 +242,7 @@ fun NavDrawerPreferenceListItem(
} }
@Composable @Composable
private fun MoveButton( fun MoveButton(
@StringRes icon: Int, @StringRes icon: Int,
enabled: Boolean, enabled: Boolean,
onClick: () -> Unit, onClick: () -> Unit,

View file

@ -1,9 +1,26 @@
package com.github.damontecres.wholphin.util package com.github.damontecres.wholphin.util
import java.util.function.IntFunction
import java.util.function.Predicate import java.util.function.Predicate
interface BlockingList<T> : List<T> { interface BlockingList<T> : List<T> {
suspend fun getBlocking(index: Int): T suspend fun getBlocking(index: Int): T
suspend fun indexOfBlocking(predicate: Predicate<T>): Int suspend fun indexOfBlocking(predicate: Predicate<T>): Int
companion object {
fun <T> of(list: List<T>): BlockingList<T> = BlockingListWrapper(list)
}
}
private class BlockingListWrapper<T>(
private val list: List<T>,
) : BlockingList<T>,
List<T> by list {
override suspend fun getBlocking(index: Int): T = get(index)
override suspend fun indexOfBlocking(predicate: Predicate<T>): Int = indexOfFirst { predicate.test(it) }
@Deprecated("Deprecated")
override fun <T> toArray(generator: IntFunction<Array<out T?>?>): Array<out T?> = super<List>.toArray(generator)
} }

View file

@ -726,5 +726,6 @@
<string name="artist">Artist</string> <string name="artist">Artist</string>
<string name="album_artist">Album artist</string> <string name="album_artist">Album artist</string>
<string name="album">Album</string> <string name="album">Album</string>
<string name="popular_songs">Popular songs</string>
</resources> </resources>