Fixes for stopping now playing

This commit is contained in:
Damontecres 2026-03-04 09:19:11 -05:00
parent c443ddb6c4
commit 3359c8ac32
No known key found for this signature in database
5 changed files with 71 additions and 29 deletions

View file

@ -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,
)
}
}

View file

@ -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,
)
}
}
}
}

View file

@ -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),

View file

@ -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),
),

View file

@ -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),
)
},