list: disable complex diffing for now
Disable the new instructions-based system for now. The way I was doing reflection was likely far too unsafe and prone to bugs. I'll want to do it some other way.
This commit is contained in:
parent
8e9a22ccf3
commit
900bcd142e
14 changed files with 39 additions and 257 deletions
|
@ -4,8 +4,8 @@
|
|||
|
||||
#### What's New
|
||||
- Added ability to play/shuffle selections
|
||||
- Resigned header components
|
||||
- Resigned settings view
|
||||
- Redesigned header components
|
||||
- Redesigned settings view
|
||||
|
||||
#### What's Improved
|
||||
- Added ability to edit previously played or currently playing items in the queue
|
||||
|
@ -13,7 +13,6 @@
|
|||
- Pressing the button will now clear the current selection before navigating back
|
||||
- Added support for non-standard `ARTISTS` tags
|
||||
- Play Next and Add To Queue now start playback if there is no queue to add
|
||||
- Made resorting list animations consistent across app
|
||||
|
||||
#### What's Fixed
|
||||
- Fixed unreliable ReplayGain adjustment application in certain situations
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.oxycblt.auxio.databinding.FragmentDetailBinding
|
|||
import org.oxycblt.auxio.detail.recycler.AlbumDetailAdapter
|
||||
import org.oxycblt.auxio.list.Item
|
||||
import org.oxycblt.auxio.list.ListFragment
|
||||
import org.oxycblt.auxio.list.adapter.BasicListInstructions
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Music
|
||||
|
@ -259,9 +260,7 @@ class AlbumDetailFragment :
|
|||
}
|
||||
|
||||
private fun updateList(items: List<Item>) {
|
||||
detailAdapter.submitList(
|
||||
items, detailModel.albumListInstructions ?: DetailListInstructions.Diff)
|
||||
detailModel.finishAlbumListInstructions()
|
||||
detailAdapter.submitList(items, BasicListInstructions.DIFF)
|
||||
}
|
||||
|
||||
private fun updateSelection(selected: List<Music>) {
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.oxycblt.auxio.detail.recycler.ArtistDetailAdapter
|
|||
import org.oxycblt.auxio.detail.recycler.DetailAdapter
|
||||
import org.oxycblt.auxio.list.Item
|
||||
import org.oxycblt.auxio.list.ListFragment
|
||||
import org.oxycblt.auxio.list.adapter.BasicListInstructions
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Music
|
||||
|
@ -235,9 +236,7 @@ class ArtistDetailFragment :
|
|||
}
|
||||
|
||||
private fun updateList(items: List<Item>) {
|
||||
detailAdapter.submitList(
|
||||
items, detailModel.artistListInstructions ?: DetailListInstructions.Diff)
|
||||
detailModel.finishArtistListInstructions()
|
||||
detailAdapter.submitList(items, BasicListInstructions.DIFF)
|
||||
}
|
||||
|
||||
private fun updateSelection(selected: List<Music>) {
|
||||
|
|
|
@ -48,18 +48,3 @@ data class SongProperties(
|
|||
val sampleRateHz: Int?,
|
||||
val resolvedMimeType: MimeType
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents the specific way to update a list of items in the detail lists.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
sealed class DetailListInstructions {
|
||||
/** Do a plain asynchronous diff. */
|
||||
object Diff : DetailListInstructions()
|
||||
|
||||
/**
|
||||
* Replace all the items starting at the given index.
|
||||
* @param at The index to start replacing at.
|
||||
*/
|
||||
data class ReplaceRest(val at: Int) : DetailListInstructions()
|
||||
}
|
||||
|
|
|
@ -78,18 +78,14 @@ class DetailViewModel(application: Application) :
|
|||
/** The current list data derived from [currentAlbum]. */
|
||||
val albumList: StateFlow<List<Item>>
|
||||
get() = _albumList
|
||||
/** Specifies how to update [albumList] when it changes. */
|
||||
var albumListInstructions: DetailListInstructions? = null
|
||||
private set
|
||||
|
||||
/** The current [Sort] used for [Song]s in [albumList]. */
|
||||
var albumSongSort: Sort
|
||||
get() = musicSettings.albumSongSort
|
||||
set(value) {
|
||||
musicSettings.albumSongSort = value
|
||||
// Refresh the album list to reflect the new sort. Make sure we only visually replace
|
||||
// the song information, however.
|
||||
currentAlbum.value?.let { refreshAlbumList(it, true) }
|
||||
// Refresh the album list to reflect the new sort.
|
||||
currentAlbum.value?.let(::refreshAlbumList)
|
||||
}
|
||||
|
||||
// --- ARTIST ---
|
||||
|
@ -102,18 +98,14 @@ class DetailViewModel(application: Application) :
|
|||
private val _artistList = MutableStateFlow(listOf<Item>())
|
||||
/** The current list derived from [currentArtist]. */
|
||||
val artistList: StateFlow<List<Item>> = _artistList
|
||||
/** Specifies how to update [artistList] when it changes. */
|
||||
var artistListInstructions: DetailListInstructions? = null
|
||||
private set
|
||||
|
||||
/** The current [Sort] used for [Song]s in [artistList]. */
|
||||
var artistSongSort: Sort
|
||||
get() = musicSettings.artistSongSort
|
||||
set(value) {
|
||||
musicSettings.artistSongSort = value
|
||||
// Refresh the artist list to reflect the new sort. Make sure we only visually replace
|
||||
// the song information, however.
|
||||
currentArtist.value?.let { refreshArtistList(it, true) }
|
||||
// Refresh the artist list to reflect the new sort.
|
||||
currentArtist.value?.let(::refreshArtistList)
|
||||
}
|
||||
|
||||
// --- GENRE ---
|
||||
|
@ -126,18 +118,14 @@ class DetailViewModel(application: Application) :
|
|||
private val _genreList = MutableStateFlow(listOf<Item>())
|
||||
/** The current list data derived from [currentGenre]. */
|
||||
val genreList: StateFlow<List<Item>> = _genreList
|
||||
/** Specifies how to update [genreList] when it changes. */
|
||||
var genreListInstructions: DetailListInstructions? = null
|
||||
private set
|
||||
|
||||
/** The current [Sort] used for [Song]s in [genreList]. */
|
||||
var genreSongSort: Sort
|
||||
get() = musicSettings.genreSongSort
|
||||
set(value) {
|
||||
musicSettings.genreSongSort = value
|
||||
// Refresh the genre list to reflect the new sort. Make sure we only visually replace
|
||||
// the song information, however.
|
||||
currentGenre.value?.let { refreshGenreList(it, true) }
|
||||
// Refresh the genre list to reflect the new sort.
|
||||
currentGenre.value?.let(::refreshGenreList)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,19 +161,19 @@ class DetailViewModel(application: Application) :
|
|||
|
||||
val album = currentAlbum.value
|
||||
if (album != null) {
|
||||
_currentAlbum.value = library.sanitize(album)?.also { refreshAlbumList(it, false) }
|
||||
_currentAlbum.value = library.sanitize(album)?.also(::refreshAlbumList)
|
||||
logD("Updated genre to ${currentAlbum.value}")
|
||||
}
|
||||
|
||||
val artist = currentArtist.value
|
||||
if (artist != null) {
|
||||
_currentArtist.value = library.sanitize(artist)?.also { refreshArtistList(it, false) }
|
||||
_currentArtist.value = library.sanitize(artist)?.also(::refreshArtistList)
|
||||
logD("Updated genre to ${currentArtist.value}")
|
||||
}
|
||||
|
||||
val genre = currentGenre.value
|
||||
if (genre != null) {
|
||||
_currentGenre.value = library.sanitize(genre)?.also { refreshGenreList(it, false) }
|
||||
_currentGenre.value = library.sanitize(genre)?.also(::refreshGenreList)
|
||||
logD("Updated genre to ${currentGenre.value}")
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +203,7 @@ class DetailViewModel(application: Application) :
|
|||
return
|
||||
}
|
||||
logD("Opening Album [uid: $uid]")
|
||||
_currentAlbum.value = requireMusic<Album>(uid)?.also { refreshAlbumList(it, false) }
|
||||
_currentAlbum.value = requireMusic<Album>(uid)?.also(::refreshAlbumList)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -229,7 +217,7 @@ class DetailViewModel(application: Application) :
|
|||
return
|
||||
}
|
||||
logD("Opening Artist [uid: $uid]")
|
||||
_currentArtist.value = requireMusic<Artist>(uid)?.also { refreshArtistList(it, false) }
|
||||
_currentArtist.value = requireMusic<Artist>(uid)?.also(::refreshArtistList)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -243,29 +231,7 @@ class DetailViewModel(application: Application) :
|
|||
return
|
||||
}
|
||||
logD("Opening Genre [uid: $uid]")
|
||||
_currentGenre.value = requireMusic<Genre>(uid)?.also { refreshGenreList(it, false) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal that the specified [DetailListInstructions] in [albumListInstructions] were performed.
|
||||
*/
|
||||
fun finishAlbumListInstructions() {
|
||||
albumListInstructions = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal that the specified [DetailListInstructions] in [artistListInstructions] were
|
||||
* performed.
|
||||
*/
|
||||
fun finishArtistListInstructions() {
|
||||
artistListInstructions = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal that the specified [DetailListInstructions] in [genreListInstructions] were performed.
|
||||
*/
|
||||
fun finishGenreListInstructions() {
|
||||
genreListInstructions = null
|
||||
_currentGenre.value = requireMusic<Genre>(uid)?.also(::refreshGenreList)
|
||||
}
|
||||
|
||||
private fun <T : Music> requireMusic(uid: Music.UID) = musicStore.library?.find<T>(uid)
|
||||
|
@ -348,11 +314,11 @@ class DetailViewModel(application: Application) :
|
|||
return SongProperties(bitrate, sampleRate, resolvedMimeType)
|
||||
}
|
||||
|
||||
private fun refreshAlbumList(album: Album, replace: Boolean): Int {
|
||||
private fun refreshAlbumList(album: Album) {
|
||||
logD("Refreshing album data")
|
||||
val data = mutableListOf<Item>(album)
|
||||
data.add(SortHeader(R.string.lbl_songs))
|
||||
val songsStartIndex = data.size
|
||||
|
||||
// To create a good user experience regarding disc numbers, we group the album's
|
||||
// songs up by disc and then delimit the groups by a disc header.
|
||||
val songs = albumSongSort.songs(album.songs)
|
||||
|
@ -368,17 +334,11 @@ class DetailViewModel(application: Application) :
|
|||
// Album only has one disc, don't add any redundant headers
|
||||
data.addAll(songs)
|
||||
}
|
||||
albumListInstructions =
|
||||
if (replace) {
|
||||
DetailListInstructions.ReplaceRest(songsStartIndex)
|
||||
} else {
|
||||
DetailListInstructions.Diff
|
||||
}
|
||||
|
||||
_albumList.value = data
|
||||
return songsStartIndex
|
||||
}
|
||||
|
||||
private fun refreshArtistList(artist: Artist, replace: Boolean) {
|
||||
private fun refreshArtistList(artist: Artist) {
|
||||
logD("Refreshing artist data")
|
||||
val data = mutableListOf<Item>(artist)
|
||||
val albums = Sort(Sort.Mode.ByDate, false).albums(artist.albums)
|
||||
|
@ -411,40 +371,24 @@ class DetailViewModel(application: Application) :
|
|||
data.addAll(entry.value)
|
||||
}
|
||||
|
||||
var songsStartIndex: Int? = null
|
||||
// Artists may not be linked to any songs, only include a header entry if we have any.
|
||||
if (artist.songs.isNotEmpty()) {
|
||||
logD("Songs present in this artist, adding header")
|
||||
data.add(SortHeader(R.string.lbl_songs))
|
||||
songsStartIndex = data.size
|
||||
data.addAll(artistSongSort.songs(artist.songs))
|
||||
}
|
||||
|
||||
artistListInstructions =
|
||||
if (replace) {
|
||||
DetailListInstructions.ReplaceRest(
|
||||
requireNotNull(songsStartIndex) { "Cannot replace empty artist song list" })
|
||||
} else {
|
||||
DetailListInstructions.Diff
|
||||
}
|
||||
_artistList.value = data.toList()
|
||||
}
|
||||
|
||||
private fun refreshGenreList(genre: Genre, replace: Boolean) {
|
||||
private fun refreshGenreList(genre: Genre) {
|
||||
logD("Refreshing genre data")
|
||||
val data = mutableListOf<Item>(genre)
|
||||
// Genre is guaranteed to always have artists and songs.
|
||||
data.add(Header(R.string.lbl_artists))
|
||||
data.addAll(genre.artists)
|
||||
data.add(SortHeader(R.string.lbl_songs))
|
||||
val songsStartIndex = data.size
|
||||
data.addAll(genreSongSort.songs(genre.songs))
|
||||
genreListInstructions =
|
||||
if (replace) {
|
||||
DetailListInstructions.ReplaceRest(songsStartIndex)
|
||||
} else {
|
||||
DetailListInstructions.Diff
|
||||
}
|
||||
_genreList.value = data
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.oxycblt.auxio.detail.recycler.DetailAdapter
|
|||
import org.oxycblt.auxio.detail.recycler.GenreDetailAdapter
|
||||
import org.oxycblt.auxio.list.Item
|
||||
import org.oxycblt.auxio.list.ListFragment
|
||||
import org.oxycblt.auxio.list.adapter.BasicListInstructions
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
|
@ -218,9 +219,7 @@ class GenreDetailFragment :
|
|||
}
|
||||
|
||||
private fun updateList(items: List<Item>) {
|
||||
detailAdapter.submitList(
|
||||
items, detailModel.genreListInstructions ?: DetailListInstructions.Diff)
|
||||
detailModel.finishGenreListInstructions()
|
||||
detailAdapter.submitList(items, BasicListInstructions.DIFF)
|
||||
}
|
||||
|
||||
private fun updateSelection(selected: List<Music>) {
|
||||
|
|
|
@ -20,23 +20,15 @@ package org.oxycblt.auxio.detail.recycler
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.TooltipCompat
|
||||
import androidx.recyclerview.widget.AdapterListUpdateCallback
|
||||
import androidx.recyclerview.widget.AsyncDifferConfig
|
||||
import androidx.recyclerview.widget.AsyncListDiffer
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListUpdateCallback
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.databinding.ItemSortHeaderBinding
|
||||
import org.oxycblt.auxio.detail.DetailListInstructions
|
||||
import org.oxycblt.auxio.detail.SortHeader
|
||||
import org.oxycblt.auxio.list.Header
|
||||
import org.oxycblt.auxio.list.Item
|
||||
import org.oxycblt.auxio.list.SelectableListListener
|
||||
import org.oxycblt.auxio.list.adapter.ListDiffer
|
||||
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
|
||||
import org.oxycblt.auxio.list.adapter.SimpleDiffCallback
|
||||
import org.oxycblt.auxio.list.adapter.overwriteList
|
||||
import org.oxycblt.auxio.list.adapter.*
|
||||
import org.oxycblt.auxio.list.recycler.*
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.util.context
|
||||
|
@ -53,8 +45,8 @@ abstract class DetailAdapter(
|
|||
private val listener: Listener<*>,
|
||||
diffCallback: DiffUtil.ItemCallback<Item>
|
||||
) :
|
||||
SelectionIndicatorAdapter<Item, DetailListInstructions, RecyclerView.ViewHolder>(
|
||||
DetailListDiffer.Factory(diffCallback)),
|
||||
SelectionIndicatorAdapter<Item, BasicListInstructions, RecyclerView.ViewHolder>(
|
||||
ListDiffer.Async(diffCallback)),
|
||||
AuxioRecyclerView.SpanSizeLookup {
|
||||
|
||||
override fun getItemViewType(position: Int) =
|
||||
|
@ -124,39 +116,6 @@ abstract class DetailAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
private class DetailListDiffer<T>(
|
||||
private val updateCallback: ListUpdateCallback,
|
||||
diffCallback: DiffUtil.ItemCallback<T>
|
||||
) : ListDiffer<T, DetailListInstructions> {
|
||||
private val inner =
|
||||
AsyncListDiffer(updateCallback, AsyncDifferConfig.Builder(diffCallback).build())
|
||||
|
||||
override val currentList: List<T>
|
||||
get() = inner.currentList
|
||||
|
||||
override fun submitList(
|
||||
newList: List<T>,
|
||||
instructions: DetailListInstructions,
|
||||
onDone: () -> Unit
|
||||
) {
|
||||
when (instructions) {
|
||||
is DetailListInstructions.Diff -> inner.submitList(newList, onDone)
|
||||
is DetailListInstructions.ReplaceRest -> {
|
||||
val amount = newList.size - instructions.at
|
||||
updateCallback.onRemoved(instructions.at, amount)
|
||||
inner.overwriteList(newList)
|
||||
updateCallback.onInserted(instructions.at, amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Factory<T>(private val diffCallback: DiffUtil.ItemCallback<T>) :
|
||||
ListDiffer.Factory<T, DetailListInstructions>() {
|
||||
override fun new(adapter: RecyclerView.Adapter<*>) =
|
||||
DetailListDiffer(AdapterListUpdateCallback(adapter), diffCallback)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays a [SortHeader], a variation on [Header] that adds a
|
||||
* button opening a menu for sorting. Use [from] to create an instance.
|
||||
|
|
|
@ -45,17 +45,11 @@ class HomeViewModel(application: Application) :
|
|||
/** A list of [Song]s, sorted by the preferred [Sort], to be shown in the home view. */
|
||||
val songsList: StateFlow<List<Song>>
|
||||
get() = _songsList
|
||||
/** Specifies how to update [songsList] when it changes. */
|
||||
var songsListInstructions: BasicListInstructions? = 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: BasicListInstructions? = null
|
||||
private set
|
||||
|
||||
private val _artistsList = MutableStateFlow(listOf<Artist>())
|
||||
/**
|
||||
|
@ -65,17 +59,11 @@ class HomeViewModel(application: Application) :
|
|||
*/
|
||||
val artistsList: MutableStateFlow<List<Artist>>
|
||||
get() = _artistsList
|
||||
/** Specifies how to update [artistsList] when it changes. */
|
||||
var artistsListInstructions: BasicListInstructions? = 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: BasicListInstructions? = null
|
||||
private set
|
||||
|
||||
/** The [MusicMode] to use when playing a [Song] from the UI. */
|
||||
val playbackMode: MusicMode
|
||||
|
@ -120,11 +108,8 @@ 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 = BasicListInstructions.DIFF
|
||||
_songsList.value = musicSettings.songSort.songs(library.songs)
|
||||
albumsListInstructions = BasicListInstructions.DIFF
|
||||
_albumsLists.value = musicSettings.albumSort.albums(library.albums)
|
||||
artistsListInstructions = BasicListInstructions.DIFF
|
||||
_artistsList.value =
|
||||
musicSettings.artistSort.artists(
|
||||
if (homeSettings.shouldHideCollaborators) {
|
||||
|
@ -133,7 +118,6 @@ class HomeViewModel(application: Application) :
|
|||
} else {
|
||||
library.artists
|
||||
})
|
||||
genresListInstructions = BasicListInstructions.DIFF
|
||||
_genresList.value = musicSettings.genreSort.genres(library.genres)
|
||||
}
|
||||
}
|
||||
|
@ -173,56 +157,23 @@ class HomeViewModel(application: Application) :
|
|||
when (_currentTabMode.value) {
|
||||
MusicMode.SONGS -> {
|
||||
musicSettings.songSort = sort
|
||||
songsListInstructions = BasicListInstructions.REPLACE
|
||||
_songsList.value = sort.songs(_songsList.value)
|
||||
}
|
||||
MusicMode.ALBUMS -> {
|
||||
musicSettings.albumSort = sort
|
||||
albumsListInstructions = BasicListInstructions.REPLACE
|
||||
_albumsLists.value = sort.albums(_albumsLists.value)
|
||||
}
|
||||
MusicMode.ARTISTS -> {
|
||||
musicSettings.artistSort = sort
|
||||
artistsListInstructions = BasicListInstructions.REPLACE
|
||||
_artistsList.value = sort.artists(_artistsList.value)
|
||||
}
|
||||
MusicMode.GENRES -> {
|
||||
musicSettings.genreSort = sort
|
||||
genresListInstructions = BasicListInstructions.REPLACE
|
||||
_genresList.value = sort.genres(_genresList.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal that the specified [BasicListInstructions] in [songsListInstructions] were performed.
|
||||
*/
|
||||
fun finishSongsListInstructions() {
|
||||
songsListInstructions = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal that the specified [BasicListInstructions] in [albumsListInstructions] were performed.
|
||||
*/
|
||||
fun finishAlbumsListInstructions() {
|
||||
albumsListInstructions = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal that the specified [BasicListInstructions] in [artistsListInstructions] were
|
||||
* performed.
|
||||
*/
|
||||
fun finishArtistsListInstructions() {
|
||||
artistsListInstructions = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal that the specified [BasicListInstructions] 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.
|
||||
|
|
|
@ -132,9 +132,7 @@ class AlbumListFragment :
|
|||
}
|
||||
|
||||
private fun updateList(albums: List<Album>) {
|
||||
albumAdapter.submitList(
|
||||
albums, homeModel.albumsListInstructions ?: BasicListInstructions.DIFF)
|
||||
homeModel.finishAlbumsListInstructions()
|
||||
albumAdapter.submitList(albums, BasicListInstructions.REPLACE)
|
||||
}
|
||||
|
||||
private fun updateSelection(selection: List<Music>) {
|
||||
|
@ -152,7 +150,7 @@ class AlbumListFragment :
|
|||
*/
|
||||
private class AlbumAdapter(private val listener: SelectableListListener<Album>) :
|
||||
SelectionIndicatorAdapter<Album, BasicListInstructions, AlbumViewHolder>(
|
||||
ListDiffer.Async(AlbumViewHolder.DIFF_CALLBACK)) {
|
||||
ListDiffer.Blocking(AlbumViewHolder.DIFF_CALLBACK)) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
AlbumViewHolder.from(parent)
|
||||
|
|
|
@ -110,9 +110,7 @@ class ArtistListFragment :
|
|||
}
|
||||
|
||||
private fun updateList(artists: List<Artist>) {
|
||||
artistAdapter.submitList(
|
||||
artists, homeModel.artistsListInstructions ?: BasicListInstructions.DIFF)
|
||||
homeModel.finishArtistsListInstructions()
|
||||
artistAdapter.submitList(artists, BasicListInstructions.REPLACE)
|
||||
}
|
||||
|
||||
private fun updateSelection(selection: List<Music>) {
|
||||
|
@ -130,7 +128,7 @@ class ArtistListFragment :
|
|||
*/
|
||||
private class ArtistAdapter(private val listener: SelectableListListener<Artist>) :
|
||||
SelectionIndicatorAdapter<Artist, BasicListInstructions, ArtistViewHolder>(
|
||||
ListDiffer.Async(ArtistViewHolder.DIFF_CALLBACK)) {
|
||||
ListDiffer.Blocking(ArtistViewHolder.DIFF_CALLBACK)) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
ArtistViewHolder.from(parent)
|
||||
|
|
|
@ -109,9 +109,7 @@ class GenreListFragment :
|
|||
}
|
||||
|
||||
private fun updateList(artists: List<Genre>) {
|
||||
genreAdapter.submitList(
|
||||
artists, homeModel.genresListInstructions ?: BasicListInstructions.DIFF)
|
||||
homeModel.finishGenresListInstructions()
|
||||
genreAdapter.submitList(artists, BasicListInstructions.REPLACE)
|
||||
}
|
||||
|
||||
private fun updateSelection(selection: List<Music>) {
|
||||
|
@ -129,7 +127,7 @@ class GenreListFragment :
|
|||
*/
|
||||
private class GenreAdapter(private val listener: SelectableListListener<Genre>) :
|
||||
SelectionIndicatorAdapter<Genre, BasicListInstructions, GenreViewHolder>(
|
||||
ListDiffer.Async(GenreViewHolder.DIFF_CALLBACK)) {
|
||||
ListDiffer.Blocking(GenreViewHolder.DIFF_CALLBACK)) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
GenreViewHolder.from(parent)
|
||||
|
||||
|
|
|
@ -139,8 +139,7 @@ class SongListFragment :
|
|||
}
|
||||
|
||||
private fun updateList(songs: List<Song>) {
|
||||
songAdapter.submitList(songs, homeModel.songsListInstructions ?: BasicListInstructions.DIFF)
|
||||
homeModel.finishSongsListInstructions()
|
||||
songAdapter.submitList(songs, BasicListInstructions.REPLACE)
|
||||
}
|
||||
|
||||
private fun updateSelection(selection: List<Music>) {
|
||||
|
@ -162,7 +161,7 @@ class SongListFragment :
|
|||
*/
|
||||
private class SongAdapter(private val listener: SelectableListListener<Song>) :
|
||||
SelectionIndicatorAdapter<Song, BasicListInstructions, SongViewHolder>(
|
||||
ListDiffer.Async(SongViewHolder.DIFF_CALLBACK)) {
|
||||
ListDiffer.Blocking(SongViewHolder.DIFF_CALLBACK)) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
SongViewHolder.from(parent)
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Auxio Project
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.list.adapter
|
||||
|
||||
import androidx.recyclerview.widget.AsyncListDiffer
|
||||
import java.lang.reflect.Field
|
||||
import org.oxycblt.auxio.util.lazyReflectedField
|
||||
import org.oxycblt.auxio.util.requireIs
|
||||
|
||||
val ASD_MAX_GENERATION_FIELD: Field by
|
||||
lazyReflectedField(AsyncListDiffer::class, "mMaxScheduledGeneration")
|
||||
val ASD_MUTABLE_LIST_FIELD: Field by lazyReflectedField(AsyncListDiffer::class, "mList")
|
||||
val ASD_READ_ONLY_LIST_FIELD: Field by lazyReflectedField(AsyncListDiffer::class, "mReadOnlyList")
|
||||
|
||||
/**
|
||||
* Force-update an [AsyncListDiffer] with new data. It's hard to state how incredibly dangerous this
|
||||
* is, so only use it when absolutely necessary.
|
||||
* @param newList The new list to write to the [AsyncListDiffer].
|
||||
*/
|
||||
fun <T> AsyncListDiffer<T>.overwriteList(newList: List<T>) {
|
||||
// Should update the generation field to prevent any previous jobs from conflicting, then
|
||||
// updates the mutable list to it's nullable value, and then updates the read-only list to
|
||||
// it's non-nullable value.
|
||||
ASD_MAX_GENERATION_FIELD.set(this, requireIs<Int>(ASD_MAX_GENERATION_FIELD.get(this)) + 1)
|
||||
ASD_MUTABLE_LIST_FIELD.set(this, newList.ifEmpty { null })
|
||||
ASD_READ_ONLY_LIST_FIELD.set(this, newList)
|
||||
}
|
|
@ -112,7 +112,7 @@ private abstract class BasicListDiffer<T> : ListDiffer<T, BasicListInstructions>
|
|||
}
|
||||
|
||||
private class RealAsyncListDiffer<T>(
|
||||
private val updateCallback: ListUpdateCallback,
|
||||
updateCallback: ListUpdateCallback,
|
||||
diffCallback: DiffUtil.ItemCallback<T>
|
||||
) : BasicListDiffer<T>() {
|
||||
private val inner =
|
||||
|
@ -126,13 +126,9 @@ private class RealAsyncListDiffer<T>(
|
|||
}
|
||||
|
||||
override fun replaceList(newList: List<T>, onDone: () -> Unit) {
|
||||
if (inner.currentList != newList) {
|
||||
val oldListSize = inner.currentList.size
|
||||
updateCallback.onRemoved(0, oldListSize)
|
||||
inner.overwriteList(newList)
|
||||
updateCallback.onInserted(0, newList.size)
|
||||
inner.submitList(null) {
|
||||
inner.submitList(newList, onDone)
|
||||
}
|
||||
onDone()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue