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 eacc17cf..f47c0915 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 @@ -96,7 +96,13 @@ class MusicService } mediaSession?.release() mediaSession = null - onMain { player.stop() } + onMain { + player.stop() + player.setMediaItems(emptyList()) + } + _state.update { + MusicServiceState.EMPTY + } } } @@ -236,7 +242,6 @@ class MusicService ) } } - start() } suspend fun moveQueue( @@ -256,7 +261,10 @@ class MusicService } suspend fun playIndex(index: Int) { - onMain { player.seekTo(index, 0L) } + onMain { + player.seekTo(index, 0L) + player.play() + } // MusicPlayerListener will update state } @@ -285,15 +293,21 @@ data class MusicServiceState( val queueSize: Int, val currentIndex: Int, val currentItemId: UUID?, - val isPlaying: Boolean, + val status: NowPlayingStatus, val currentItemTitle: String?, val loadingState: LoadingState = LoadingState.Pending, ) { companion object { - val EMPTY = MusicServiceState(0L, 0, 0, null, false, null) + val EMPTY = MusicServiceState(0L, 0, 0, null, NowPlayingStatus.IDLE, null) } } +enum class NowPlayingStatus { + PLAYING, + PAUSED, + IDLE, +} + /** * Listens to [Player] events and updates the [StateFlow] */ @@ -307,7 +321,7 @@ private class MusicPlayerListener( it.copy( queueSize = player.mediaItemCount, currentIndex = player.currentMediaItemIndex, - isPlaying = player.isPlaying, + status = if (player.isPlaying) NowPlayingStatus.PLAYING else NowPlayingStatus.IDLE, ) } } @@ -316,7 +330,7 @@ private class MusicPlayerListener( Timber.v("MusicPlayerListener onIsPlayingChanged") state.update { it.copy( - isPlaying = isPlaying, + status = if (isPlaying) NowPlayingStatus.PLAYING else NowPlayingStatus.PAUSED, ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/services/NavDrawerService.kt b/app/src/main/java/com/github/damontecres/wholphin/services/NavDrawerService.kt index 12cb5808..0eb1c4e8 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/services/NavDrawerService.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/NavDrawerService.kt @@ -36,6 +36,7 @@ import timber.log.Timber import java.util.UUID import javax.inject.Inject import javax.inject.Singleton +import kotlin.time.Duration.Companion.minutes @Singleton class NavDrawerService @@ -80,22 +81,33 @@ class NavDrawerService coroutineScope.launchDefault { musicService.state.collectLatest { music -> Timber.v("MusicService updated") - if (music.isPlaying) { - _state.update { - it.copy( - nowPlayingEnabled = true, - nowPlayingTitle = music.currentItemTitle, - ) + when (music.status) { + NowPlayingStatus.PLAYING -> { + _state.update { + it.copy( + nowPlayingEnabled = true, + nowPlayingTitle = music.currentItemTitle, + ) + } } - } else { - // Don't immediately remove the now playing - // TODO need better now playing state to distinguish between paused & stopped - delay(30_000) - _state.update { - it.copy( - nowPlayingEnabled = false, - nowPlayingTitle = null, - ) + + NowPlayingStatus.IDLE -> { + _state.update { + it.copy( + nowPlayingEnabled = false, + nowPlayingTitle = null, + ) + } + } + + NowPlayingStatus.PAUSED -> { + delay(2.minutes) + _state.update { + it.copy( + nowPlayingEnabled = false, + nowPlayingTitle = null, + ) + } } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingButtons.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingButtons.kt index 13fd85f0..b987484e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingButtons.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/NowPlayingButtons.kt @@ -3,6 +3,7 @@ package com.github.damontecres.wholphin.ui.detail.music import androidx.annotation.OptIn import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background +import androidx.compose.foundation.focusGroup import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsFocusedAsState import androidx.compose.foundation.layout.Arrangement @@ -70,7 +71,7 @@ fun NowPlayingButtons( val onControllerInteraction = remember { { controllerViewState.pulseControls() } } Box( - modifier = modifier, + modifier = modifier.focusGroup(), ) { Row( horizontalArrangement = Arrangement.spacedBy(buttonSpacing), 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 2574d22a..66cde708 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 @@ -34,7 +34,9 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusProperties import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.res.stringResource @@ -151,6 +153,10 @@ fun NowPlayingOverlay( .fillMaxSize() .onFocusChanged { queueHasFocus = it.hasFocus + }.focusProperties { + onExit = { + if (requestedFocusDirection == FocusDirection.Up) focusRequester.requestFocus() + } }, ) { itemsIndexed(queue, key = { _, song -> song.key }) { index, song -> @@ -162,7 +168,10 @@ fun NowPlayingOverlay( .background( color = MaterialTheme.colorScheme.surface.copy(alpha = .75f), shape = RoundedCornerShape(8.dp), - ).animateItem(), + ).onFocusChanged { + if (it.hasFocus) showButtons = index < 3 + controllerViewState.pulseControls() + }.animateItem(), ) { SongListItem( title = song.title, @@ -176,10 +185,7 @@ fun NowPlayingOverlay( modifier = Modifier .weight(1f) - .onFocusChanged { - if (it.isFocused) showButtons = index < 3 - controllerViewState.pulseControls() - }.ifElse( + .ifElse( index == 0, Modifier.focusRequester(firstFocusRequester), ), diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt index 6deb10af..6e2a2260 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt @@ -76,6 +76,7 @@ import com.github.damontecres.wholphin.data.model.JellyfinUser import com.github.damontecres.wholphin.preferences.AppThemeColors import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.services.BackdropService +import com.github.damontecres.wholphin.services.MusicService import com.github.damontecres.wholphin.services.NavDrawerService import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.services.SetupDestination @@ -108,6 +109,7 @@ class NavDrawerViewModel val navigationManager: NavigationManager, val setupNavigationManager: SetupNavigationManager, val backdropService: BackdropService, + private val musicService: MusicService, ) : ViewModel() { val state = navDrawerService.state @@ -204,6 +206,13 @@ class NavDrawerViewModel } } } + + fun navigateToSetup(userList: SetupDestination) { + viewModelScope.launchDefault { + musicService.stop() + setupNavigationManager.navigateTo(userList) + } + } } sealed interface NavDrawerItem { @@ -334,7 +343,7 @@ fun NavDrawer( drawerOpen = isOpen, interactionSource = interactionSource, onClick = { - viewModel.setupNavigationManager.navigateTo( + viewModel.navigateToSetup( SetupDestination.UserList(server), ) },