From 7044b4aa32b6e96d304ddd98a21ff789695be241 Mon Sep 17 00:00:00 2001 From: Damontecres Date: Mon, 2 Mar 2026 16:36:49 -0500 Subject: [PATCH] Move now playing song title to page --- .../wholphin/services/MusicService.kt | 36 ++++++++++------ .../ui/detail/music/MusicViewModel.kt | 5 +++ .../ui/detail/music/NowPlayingOverlay.kt | 20 +-------- .../ui/detail/music/NowPlayingPage.kt | 42 ++++++++++++++++--- 4 files changed, 66 insertions(+), 37 deletions(-) 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 a0b7bb75..983d296e 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 @@ -21,6 +21,7 @@ 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 +import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.profile.Codec import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers @@ -71,18 +72,19 @@ class MusicService /** * Fetches instant mix items, replaces the queue, and begins playback */ - suspend fun startInstantMix(itemId: UUID) { - val items = - api.instantMixApi - .getInstantMixFromItem( - userId = serverRepository.currentUser.value?.id, - itemId = itemId, - limit = 200, - fields = DefaultItemFields, - ).content.items - .map { BaseItem(it, false) } - setQueue(items, false) - } + suspend fun startInstantMix(itemId: UUID) = + loading { + val items = + api.instantMixApi + .getInstantMixFromItem( + userId = serverRepository.currentUser.value?.id, + itemId = itemId, + limit = 200, + fields = DefaultItemFields, + ).content.items + .map { BaseItem(it, false) } + setQueue(items, false) + } /** * Replace the queue with the given list and starting playing the song as startIndex as soon as its ready @@ -144,7 +146,7 @@ class MusicService suspend fun addAllToQueue( list: BlockingList, startIndex: Int, - ) { + ) = loading { var remaining = startIndex list.indices .chunked(25) @@ -215,6 +217,13 @@ class MusicService onMain { player.addMediaItem(state.value.currentIndex + 1, mediaItem) } updateQueueSize() } + + private suspend fun loading(block: suspend () -> T): T { + _state.update { it.copy(loadingState = LoadingState.Loading) } + val result = block.invoke() + _state.update { it.copy(loadingState = LoadingState.Success) } + return result + } } @Stable @@ -224,6 +233,7 @@ data class MusicServiceState( val currentItemId: UUID?, val isPlaying: Boolean, val currentItemTitle: String?, + val loadingState: LoadingState = LoadingState.Pending, ) { companion object { val EMPTY = MusicServiceState(0, 0, null, false, null) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/MusicViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/MusicViewModel.kt index c9823371..5fa0a5e3 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/MusicViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/MusicViewModel.kt @@ -10,6 +10,7 @@ import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.BlockingList +import kotlinx.coroutines.delay import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.model.api.BaseItemKind import timber.log.Timber @@ -93,6 +94,10 @@ abstract class MusicViewModel( viewModelScope.launchIO { Timber.v("Starting instant mix for %s", itemId) musicService.startInstantMix(itemId) + } + viewModelScope.launchDefault { + // TODO better way to wait for query above to start + delay(250) navigationManager.navigateTo(Destination.NowPlaying) } } 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 2eac11df..35a9a37d 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 @@ -86,7 +86,7 @@ fun NowPlayingOverlay( if (queueHasFocus) { 1f } else { - .5f + .33f }, animationSpec = tween(durationMillis = 500), ) @@ -111,24 +111,6 @@ fun NowPlayingOverlay( .padding(16.dp) .fillMaxHeight(height), ) { - current?.title?.let { - Text( - text = it, - style = MaterialTheme.typography.titleMedium, - ) - } - current?.albumTitle?.let { - Text( - text = it, - style = MaterialTheme.typography.titleSmall, - ) - } - current?.artistNames?.let { - Text( - text = it, - style = MaterialTheme.typography.titleSmall, - ) - } SeekBar( player = player, controllerViewState = controllerViewState, 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 a66a1874..1a296070 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 @@ -11,6 +11,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize @@ -29,13 +30,17 @@ import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.media3.common.util.UnstableApi +import androidx.tv.material3.MaterialTheme +import androidx.tv.material3.Text import coil3.compose.AsyncImage import com.github.damontecres.wholphin.preferences.AppPreferences import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.services.rememberQueue import com.github.damontecres.wholphin.ui.AppColors +import com.github.damontecres.wholphin.ui.components.LoadingPage import com.github.damontecres.wholphin.ui.playback.PlaybackKeyHandler import com.github.damontecres.wholphin.ui.tryRequestFocus +import com.github.damontecres.wholphin.util.LoadingState import kotlin.time.Duration.Companion.seconds @OptIn(UnstableApi::class) @@ -118,15 +123,39 @@ fun NowPlayingPage( .width(320.dp), ) } - AsyncImage( - contentDescription = null, - model = current?.imageUrl, + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier - .padding(80.dp) + .padding(40.dp) .fillMaxSize() .weight(1f), - ) + ) { + AsyncImage( + contentDescription = null, + model = current?.imageUrl, + modifier = Modifier.fillMaxSize(.7f), + ) + current?.title?.let { + Text( + text = it, + style = MaterialTheme.typography.titleLarge, + ) + } + current?.albumTitle?.let { + Text( + text = it, + style = MaterialTheme.typography.titleMedium, + ) + } + current?.artistNames?.let { + Text( + text = it, + style = MaterialTheme.typography.titleMedium, + ) + } + } } } @@ -154,5 +183,8 @@ fun NowPlayingPage( .align(Alignment.BottomCenter), ) } + if (state.musicServiceState.loadingState is LoadingState.Loading) { + LoadingPage(focusEnabled = false) + } } }