list: make list instructions generic
Make list instructions generic in preparation for the detail list update. Detail views need their own instructions datatype, so this is meant to allow that to be implemented without issue.
This commit is contained in:
parent
4a7bc4e511
commit
d38da9b892
20 changed files with 102 additions and 103 deletions
|
@ -4,13 +4,13 @@
|
|||
|
||||
#### What's New
|
||||
- Added ability to play/shuffle selections
|
||||
- Settings UI has been visually refreshed
|
||||
|
||||
#### What's Improved
|
||||
- Added ability to edit previously played or currently playing items in the queue
|
||||
- Added support for date values formatted as "YYYYMMDD"
|
||||
- Pressing the button will now clear the current selection before navigating back
|
||||
- Added support for non-standard `ARTISTS` tags
|
||||
- Reworked music folders dialog to be more visually straightforward
|
||||
- Play Next and Add To Queue now start playback if there is no queue to add
|
||||
|
||||
#### What's Fixed
|
||||
|
|
|
@ -35,7 +35,7 @@ import org.oxycblt.auxio.playback.PlaybackSettings
|
|||
import org.oxycblt.auxio.ui.UISettings
|
||||
|
||||
/**
|
||||
* Auxio: A simple, rational music player for android.
|
||||
* A simple, rational music player for android.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class Auxio : Application(), ImageLoaderFactory {
|
||||
|
|
|
@ -29,7 +29,9 @@ import com.google.android.material.transition.MaterialSharedAxis
|
|||
import org.oxycblt.auxio.R
|
||||
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.UpdateInstructions
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Music
|
||||
|
@ -82,7 +84,7 @@ class AlbumDetailFragment :
|
|||
// DetailViewModel handles most initialization from the navigation argument.
|
||||
detailModel.setAlbumUid(args.albumUid)
|
||||
collectImmediately(detailModel.currentAlbum, ::updateAlbum)
|
||||
collectImmediately(detailModel.albumList, detailAdapter::diffList)
|
||||
collectImmediately(detailModel.albumList, ::updateList)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
collect(navModel.exploreNavigationItem, ::handleNavigation)
|
||||
|
@ -257,6 +259,10 @@ class AlbumDetailFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateList(items: List<Item>) {
|
||||
detailAdapter.submitList(items, UpdateInstructions.DIFF)
|
||||
}
|
||||
|
||||
private fun updateSelection(selected: List<Music>) {
|
||||
detailAdapter.setSelected(selected.toSet())
|
||||
requireBinding().detailSelectionToolbar.updateSelectionAmount(selected.size)
|
||||
|
|
|
@ -29,7 +29,9 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.FragmentDetailBinding
|
||||
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.UpdateInstructions
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Music
|
||||
|
@ -85,7 +87,7 @@ class ArtistDetailFragment :
|
|||
// DetailViewModel handles most initialization from the navigation argument.
|
||||
detailModel.setArtistUid(args.artistUid)
|
||||
collectImmediately(detailModel.currentArtist, ::updateItem)
|
||||
collectImmediately(detailModel.artistList, detailAdapter::diffList)
|
||||
collectImmediately(detailModel.artistList, ::updateList)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
collect(navModel.exploreNavigationItem, ::handleNavigation)
|
||||
|
@ -233,6 +235,10 @@ class ArtistDetailFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateList(items: List<Item>) {
|
||||
detailAdapter.submitList(items, UpdateInstructions.DIFF)
|
||||
}
|
||||
|
||||
private fun updateSelection(selected: List<Music>) {
|
||||
detailAdapter.setSelected(selected.toSet())
|
||||
requireBinding().detailSelectionToolbar.updateSelectionAmount(selected.size)
|
||||
|
|
|
@ -29,7 +29,9 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.FragmentDetailBinding
|
||||
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.UpdateInstructions
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
|
@ -84,7 +86,7 @@ class GenreDetailFragment :
|
|||
// DetailViewModel handles most initialization from the navigation argument.
|
||||
detailModel.setGenreUid(args.genreUid)
|
||||
collectImmediately(detailModel.currentGenre, ::updateItem)
|
||||
collectImmediately(detailModel.genreList, detailAdapter::diffList)
|
||||
collectImmediately(detailModel.genreList, ::updateList)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
collect(navModel.exploreNavigationItem, ::handleNavigation)
|
||||
|
@ -216,6 +218,10 @@ class GenreDetailFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateList(items: List<Item>) {
|
||||
detailAdapter.submitList(items, UpdateInstructions.DIFF)
|
||||
}
|
||||
|
||||
private fun updateSelection(selected: List<Music>) {
|
||||
detailAdapter.setSelected(selected.toSet())
|
||||
requireBinding().detailSelectionToolbar.updateSelectionAmount(selected.size)
|
||||
|
|
|
@ -28,6 +28,7 @@ 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.UpdateInstructions
|
||||
import org.oxycblt.auxio.list.recycler.*
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.util.context
|
||||
|
@ -44,7 +45,8 @@ abstract class DetailAdapter(
|
|||
private val listener: Listener<*>,
|
||||
diffCallback: DiffUtil.ItemCallback<Item>
|
||||
) :
|
||||
SelectionIndicatorAdapter<Item, RecyclerView.ViewHolder>(ListDiffer.Async(diffCallback)),
|
||||
SelectionIndicatorAdapter<Item, UpdateInstructions, RecyclerView.ViewHolder>(
|
||||
ListDiffer.Async(diffCallback)),
|
||||
AuxioRecyclerView.SpanSizeLookup {
|
||||
|
||||
override fun getItemViewType(position: Int) =
|
||||
|
|
|
@ -149,7 +149,7 @@ class AlbumListFragment :
|
|||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
private class AlbumAdapter(private val listener: SelectableListListener<Album>) :
|
||||
SelectionIndicatorAdapter<Album, AlbumViewHolder>(
|
||||
SelectionIndicatorAdapter<Album, UpdateInstructions, AlbumViewHolder>(
|
||||
ListDiffer.Async(AlbumViewHolder.DIFF_CALLBACK)) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
|
|
|
@ -128,7 +128,7 @@ class ArtistListFragment :
|
|||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
private class ArtistAdapter(private val listener: SelectableListListener<Artist>) :
|
||||
SelectionIndicatorAdapter<Artist, ArtistViewHolder>(
|
||||
SelectionIndicatorAdapter<Artist, UpdateInstructions, ArtistViewHolder>(
|
||||
ListDiffer.Async(ArtistViewHolder.DIFF_CALLBACK)) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
|
|
|
@ -127,7 +127,7 @@ class GenreListFragment :
|
|||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
private class GenreAdapter(private val listener: SelectableListListener<Genre>) :
|
||||
SelectionIndicatorAdapter<Genre, GenreViewHolder>(
|
||||
SelectionIndicatorAdapter<Genre, UpdateInstructions, GenreViewHolder>(
|
||||
ListDiffer.Async(GenreViewHolder.DIFF_CALLBACK)) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
GenreViewHolder.from(parent)
|
||||
|
|
|
@ -160,7 +160,7 @@ class SongListFragment :
|
|||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
private class SongAdapter(private val listener: SelectableListListener<Song>) :
|
||||
SelectionIndicatorAdapter<Song, SongViewHolder>(
|
||||
SelectionIndicatorAdapter<Song, UpdateInstructions, SongViewHolder>(
|
||||
ListDiffer.Async(SongViewHolder.DIFF_CALLBACK)) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
|
|
|
@ -18,14 +18,14 @@
|
|||
package org.oxycblt.auxio.list.recycler
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.list.UpdateInstructions
|
||||
|
||||
/**
|
||||
* A [RecyclerView.Adapter] with [ListDiffer] integration.
|
||||
* @param differFactory The [ListDiffer.Factory] that defines the type of [ListDiffer] to use.
|
||||
*/
|
||||
abstract class DiffAdapter<T, VH : RecyclerView.ViewHolder>(differFactory: ListDiffer.Factory<T>) :
|
||||
RecyclerView.Adapter<VH>() {
|
||||
abstract class DiffAdapter<T, I, VH : RecyclerView.ViewHolder>(
|
||||
differFactory: ListDiffer.Factory<T, I>
|
||||
) : RecyclerView.Adapter<VH>() {
|
||||
private val differ = differFactory.new(@Suppress("LeakingThis") this)
|
||||
|
||||
final override fun getItemCount() = differ.currentList.size
|
||||
|
@ -42,30 +42,12 @@ abstract class DiffAdapter<T, VH : RecyclerView.ViewHolder>(differFactory: ListD
|
|||
fun getItem(at: Int) = differ.currentList[at]
|
||||
|
||||
/**
|
||||
* Dynamically determine how to update the list based on the given [UpdateInstructions].
|
||||
* Dynamically determine how to update the list based on the given instructions.
|
||||
* @param newList The new list of [T] items to show.
|
||||
* @param instructions The [UpdateInstructions] specifying how to update the list.
|
||||
* @param instructions The instructions specifying how to update the list.
|
||||
* @param onDone Called when the update process is completed. Defaults to a no-op.
|
||||
*/
|
||||
fun submitList(newList: List<T>, instructions: UpdateInstructions) {
|
||||
when (instructions) {
|
||||
UpdateInstructions.DIFF -> diffList(newList)
|
||||
UpdateInstructions.REPLACE -> replaceList(newList)
|
||||
}
|
||||
fun submitList(newList: List<T>, instructions: I, onDone: () -> Unit = {}) {
|
||||
differ.submitList(newList, instructions, onDone)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this list using DiffUtil. This can simplify the work of updating the list, but can
|
||||
* also cause erratic behavior.
|
||||
* @param newList The new list of [T] items to show.
|
||||
* @param onDone Callback that will be invoked when the update is completed, allowing means to
|
||||
* reset the state.
|
||||
*/
|
||||
fun diffList(newList: List<T>, onDone: () -> Unit = {}) = differ.diffList(newList, onDone)
|
||||
|
||||
/**
|
||||
* Visually replace the previous list with a new list. This is useful for large diffs that are
|
||||
* too erratic for [diffList].
|
||||
* @param newList The new list of [T] items to show.
|
||||
*/
|
||||
fun replaceList(newList: List<T>) = differ.replaceList(newList)
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import androidx.recyclerview.widget.DiffUtil
|
|||
import androidx.recyclerview.widget.ListUpdateCallback
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import java.lang.reflect.Field
|
||||
import org.oxycblt.auxio.list.UpdateInstructions
|
||||
import org.oxycblt.auxio.util.lazyReflectedField
|
||||
import org.oxycblt.auxio.util.requireIs
|
||||
|
||||
|
@ -31,36 +32,28 @@ import org.oxycblt.auxio.util.requireIs
|
|||
* List differ wrapper that provides more flexibility regarding the way lists are updated.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface ListDiffer<T> {
|
||||
interface ListDiffer<T, I> {
|
||||
/** The current list of [T] items. */
|
||||
val currentList: List<T>
|
||||
|
||||
/**
|
||||
* Update this list using [DiffUtil]. This can simplify the work of updating the list, but can
|
||||
* also cause erratic behavior.
|
||||
* Dynamically determine how to update the list based on the given instructions.
|
||||
* @param newList The new list of [T] items to show.
|
||||
* @param onDone Callback that will be invoked when the update is completed, allowing means to
|
||||
* reset the state.
|
||||
* @param instructions The [UpdateInstructions] specifying how to update the list.
|
||||
* @param onDone Called when the update process is completed.
|
||||
*/
|
||||
fun diffList(newList: List<T>, onDone: () -> Unit = {})
|
||||
|
||||
/**
|
||||
* Visually replace the previous list with a new list. This is useful for large diffs that are
|
||||
* too erratic for [diffList].
|
||||
* @param newList The new list of [T] items to show.
|
||||
*/
|
||||
fun replaceList(newList: List<T>)
|
||||
fun submitList(newList: List<T>, instructions: I, onDone: () -> Unit)
|
||||
|
||||
/**
|
||||
* Defines the creation of new [ListDiffer] instances. Allows such [ListDiffer]s to be passed as
|
||||
* arguments without reliance on a `this` [RecyclerView.Adapter].
|
||||
*/
|
||||
abstract class Factory<T> {
|
||||
abstract class Factory<T, I> {
|
||||
/**
|
||||
* Create a new [ListDiffer] bound to the given [RecyclerView.Adapter].
|
||||
* @param adapter The [RecyclerView.Adapter] to bind to.
|
||||
*/
|
||||
abstract fun new(adapter: RecyclerView.Adapter<*>): ListDiffer<T>
|
||||
abstract fun new(adapter: RecyclerView.Adapter<*>): ListDiffer<T, I>
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,8 +62,9 @@ interface ListDiffer<T> {
|
|||
* @param diffCallback A [DiffUtil.ItemCallback] to use for item comparison when diffing the
|
||||
* internal list.
|
||||
*/
|
||||
class Async<T>(private val diffCallback: DiffUtil.ItemCallback<T>) : Factory<T>() {
|
||||
override fun new(adapter: RecyclerView.Adapter<*>): ListDiffer<T> =
|
||||
class Async<T>(private val diffCallback: DiffUtil.ItemCallback<T>) :
|
||||
Factory<T, UpdateInstructions>() {
|
||||
override fun new(adapter: RecyclerView.Adapter<*>): ListDiffer<T, UpdateInstructions> =
|
||||
RealAsyncListDiffer(AdapterListUpdateCallback(adapter), diffCallback)
|
||||
}
|
||||
|
||||
|
@ -80,16 +74,33 @@ interface ListDiffer<T> {
|
|||
* @param diffCallback A [DiffUtil.ItemCallback] to use for item comparison when diffing the
|
||||
* internal list.
|
||||
*/
|
||||
class Blocking<T>(private val diffCallback: DiffUtil.ItemCallback<T>) : Factory<T>() {
|
||||
override fun new(adapter: RecyclerView.Adapter<*>): ListDiffer<T> =
|
||||
class Blocking<T>(private val diffCallback: DiffUtil.ItemCallback<T>) :
|
||||
Factory<T, UpdateInstructions>() {
|
||||
override fun new(adapter: RecyclerView.Adapter<*>): ListDiffer<T, UpdateInstructions> =
|
||||
RealBlockingListDiffer(AdapterListUpdateCallback(adapter), diffCallback)
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class RealListDiffer<T>() : ListDiffer<T, UpdateInstructions> {
|
||||
override fun submitList(
|
||||
newList: List<T>,
|
||||
instructions: UpdateInstructions,
|
||||
onDone: () -> Unit
|
||||
) {
|
||||
when (instructions) {
|
||||
UpdateInstructions.DIFF -> diffList(newList, onDone)
|
||||
UpdateInstructions.REPLACE -> replaceList(newList, onDone)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun diffList(newList: List<T>, onDone: () -> Unit)
|
||||
protected abstract fun replaceList(newList: List<T>, onDone: () -> Unit)
|
||||
}
|
||||
|
||||
private class RealAsyncListDiffer<T>(
|
||||
private val updateCallback: ListUpdateCallback,
|
||||
diffCallback: DiffUtil.ItemCallback<T>
|
||||
) : ListDiffer<T> {
|
||||
) : RealListDiffer<T>() {
|
||||
private val inner =
|
||||
AsyncListDiffer(updateCallback, AsyncDifferConfig.Builder(diffCallback).build())
|
||||
|
||||
|
@ -100,21 +111,21 @@ private class RealAsyncListDiffer<T>(
|
|||
inner.submitList(newList, onDone)
|
||||
}
|
||||
|
||||
override fun replaceList(newList: List<T>) {
|
||||
if (inner.currentList == newList) {
|
||||
// Nothing to do.
|
||||
return
|
||||
override fun replaceList(newList: List<T>, onDone: () -> Unit) {
|
||||
if (inner.currentList != newList) {
|
||||
// Do possibly the most idiotic thing possible and mutate the internal differ state
|
||||
// so we don't have to deal with any disjoint list garbage. This should cancel any prior
|
||||
// updates and correctly set up the list values while still allowing for the same
|
||||
// visual animation as the blocking replaceList.
|
||||
val oldListSize = inner.currentList.size
|
||||
ASD_MAX_GENERATION_FIELD.set(
|
||||
inner, requireIs<Int>(ASD_MAX_GENERATION_FIELD.get(inner)) + 1)
|
||||
ASD_MUTABLE_LIST_FIELD.set(inner, newList.ifEmpty { null })
|
||||
ASD_READ_ONLY_LIST_FIELD.set(inner, newList)
|
||||
updateCallback.onRemoved(0, oldListSize)
|
||||
updateCallback.onInserted(0, newList.size)
|
||||
}
|
||||
// Do possibly the most idiotic thing possible and mutate the internal differ state
|
||||
// so we don't have to deal with any disjoint list garbage. This should cancel any prior
|
||||
// updates and correctly set up the list values while still allowing for the same
|
||||
// visual animation as the blocking replaceList.
|
||||
val oldListSize = inner.currentList.size
|
||||
ASD_MAX_GENERATION_FIELD.set(inner, requireIs<Int>(ASD_MAX_GENERATION_FIELD.get(inner)) + 1)
|
||||
ASD_MUTABLE_LIST_FIELD.set(inner, newList.ifEmpty { null })
|
||||
ASD_READ_ONLY_LIST_FIELD.set(inner, newList)
|
||||
updateCallback.onRemoved(0, oldListSize)
|
||||
updateCallback.onInserted(0, newList.size)
|
||||
onDone()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
@ -129,7 +140,7 @@ private class RealAsyncListDiffer<T>(
|
|||
private class RealBlockingListDiffer<T>(
|
||||
private val updateCallback: ListUpdateCallback,
|
||||
private val diffCallback: DiffUtil.ItemCallback<T>
|
||||
) : ListDiffer<T> {
|
||||
) : RealListDiffer<T>() {
|
||||
override var currentList = listOf<T>()
|
||||
|
||||
override fun diffList(newList: List<T>, onDone: () -> Unit) {
|
||||
|
@ -212,13 +223,9 @@ private class RealBlockingListDiffer<T>(
|
|||
onDone()
|
||||
}
|
||||
|
||||
override fun replaceList(newList: List<T>) {
|
||||
if (currentList == newList) {
|
||||
// Nothing to do.
|
||||
return
|
||||
override fun replaceList(newList: List<T>, onDone: () -> Unit) {
|
||||
if (currentList != newList) {
|
||||
diffList(listOf()) { diffList(newList, onDone) }
|
||||
}
|
||||
|
||||
diffList(listOf())
|
||||
diffList(newList)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,9 +26,9 @@ import org.oxycblt.auxio.util.logD
|
|||
* @param differFactory The [ListDiffer.Factory] that defines the type of [ListDiffer] to use.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
abstract class PlayingIndicatorAdapter<T, VH : RecyclerView.ViewHolder>(
|
||||
differFactory: ListDiffer.Factory<T>
|
||||
) : DiffAdapter<T, VH>(differFactory) {
|
||||
abstract class PlayingIndicatorAdapter<T, I, VH : RecyclerView.ViewHolder>(
|
||||
differFactory: ListDiffer.Factory<T, I>
|
||||
) : DiffAdapter<T, I, VH>(differFactory) {
|
||||
// There are actually two states for this adapter:
|
||||
// - The currently playing item, which is usually marked as "selected" and becomes accented.
|
||||
// - Whether playback is ongoing, which corresponds to whether the item's ImageGroup is
|
||||
|
|
|
@ -27,9 +27,9 @@ import org.oxycblt.auxio.music.Music
|
|||
* @param differFactory The [ListDiffer.Factory] that defines the type of [ListDiffer] to use.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
abstract class SelectionIndicatorAdapter<T, VH : RecyclerView.ViewHolder>(
|
||||
differFactory: ListDiffer.Factory<T>
|
||||
) : PlayingIndicatorAdapter<T, VH>(differFactory) {
|
||||
abstract class SelectionIndicatorAdapter<T, I, VH : RecyclerView.ViewHolder>(
|
||||
differFactory: ListDiffer.Factory<T, I>
|
||||
) : PlayingIndicatorAdapter<T, I, VH>(differFactory) {
|
||||
private var selectedItems = setOf<T>()
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, payloads: List<Any>) {
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.google.android.material.shape.MaterialShapeDrawable
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
||||
import org.oxycblt.auxio.list.EditableListListener
|
||||
import org.oxycblt.auxio.list.UpdateInstructions
|
||||
import org.oxycblt.auxio.list.recycler.DiffAdapter
|
||||
import org.oxycblt.auxio.list.recycler.ListDiffer
|
||||
import org.oxycblt.auxio.list.recycler.PlayingIndicatorAdapter
|
||||
|
@ -40,7 +41,8 @@ import org.oxycblt.auxio.util.*
|
|||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class QueueAdapter(private val listener: EditableListListener<Song>) :
|
||||
DiffAdapter<Song, QueueSongViewHolder>(ListDiffer.Blocking(QueueSongViewHolder.DIFF_CALLBACK)) {
|
||||
DiffAdapter<Song, UpdateInstructions, QueueSongViewHolder>(
|
||||
ListDiffer.Blocking(QueueSongViewHolder.DIFF_CALLBACK)) {
|
||||
// Since PlayingIndicator adapter relies on an item value, we cannot use it for this
|
||||
// adapter, as one item can appear at several points in the UI. Use a similar implementation
|
||||
// with an index value instead.
|
||||
|
|
|
@ -67,14 +67,14 @@ class QueueViewModel : ViewModel(), PlaybackStateManager.Listener {
|
|||
|
||||
override fun onQueueReordered(queue: Queue) {
|
||||
// Queue changed completely -> Replace queue, update index
|
||||
instructions = Instructions(UpdateInstructions.REPLACE, null)
|
||||
instructions = Instructions(UpdateInstructions.REPLACE, queue.index)
|
||||
_queue.value = queue.resolve()
|
||||
_index.value = queue.index
|
||||
}
|
||||
|
||||
override fun onNewPlayback(queue: Queue, parent: MusicParent?) {
|
||||
// Entirely new queue -> Replace queue, update index
|
||||
instructions = Instructions(UpdateInstructions.REPLACE, null)
|
||||
instructions = Instructions(UpdateInstructions.REPLACE, queue.index)
|
||||
_queue.value = queue.resolve()
|
||||
_index.value = queue.index
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@ import org.oxycblt.auxio.music.*
|
|||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class SearchAdapter(private val listener: SelectableListListener<Music>) :
|
||||
SelectionIndicatorAdapter<Item, RecyclerView.ViewHolder>(ListDiffer.Async(DIFF_CALLBACK)),
|
||||
SelectionIndicatorAdapter<Item, UpdateInstructions, RecyclerView.ViewHolder>(
|
||||
ListDiffer.Async(DIFF_CALLBACK)),
|
||||
AuxioRecyclerView.SpanSizeLookup {
|
||||
|
||||
override fun getItemViewType(position: Int) =
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.FragmentSearchBinding
|
||||
import org.oxycblt.auxio.list.Item
|
||||
import org.oxycblt.auxio.list.ListFragment
|
||||
import org.oxycblt.auxio.list.UpdateInstructions
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
|
@ -153,7 +154,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
|||
// Don't show the RecyclerView (and it's stray overscroll effects) when there
|
||||
// are no results.
|
||||
binding.searchRecycler.isInvisible = results.isEmpty()
|
||||
searchAdapter.diffList(results.toMutableList()) {
|
||||
searchAdapter.submitList(results.toMutableList(), UpdateInstructions.DIFF) {
|
||||
// I would make it so that the position is only scrolled back to the top when
|
||||
// the query actually changes instead of once every re-creation event, but sadly
|
||||
// that doesn't seem possible.
|
||||
|
|
|
@ -13,10 +13,4 @@
|
|||
android:layout_height="wrap_content"
|
||||
tools:text="Songs" />
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:id="@+id/header_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</LinearLayout>
|
|
@ -12,7 +12,6 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/spacing_mid_medium"
|
||||
app:layout_constraintBottom_toTopOf="@id/header_divider"
|
||||
app:layout_constraintEnd_toStartOf="@+id/header_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
@ -26,14 +25,7 @@
|
|||
android:layout_marginEnd="@dimen/spacing_mid_medium"
|
||||
android:contentDescription="@string/lbl_sort"
|
||||
app:icon="@drawable/ic_sort_24"
|
||||
app:layout_constraintBottom_toTopOf="@id/header_divider"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:id="@+id/header_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in a new issue