Basic show/hide now playing nav drawer item

This commit is contained in:
Damontecres 2026-02-24 13:09:59 -05:00
parent cd06c0380e
commit 1d47220991
No known key found for this signature in database
4 changed files with 82 additions and 17 deletions

View file

@ -179,7 +179,7 @@ private class MusicPlayerListener(
Timber.v("MusicPlayerListener init")
state.update {
it.copy(
queue = PlayerMediaItemList(player),
queue = PlayerMediaItemList(player, player.mediaItemCount),
currentIndex = player.currentMediaItemIndex,
isPlaying = player.isPlaying,
)
@ -215,7 +215,7 @@ private class MusicPlayerListener(
if (reason == Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED) {
state.update {
it.copy(
queue = PlayerMediaItemList(player),
queue = PlayerMediaItemList(player, player.mediaItemCount),
currentIndex = player.currentMediaItemIndex,
)
}
@ -225,15 +225,28 @@ private class MusicPlayerListener(
private class PlayerMediaItemList(
private val player: Player,
override val size: Int,
) : AbstractList<AudioItem>() {
override fun get(index: Int): AudioItem {
// Timber.v("get %s", index)
return player.getMediaItemAt(index).localConfiguration?.tag as AudioItem
}
override val size: Int
get() {
// Timber.v("size %s", player.mediaItemCount)
return player.mediaItemCount
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is PlayerMediaItemList) return false
if (!super.equals(other)) return false
if (size != other.size) return false
if (player != other.player) return false
return true
}
override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + size
result = 31 * result + player.hashCode()
return result
}
}

View file

@ -7,6 +7,7 @@ import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.data.model.JellyfinUser
import com.github.damontecres.wholphin.data.model.NavPinType
import com.github.damontecres.wholphin.services.hilt.DefaultCoroutineScope
import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.ui.main.settings.Library
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
@ -14,8 +15,10 @@ import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem
import com.github.damontecres.wholphin.util.supportedCollectionTypes
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@ -40,6 +43,7 @@ class NavDrawerService
private val serverRepository: ServerRepository,
private val serverPreferencesDao: ServerPreferencesDao,
private val seerrServerRepository: SeerrServerRepository,
private val musicService: MusicService,
) {
private val _state = MutableStateFlow(NavDrawerItemState.EMPTY)
val state: StateFlow<NavDrawerItemState> = _state
@ -65,6 +69,23 @@ class NavDrawerService
.onEach { discoverActive ->
_state.update { it.copy(discoverEnabled = discoverActive) }
}.launchIn(coroutineScope)
coroutineScope.launchDefault {
musicService.state.collectLatest {
Timber.v("MusicService updated")
if (it.isPlaying) {
_state.update {
it.copy(nowPlayingEnabled = true)
}
} 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)
}
}
}
}
}
suspend fun getAllUserLibraries(
@ -175,9 +196,10 @@ data class NavDrawerItemState(
val items: List<NavDrawerItem>,
val moreItems: List<NavDrawerItem>,
val discoverEnabled: Boolean,
val nowPlayingEnabled: Boolean,
) {
companion object {
val EMPTY = NavDrawerItemState(emptyList(), emptyList(), false)
val EMPTY = NavDrawerItemState(emptyList(), emptyList(), false, false)
}
}

View file

@ -190,10 +190,11 @@ class RecommendedMusicViewModel
override fun update(
@StringRes title: Int,
row: HomeRowLoadingState,
) {
): HomeRowLoadingState {
rows.update { current ->
current.toMutableList().apply { set(rowTitles[title]!!, row) }
}
return row
}
companion object {

View file

@ -24,6 +24,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.KeyboardArrowLeft
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Settings
import androidx.compose.runtime.Composable
@ -178,9 +179,11 @@ class NavDrawerViewModel
if (key is Destination) {
val index =
if (key is Destination.Home) {
-1
HOME_INDEX
} else if (key is Destination.Search) {
-2
SEARCH_INDEX
} else if (key is Destination.NowPlaying) {
NOW_PLAYING_INDEX
} else {
val idx = asDestinations.indexOf(key)
if (idx >= 0) {
@ -242,6 +245,10 @@ data class ServerNavDrawerItem(
}
}
private const val HOME_INDEX = -1
private const val SEARCH_INDEX = -2
private const val NOW_PLAYING_INDEX = -3
/**
* Display the left side navigation drawer with [DestinationContent] on the right
*/
@ -353,32 +360,54 @@ fun NavDrawer(
IconNavItem(
text = stringResource(R.string.search),
icon = Icons.Default.Search,
selected = selectedIndex == -2,
selected = selectedIndex == SEARCH_INDEX,
drawerOpen = isOpen,
interactionSource = interactionSource,
onClick = {
viewModel.setIndex(-2)
viewModel.setIndex(SEARCH_INDEX)
viewModel.navigationManager.navigateToFromDrawer(Destination.Search)
},
modifier =
Modifier
.focusRequester(searchFocusRequester)
.ifElse(
selectedIndex == -2,
selectedIndex == SEARCH_INDEX,
Modifier.focusRequester(focusRequester),
),
)
}
if (state.nowPlayingEnabled) {
item {
val interactionSource = remember { MutableInteractionSource() }
IconNavItem(
text = stringResource(R.string.now_playing),
icon = Icons.Default.PlayArrow,
selected = selectedIndex == NOW_PLAYING_INDEX,
drawerOpen = isOpen,
interactionSource = interactionSource,
onClick = {
viewModel.setIndex(NOW_PLAYING_INDEX)
viewModel.navigationManager.navigateToFromDrawer(Destination.NowPlaying)
},
modifier =
Modifier
.ifElse(
selectedIndex == NOW_PLAYING_INDEX,
Modifier.focusRequester(focusRequester),
).animateItem(),
)
}
}
item {
val interactionSource = remember { MutableInteractionSource() }
IconNavItem(
text = stringResource(R.string.home),
icon = Icons.Default.Home,
selected = selectedIndex == -1,
selected = selectedIndex == HOME_INDEX,
drawerOpen = isOpen,
interactionSource = interactionSource,
onClick = {
viewModel.setIndex(-1)
viewModel.setIndex(HOME_INDEX)
if (destination is Destination.Home) {
viewModel.navigationManager.reloadHome()
} else {
@ -388,7 +417,7 @@ fun NavDrawer(
modifier =
Modifier
.ifElse(
selectedIndex == -1,
selectedIndex == HOME_INDEX,
Modifier.focusRequester(focusRequester),
),
)