diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt index ceeb3ca0b..4070f4950 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt @@ -29,7 +29,7 @@ import org.oxycblt.auxio.databinding.ItemDiscHeaderBinding import org.oxycblt.auxio.detail.DiscHeader import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Song -import org.oxycblt.auxio.ui.recycler.IndicatorViewHolder +import org.oxycblt.auxio.ui.recycler.IndicatorAdapter import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.MenuItemListener import org.oxycblt.auxio.ui.recycler.SimpleItemCallback @@ -175,7 +175,7 @@ class DiscHeaderViewHolder(private val binding: ItemDiscHeaderBinding) : } private class AlbumSongViewHolder private constructor(private val binding: ItemAlbumSongBinding) : - IndicatorViewHolder(binding.root) { + IndicatorAdapter.ViewHolder(binding.root) { fun bind(item: Song, listener: MenuItemListener) { // Hide the track number view if the song does not have a track. if (item.track != null) { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt index 08ae90468..9c22ec80d 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt @@ -30,7 +30,7 @@ import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.resolveYear import org.oxycblt.auxio.ui.recycler.ArtistViewHolder -import org.oxycblt.auxio.ui.recycler.IndicatorViewHolder +import org.oxycblt.auxio.ui.recycler.IndicatorAdapter import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.MenuItemListener import org.oxycblt.auxio.ui.recycler.SimpleItemCallback @@ -142,7 +142,7 @@ private class ArtistDetailViewHolder private constructor(private val binding: It private class ArtistAlbumViewHolder private constructor( private val binding: ItemParentBinding, -) : IndicatorViewHolder(binding.root) { +) : IndicatorAdapter.ViewHolder(binding.root) { fun bind(item: Album, listener: MenuItemListener) { binding.parentImage.bind(item) binding.parentName.text = item.resolveName(binding.context) @@ -177,7 +177,7 @@ private constructor( private class ArtistSongViewHolder private constructor( private val binding: ItemSongBinding, -) : IndicatorViewHolder(binding.root) { +) : IndicatorAdapter.ViewHolder(binding.root) { fun bind(item: Song, listener: MenuItemListener) { binding.songAlbumCover.bind(item) binding.songName.text = item.resolveName(binding.context) diff --git a/app/src/main/java/org/oxycblt/auxio/home/tabs/TabAdapter.kt b/app/src/main/java/org/oxycblt/auxio/home/tabs/TabAdapter.kt index 571e9b280..7ab2c9df0 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/tabs/TabAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/tabs/TabAdapter.kt @@ -24,6 +24,7 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import org.oxycblt.auxio.databinding.ItemTabBinding import org.oxycblt.auxio.ui.DisplayMode +import org.oxycblt.auxio.ui.recycler.DialogViewHolder import org.oxycblt.auxio.util.inflater class TabAdapter(private val listener: Listener) : RecyclerView.Adapter() { @@ -68,14 +69,9 @@ class TabAdapter(private val listener: Listener) : RecyclerView.Adapter. + */ + +package org.oxycblt.auxio.ui.recycler + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import org.oxycblt.auxio.util.logW + +/** + * An adapter capable of highlighting particular viewholders as "Playing". All behavior is handled + * by the adapter, only the implementation ViewHolders need to add code to handle the indicator UI + * itself. + * @author OxygenCobalt + */ +abstract class IndicatorAdapter : RecyclerView.Adapter() { + private var isPlaying = false + private var currentItem: Item? = null + + override fun onBindViewHolder(holder: VH, position: Int) = throw UnsupportedOperationException() + + override fun onBindViewHolder(holder: VH, position: Int, payloads: List) { + if (holder is ViewHolder) { + val item = currentList[position] + val currentItem = currentItem + holder.updateIndicator( + currentItem != null && + item.javaClass == currentItem.javaClass && + item.id == currentItem.id, + isPlaying) + } + } + + abstract val currentList: List + + fun updateIndicator(item: Item?, isPlaying: Boolean) { + var updatedItem = false + + if (currentItem != item) { + val oldItem = currentItem + currentItem = item + + if (oldItem != null) { + val pos = + currentList.indexOfFirst { + it.javaClass == oldItem.javaClass && it.id == oldItem.id + } + + if (pos > -1) { + notifyItemChanged(pos, PAYLOAD_INDICATOR_CHANGED) + } else { + logW("oldItem was not in adapter data") + } + } + + if (item != null) { + val pos = + currentList.indexOfFirst { it.javaClass == item.javaClass && it.id == item.id } + + if (pos > -1) { + notifyItemChanged(pos, PAYLOAD_INDICATOR_CHANGED) + } else { + logW("newItem was not in adapter data") + } + } + + updatedItem = true + } + + if (this.isPlaying != isPlaying) { + this.isPlaying = isPlaying + + if (!updatedItem && item != null) { + val pos = + currentList.indexOfFirst { it.javaClass == item.javaClass && it.id == item.id } + + if (pos > -1) { + notifyItemChanged(pos, PAYLOAD_INDICATOR_CHANGED) + } else { + logW("newItem was not in adapter data") + } + } + } + } + + companion object { + val PAYLOAD_INDICATOR_CHANGED = Any() + } + + /** A ViewHolder that can respond to indicator updates. */ + abstract class ViewHolder(root: View) : RecyclerView.ViewHolder(root) { + abstract fun updateIndicator(isActive: Boolean, isPlaying: Boolean) + } +} + +/** + * ViewHolder that correctly resizes the item to match the parent width, which it is not normally. + */ +abstract class DialogViewHolder(root: View) : RecyclerView.ViewHolder(root) { + init { + // Actually make the item full-width, which it won't be in dialogs + root.layoutParams = + RecyclerView.LayoutParams( + RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT) + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/ui/recycler/RecyclerFramework.kt b/app/src/main/java/org/oxycblt/auxio/ui/recycler/Data.kt similarity index 69% rename from app/src/main/java/org/oxycblt/auxio/ui/recycler/RecyclerFramework.kt rename to app/src/main/java/org/oxycblt/auxio/ui/recycler/Data.kt index 8f1dfea74..d8f5b05ee 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/recycler/RecyclerFramework.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/recycler/Data.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Auxio Project + * Copyright (c) 2022 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 @@ -20,10 +20,8 @@ package org.oxycblt.auxio.ui.recycler import android.view.View import androidx.annotation.StringRes import androidx.recyclerview.widget.AdapterListUpdateCallback -import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView -import org.oxycblt.auxio.util.logW /** * The base for all items in Auxio. Any datatype can derive this type and gain some behavior not @@ -172,85 +170,3 @@ abstract class SimpleItemCallback : DiffUtil.ItemCallback() { return oldItem.id == newItem.id } } - -// TODO: Base adapter that automates current list stuff for span size lookup -// TODO: Dialog view holder that automates the dumb sizing hack I have to do - -abstract class IndicatorAdapter : RecyclerView.Adapter() { - private var isPlaying = false - private var currentItem: Item? = null - - override fun onBindViewHolder(holder: VH, position: Int) = throw UnsupportedOperationException() - - override fun onBindViewHolder(holder: VH, position: Int, payloads: List) { - if (holder is IndicatorViewHolder) { - val item = currentList[position] - val currentItem = currentItem - holder.updateIndicator( - currentItem != null && - item.javaClass == currentItem.javaClass && - item.id == currentItem.id, - isPlaying) - } - } - - abstract val currentList: List - - fun updateIndicator(item: Item?, isPlaying: Boolean) { - var updatedItem = false - - if (currentItem != item) { - val oldItem = currentItem - currentItem = item - - if (oldItem != null) { - val pos = - currentList.indexOfFirst { - it.javaClass == oldItem.javaClass && it.id == oldItem.id - } - - if (pos > -1) { - notifyItemChanged(pos, PAYLOAD_INDICATOR_CHANGED) - } else { - logW("oldItem was not in adapter data") - } - } - - if (item != null) { - val pos = - currentList.indexOfFirst { it.javaClass == item.javaClass && it.id == item.id } - - if (pos > -1) { - notifyItemChanged(pos, PAYLOAD_INDICATOR_CHANGED) - } else { - logW("newItem was not in adapter data") - } - } - - updatedItem = true - } - - if (this.isPlaying != isPlaying) { - this.isPlaying = isPlaying - - if (!updatedItem && item != null) { - val pos = - currentList.indexOfFirst { it.javaClass == item.javaClass && it.id == item.id } - - if (pos > -1) { - notifyItemChanged(pos, PAYLOAD_INDICATOR_CHANGED) - } else { - logW("newItem was not in adapter data") - } - } - } - } - - companion object { - val PAYLOAD_INDICATOR_CHANGED = Any() - } -} - -abstract class IndicatorViewHolder(root: View) : RecyclerView.ViewHolder(root) { - abstract fun updateIndicator(isActive: Boolean, isPlaying: Boolean) -} diff --git a/app/src/main/java/org/oxycblt/auxio/ui/recycler/ViewHolders.kt b/app/src/main/java/org/oxycblt/auxio/ui/recycler/ViewHolders.kt index 393b0bb9a..778d564df 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/recycler/ViewHolders.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/recycler/ViewHolders.kt @@ -37,7 +37,7 @@ import org.oxycblt.auxio.util.inflater * @author OxygenCobalt */ class SongViewHolder private constructor(private val binding: ItemSongBinding) : - IndicatorViewHolder(binding.root) { + IndicatorAdapter.ViewHolder(binding.root) { fun bind(item: Song, listener: MenuItemListener) { binding.songAlbumCover.bind(item) binding.songName.text = item.resolveName(binding.context) @@ -76,7 +76,7 @@ class SongViewHolder private constructor(private val binding: ItemSongBinding) : class AlbumViewHolder private constructor( private val binding: ItemParentBinding, -) : IndicatorViewHolder(binding.root) { +) : IndicatorAdapter.ViewHolder(binding.root) { fun bind(item: Album, listener: MenuItemListener) { binding.parentImage.bind(item) @@ -115,7 +115,7 @@ private constructor( * @author OxygenCobalt */ class ArtistViewHolder private constructor(private val binding: ItemParentBinding) : - IndicatorViewHolder(binding.root) { + IndicatorAdapter.ViewHolder(binding.root) { fun bind(item: Artist, listener: MenuItemListener) { binding.parentImage.bind(item) @@ -160,7 +160,7 @@ class ArtistViewHolder private constructor(private val binding: ItemParentBindin class GenreViewHolder private constructor( private val binding: ItemParentBinding, -) : IndicatorViewHolder(binding.root) { +) : IndicatorAdapter.ViewHolder(binding.root) { fun bind(item: Genre, listener: MenuItemListener) { binding.parentImage.bind(item)