detail: improve playlist presentation
Improve playlist presentation in the detail views, especially when it is empty.
This commit is contained in:
parent
7435165929
commit
949a9c879c
7 changed files with 62 additions and 34 deletions
|
@ -46,6 +46,8 @@ import org.oxycblt.auxio.util.*
|
||||||
* [ViewModel] that manages the Song, Album, Artist, and Genre detail views. Keeps track of the
|
* [ViewModel] that manages the Song, Album, Artist, and Genre detail views. Keeps track of the
|
||||||
* current item they are showing, sub-data to display, and configuration.
|
* current item they are showing, sub-data to display, and configuration.
|
||||||
*
|
*
|
||||||
|
* FIXME: Need to do direct item comparison in equality checks, or reset on navigation.
|
||||||
|
*
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
|
@ -416,15 +418,16 @@ constructor(
|
||||||
|
|
||||||
private fun refreshPlaylistList(playlist: Playlist, replace: Boolean = false) {
|
private fun refreshPlaylistList(playlist: Playlist, replace: Boolean = false) {
|
||||||
logD("Refreshing playlist list")
|
logD("Refreshing playlist list")
|
||||||
|
var instructions: UpdateInstructions = UpdateInstructions.Diff
|
||||||
val list = mutableListOf<Item>()
|
val list = mutableListOf<Item>()
|
||||||
list.add(SortHeader(R.string.lbl_songs))
|
|
||||||
val instructions =
|
if (playlist.songs.isNotEmpty()) {
|
||||||
|
list.add(SortHeader(R.string.lbl_songs))
|
||||||
if (replace) {
|
if (replace) {
|
||||||
UpdateInstructions.Replace(list.size)
|
instructions = UpdateInstructions.Replace(list.size)
|
||||||
} else {
|
|
||||||
UpdateInstructions.Diff
|
|
||||||
}
|
}
|
||||||
list.addAll(playlistSongSort.songs(playlist.songs))
|
list.addAll(playlistSongSort.songs(playlist.songs))
|
||||||
|
}
|
||||||
_playlistInstructions.put(instructions)
|
_playlistInstructions.put(instructions)
|
||||||
_playlistList.value = list
|
_playlistList.value = list
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,17 @@ private constructor(private val binding: ItemDetailHeaderBinding) :
|
||||||
binding.detailType.text = binding.context.getString(R.string.lbl_artist)
|
binding.detailType.text = binding.context.getString(R.string.lbl_artist)
|
||||||
binding.detailName.text = artist.name.resolve(binding.context)
|
binding.detailName.text = artist.name.resolve(binding.context)
|
||||||
|
|
||||||
|
// Song and album counts map to the info
|
||||||
|
binding.detailInfo.text =
|
||||||
|
binding.context.getString(
|
||||||
|
R.string.fmt_two,
|
||||||
|
binding.context.getPlural(R.plurals.fmt_album_count, artist.albums.size),
|
||||||
|
if (artist.songs.isNotEmpty()) {
|
||||||
|
binding.context.getPlural(R.plurals.fmt_song_count, artist.songs.size)
|
||||||
|
} else {
|
||||||
|
binding.context.getString(R.string.def_song_count)
|
||||||
|
})
|
||||||
|
|
||||||
if (artist.songs.isNotEmpty()) {
|
if (artist.songs.isNotEmpty()) {
|
||||||
// Information about the artist's genre(s) map to the sub-head text
|
// Information about the artist's genre(s) map to the sub-head text
|
||||||
binding.detailSubhead.apply {
|
binding.detailSubhead.apply {
|
||||||
|
@ -72,13 +83,6 @@ private constructor(private val binding: ItemDetailHeaderBinding) :
|
||||||
text = artist.genres.resolveNames(context)
|
text = artist.genres.resolveNames(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Song and album counts map to the info
|
|
||||||
binding.detailInfo.text =
|
|
||||||
binding.context.getString(
|
|
||||||
R.string.fmt_two,
|
|
||||||
binding.context.getPlural(R.plurals.fmt_album_count, artist.albums.size),
|
|
||||||
binding.context.getPlural(R.plurals.fmt_song_count, artist.songs.size))
|
|
||||||
|
|
||||||
// In the case that this header used to he configured to have no songs,
|
// In the case that this header used to he configured to have no songs,
|
||||||
// we want to reset the visibility of all information that was hidden.
|
// we want to reset the visibility of all information that was hidden.
|
||||||
binding.detailPlayButton.isVisible = true
|
binding.detailPlayButton.isVisible = true
|
||||||
|
@ -88,10 +92,8 @@ private constructor(private val binding: ItemDetailHeaderBinding) :
|
||||||
// ex. Play and Shuffle, Song Counts, and Genre Information.
|
// ex. Play and Shuffle, Song Counts, and Genre Information.
|
||||||
// Artists are always guaranteed to have albums however, so continue to show those.
|
// Artists are always guaranteed to have albums however, so continue to show those.
|
||||||
binding.detailSubhead.isVisible = false
|
binding.detailSubhead.isVisible = false
|
||||||
binding.detailInfo.text =
|
binding.detailPlayButton.isEnabled = false
|
||||||
binding.context.getPlural(R.plurals.fmt_album_count, artist.albums.size)
|
binding.detailShuffleButton.isEnabled = false
|
||||||
binding.detailPlayButton.isVisible = false
|
|
||||||
binding.detailShuffleButton.isVisible = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.detailPlayButton.setOnClickListener { listener.onPlay() }
|
binding.detailPlayButton.setOnClickListener { listener.onPlay() }
|
||||||
|
|
|
@ -65,11 +65,26 @@ private constructor(private val binding: ItemDetailHeaderBinding) :
|
||||||
binding.detailName.text = playlist.name.resolve(binding.context)
|
binding.detailName.text = playlist.name.resolve(binding.context)
|
||||||
// Nothing about a playlist is applicable to the sub-head text.
|
// Nothing about a playlist is applicable to the sub-head text.
|
||||||
binding.detailSubhead.isVisible = false
|
binding.detailSubhead.isVisible = false
|
||||||
|
|
||||||
// The song count of the playlist maps to the info text.
|
// The song count of the playlist maps to the info text.
|
||||||
binding.detailInfo.text =
|
binding.detailInfo.apply {
|
||||||
binding.context.getPlural(R.plurals.fmt_song_count, playlist.songs.size)
|
isVisible = true
|
||||||
binding.detailPlayButton.setOnClickListener { listener.onPlay() }
|
text =
|
||||||
binding.detailShuffleButton.setOnClickListener { listener.onShuffle() }
|
if (playlist.songs.isNotEmpty()) {
|
||||||
|
binding.context.getPlural(R.plurals.fmt_song_count, playlist.songs.size)
|
||||||
|
} else {
|
||||||
|
binding.context.getString(R.string.def_song_count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.detailPlayButton.apply {
|
||||||
|
isEnabled = playlist.songs.isNotEmpty()
|
||||||
|
setOnClickListener { listener.onPlay() }
|
||||||
|
}
|
||||||
|
binding.detailShuffleButton.apply {
|
||||||
|
isEnabled = playlist.songs.isNotEmpty()
|
||||||
|
setOnClickListener { listener.onShuffle() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -157,15 +157,14 @@ class ArtistViewHolder private constructor(private val binding: ItemParentBindin
|
||||||
binding.parentImage.bind(artist)
|
binding.parentImage.bind(artist)
|
||||||
binding.parentName.text = artist.name.resolve(binding.context)
|
binding.parentName.text = artist.name.resolve(binding.context)
|
||||||
binding.parentInfo.text =
|
binding.parentInfo.text =
|
||||||
if (artist.songs.isNotEmpty()) {
|
binding.context.getString(
|
||||||
binding.context.getString(
|
R.string.fmt_two,
|
||||||
R.string.fmt_two,
|
binding.context.getPlural(R.plurals.fmt_album_count, artist.albums.size),
|
||||||
binding.context.getPlural(R.plurals.fmt_album_count, artist.albums.size),
|
if (artist.songs.isNotEmpty()) {
|
||||||
binding.context.getPlural(R.plurals.fmt_song_count, artist.songs.size))
|
binding.context.getPlural(R.plurals.fmt_song_count, artist.songs.size)
|
||||||
} else {
|
} else {
|
||||||
// Artist has no songs, only display an album count.
|
binding.context.getString(R.string.def_song_count)
|
||||||
binding.context.getPlural(R.plurals.fmt_album_count, artist.albums.size)
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updatePlayingIndicator(isActive: Boolean, isPlaying: Boolean) {
|
override fun updatePlayingIndicator(isActive: Boolean, isPlaying: Boolean) {
|
||||||
|
@ -275,7 +274,11 @@ class PlaylistViewHolder private constructor(private val binding: ItemParentBind
|
||||||
binding.parentImage.bind(playlist)
|
binding.parentImage.bind(playlist)
|
||||||
binding.parentName.text = playlist.name.resolve(binding.context)
|
binding.parentName.text = playlist.name.resolve(binding.context)
|
||||||
binding.parentInfo.text =
|
binding.parentInfo.text =
|
||||||
binding.context.getPlural(R.plurals.fmt_song_count, playlist.songs.size)
|
if (playlist.songs.isNotEmpty()) {
|
||||||
|
binding.context.getPlural(R.plurals.fmt_song_count, playlist.songs.size)
|
||||||
|
} else {
|
||||||
|
binding.context.getString(R.string.def_song_count)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updatePlayingIndicator(isActive: Boolean, isPlaying: Boolean) {
|
override fun updatePlayingIndicator(isActive: Boolean, isPlaying: Boolean) {
|
||||||
|
|
|
@ -326,6 +326,7 @@
|
||||||
<string name="def_genre">Unknown genre</string>
|
<string name="def_genre">Unknown genre</string>
|
||||||
<string name="def_date">No date</string>
|
<string name="def_date">No date</string>
|
||||||
<string name="def_track">No track</string>
|
<string name="def_track">No track</string>
|
||||||
|
<string name="def_song_count">No songs</string>
|
||||||
<string name="def_playback">No music playing</string>
|
<string name="def_playback">No music playing</string>
|
||||||
|
|
||||||
<!-- Codec Namespace | Format names -->
|
<!-- Codec Namespace | Format names -->
|
||||||
|
|
|
@ -62,6 +62,10 @@ open class FakeMusicRepository : MusicRepository {
|
||||||
throw NotImplementedError()
|
throw NotImplementedError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addToPlaylist(songs: List<Song>, playlist: Playlist) {
|
||||||
|
throw NotImplementedError()
|
||||||
|
}
|
||||||
|
|
||||||
override fun requestIndex(withCache: Boolean) {
|
override fun requestIndex(withCache: Boolean) {
|
||||||
throw NotImplementedError()
|
throw NotImplementedError()
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ class MusicViewModelTest {
|
||||||
TestMusicRepository().apply {
|
TestMusicRepository().apply {
|
||||||
indexingState = IndexingState.Indexing(IndexingProgress.Indeterminate)
|
indexingState = IndexingState.Indexing(IndexingProgress.Indeterminate)
|
||||||
}
|
}
|
||||||
val musicViewModel = MusicViewModel(indexer)
|
val musicViewModel = MusicViewModel(indexer, FakeMusicSettings())
|
||||||
assertTrue(indexer.updateListener is MusicViewModel)
|
assertTrue(indexer.updateListener is MusicViewModel)
|
||||||
assertTrue(indexer.indexingListener is MusicViewModel)
|
assertTrue(indexer.indexingListener is MusicViewModel)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
@ -47,7 +47,7 @@ class MusicViewModelTest {
|
||||||
@Test
|
@Test
|
||||||
fun statistics() {
|
fun statistics() {
|
||||||
val musicRepository = TestMusicRepository()
|
val musicRepository = TestMusicRepository()
|
||||||
val musicViewModel = MusicViewModel(musicRepository)
|
val musicViewModel = MusicViewModel(musicRepository, FakeMusicSettings())
|
||||||
assertEquals(null, musicViewModel.statistics.value)
|
assertEquals(null, musicViewModel.statistics.value)
|
||||||
musicRepository.deviceLibrary = TestDeviceLibrary()
|
musicRepository.deviceLibrary = TestDeviceLibrary()
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
@ -64,7 +64,7 @@ class MusicViewModelTest {
|
||||||
@Test
|
@Test
|
||||||
fun requests() {
|
fun requests() {
|
||||||
val indexer = TestMusicRepository()
|
val indexer = TestMusicRepository()
|
||||||
val musicViewModel = MusicViewModel(indexer)
|
val musicViewModel = MusicViewModel(indexer, FakeMusicSettings())
|
||||||
musicViewModel.refresh()
|
musicViewModel.refresh()
|
||||||
musicViewModel.rescan()
|
musicViewModel.rescan()
|
||||||
assertEquals(listOf(true, false), indexer.requests)
|
assertEquals(listOf(true, false), indexer.requests)
|
||||||
|
|
Loading…
Reference in a new issue