ui: handle playing indicator edge cases

Handle two edge cases identified with the playing indicator behavior:
1. When enqueing songs from another parent, the prior parent is still
indicates as "playing" when it kind-of isn't.
2. When playback is stopped, the parent is not reset, and thus will
still be indicated as "playing" after the song has disappeared. This
is rarer and should be resolved in other ways, but the solution to
1 also fixes this.

Resolves #380.
This commit is contained in:
Alexander Capehart 2023-05-25 13:10:49 -06:00
parent 21a6b97bfa
commit c2def19aee
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
7 changed files with 49 additions and 36 deletions

View file

@ -254,14 +254,14 @@ class ArtistDetailFragment :
val currentArtist = unlikelyToBeNull(detailModel.currentArtist.value) val currentArtist = unlikelyToBeNull(detailModel.currentArtist.value)
val playingItem = val playingItem =
when (parent) { when (parent) {
// Always highlight a playing album if it's from this artist. // Always highlight a playing album if it's from this artist, and if the currently
is Album -> parent // playing song is contained within.
is Album -> parent.takeIf { song?.album == it }
// If the parent is the artist itself, use the currently playing song. // If the parent is the artist itself, use the currently playing song.
currentArtist -> song currentArtist -> song
// Nothing is playing from this artist. // Nothing is playing from this artist.
else -> null else -> null
} }
artistListAdapter.setPlaying(playingItem, isPlaying) artistListAdapter.setPlaying(playingItem, isPlaying)
} }

View file

@ -239,15 +239,18 @@ class GenreDetailFragment :
} }
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) { private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
var playingMusic: Music? = null val currentGenre = unlikelyToBeNull(detailModel.currentGenre.value)
if (parent is Artist) { val playingItem =
playingMusic = parent when (parent) {
} // Always highlight a playing artist if it's from this genre, and if the currently
// Prefer songs that might be playing from this genre. // playing song is contained within.
if (parent is Genre && parent.uid == unlikelyToBeNull(detailModel.currentGenre.value).uid) { is Artist -> parent.takeIf { song?.run { artists.contains(it) } ?: false }
playingMusic = song // If the parent is the artist itself, use the currently playing song.
} currentGenre -> song
genreListAdapter.setPlaying(playingMusic, isPlaying) // Nothing is playing from this artist.
else -> null
}
genreListAdapter.setPlaying(playingItem, isPlaying)
} }
private fun handleNavigation(item: Music?) { private fun handleNavigation(item: Music?) {

View file

@ -41,6 +41,7 @@ import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.navigation.NavigationViewModel import org.oxycblt.auxio.navigation.NavigationViewModel
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.formatDurationMs import org.oxycblt.auxio.playback.formatDurationMs
@ -82,7 +83,8 @@ class AlbumListFragment :
collectImmediately(homeModel.albumsList, ::updateAlbums) collectImmediately(homeModel.albumsList, ::updateAlbums)
collectImmediately(selectionModel.selected, ::updateSelection) collectImmediately(selectionModel.selected, ::updateSelection)
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
} }
override fun onDestroyBinding(binding: FragmentHomeListBinding) { override fun onDestroyBinding(binding: FragmentHomeListBinding) {
@ -151,9 +153,11 @@ class AlbumListFragment :
albumAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf())) albumAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf()))
} }
private fun updatePlayback(parent: MusicParent?, isPlaying: Boolean) { private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
// If an album is playing, highlight it within this adapter. // Only highlight the album if it is currently playing, and if the currently
albumAdapter.setPlaying(parent as? Album, isPlaying) // playing song is also contained within.
val playlist = (parent as? Album)?.takeIf { song?.album == it }
albumAdapter.setPlaying(playlist, isPlaying)
} }
/** /**

View file

@ -39,6 +39,7 @@ import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.navigation.NavigationViewModel import org.oxycblt.auxio.navigation.NavigationViewModel
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.formatDurationMs import org.oxycblt.auxio.playback.formatDurationMs
@ -78,7 +79,8 @@ class ArtistListFragment :
collectImmediately(homeModel.artistsList, ::updateArtists) collectImmediately(homeModel.artistsList, ::updateArtists)
collectImmediately(selectionModel.selected, ::updateSelection) collectImmediately(selectionModel.selected, ::updateSelection)
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
} }
override fun onDestroyBinding(binding: FragmentHomeListBinding) { override fun onDestroyBinding(binding: FragmentHomeListBinding) {
@ -128,9 +130,11 @@ class ArtistListFragment :
artistAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf())) artistAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf()))
} }
private fun updatePlayback(parent: MusicParent?, isPlaying: Boolean) { private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
// If an artist is playing, highlight it within this adapter. // Only highlight the artist if it is currently playing, and if the currently
artistAdapter.setPlaying(parent as? Artist, isPlaying) // playing song is also contained within.
val playlist = (parent as? Artist)?.takeIf { song?.run { artists.contains(it) } ?: false }
artistAdapter.setPlaying(playlist, isPlaying)
} }
/** /**

View file

@ -39,6 +39,7 @@ import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.navigation.NavigationViewModel import org.oxycblt.auxio.navigation.NavigationViewModel
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.formatDurationMs import org.oxycblt.auxio.playback.formatDurationMs
@ -77,7 +78,8 @@ class GenreListFragment :
collectImmediately(homeModel.genresList, ::updateGenres) collectImmediately(homeModel.genresList, ::updateGenres)
collectImmediately(selectionModel.selected, ::updateSelection) collectImmediately(selectionModel.selected, ::updateSelection)
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
} }
override fun onDestroyBinding(binding: FragmentHomeListBinding) { override fun onDestroyBinding(binding: FragmentHomeListBinding) {
@ -127,9 +129,11 @@ class GenreListFragment :
genreAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf())) genreAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf()))
} }
private fun updatePlayback(parent: MusicParent?, isPlaying: Boolean) { private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
// If a genre is playing, highlight it within this adapter. // Only highlight the genre if it is currently playing, and if the currently
genreAdapter.setPlaying(parent as? Genre, isPlaying) // playing song is also contained within.
val playlist = (parent as? Genre)?.takeIf { song?.run { genres.contains(it) } ?: false }
genreAdapter.setPlaying(playlist, isPlaying)
} }
/** /**

View file

@ -38,6 +38,7 @@ import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.navigation.NavigationViewModel import org.oxycblt.auxio.navigation.NavigationViewModel
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.formatDurationMs import org.oxycblt.auxio.playback.formatDurationMs
@ -48,8 +49,6 @@ import org.oxycblt.auxio.util.logD
* A [ListFragment] that shows a list of [Playlist]s. * A [ListFragment] that shows a list of [Playlist]s.
* *
* @author Alexander Capehart (OxygenCobalt) * @author Alexander Capehart (OxygenCobalt)
*
* TODO: Show a placeholder when there are no playlists.
*/ */
class PlaylistListFragment : class PlaylistListFragment :
ListFragment<Playlist, FragmentHomeListBinding>(), ListFragment<Playlist, FragmentHomeListBinding>(),
@ -77,7 +76,8 @@ class PlaylistListFragment :
collectImmediately(homeModel.playlistsList, ::updatePlaylists) collectImmediately(homeModel.playlistsList, ::updatePlaylists)
collectImmediately(selectionModel.selected, ::updateSelection) collectImmediately(selectionModel.selected, ::updateSelection)
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
} }
override fun onDestroyBinding(binding: FragmentHomeListBinding) { override fun onDestroyBinding(binding: FragmentHomeListBinding) {
@ -128,9 +128,11 @@ class PlaylistListFragment :
playlistAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf())) playlistAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf()))
} }
private fun updatePlayback(parent: MusicParent?, isPlaying: Boolean) { private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
// If a playlist is playing, highlight it within this adapter. // Only highlight the playlist if it is currently playing, and if the currently
playlistAdapter.setPlaying(parent as? Playlist, isPlaying) // playing song is also contained within.
val playlist = (parent as? Playlist)?.takeIf { it.songs.contains(song) } ?: return
playlistAdapter.setPlaying(playlist, isPlaying)
} }
/** /**

View file

@ -155,12 +155,8 @@ class SongListFragment :
} }
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) { private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
if (parent == null) { // Only indicate playback that is from all songs
songAdapter.setPlaying(song, isPlaying) songAdapter.setPlaying(song.takeIf { parent == null }, isPlaying)
} else {
// Ignore playback that is not from all songs
songAdapter.setPlaying(null, isPlaying)
}
} }
/** /**