From e7ff7293c0e2bb05e8d146f8c84968984b9054b5 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Mon, 16 Jan 2023 11:19:06 -0700 Subject: [PATCH] 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. --- .../org/oxycblt/auxio/home/HomeFragment.kt | 2 +- .../org/oxycblt/auxio/home/HomeViewModel.kt | 83 +++++++++++++++---- .../auxio/home/list/AlbumListFragment.kt | 7 +- .../auxio/home/list/ArtistListFragment.kt | 8 +- .../auxio/home/list/GenreListFragment.kt | 8 +- .../auxio/home/list/SongListFragment.kt | 9 +- .../org/oxycblt/auxio/util/ContextUtil.kt | 2 - 7 files changed, 93 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt index c1e0278a6..39ae497f0 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt @@ -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) diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt index c49fb75f1..c3168fb6d 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt @@ -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()) /** A list of [Song]s, sorted by the preferred [Sort], to be shown in the home view. */ - val songLists: StateFlow> + val songsList: StateFlow> get() = _songsList + /** Specifies how to update [songsList] when it changes. */ + var songsListInstructions: UpdateInstructions? = null + private set private val _albumsLists = MutableStateFlow(listOf()) /** A list of [Album]s, sorted by the preferred [Sort], to be shown in the home view. */ val albumsList: StateFlow> get() = _albumsLists + /** Specifies how to update [albumsList] when it changes. */ + var albumsListInstructions: UpdateInstructions? = null + private set private val _artistsList = MutableStateFlow(listOf()) /** @@ -58,11 +65,17 @@ class HomeViewModel(application: Application) : */ val artistsList: MutableStateFlow> get() = _artistsList + /** Specifies how to update [artistsList] when it changes. */ + var artistsListInstructions: UpdateInstructions? = null + private set private val _genresList = MutableStateFlow(listOf()) /** A list of [Genre]s, sorted by the preferred [Sort], to be shown in the home view. */ val genresList: StateFlow> 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. diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt index e8911eea4..be30f78da 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt @@ -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) { + albumAdapter.submitList(albums, homeModel.albumsListInstructions ?: UpdateInstructions.DIFF) + homeModel.finishAlbumsListInstructions() + } + private fun updateSelection(selection: List) { albumAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf())) } diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt index 278f0d835..4bdd0728a 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt @@ -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) { + artistAdapter.submitList( + artists, homeModel.artistsListInstructions ?: UpdateInstructions.DIFF) + homeModel.finishArtistsListInstructions() + } + private fun updateSelection(selection: List) { artistAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf())) } diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt index 30109b43a..b738bcbb0 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt @@ -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) { + genreAdapter.submitList( + artists, homeModel.genresListInstructions ?: UpdateInstructions.DIFF) + homeModel.finishGenresListInstructions() + } + private fun updateSelection(selection: List) { genreAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf())) } diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt index e0ab09b87..87f7a3407 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt @@ -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) { + songAdapter.submitList(songs, homeModel.songsListInstructions ?: UpdateInstructions.DIFF) + homeModel.finishSongsListInstructions() + } + private fun updateSelection(selection: List) { songAdapter.setSelected(selection.filterIsInstanceTo(mutableSetOf())) } diff --git a/app/src/main/java/org/oxycblt/auxio/util/ContextUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/ContextUtil.kt index 3ba0fee00..de624a85d 100644 --- a/app/src/main/java/org/oxycblt/auxio/util/ContextUtil.kt +++ b/app/src/main/java/org/oxycblt/auxio/util/ContextUtil.kt @@ -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