list: make listeners generic

Make all list listeners operate on generic values.

Wanted to do this for awhile, but couldn't figure out how to get the
listener to work with sub-typed listeners until I learned what the in
keyword actually does. This removes a ton of type-checking boilerplate
that it's not even funny.
This commit is contained in:
Alexander Capehart 2023-01-04 09:40:25 -07:00
parent d5941aa705
commit d55dfbc849
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
34 changed files with 168 additions and 203 deletions

View file

@ -29,7 +29,6 @@ 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.music.Album
import org.oxycblt.auxio.music.Artist
@ -39,18 +38,14 @@ import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.util.canScroll
import org.oxycblt.auxio.util.collect
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull
import org.oxycblt.auxio.util.*
/**
* A [ListFragment] that shows information about an [Album].
* @author Alexander Capehart (OxygenCobalt)
*/
class AlbumDetailFragment : ListFragment<FragmentDetailBinding>(), AlbumDetailAdapter.Listener {
class AlbumDetailFragment :
ListFragment<Music, FragmentDetailBinding>(), AlbumDetailAdapter.Listener {
private val detailModel: DetailViewModel by activityViewModels()
// Information about what album to display is initially within the navigation arguments
// as a UID, as that is the only safe way to parcel an album.
@ -126,20 +121,20 @@ class AlbumDetailFragment : ListFragment<FragmentDetailBinding>(), AlbumDetailAd
}
}
override fun onRealClick(music: Music) {
check(music is Song) { "Unexpected datatype: ${music::class.java}" }
override fun onRealClick(item: Music) {
val song = requireIs<Song>(item)
when (Settings(requireContext()).detailPlaybackMode) {
// "Play from shown item" and "Play from album" functionally have the same
// behavior since a song can only have one album.
null,
MusicMode.ALBUMS -> playbackModel.playFromAlbum(music)
MusicMode.SONGS -> playbackModel.playFromAll(music)
MusicMode.ARTISTS -> playbackModel.playFromArtist(music)
MusicMode.GENRES -> playbackModel.playFromGenre(music)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(song)
MusicMode.SONGS -> playbackModel.playFromAll(song)
MusicMode.ARTISTS -> playbackModel.playFromArtist(song)
MusicMode.GENRES -> playbackModel.playFromGenre(song)
}
}
override fun onOpenMenu(item: Item, anchor: View) {
override fun onOpenMenu(item: Music, anchor: View) {
check(item is Song) { "Unexpected datatype: ${item::class.simpleName}" }
openMusicMenu(anchor, R.menu.menu_album_song_actions, item)
}

View file

@ -29,7 +29,6 @@ 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.music.Album
import org.oxycblt.auxio.music.Artist
@ -49,7 +48,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
* A [ListFragment] that shows information about an [Artist].
* @author Alexander Capehart (OxygenCobalt)
*/
class ArtistDetailFragment : ListFragment<FragmentDetailBinding>(), DetailAdapter.Listener {
class ArtistDetailFragment : ListFragment<Music, FragmentDetailBinding>(), DetailAdapter.Listener {
private val detailModel: DetailViewModel by activityViewModels()
// Information about what artist to display is initially within the navigation arguments
// as a UID, as that is the only safe way to parcel an artist.
@ -121,27 +120,27 @@ class ArtistDetailFragment : ListFragment<FragmentDetailBinding>(), DetailAdapte
}
}
override fun onRealClick(music: Music) {
when (music) {
override fun onRealClick(item: Music) {
when (item) {
is Song -> {
when (Settings(requireContext()).detailPlaybackMode) {
// When configured to play from the selected item, we already have an Artist
// to play from.
null ->
playbackModel.playFromArtist(
music, unlikelyToBeNull(detailModel.currentArtist.value))
MusicMode.SONGS -> playbackModel.playFromAll(music)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(music)
MusicMode.ARTISTS -> playbackModel.playFromArtist(music)
MusicMode.GENRES -> playbackModel.playFromGenre(music)
item, unlikelyToBeNull(detailModel.currentArtist.value))
MusicMode.SONGS -> playbackModel.playFromAll(item)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
MusicMode.ARTISTS -> playbackModel.playFromArtist(item)
MusicMode.GENRES -> playbackModel.playFromGenre(item)
}
}
is Album -> navModel.exploreNavigateTo(music)
else -> error("Unexpected datatype: ${music::class.simpleName}")
is Album -> navModel.exploreNavigateTo(item)
else -> error("Unexpected datatype: ${item::class.simpleName}")
}
}
override fun onOpenMenu(item: Item, anchor: View) {
override fun onOpenMenu(item: Music, anchor: View) {
when (item) {
is Song -> openMusicMenu(anchor, R.menu.menu_artist_song_actions, item)
is Album -> openMusicMenu(anchor, R.menu.menu_artist_album_actions, item)

View file

@ -29,7 +29,6 @@ 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.music.Album
import org.oxycblt.auxio.music.Artist
@ -50,7 +49,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
* A [ListFragment] that shows information for a particular [Genre].
* @author Alexander Capehart (OxygenCobalt)
*/
class GenreDetailFragment : ListFragment<FragmentDetailBinding>(), DetailAdapter.Listener {
class GenreDetailFragment : ListFragment<Music, FragmentDetailBinding>(), DetailAdapter.Listener {
private val detailModel: DetailViewModel by activityViewModels()
// Information about what genre to display is initially within the navigation arguments
// as a UID, as that is the only safe way to parcel an genre.
@ -120,26 +119,26 @@ class GenreDetailFragment : ListFragment<FragmentDetailBinding>(), DetailAdapter
}
}
override fun onRealClick(music: Music) {
when (music) {
is Artist -> navModel.exploreNavigateTo(music)
override fun onRealClick(item: Music) {
when (item) {
is Artist -> navModel.exploreNavigateTo(item)
is Song ->
when (Settings(requireContext()).detailPlaybackMode) {
// When configured to play from the selected item, we already have a Genre
// to play from.
null ->
playbackModel.playFromGenre(
music, unlikelyToBeNull(detailModel.currentGenre.value))
MusicMode.SONGS -> playbackModel.playFromAll(music)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(music)
MusicMode.ARTISTS -> playbackModel.playFromArtist(music)
MusicMode.GENRES -> playbackModel.playFromGenre(music)
item, unlikelyToBeNull(detailModel.currentGenre.value))
MusicMode.SONGS -> playbackModel.playFromAll(item)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
MusicMode.ARTISTS -> playbackModel.playFromArtist(item)
MusicMode.GENRES -> playbackModel.playFromGenre(item)
}
else -> error("Unexpected datatype: ${music::class.simpleName}")
else -> error("Unexpected datatype: ${item::class.simpleName}")
}
}
override fun onOpenMenu(item: Item, anchor: View) {
override fun onOpenMenu(item: Music, anchor: View) {
when (item) {
is Artist -> openMusicMenu(anchor, R.menu.menu_artist_actions, item)
is Song -> openMusicMenu(anchor, R.menu.menu_song_actions, item)
@ -184,17 +183,15 @@ class GenreDetailFragment : ListFragment<FragmentDetailBinding>(), DetailAdapter
}
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
var item: Item? = null
var playingMusic: Music? = null
if (parent is Artist) {
item = parent
playingMusic = parent
}
// Prefer songs that might be playing from this genre.
if (parent is Genre && parent.uid == unlikelyToBeNull(detailModel.currentGenre.value).uid) {
item = song
playingMusic = song
}
detailAdapter.setPlayingItem(item, isPlaying)
detailAdapter.setPlayingItem(playingMusic, isPlaying)
}
private fun handleNavigation(item: Music?) {

View file

@ -226,7 +226,7 @@ private class AlbumSongViewHolder private constructor(private val binding: ItemA
* @param song The new [Song] to bind.
* @param listener A [SelectableListListener] to bind interactions to.
*/
fun bind(song: Song, listener: SelectableListListener) {
fun bind(song: Song, listener: SelectableListListener<Song>) {
listener.bind(song, this, menuButton = binding.songMenu)
binding.songTrack.apply {

View file

@ -183,7 +183,7 @@ private class ArtistAlbumViewHolder private constructor(private val binding: Ite
* @param album The new [Album] to bind.
* @param listener An [SelectableListListener] to bind interactions to.
*/
fun bind(album: Album, listener: SelectableListListener) {
fun bind(album: Album, listener: SelectableListListener<Album>) {
listener.bind(album, this, menuButton = binding.parentMenu)
binding.parentImage.bind(album)
binding.parentName.text = album.resolveName(binding.context)
@ -235,7 +235,7 @@ private class ArtistSongViewHolder private constructor(private val binding: Item
* @param song The new [Song] to bind.
* @param listener An [SelectableListListener] to bind interactions to.
*/
fun bind(song: Song, listener: SelectableListListener) {
fun bind(song: Song, listener: SelectableListListener<Song>) {
listener.bind(song, this, menuButton = binding.songMenu)
binding.songAlbumCover.bind(song)
binding.songName.text = song.resolveName(binding.context)

View file

@ -30,6 +30,7 @@ import org.oxycblt.auxio.list.Header
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.list.SelectableListListener
import org.oxycblt.auxio.list.recycler.*
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.util.context
import org.oxycblt.auxio.util.inflater
@ -88,7 +89,7 @@ abstract class DetailAdapter(
}
/** An extended [SelectableListListener] for [DetailAdapter] implementations. */
interface Listener : SelectableListListener {
interface Listener : SelectableListListener<Music> {
// TODO: Split off into sub-listeners if a collapsing toolbar is implemented.
/**
* Called when the play button in a detail header is pressed, requesting that the current

View file

@ -33,11 +33,7 @@ import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.recycler.AlbumViewHolder
import org.oxycblt.auxio.list.recycler.SelectionIndicatorAdapter
import org.oxycblt.auxio.list.recycler.SyncListDiffer
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.*
import org.oxycblt.auxio.playback.formatDurationMs
import org.oxycblt.auxio.playback.secsToMs
import org.oxycblt.auxio.util.collectImmediately
@ -47,7 +43,7 @@ import org.oxycblt.auxio.util.collectImmediately
* @author Alexander Capehart (OxygenCobalt)
*/
class AlbumListFragment :
ListFragment<FragmentHomeListBinding>(),
ListFragment<Album, FragmentHomeListBinding>(),
FastScrollRecyclerView.Listener,
FastScrollRecyclerView.PopupProvider {
private val homeModel: HomeViewModel by activityViewModels()
@ -125,13 +121,11 @@ class AlbumListFragment :
homeModel.setFastScrolling(isFastScrolling)
}
override fun onRealClick(music: Music) {
check(music is Album) { "Unexpected datatype: ${music::class.java}" }
navModel.exploreNavigateTo(music)
override fun onRealClick(item: Album) {
navModel.exploreNavigateTo(item)
}
override fun onOpenMenu(item: Item, anchor: View) {
check(item is Album) { "Unexpected datatype: ${item::class.java}" }
override fun onOpenMenu(item: Album, anchor: View) {
openMusicMenu(anchor, R.menu.menu_album_actions, item)
}
@ -144,7 +138,7 @@ class AlbumListFragment :
* A [SelectionIndicatorAdapter] that shows a list of [Album]s using [AlbumViewHolder].
* @param listener An [SelectableListListener] to bind interactions to.
*/
private class AlbumAdapter(private val listener: SelectableListListener) :
private class AlbumAdapter(private val listener: SelectableListListener<Album>) :
SelectionIndicatorAdapter<AlbumViewHolder>() {
private val differ = SyncListDiffer(this, AlbumViewHolder.DIFF_CALLBACK)

View file

@ -32,7 +32,6 @@ import org.oxycblt.auxio.list.recycler.ArtistViewHolder
import org.oxycblt.auxio.list.recycler.SelectionIndicatorAdapter
import org.oxycblt.auxio.list.recycler.SyncListDiffer
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Sort
@ -45,7 +44,7 @@ import org.oxycblt.auxio.util.nonZeroOrNull
* @author Alexander Capehart (OxygenCobalt)
*/
class ArtistListFragment :
ListFragment<FragmentHomeListBinding>(),
ListFragment<Artist, FragmentHomeListBinding>(),
FastScrollRecyclerView.PopupProvider,
FastScrollRecyclerView.Listener {
private val homeModel: HomeViewModel by activityViewModels()
@ -100,13 +99,11 @@ class ArtistListFragment :
homeModel.setFastScrolling(isFastScrolling)
}
override fun onRealClick(music: Music) {
check(music is Artist) { "Unexpected datatype: ${music::class.java}" }
navModel.exploreNavigateTo(music)
override fun onRealClick(item: Artist) {
navModel.exploreNavigateTo(item)
}
override fun onOpenMenu(item: Item, anchor: View) {
check(item is Artist) { "Unexpected datatype: ${item::class.java}" }
override fun onOpenMenu(item: Artist, anchor: View) {
openMusicMenu(anchor, R.menu.menu_artist_actions, item)
}
@ -119,7 +116,7 @@ class ArtistListFragment :
* A [SelectionIndicatorAdapter] that shows a list of [Artist]s using [ArtistViewHolder].
* @param listener An [SelectableListListener] to bind interactions to.
*/
private class ArtistAdapter(private val listener: SelectableListListener) :
private class ArtistAdapter(private val listener: SelectableListListener<Artist>) :
SelectionIndicatorAdapter<ArtistViewHolder>() {
private val differ = SyncListDiffer(this, ArtistViewHolder.DIFF_CALLBACK)

View file

@ -32,7 +32,6 @@ import org.oxycblt.auxio.list.recycler.GenreViewHolder
import org.oxycblt.auxio.list.recycler.SelectionIndicatorAdapter
import org.oxycblt.auxio.list.recycler.SyncListDiffer
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Sort
@ -44,7 +43,7 @@ import org.oxycblt.auxio.util.collectImmediately
* @author Alexander Capehart (OxygenCobalt)
*/
class GenreListFragment :
ListFragment<FragmentHomeListBinding>(),
ListFragment<Genre, FragmentHomeListBinding>(),
FastScrollRecyclerView.PopupProvider,
FastScrollRecyclerView.Listener {
private val homeModel: HomeViewModel by activityViewModels()
@ -99,13 +98,11 @@ class GenreListFragment :
homeModel.setFastScrolling(isFastScrolling)
}
override fun onRealClick(music: Music) {
check(music is Genre) { "Unexpected datatype: ${music::class.java}" }
navModel.exploreNavigateTo(music)
override fun onRealClick(item: Genre) {
navModel.exploreNavigateTo(item)
}
override fun onOpenMenu(item: Item, anchor: View) {
check(item is Genre) { "Unexpected datatype: ${item::class.java}" }
override fun onOpenMenu(item: Genre, anchor: View) {
openMusicMenu(anchor, R.menu.menu_artist_actions, item)
}
@ -118,7 +115,7 @@ class GenreListFragment :
* A [SelectionIndicatorAdapter] that shows a list of [Genre]s using [GenreViewHolder].
* @param listener An [SelectableListListener] to bind interactions to.
*/
private class GenreAdapter(private val listener: SelectableListListener) :
private class GenreAdapter(private val listener: SelectableListListener<Genre>) :
SelectionIndicatorAdapter<GenreViewHolder>() {
private val differ = SyncListDiffer(this, GenreViewHolder.DIFF_CALLBACK)

View file

@ -33,7 +33,6 @@ import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.recycler.SelectionIndicatorAdapter
import org.oxycblt.auxio.list.recycler.SongViewHolder
import org.oxycblt.auxio.list.recycler.SyncListDiffer
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song
@ -48,7 +47,7 @@ import org.oxycblt.auxio.util.collectImmediately
* @author Alexander Capehart (OxygenCobalt)
*/
class SongListFragment :
ListFragment<FragmentHomeListBinding>(),
ListFragment<Song, FragmentHomeListBinding>(),
FastScrollRecyclerView.PopupProvider,
FastScrollRecyclerView.Listener {
private val homeModel: HomeViewModel by activityViewModels()
@ -130,18 +129,16 @@ class SongListFragment :
homeModel.setFastScrolling(isFastScrolling)
}
override fun onRealClick(music: Music) {
check(music is Song) { "Unexpected datatype: ${music::class.java}" }
override fun onRealClick(item: Song) {
when (Settings(requireContext()).libPlaybackMode) {
MusicMode.SONGS -> playbackModel.playFromAll(music)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(music)
MusicMode.ARTISTS -> playbackModel.playFromArtist(music)
MusicMode.GENRES -> playbackModel.playFromGenre(music)
MusicMode.SONGS -> playbackModel.playFromAll(item)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
MusicMode.ARTISTS -> playbackModel.playFromArtist(item)
MusicMode.GENRES -> playbackModel.playFromGenre(item)
}
}
override fun onOpenMenu(item: Item, anchor: View) {
check(item is Song) { "Unexpected datatype: ${item::class.java}" }
override fun onOpenMenu(item: Song, anchor: View) {
openMusicMenu(anchor, R.menu.menu_song_actions, item)
}
@ -158,7 +155,7 @@ class SongListFragment :
* A [SelectionIndicatorAdapter] that shows a list of [Song]s using [SongViewHolder].
* @param listener An [SelectableListListener] to bind interactions to.
*/
private class SongAdapter(private val listener: SelectableListListener) :
private class SongAdapter(private val listener: SelectableListListener<Song>) :
SelectionIndicatorAdapter<SongViewHolder>() {
private val differ = SyncListDiffer(this, SongViewHolder.DIFF_CALLBACK)

View file

@ -17,7 +17,6 @@
package org.oxycblt.auxio.home.tabs
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.util.logE
@ -26,7 +25,7 @@ import org.oxycblt.auxio.util.logE
* @param mode The type of list in the home view this instance corresponds to.
* @author Alexander Capehart (OxygenCobalt)
*/
sealed class Tab(open val mode: MusicMode) : Item {
sealed class Tab(open val mode: MusicMode) {
/**
* A visible tab. This will be visible in the home and tab configuration views.
* @param mode The type of list in the home view this instance corresponds to.

View file

@ -32,7 +32,7 @@ import org.oxycblt.auxio.util.inflater
* A [RecyclerView.Adapter] that displays an array of [Tab]s open for configuration.
* @param listener A [EditableListListener] for tab interactions.
*/
class TabAdapter(private val listener: EditableListListener) :
class TabAdapter(private val listener: EditableListListener<Tab>) :
RecyclerView.Adapter<TabViewHolder>() {
/** The current array of [Tab]s. */
var tabs = arrayOf<Tab>()
@ -93,7 +93,7 @@ class TabViewHolder private constructor(private val binding: ItemTabBinding) :
* @param listener A [EditableListListener] to bind interactions to.
*/
@SuppressLint("ClickableViewAccessibility")
fun bind(tab: Tab, listener: EditableListListener) {
fun bind(tab: Tab, listener: EditableListListener<Tab>) {
listener.bind(tab, this, dragHandle = binding.tabDragHandle)
binding.tabCheckBox.apply {
// Update the CheckBox name to align with the mode

View file

@ -26,7 +26,6 @@ import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogTabsBinding
import org.oxycblt.auxio.list.EditableListListener
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.util.logD
@ -35,7 +34,8 @@ import org.oxycblt.auxio.util.logD
* A [ViewBindingDialogFragment] that allows the user to modify the home [Tab] configuration.
* @author Alexander Capehart (OxygenCobalt)
*/
class TabCustomizeDialog : ViewBindingDialogFragment<DialogTabsBinding>(), EditableListListener {
class TabCustomizeDialog :
ViewBindingDialogFragment<DialogTabsBinding>(), EditableListListener<Tab> {
private val tabAdapter = TabAdapter(this)
private var touchHelper: ItemTouchHelper? = null
@ -81,8 +81,7 @@ class TabCustomizeDialog : ViewBindingDialogFragment<DialogTabsBinding>(), Edita
binding.tabRecycler.adapter = null
}
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
check(item is Tab) { "Unexpected datatype: ${item::class.java}" }
override fun onClick(item: Tab, viewHolder: RecyclerView.ViewHolder) {
// We will need the exact index of the tab to update on in order to
// notify the adapter of the change.
val index = tabAdapter.tabs.indexOfFirst { it.mode == item.mode }

View file

@ -37,7 +37,8 @@ import org.oxycblt.auxio.util.showToast
* A Fragment containing a selectable list.
* @author Alexander Capehart (OxygenCobalt)
*/
abstract class ListFragment<VB : ViewBinding> : SelectionFragment<VB>(), SelectableListListener {
abstract class ListFragment<in T : Music, VB : ViewBinding> :
SelectionFragment<VB>(), SelectableListListener<T> {
protected val navModel: NavigationViewModel by activityViewModels()
private var currentMenu: PopupMenu? = null
@ -50,12 +51,11 @@ abstract class ListFragment<VB : ViewBinding> : SelectionFragment<VB>(), Selecta
/**
* Called when [onClick] is called, but does not result in the item being selected. This more or
* less corresponds to an [onClick] implementation in a non-[ListFragment].
* @param music The [Music] item that was clicked.
* @param item The [T] data of the item that was clicked.
*/
abstract fun onRealClick(music: Music)
abstract fun onRealClick(item: T)
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
check(item is Music) { "Unexpected datatype: ${item::class.simpleName}" }
override fun onClick(item: T, viewHolder: RecyclerView.ViewHolder) {
if (selectionModel.selected.value.isNotEmpty()) {
// Map clicking an item to selecting an item when items are already selected.
selectionModel.select(item)
@ -65,8 +65,7 @@ abstract class ListFragment<VB : ViewBinding> : SelectionFragment<VB>(), Selecta
}
}
override fun onSelect(item: Item) {
check(item is Music) { "Unexpected datatype: ${item::class.simpleName}" }
override fun onSelect(item: T) {
selectionModel.select(item)
}

View file

@ -25,26 +25,22 @@ import androidx.recyclerview.widget.RecyclerView
* A basic listener for list interactions.
* @author Alexander Capehart (OxygenCobalt)
*/
interface ClickableListListener {
interface ClickableListListener<in T> {
/**
* Called when an [Item] in the list is clicked.
* @param item The [Item] that was clicked.
* Called when an item in the list is clicked.
* @param item The [T] item that was clicked.
* @param viewHolder The [RecyclerView.ViewHolder] of the item that was clicked.
*/
fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder)
fun onClick(item: T, viewHolder: RecyclerView.ViewHolder)
/**
* Binds this instance to a list item.
* @param item The [Item] that this list entry is bound to.
* @param item The [T] to bind this item to.
* @param viewHolder The [RecyclerView.ViewHolder] of the item that was clicked.
* @param bodyView The [View] containing the main body of the list item. Any click events on
* this [View] are routed to the listener. Defaults to the root view.
*/
fun bind(
item: Item,
viewHolder: RecyclerView.ViewHolder,
bodyView: View = viewHolder.itemView
) {
fun bind(item: T, viewHolder: RecyclerView.ViewHolder, bodyView: View = viewHolder.itemView) {
bodyView.setOnClickListener { onClick(item, viewHolder) }
}
}
@ -53,7 +49,7 @@ interface ClickableListListener {
* An extension of [ClickableListListener] that enables list editing functionality.
* @author Alexander Capehart (OxygenCobalt)
*/
interface EditableListListener : ClickableListListener {
interface EditableListListener<in T> : ClickableListListener<T> {
/**
* Called when a [RecyclerView.ViewHolder] requests that it should be dragged.
* @param viewHolder The [RecyclerView.ViewHolder] that should start being dragged.
@ -62,14 +58,14 @@ interface EditableListListener : ClickableListListener {
/**
* Binds this instance to a list item.
* @param item The [Item] that this list entry is bound to.
* @param item The [T] to bind this item to.
* @param viewHolder The [RecyclerView.ViewHolder] to bind.
* @param bodyView The [View] containing the main body of the list item. Any click events on
* this [View] are routed to the listener. Defaults to the root view.
* @param dragHandle A touchable [View]. Any drag on this view will start a drag event.
*/
fun bind(
item: Item,
item: T,
viewHolder: RecyclerView.ViewHolder,
bodyView: View = viewHolder.itemView,
dragHandle: View
@ -89,30 +85,30 @@ interface EditableListListener : ClickableListListener {
* An extension of [ClickableListListener] that enables menu and selection functionality.
* @author Alexander Capehart (OxygenCobalt)
*/
interface SelectableListListener : ClickableListListener {
interface SelectableListListener<in T> : ClickableListListener<T> {
/**
* Called when an [Item] in the list requests that a menu related to it should be opened.
* @param item The [Item] to show a menu for.
* Called when an item in the list requests that a menu related to it should be opened.
* @param item The [T] item to open a menu for.
* @param anchor The [View] to anchor the menu to.
*/
fun onOpenMenu(item: Item, anchor: View)
fun onOpenMenu(item: T, anchor: View)
/**
* Called when an [Item] in the list requests that it be selected.
* @param item The [Item] to select.
* Called when an item in the list requests that it be selected.
* @param item The [T] item to select.
*/
fun onSelect(item: Item)
fun onSelect(item: T)
/**
* Binds this instance to a list item.
* @param item The [Item] that this list entry is bound to.
* @param item The [T] to bind this item to.
* @param viewHolder The [RecyclerView.ViewHolder] to bind.
* @param bodyView The [View] containing the main body of the list item. Any click events on
* this [View] are routed to the listener. Defaults to the root view.
* @param menuButton A clickable [View]. Any click events on this [View] will open a menu.
*/
fun bind(
item: Item,
item: T,
viewHolder: RecyclerView.ViewHolder,
bodyView: View = viewHolder.itemView,
menuButton: View

View file

@ -20,6 +20,7 @@ package org.oxycblt.auxio.list.recycler
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.util.logD
/**
@ -31,7 +32,7 @@ abstract class PlayingIndicatorAdapter<VH : RecyclerView.ViewHolder> : RecyclerV
// - 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
// marked as "playing" or not.
private var currentItem: Item? = null
private var currentMusic: Music? = null
private var isPlaying = false
/**
@ -44,7 +45,7 @@ abstract class PlayingIndicatorAdapter<VH : RecyclerView.ViewHolder> : RecyclerV
override fun onBindViewHolder(holder: VH, position: Int, payloads: List<Any>) {
// Only try to update the playing indicator if the ViewHolder supports it
if (holder is ViewHolder) {
holder.updatePlayingIndicator(currentList[position] == currentItem, isPlaying)
holder.updatePlayingIndicator(currentList[position] == currentMusic, isPlaying)
}
if (payloads.isEmpty()) {
@ -55,14 +56,14 @@ abstract class PlayingIndicatorAdapter<VH : RecyclerView.ViewHolder> : RecyclerV
}
/**
* Update the currently playing item in the list.
* @param item The item currently being played, or null if it is not being played.
* @param music The [Music] currently being played, or null if it is not being played.
* @param isPlaying Whether playback is ongoing or paused.
*/
fun setPlayingItem(item: Item?, isPlaying: Boolean) {
fun setPlayingItem(music: Music?, isPlaying: Boolean) {
var updatedItem = false
if (currentItem != item) {
val oldItem = currentItem
currentItem = item
if (currentMusic != music) {
val oldItem = currentMusic
currentMusic = music
// Remove the playing indicator from the old item
if (oldItem != null) {
@ -75,8 +76,8 @@ abstract class PlayingIndicatorAdapter<VH : RecyclerView.ViewHolder> : RecyclerV
}
// Enable the playing indicator on the new item
if (item != null) {
val pos = currentList.indexOfFirst { it == item }
if (music != null) {
val pos = currentList.indexOfFirst { it == music }
if (pos > -1) {
notifyItemChanged(pos, PAYLOAD_PLAYING_INDICATOR_CHANGED)
} else {
@ -93,8 +94,8 @@ abstract class PlayingIndicatorAdapter<VH : RecyclerView.ViewHolder> : RecyclerV
// We may have already called notifyItemChanged before when checking
// if the item was being played, so in that case we don't need to
// update again here.
if (!updatedItem && item != null) {
val pos = currentList.indexOfFirst { it == item }
if (!updatedItem && music != null) {
val pos = currentList.indexOfFirst { it == music }
if (pos > -1) {
notifyItemChanged(pos, PAYLOAD_PLAYING_INDICATOR_CHANGED)
} else {

View file

@ -26,10 +26,7 @@ import org.oxycblt.auxio.databinding.ItemParentBinding
import org.oxycblt.auxio.databinding.ItemSongBinding
import org.oxycblt.auxio.list.Header
import org.oxycblt.auxio.list.SelectableListListener
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.*
import org.oxycblt.auxio.util.context
import org.oxycblt.auxio.util.getPlural
import org.oxycblt.auxio.util.inflater
@ -45,7 +42,7 @@ class SongViewHolder private constructor(private val binding: ItemSongBinding) :
* @param song The new [Song] to bind.
* @param listener An [SelectableListListener] to bind interactions to.
*/
fun bind(song: Song, listener: SelectableListListener) {
fun bind(song: Song, listener: SelectableListListener<Song>) {
listener.bind(song, this, menuButton = binding.songMenu)
binding.songAlbumCover.bind(song)
binding.songName.text = song.resolveName(binding.context)
@ -92,7 +89,7 @@ class AlbumViewHolder private constructor(private val binding: ItemParentBinding
* @param album The new [Album] to bind.
* @param listener An [SelectableListListener] to bind interactions to.
*/
fun bind(album: Album, listener: SelectableListListener) {
fun bind(album: Album, listener: SelectableListListener<Album>) {
listener.bind(album, this, menuButton = binding.parentMenu)
binding.parentImage.bind(album)
binding.parentName.text = album.resolveName(binding.context)
@ -141,7 +138,7 @@ class ArtistViewHolder private constructor(private val binding: ItemParentBindin
* @param artist The new [Artist] to bind.
* @param listener An [SelectableListListener] to bind interactions to.
*/
fun bind(artist: Artist, listener: SelectableListListener) {
fun bind(artist: Artist, listener: SelectableListListener<Artist>) {
listener.bind(artist, this, menuButton = binding.parentMenu)
binding.parentImage.bind(artist)
binding.parentName.text = artist.resolveName(binding.context)
@ -200,7 +197,7 @@ class GenreViewHolder private constructor(private val binding: ItemParentBinding
* @param genre The new [Genre] to bind.
* @param listener An [SelectableListListener] to bind interactions to.
*/
fun bind(genre: Genre, listener: SelectableListListener) {
fun bind(genre: Genre, listener: SelectableListListener<Genre>) {
listener.bind(genre, this, menuButton = binding.parentMenu)
binding.parentImage.bind(genre)
binding.parentName.text = genre.resolveName(binding.context)

View file

@ -85,9 +85,10 @@ class MusicDirsDialog :
val dialog = it as AlertDialog
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)?.setOnClickListener {
logD("Opening launcher")
val launcher = requireNotNull(openDocumentTreeLauncher) {
"Document tree launcher was not available"
}
val launcher =
requireNotNull(openDocumentTreeLauncher) {
"Document tree launcher was not available"
}
try {
launcher.launch(null)

View file

@ -32,7 +32,7 @@ import org.oxycblt.auxio.util.inflater
* @param listener A [ClickableListListener] to bind interactions to.
* @author OxygenCobalt.
*/
class ArtistChoiceAdapter(private val listener: ClickableListListener) :
class ArtistChoiceAdapter(private val listener: ClickableListListener<Artist>) :
RecyclerView.Adapter<ArtistChoiceViewHolder>() {
private var artists = listOf<Artist>()
@ -67,7 +67,7 @@ class ArtistChoiceViewHolder(private val binding: ItemPickerChoiceBinding) :
* @param artist The new [Artist] to bind.
* @param listener A [ClickableListListener] to bind interactions to.
*/
fun bind(artist: Artist, listener: ClickableListListener) {
fun bind(artist: Artist, listener: ClickableListListener<Artist>) {
listener.bind(artist, this)
binding.pickerImage.bind(artist)
binding.pickerName.text = artist.resolveName(binding.context)

View file

@ -22,7 +22,6 @@ import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.ui.NavigationViewModel
@ -41,9 +40,8 @@ class ArtistNavigationPickerDialog : ArtistPickerDialog() {
super.onBindingCreated(binding, savedInstanceState)
}
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
override fun onClick(item: Artist, viewHolder: RecyclerView.ViewHolder) {
super.onClick(item, viewHolder)
check(item is Artist) { "Unexpected datatype: ${item::class.simpleName}" }
// User made a choice, navigate to it.
navModel.exploreNavigateTo(item)
}

View file

@ -26,7 +26,6 @@ import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
import org.oxycblt.auxio.list.ClickableListListener
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.util.collectImmediately
@ -38,7 +37,7 @@ import org.oxycblt.auxio.util.collectImmediately
* @author Alexander Capehart (OxygenCobalt)
*/
abstract class ArtistPickerDialog :
ViewBindingDialogFragment<DialogMusicPickerBinding>(), ClickableListListener {
ViewBindingDialogFragment<DialogMusicPickerBinding>(), ClickableListListener<Artist> {
protected val pickerModel: PickerViewModel by viewModels()
// Okay to leak this since the Listener will not be called until after initialization.
private val artistAdapter = ArtistChoiceAdapter(@Suppress("LeakingThis") this)
@ -68,7 +67,7 @@ abstract class ArtistPickerDialog :
binding.pickerRecycler.adapter = null
}
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
override fun onClick(item: Artist, viewHolder: RecyclerView.ViewHolder) {
findNavController().navigateUp()
}
}

View file

@ -21,11 +21,12 @@ import android.os.Bundle
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.util.androidActivityViewModels
import org.oxycblt.auxio.util.requireIs
import org.oxycblt.auxio.util.unlikelyToBeNull
/**
* An [ArtistPickerDialog] intended for when [Artist] playback is ambiguous.
@ -42,12 +43,10 @@ class ArtistPlaybackPickerDialog : ArtistPickerDialog() {
super.onBindingCreated(binding, savedInstanceState)
}
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
override fun onClick(item: Artist, viewHolder: RecyclerView.ViewHolder) {
super.onClick(item, viewHolder)
// User made a choice, play the given song from that artist.
check(item is Artist) { "Unexpected datatype: ${item::class.simpleName}" }
val song = pickerModel.currentItem.value
check(song is Song) { "Unexpected datatype: ${item::class.simpleName}" }
val song = requireIs<Song>(unlikelyToBeNull(pickerModel.currentItem.value))
playbackModel.playFromArtist(song, item)
}
}

View file

@ -32,7 +32,7 @@ import org.oxycblt.auxio.util.inflater
* @param listener A [ClickableListListener] to bind interactions to.
* @author OxygenCobalt.
*/
class GenreChoiceAdapter(private val listener: ClickableListListener) :
class GenreChoiceAdapter(private val listener: ClickableListListener<Genre>) :
RecyclerView.Adapter<GenreChoiceViewHolder>() {
private var genres = listOf<Genre>()
@ -67,7 +67,7 @@ class GenreChoiceViewHolder(private val binding: ItemPickerChoiceBinding) :
* @param genre The new [Genre] to bind.
* @param listener A [ClickableListListener] to bind interactions to.
*/
fun bind(genre: Genre, listener: ClickableListListener) {
fun bind(genre: Genre, listener: ClickableListListener<Genre>) {
listener.bind(genre, this)
binding.pickerImage.bind(genre)
binding.pickerName.text = genre.resolveName(binding.context)

View file

@ -27,20 +27,21 @@ import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
import org.oxycblt.auxio.list.ClickableListListener
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.util.androidActivityViewModels
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.requireIs
import org.oxycblt.auxio.util.unlikelyToBeNull
/**
* A picker [ViewBindingDialogFragment] intended for when [Genre] playback is ambiguous.
* @author Alexander Capehart (OxygenCobalt)
*/
class GenrePlaybackPickerDialog :
ViewBindingDialogFragment<DialogMusicPickerBinding>(), ClickableListListener {
ViewBindingDialogFragment<DialogMusicPickerBinding>(), ClickableListListener<Genre> {
private val pickerModel: PickerViewModel by viewModels()
private val playbackModel: PlaybackViewModel by androidActivityViewModels()
// Information about what Song to show choices for is initially within the navigation arguments
@ -75,11 +76,9 @@ class GenrePlaybackPickerDialog :
binding.pickerRecycler.adapter = null
}
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
override fun onClick(item: Genre, viewHolder: RecyclerView.ViewHolder) {
// User made a choice, play the given song from that genre.
check(item is Genre) { "Unexpected datatype: ${item::class.simpleName}" }
val song = pickerModel.currentItem.value
check(song is Song) { "Unexpected datatype: ${item::class.simpleName}" }
val song = requireIs<Song>(unlikelyToBeNull(pickerModel.currentItem.value))
playbackModel.playFromGenre(song, item)
}
}

View file

@ -41,7 +41,7 @@ import org.oxycblt.auxio.util.inflater
* @param listener A [EditableListListener] to bind interactions to.
* @author Alexander Capehart (OxygenCobalt)
*/
class QueueAdapter(private val listener: EditableListListener) :
class QueueAdapter(private val listener: EditableListListener<Song>) :
RecyclerView.Adapter<QueueSongViewHolder>() {
private var differ = SyncListDiffer(this, QueueSongViewHolder.DIFF_CALLBACK)
// Since PlayingIndicator adapter relies on an item value, we cannot use it for this
@ -178,7 +178,7 @@ class QueueSongViewHolder private constructor(private val binding: ItemQueueSong
* @param listener A [EditableListListener] to bind interactions to.
*/
@SuppressLint("ClickableViewAccessibility")
fun bind(song: Song, listener: EditableListListener) {
fun bind(song: Song, listener: EditableListListener<Song>) {
listener.bind(song, this, bodyView, binding.songDragHandle)
binding.songAlbumCover.bind(song)
binding.songName.text = song.resolveName(binding.context)

View file

@ -27,7 +27,6 @@ import androidx.recyclerview.widget.RecyclerView
import kotlin.math.min
import org.oxycblt.auxio.databinding.FragmentQueueBinding
import org.oxycblt.auxio.list.EditableListListener
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.ViewBindingFragment
@ -39,7 +38,7 @@ import org.oxycblt.auxio.util.logD
* A [ViewBindingFragment] that displays an editable queue.
* @author Alexander Capehart (OxygenCobalt)
*/
class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), EditableListListener {
class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), EditableListListener<Song> {
private val queueModel: QueueViewModel by activityViewModels()
private val playbackModel: PlaybackViewModel by androidActivityViewModels()
private val queueAdapter = QueueAdapter(this)
@ -81,7 +80,7 @@ class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), EditableListL
binding.queueRecycler.adapter = null
}
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
override fun onClick(item: Song, viewHolder: RecyclerView.ViewHolder) {
queueModel.goto(viewHolder.bindingAdapterPosition)
}

View file

@ -127,11 +127,9 @@ class ReplayGainAudioProcessor(private val context: Context) :
// User wants track gain to be preferred. Default to album gain only if
// there is no track gain.
ReplayGainMode.TRACK -> gain.track == 0f
// User wants album gain to be preferred. Default to track gain only if
// here is no album gain.
ReplayGainMode.ALBUM -> gain.album != 0f
// User wants album gain to be used when in an album, track gain otherwise.
ReplayGainMode.DYNAMIC ->
playbackManager.parent is Album &&

View file

@ -22,17 +22,14 @@ import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.list.*
import org.oxycblt.auxio.list.recycler.*
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.*
/**
* An adapter that displays search results.
* @param listener An [SelectableListListener] to bind interactions to.
* @author Alexander Capehart (OxygenCobalt)
*/
class SearchAdapter(private val listener: SelectableListListener) :
class SearchAdapter(private val listener: SelectableListListener<Music>) :
SelectionIndicatorAdapter<RecyclerView.ViewHolder>(), AuxioRecyclerView.SpanSizeLookup {
private val differ = AsyncListDiffer(this, DIFF_CALLBACK)

View file

@ -50,7 +50,7 @@ import org.oxycblt.auxio.util.*
*
* @author Alexander Capehart (OxygenCobalt)
*/
class SearchFragment : ListFragment<FragmentSearchBinding>() {
class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
private val searchModel: SearchViewModel by androidViewModels()
private val searchAdapter = SearchAdapter(this)
private var imm: InputMethodManager? = null
@ -134,26 +134,25 @@ class SearchFragment : ListFragment<FragmentSearchBinding>() {
return false
}
override fun onRealClick(music: Music) {
when (music) {
override fun onRealClick(item: Music) {
when (item) {
is Song ->
when (Settings(requireContext()).libPlaybackMode) {
MusicMode.SONGS -> playbackModel.playFromAll(music)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(music)
MusicMode.ARTISTS -> playbackModel.playFromArtist(music)
MusicMode.GENRES -> playbackModel.playFromGenre(music)
MusicMode.SONGS -> playbackModel.playFromAll(item)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
MusicMode.ARTISTS -> playbackModel.playFromArtist(item)
MusicMode.GENRES -> playbackModel.playFromGenre(item)
}
is MusicParent -> navModel.exploreNavigateTo(music)
is MusicParent -> navModel.exploreNavigateTo(item)
}
}
override fun onOpenMenu(item: Item, anchor: View) {
override fun onOpenMenu(item: Music, anchor: View) {
when (item) {
is Song -> openMusicMenu(anchor, R.menu.menu_song_actions, item)
is Album -> openMusicMenu(anchor, R.menu.menu_album_actions, item)
is Artist -> openMusicMenu(anchor, R.menu.menu_artist_actions, item)
is Genre -> openMusicMenu(anchor, R.menu.menu_artist_actions, item)
else -> logW("Unexpected datatype when opening menu: ${item::class.java}")
}
}

View file

@ -79,15 +79,15 @@ class NavigationViewModel : ViewModel() {
/**
* Navigate to a given [Music] item. Will do nothing if already navigating.
* @param item The [Music] to navigate to.
* @param music The [Music] to navigate to.
*/
fun exploreNavigateTo(item: Music) {
fun exploreNavigateTo(music: Music) {
if (_exploreNavigationItem.value != null) {
logD("Already navigating, not doing explore action")
return
}
logD("Navigating to ${item.rawName}")
_exploreNavigationItem.value = item
logD("Navigating to ${music.rawName}")
_exploreNavigationItem.value = music
}
/**

View file

@ -19,7 +19,6 @@ package org.oxycblt.auxio.ui.accent
import android.os.Build
import org.oxycblt.auxio.R
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.util.logW
private val ACCENT_NAMES =
@ -112,7 +111,7 @@ private val ACCENT_PRIMARY_COLORS =
* @param index The unique number for this particular accent.
* @author Alexander Capehart (OxygenCobalt)
*/
class Accent private constructor(val index: Int) : Item {
class Accent private constructor(val index: Int) {
/** The name of this [Accent]. */
val name: Int
get() = ACCENT_NAMES[index]

View file

@ -33,7 +33,7 @@ import org.oxycblt.auxio.util.inflater
* @param listener A [ClickableListListener] to bind interactions to.
* @author Alexander Capehart (OxygenCobalt)
*/
class AccentAdapter(private val listener: ClickableListListener) :
class AccentAdapter(private val listener: ClickableListListener<Accent>) :
RecyclerView.Adapter<AccentViewHolder>() {
/** The currently selected [Accent]. */
var selectedAccent: Accent? = null
@ -93,7 +93,7 @@ class AccentViewHolder private constructor(private val binding: ItemAccentBindin
* @param accent The new [Accent] to bind.
* @param listener A [ClickableListListener] to bind interactions to.
*/
fun bind(accent: Accent, listener: ClickableListListener) {
fun bind(accent: Accent, listener: ClickableListListener<Accent>) {
listener.bind(accent, this, binding.accent)
binding.accent.apply {
// Add a Tooltip based on the content description so that the purpose of this

View file

@ -25,7 +25,6 @@ import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogAccentBinding
import org.oxycblt.auxio.list.ClickableListListener
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.util.logD
@ -36,7 +35,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
* @author Alexander Capehart (OxygenCobalt)
*/
class AccentCustomizeDialog :
ViewBindingDialogFragment<DialogAccentBinding>(), ClickableListListener {
ViewBindingDialogFragment<DialogAccentBinding>(), ClickableListListener<Accent> {
private var accentAdapter = AccentAdapter(this)
override fun onCreateBinding(inflater: LayoutInflater) = DialogAccentBinding.inflate(inflater)
@ -80,8 +79,7 @@ class AccentCustomizeDialog :
binding.accentRecycler.adapter = null
}
override fun onClick(item: Item, viewHolder: RecyclerView.ViewHolder) {
check(item is Accent) { "Unexpected datatype: ${item::class.java}" }
override fun onClick(item: Accent, viewHolder: RecyclerView.ViewHolder) {
accentAdapter.setSelectedAccent(item)
}

View file

@ -33,6 +33,17 @@ fun <T> unlikelyToBeNull(value: T?) =
value!!
}
/**
* Require that the given data is a specific type [T].
* @param data The data to check.
* @return A data casted to [T].
* @throws IllegalStateException If the data cannot be casted to [T].
*/
inline fun <reified T> requireIs(data: Any): T {
check(data is T) { "Unexpected datatype: ${data::class.simpleName}" }
return data
}
/**
* Aliases a check to ensure that the given number is non-zero.
* @return The given number if it's non-zero, null otherwise.