home: replace list only when resorting

Leverage the new UpdateInstructions system to allow the home view to
diff the lists on music updates, and then replace the lists on
resorting events.

This just improves quality-of-life overall.
This commit is contained in:
Alexander Capehart 2023-01-16 11:19:06 -07:00
parent 6e02929982
commit e7ff7293c0
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
7 changed files with 93 additions and 26 deletions

View file

@ -145,7 +145,7 @@ class HomeFragment :
// --- VIEWMODEL SETUP ---
collect(homeModel.shouldRecreate, ::handleRecreate)
collectImmediately(homeModel.currentTabMode, ::updateCurrentTab)
collectImmediately(homeModel.songLists, homeModel.isFastScrolling, ::updateFab)
collectImmediately(homeModel.songsList, homeModel.isFastScrolling, ::updateFab)
collectImmediately(musicModel.indexerState, ::updateIndexerState)
collect(navModel.exploreNavigationItem, ::handleNavigation)
collectImmediately(selectionModel.selected, ::updateSelection)

View file

@ -22,6 +22,7 @@ import androidx.lifecycle.AndroidViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.oxycblt.auxio.home.tabs.Tab
import org.oxycblt.auxio.list.UpdateInstructions
import org.oxycblt.auxio.music.*
import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.library.Library
@ -42,13 +43,19 @@ class HomeViewModel(application: Application) :
private val _songsList = MutableStateFlow(listOf<Song>())
/** A list of [Song]s, sorted by the preferred [Sort], to be shown in the home view. */
val songLists: StateFlow<List<Song>>
val songsList: StateFlow<List<Song>>
get() = _songsList
/** Specifies how to update [songsList] when it changes. */
var songsListInstructions: UpdateInstructions? = null
private set
private val _albumsLists = MutableStateFlow(listOf<Album>())
/** A list of [Album]s, sorted by the preferred [Sort], to be shown in the home view. */
val albumsList: StateFlow<List<Album>>
get() = _albumsLists
/** Specifies how to update [albumsList] when it changes. */
var albumsListInstructions: UpdateInstructions? = null
private set
private val _artistsList = MutableStateFlow(listOf<Artist>())
/**
@ -58,11 +65,17 @@ class HomeViewModel(application: Application) :
*/
val artistsList: MutableStateFlow<List<Artist>>
get() = _artistsList
/** Specifies how to update [artistsList] when it changes. */
var artistsListInstructions: UpdateInstructions? = null
private set
private val _genresList = MutableStateFlow(listOf<Genre>())
/** A list of [Genre]s, sorted by the preferred [Sort], to be shown in the home view. */
val genresList: StateFlow<List<Genre>>
get() = _genresList
/** Specifies how to update [genresList] when it changes. */
var genresListInstructions: UpdateInstructions? = null
private set
/** The [MusicMode] to use when playing a [Song] from the UI. */
val playbackMode: MusicMode
@ -107,8 +120,11 @@ class HomeViewModel(application: Application) :
logD("Library changed, refreshing library")
// Get the each list of items in the library to use as our list data.
// Applying the preferred sorting to them.
songsListInstructions = UpdateInstructions.DIFF
_songsList.value = musicSettings.songSort.songs(library.songs)
albumsListInstructions = UpdateInstructions.DIFF
_albumsLists.value = musicSettings.albumSort.albums(library.albums)
artistsListInstructions = UpdateInstructions.DIFF
_artistsList.value =
musicSettings.artistSort.artists(
if (homeSettings.shouldHideCollaborators) {
@ -117,6 +133,7 @@ class HomeViewModel(application: Application) :
} else {
library.artists
})
genresListInstructions = UpdateInstructions.DIFF
_genresList.value = musicSettings.genreSort.genres(library.genres)
}
}
@ -133,23 +150,6 @@ class HomeViewModel(application: Application) :
onLibraryChanged(musicStore.library)
}
/**
* Update [currentTabMode] to reflect a new ViewPager2 position
* @param pagerPos The new position of the ViewPager2 instance.
*/
fun synchronizeTabPosition(pagerPos: Int) {
logD("Updating current tab to ${currentTabModes[pagerPos]}")
_currentTabMode.value = currentTabModes[pagerPos]
}
/**
* Mark the recreation process as complete.
* @see shouldRecreate
*/
fun finishRecreate() {
_shouldRecreate.value = false
}
/**
* Get the preferred [Sort] for a given [Tab].
* @param tabMode The [MusicMode] of the [Tab] desired.
@ -173,23 +173,70 @@ class HomeViewModel(application: Application) :
when (_currentTabMode.value) {
MusicMode.SONGS -> {
musicSettings.songSort = sort
songsListInstructions = UpdateInstructions.REPLACE
_songsList.value = sort.songs(_songsList.value)
}
MusicMode.ALBUMS -> {
musicSettings.albumSort = sort
albumsListInstructions = UpdateInstructions.REPLACE
_albumsLists.value = sort.albums(_albumsLists.value)
}
MusicMode.ARTISTS -> {
musicSettings.artistSort = sort
artistsListInstructions = UpdateInstructions.REPLACE
_artistsList.value = sort.artists(_artistsList.value)
}
MusicMode.GENRES -> {
musicSettings.genreSort = sort
genresListInstructions = UpdateInstructions.REPLACE
_genresList.value = sort.genres(_genresList.value)
}
}
}
/** Signal that the specified [UpdateInstructions] in [songsListInstructions] were performed. */
fun finishSongsListInstructions() {
songsListInstructions = null
}
/**
* Signal that the specified [UpdateInstructions] in [albumsListInstructions] were performed.
*/
fun finishAlbumsListInstructions() {
albumsListInstructions = null
}
/**
* Signal that the specified [UpdateInstructions] in [artistsListInstructions] were performed.
*/
fun finishArtistsListInstructions() {
artistsListInstructions = null
}
/**
* Signal that the specified [UpdateInstructions] in [genresListInstructions] were performed.
*/
fun finishGenresListInstructions() {
genresListInstructions = null
}
/**
* Update [currentTabMode] to reflect a new ViewPager2 position
* @param pagerPos The new position of the ViewPager2 instance.
*/
fun synchronizeTabPosition(pagerPos: Int) {
logD("Updating current tab to ${currentTabModes[pagerPos]}")
_currentTabMode.value = currentTabModes[pagerPos]
}
/**
* Mark the recreation process as complete.
* @see shouldRecreate
*/
fun finishRecreate() {
_shouldRecreate.value = false
}
/**
* Update whether the user is fast scrolling or not in the home view.
* @param isFastScrolling true if the user is currently fast scrolling, false otherwise.

View file

@ -66,7 +66,7 @@ class AlbumListFragment :
listener = this@AlbumListFragment
}
collectImmediately(homeModel.albumsList, albumAdapter::replaceList)
collectImmediately(homeModel.albumsList, ::updateList)
collectImmediately(selectionModel.selected, ::updateSelection)
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
}
@ -130,6 +130,11 @@ class AlbumListFragment :
openMusicMenu(anchor, R.menu.menu_album_actions, item)
}
private fun updateList(albums: List<Album>) {
albumAdapter.submitList(albums, homeModel.albumsListInstructions ?: UpdateInstructions.DIFF)
homeModel.finishAlbumsListInstructions()
}
private fun updateSelection(selection: List<Music>) {
albumAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf()))
}

View file

@ -64,7 +64,7 @@ class ArtistListFragment :
listener = this@ArtistListFragment
}
collectImmediately(homeModel.artistsList, artistAdapter::replaceList)
collectImmediately(homeModel.artistsList, ::updateList)
collectImmediately(selectionModel.selected, ::updateSelection)
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
}
@ -108,6 +108,12 @@ class ArtistListFragment :
openMusicMenu(anchor, R.menu.menu_artist_actions, item)
}
private fun updateList(artists: List<Artist>) {
artistAdapter.submitList(
artists, homeModel.artistsListInstructions ?: UpdateInstructions.DIFF)
homeModel.finishArtistsListInstructions()
}
private fun updateSelection(selection: List<Music>) {
artistAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf()))
}

View file

@ -63,7 +63,7 @@ class GenreListFragment :
listener = this@GenreListFragment
}
collectImmediately(homeModel.genresList, genreAdapter::replaceList)
collectImmediately(homeModel.genresList, ::updateList)
collectImmediately(selectionModel.selected, ::updateSelection)
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
}
@ -107,6 +107,12 @@ class GenreListFragment :
openMusicMenu(anchor, R.menu.menu_artist_actions, item)
}
private fun updateList(artists: List<Genre>) {
genreAdapter.submitList(
artists, homeModel.genresListInstructions ?: UpdateInstructions.DIFF)
homeModel.finishGenresListInstructions()
}
private fun updateSelection(selection: List<Music>) {
genreAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf()))
}

View file

@ -69,7 +69,7 @@ class SongListFragment :
listener = this@SongListFragment
}
collectImmediately(homeModel.songLists, songAdapter::replaceList)
collectImmediately(homeModel.songsList, ::updateList)
collectImmediately(selectionModel.selected, ::updateSelection)
collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
@ -85,7 +85,7 @@ class SongListFragment :
}
override fun getPopup(pos: Int): String? {
val song = homeModel.songLists.value[pos]
val song = homeModel.songsList.value[pos]
// Change how we display the popup depending on the current sort mode.
// Note: We don't use the more correct individual artist name here, as sorts are largely
// based off the names of the parent objects and not the child objects.
@ -137,6 +137,11 @@ class SongListFragment :
openMusicMenu(anchor, R.menu.menu_song_actions, item)
}
private fun updateList(songs: List<Song>) {
songAdapter.submitList(songs, homeModel.songsListInstructions ?: UpdateInstructions.DIFF)
homeModel.finishSongsListInstructions()
}
private fun updateSelection(selection: List<Music>) {
songAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf()))
}

View file

@ -18,7 +18,6 @@
package org.oxycblt.auxio.util
import android.app.PendingIntent
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
@ -27,7 +26,6 @@ import android.os.Build
import android.util.TypedValue
import android.view.LayoutInflater
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.AttrRes
import androidx.annotation.ColorRes
import androidx.annotation.DimenRes