From 826bbad49c694f1e0515520f14eb9918bb2d7bd9 Mon Sep 17 00:00:00 2001 From: Damontecres Date: Wed, 2 Sep 2026 02:50:42 -0400 Subject: [PATCH] Fixes to Now Playing page (#1915) ## Description Fixes several issues on the Now Playing page - Keep controller focus after lyrics are focused - Fix the queue not visually updating until recomposition - The first focused queue shown/focused is now what's currently playing ### Related issues Fixes #1821 ### Testing Emulator ## Screenshots N/A, just behavior fixes ## AI or LLM usage None --- .../ui/detail/music/NowPlayingOverlay.kt | 42 ++++++++++++++++--- .../ui/detail/music/NowPlayingPage.kt | 20 ++++----- 2 files changed, 44 insertions(+), 18 deletions(-) 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 a340f66c..8d12bb33 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 @@ -22,6 +22,8 @@ import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.relocation.BringIntoViewRequester +import androidx.compose.foundation.relocation.bringIntoViewRequester import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MoreVert @@ -48,6 +50,7 @@ import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.AudioItem +import com.github.damontecres.wholphin.services.rememberQueue import com.github.damontecres.wholphin.ui.components.Button import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.main.settings.MoveDirection @@ -65,7 +68,6 @@ fun NowPlayingOverlay( state: NowPlayingState, player: Player, current: AudioItem?, - queue: List, controllerViewState: ControllerViewState, onClickSong: (Int, AudioItem) -> Unit, onLongClickSong: (Int, AudioItem) -> Unit, @@ -78,6 +80,7 @@ fun NowPlayingOverlay( ) { val scope = rememberCoroutineScope() val focusRequester = remember { FocusRequester() } + val currentItemFocusRequester = remember { FocusRequester() } LaunchedEffect(Unit) { focusRequester.tryRequestFocus() } var queueHasFocus by remember { mutableStateOf(false) } @@ -89,13 +92,16 @@ fun NowPlayingOverlay( }, animationSpec = tween(durationMillis = 500), ) - val listState = rememberLazyListState() + val listState = + rememberLazyListState( + initialFirstVisibleItemIndex = state.musicServiceState.currentIndex, + ) var showButtons by remember { mutableStateOf(true) } val firstFocusRequester = remember { FocusRequester() } BackHandler(!showButtons) { scope.launch { - listState.animateScrollToItem(0) + listState.scrollToItem(0) firstFocusRequester.tryRequestFocus() } } @@ -141,6 +147,12 @@ fun NowPlayingOverlay( .align(Alignment.CenterHorizontally), ) } + val queue = + rememberQueue( + player, + state.musicServiceState.queueVersion, + state.musicServiceState.queueSize, + ) if (queue.isEmpty()) { Text("No items") } else { @@ -158,12 +170,16 @@ fun NowPlayingOverlay( .onFocusChanged { queueHasFocus = it.hasFocus }.focusProperties { + onEnter = { + currentItemFocusRequester.tryRequestFocus() + } onExit = { if (requestedFocusDirection == FocusDirection.Up) focusRequester.requestFocus() } }, ) { itemsIndexed(queue, key = { _, song -> song.key }) { index, song -> + val bringIntoViewRequester = remember { BringIntoViewRequester() } Row( verticalAlignment = Alignment.CenterVertically, modifier = @@ -176,7 +192,8 @@ fun NowPlayingOverlay( .onFocusChanged { if (it.hasFocus) showButtons = index < 3 controllerViewState.pulseControls() - }.animateItem(), + }.animateItem() + .bringIntoViewRequester(bringIntoViewRequester), ) { SongListItem( title = song.title, @@ -193,6 +210,9 @@ fun NowPlayingOverlay( .ifElse( index == 0, Modifier.focusRequester(firstFocusRequester), + ).ifElse( + index == state.musicServiceState.currentIndex, + Modifier.focusRequester(currentItemFocusRequester), ), ) Row( @@ -203,12 +223,22 @@ fun NowPlayingOverlay( MoveButton( icon = R.string.fa_caret_up, enabled = index > 0, - onClick = { onMoveQueue.invoke(index, MoveDirection.UP) }, + onClick = { + onMoveQueue.invoke(index, MoveDirection.UP) + scope.launch { + bringIntoViewRequester.bringIntoView() + } + }, ) MoveButton( icon = R.string.fa_caret_down, enabled = index < queue.lastIndex, - onClick = { onMoveQueue.invoke(index, MoveDirection.DOWN) }, + onClick = { + onMoveQueue.invoke(index, MoveDirection.DOWN) + scope.launch { + bringIntoViewRequester.bringIntoView() + } + }, ) Button( onClick = { onClickMoreItem.invoke(index, song) }, 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 30cd20ad..1c4cb71f 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 @@ -51,6 +51,7 @@ import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.media3.common.util.UnstableApi +import androidx.media3.ui.compose.state.rememberCurrentMediaItemState import androidx.tv.material3.Button import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text @@ -60,7 +61,6 @@ import com.github.damontecres.wholphin.data.model.AudioItem import com.github.damontecres.wholphin.preferences.AppPreferences import com.github.damontecres.wholphin.preferences.BackdropStyle import com.github.damontecres.wholphin.preferences.UserPreferences -import com.github.damontecres.wholphin.services.rememberQueue import com.github.damontecres.wholphin.ui.components.BasicDialog import com.github.damontecres.wholphin.ui.components.ContextMenu import com.github.damontecres.wholphin.ui.components.ContextMenuDialog @@ -91,13 +91,8 @@ fun NowPlayingPage( val context = LocalContext.current val state by viewModel.state.collectAsState() val player = viewModel.player - val queue = - rememberQueue( - player, - state.musicServiceState.queueVersion, - state.musicServiceState.queueSize, - ) - val current = queue.getOrNull(state.musicServiceState.currentIndex) + val currentMediaItem = rememberCurrentMediaItemState(player) + val current = (currentMediaItem.mediaItem?.localConfiguration?.tag as? AudioItem) val viz by viewModel.viz.collectAsState() val controllerViewState = viewModel.controllerViewState @@ -152,10 +147,12 @@ fun NowPlayingPage( LaunchedEffect(lyricsFocused) { if (lyricsFocused != null) { controllerViewState.hideControls() + delay(5.seconds) + if (!controllerViewState.controlsVisible) { + focusRequester.tryRequestFocus() + } + lyricsFocused = null } - delay(5.seconds) - lyricsFocused = null - focusRequester.tryRequestFocus() } BackHandler(lyricsFocused != null) { focusRequester.tryRequestFocus() @@ -351,7 +348,6 @@ fun NowPlayingPage( state = state, player = player, current = current, - queue = queue, controllerViewState = controllerViewState, onMoveQueue = { index, direction -> viewModel.moveQueue(index, direction) }, onClickMore = { showViewOptionsDialog = true },