Replace ClickListener with lambdas

Replace ClickListener with direct lambdas, so that adapters with multiple datatypes dont have to duplicate ClickListener for each type.
This commit is contained in:
OxygenCobalt 2020-10-13 11:09:48 -06:00
parent a72ab10007
commit d8f40bfd27
24 changed files with 106 additions and 147 deletions

View file

@ -15,7 +15,6 @@ import com.google.android.material.tabs.TabLayoutMediator
import org.oxycblt.auxio.databinding.FragmentMainBinding import org.oxycblt.auxio.databinding.FragmentMainBinding
import org.oxycblt.auxio.library.LibraryFragment import org.oxycblt.auxio.library.LibraryFragment
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.songs.SongsFragment import org.oxycblt.auxio.songs.SongsFragment
import org.oxycblt.auxio.theme.accent import org.oxycblt.auxio.theme.accent
import org.oxycblt.auxio.theme.getInactiveAlpha import org.oxycblt.auxio.theme.getInactiveAlpha
@ -27,8 +26,6 @@ class MainFragment : Fragment() {
MusicViewModel.Factory(requireActivity().application) MusicViewModel.Factory(requireActivity().application)
} }
private val playbackModel: PlaybackViewModel by activityViewModels()
private val shownFragments = listOf(0, 1) private val shownFragments = listOf(0, 1)
private val tabIcons = listOf( private val tabIcons = listOf(
R.drawable.ic_library, R.drawable.ic_library,
@ -91,8 +88,6 @@ class MainFragment : Fragment() {
} }
) )
// --- VIEWMODEL SETUP ---
Log.d(this::class.simpleName, "Fragment Created.") Log.d(this::class.simpleName, "Fragment Created.")
return binding.root return binding.root

View file

@ -14,7 +14,6 @@ import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding
import org.oxycblt.auxio.detail.adapters.DetailSongAdapter import org.oxycblt.auxio.detail.adapters.DetailSongAdapter
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.disable import org.oxycblt.auxio.theme.disable
@ -45,11 +44,9 @@ class AlbumDetailFragment : Fragment() {
) )
} }
val songAdapter = DetailSongAdapter( val songAdapter = DetailSongAdapter {
ClickListener {
playbackModel.updateSong(it) playbackModel.updateSong(it)
} }
)
// --- UI SETUP --- // --- UI SETUP ---

View file

@ -12,7 +12,6 @@ import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding
import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.disable import org.oxycblt.auxio.theme.disable
@ -45,8 +44,7 @@ class ArtistDetailFragment : Fragment() {
) )
} }
val albumAdapter = DetailAlbumAdapter( val albumAdapter = DetailAlbumAdapter {
ClickListener {
if (!detailModel.isNavigating) { if (!detailModel.isNavigating) {
detailModel.updateNavigationStatus(true) detailModel.updateNavigationStatus(true)
@ -55,7 +53,6 @@ class ArtistDetailFragment : Fragment() {
) )
} }
} }
)
// --- UI SETUP --- // --- UI SETUP ---

View file

@ -12,7 +12,6 @@ import org.oxycblt.auxio.recycler.SortMode
// TODO: // TODO:
// - Implement a system where the Toolbar will update with the info [And Media Controls] when // - Implement a system where the Toolbar will update with the info [And Media Controls] when
// the main info of the detail fragment is removed. // the main info of the detail fragment is removed.
// - Implement shared element transitions [If that is even possible]
class DetailViewModel : ViewModel() { class DetailViewModel : ViewModel() {
private var mIsNavigating = false private var mIsNavigating = false
val isNavigating: Boolean get() = mIsNavigating val isNavigating: Boolean get() = mIsNavigating
@ -39,24 +38,8 @@ class DetailViewModel : ViewModel() {
private val mNavToParent = MutableLiveData<Boolean>() private val mNavToParent = MutableLiveData<Boolean>()
val navToParent: LiveData<Boolean> get() = mNavToParent val navToParent: LiveData<Boolean> get() = mNavToParent
fun updateGenre(genre: Genre) { fun updateNavigationStatus(value: Boolean) {
mCurrentGenre.value = genre mIsNavigating = value
}
fun updateArtist(artist: Artist) {
mCurrentArtist.value = artist
}
fun updateAlbum(album: Album) {
mCurrentAlbum.value = album
}
fun doNavToParent() {
mNavToParent.value = true
}
fun doneWithNavToParent() {
mNavToParent.value = false
} }
fun incrementGenreSortMode() { fun incrementGenreSortMode() {
@ -88,7 +71,23 @@ class DetailViewModel : ViewModel() {
} }
} }
fun updateNavigationStatus(value: Boolean) { fun updateGenre(genre: Genre) {
mIsNavigating = value mCurrentGenre.value = genre
}
fun updateArtist(artist: Artist) {
mCurrentArtist.value = artist
}
fun updateAlbum(album: Album) {
mCurrentAlbum.value = album
}
fun doNavToParent() {
mNavToParent.value = true
}
fun doneWithNavToParent() {
mNavToParent.value = false
} }
} }

View file

@ -12,7 +12,6 @@ import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.databinding.FragmentGenreDetailBinding import org.oxycblt.auxio.databinding.FragmentGenreDetailBinding
import org.oxycblt.auxio.detail.adapters.DetailArtistAdapter import org.oxycblt.auxio.detail.adapters.DetailArtistAdapter
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.disable import org.oxycblt.auxio.theme.disable
@ -42,8 +41,7 @@ class GenreDetailFragment : Fragment() {
) )
} }
val artistAdapter = DetailArtistAdapter( val artistAdapter = DetailArtistAdapter {
ClickListener {
if (!detailModel.isNavigating) { if (!detailModel.isNavigating) {
detailModel.updateNavigationStatus(true) detailModel.updateNavigationStatus(true)
@ -52,7 +50,6 @@ class GenreDetailFragment : Fragment() {
) )
} }
} }
)
// --- UI SETUP --- // --- UI SETUP ---

View file

@ -5,12 +5,11 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import org.oxycblt.auxio.databinding.ItemArtistAlbumBinding import org.oxycblt.auxio.databinding.ItemArtistAlbumBinding
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.recycler.DiffCallback import org.oxycblt.auxio.recycler.DiffCallback
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
class DetailAlbumAdapter( class DetailAlbumAdapter(
private val listener: ClickListener<Album> private val doOnClick: (Album) -> Unit
) : ListAdapter<Album, DetailAlbumAdapter.ViewHolder>(DiffCallback()) { ) : ListAdapter<Album, DetailAlbumAdapter.ViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@ -26,7 +25,7 @@ class DetailAlbumAdapter(
// Generic ViewHolder for a detail album // Generic ViewHolder for a detail album
inner class ViewHolder( inner class ViewHolder(
private val binding: ItemArtistAlbumBinding private val binding: ItemArtistAlbumBinding
) : BaseViewHolder<Album>(binding, listener) { ) : BaseViewHolder<Album>(binding, doOnClick) {
override fun onBind(model: Album) { override fun onBind(model: Album) {
binding.album = model binding.album = model

View file

@ -5,12 +5,11 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import org.oxycblt.auxio.databinding.ItemGenreArtistBinding import org.oxycblt.auxio.databinding.ItemGenreArtistBinding
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.recycler.DiffCallback import org.oxycblt.auxio.recycler.DiffCallback
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
class DetailArtistAdapter( class DetailArtistAdapter(
private val listener: ClickListener<Artist> private val doOnClick: (Artist) -> Unit
) : ListAdapter<Artist, DetailArtistAdapter.ViewHolder>(DiffCallback()) { ) : ListAdapter<Artist, DetailArtistAdapter.ViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@ -26,7 +25,7 @@ class DetailArtistAdapter(
// Generic ViewHolder for an album // Generic ViewHolder for an album
inner class ViewHolder( inner class ViewHolder(
private val binding: ItemGenreArtistBinding private val binding: ItemGenreArtistBinding
) : BaseViewHolder<Artist>(binding, listener) { ) : BaseViewHolder<Artist>(binding, doOnClick) {
override fun onBind(model: Artist) { override fun onBind(model: Artist) {
binding.artist = model binding.artist = model

View file

@ -5,12 +5,11 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.recycler.DiffCallback import org.oxycblt.auxio.recycler.DiffCallback
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
class DetailSongAdapter( class DetailSongAdapter(
private val listener: ClickListener<Song> private val doOnClick: (Song) -> Unit
) : ListAdapter<Song, DetailSongAdapter.ViewHolder>(DiffCallback()) { ) : ListAdapter<Song, DetailSongAdapter.ViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@ -26,7 +25,7 @@ class DetailSongAdapter(
// Generic ViewHolder for a song // Generic ViewHolder for a song
inner class ViewHolder( inner class ViewHolder(
private val binding: ItemAlbumSongBinding private val binding: ItemAlbumSongBinding
) : BaseViewHolder<Song>(binding, listener) { ) : BaseViewHolder<Song>(binding, doOnClick) {
override fun onBind(model: Song) { override fun onBind(model: Song) {
binding.song = model binding.song = model

View file

@ -86,6 +86,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
val searchView = item.actionView as SearchView val searchView = item.actionView as SearchView
searchView.queryHint = getString(R.string.hint_search_library) searchView.queryHint = getString(R.string.hint_search_library)
searchView.maxWidth = Int.MAX_VALUE
searchView.setOnQueryTextListener(this@LibraryFragment) searchView.setOnQueryTextListener(this@LibraryFragment)
searchView.setOnQueryTextFocusChangeListener { _, hasFocus -> searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
libraryModel.updateSearchFocusStatus(hasFocus) libraryModel.updateSearchFocusStatus(hasFocus)

View file

@ -6,7 +6,6 @@ import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.recycler.ShowMode import org.oxycblt.auxio.recycler.ShowMode
import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder
import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder
@ -20,12 +19,6 @@ class LibraryAdapter(
private val doOnClick: (BaseModel) -> Unit private val doOnClick: (BaseModel) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
// Create separate listeners for each type, as ClickListeners can be converted
// to a type-specific form.
private val genreListener = ClickListener<Genre> { doOnClick(it) }
private val artistListener = ClickListener<Artist> { doOnClick(it) }
private val albumListener = ClickListener<Album> { doOnClick(it) }
private var data: List<BaseModel> private var data: List<BaseModel>
init { init {
@ -44,10 +37,10 @@ class LibraryAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
// Return a different View Holder depending on the show type // Return a different View Holder depending on the show type
return when (showMode) { return when (showMode) {
ShowMode.SHOW_GENRES -> GenreViewHolder.from(parent.context, genreListener) ShowMode.SHOW_GENRES -> GenreViewHolder.from(parent.context, doOnClick)
ShowMode.SHOW_ARTISTS -> ArtistViewHolder.from(parent.context, artistListener) ShowMode.SHOW_ARTISTS -> ArtistViewHolder.from(parent.context, doOnClick)
ShowMode.SHOW_ALBUMS -> AlbumViewHolder.from(parent.context, albumListener) ShowMode.SHOW_ALBUMS -> AlbumViewHolder.from(parent.context, doOnClick)
else -> ArtistViewHolder.from(parent.context, artistListener) else -> ArtistViewHolder.from(parent.context, doOnClick)
} }
} }

View file

@ -9,7 +9,6 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Header import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.recycler.DiffCallback import org.oxycblt.auxio.recycler.DiffCallback
import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder
import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder
@ -21,14 +20,6 @@ class SearchAdapter(
private val doOnClick: (BaseModel) -> Unit private val doOnClick: (BaseModel) -> Unit
) : ListAdapter<BaseModel, RecyclerView.ViewHolder>(DiffCallback<BaseModel>()) { ) : ListAdapter<BaseModel, RecyclerView.ViewHolder>(DiffCallback<BaseModel>()) {
// Create separate listeners for each type, as a BaseModel ClickListener cant be
// casted to a ClickListener of a class that inherits BaseModel.
// FIXME: Maybe there's a way for this to be improved?
private val genreListener = ClickListener<Genre> { doOnClick(it) }
private val artistListener = ClickListener<Artist> { doOnClick(it) }
private val albumListener = ClickListener<Album> { doOnClick(it) }
private val songListener = ClickListener<Song> { doOnClick(it) }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
return when (getItem(position)) { return when (getItem(position)) {
is Genre -> GenreViewHolder.ITEM_TYPE is Genre -> GenreViewHolder.ITEM_TYPE
@ -41,10 +32,10 @@ class SearchAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) { return when (viewType) {
GenreViewHolder.ITEM_TYPE -> GenreViewHolder.from(parent.context, genreListener) GenreViewHolder.ITEM_TYPE -> GenreViewHolder.from(parent.context, doOnClick)
ArtistViewHolder.ITEM_TYPE -> ArtistViewHolder.from(parent.context, artistListener) ArtistViewHolder.ITEM_TYPE -> ArtistViewHolder.from(parent.context, doOnClick)
AlbumViewHolder.ITEM_TYPE -> AlbumViewHolder.from(parent.context, albumListener) AlbumViewHolder.ITEM_TYPE -> AlbumViewHolder.from(parent.context, doOnClick)
SongViewHolder.ITEM_TYPE -> SongViewHolder.from(parent.context, songListener) SongViewHolder.ITEM_TYPE -> SongViewHolder.from(parent.context, doOnClick)
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context) HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
else -> HeaderViewHolder.from(parent.context) else -> HeaderViewHolder.from(parent.context)

View file

@ -14,7 +14,6 @@ sealed class BaseModel {
} }
// Song // Song
// TODO: Maybe move durations to a solely-millis system
data class Song( data class Song(
override val id: Long = -1, override val id: Long = -1,
override var name: String, override var name: String,

View file

@ -46,6 +46,8 @@ class MosaicFetcher(private val context: Context) : Fetcher<List<Uri>> {
if (streams.size < 4) { if (streams.size < 4) {
streams.forEach { it.close() } streams.forEach { it.close() }
// FIXME: What if all the streams fail?
return SourceResult( return SourceResult(
source = streams[0].source().buffer(), source = streams[0].source().buffer(),
mimeType = context.contentResolver.getType(data[0]), mimeType = context.contentResolver.getType(data[0]),

View file

@ -66,7 +66,7 @@ class MusicLoader(
private fun loadGenres() { private fun loadGenres() {
Log.d(this::class.simpleName, "Starting genre search...") Log.d(this::class.simpleName, "Starting genre search...")
// First, get a ui_cursor for every genre in the android system // First, get a cursor for every genre in the android system
genreCursor = resolver.query( genreCursor = resolver.query(
Genres.EXTERNAL_CONTENT_URI, Genres.EXTERNAL_CONTENT_URI,
arrayOf( arrayOf(

View file

@ -35,7 +35,7 @@ class MusicSorter(
for (album in albums) { for (album in albums) {
// Find all songs that match the current album ID to prevent any bugs w/comparing names. // Find all songs that match the current album ID to prevent any bugs w/comparing names.
// This cant be done with artists/genres sadly. // This cant be done anywhere else sadly. Blame the genre system.
val albumSongs = songs.filter { it.albumId == album.id } val albumSongs = songs.filter { it.albumId == album.id }
// Then add them to the album // Then add them to the album
@ -50,7 +50,6 @@ class MusicSorter(
// Any remaining songs will be added to an unknown album // Any remaining songs will be added to an unknown album
if (unknownSongs.size > 0) { if (unknownSongs.size > 0) {
// Reuse an existing unknown album if one is found
val unknownAlbum = Album( val unknownAlbum = Album(
name = albumPlaceholder, name = albumPlaceholder,
artistName = artistPlaceholder artistName = artistPlaceholder

View file

@ -21,6 +21,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
private val playbackModel: PlaybackViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels()
// TODO: Implement media controls // TODO: Implement media controls
// TODO: Make exit icon bigger
// TODO: Implement nav to artists/albums // TODO: Implement nav to artists/albums
// TODO: Possibly implement a trackbar with a spectrum shown as well. // TODO: Possibly implement a trackbar with a spectrum shown as well.
override fun onCreateView( override fun onCreateView(
@ -53,6 +54,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
// Make marquee scroll work // Make marquee scroll work
binding.playbackSong.isSelected = true binding.playbackSong.isSelected = true
binding.playbackSeekBar.setOnSeekBarChangeListener(this) binding.playbackSeekBar.setOnSeekBarChangeListener(this)
// --- VIEWMODEL SETUP -- // --- VIEWMODEL SETUP --
@ -103,8 +105,10 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
// Seeking callbacks // Seeking callbacks
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
playbackModel.updateCurrentDurationWithProgress(progress) playbackModel.updateCurrentDurationWithProgress(progress)
} }
}
override fun onStartTrackingTouch(seekBar: SeekBar) { override fun onStartTrackingTouch(seekBar: SeekBar) {
playbackModel.setSeekingStatus(true) playbackModel.setSeekingStatus(true)

View file

@ -3,9 +3,6 @@ package org.oxycblt.auxio.recycler
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.BaseModel
// RecyclerView click listener
class ClickListener<T : BaseModel>(val onClick: (T) -> Unit)
// Base Diff callback // Base Diff callback
class DiffCallback<T : BaseModel> : DiffUtil.ItemCallback<T>() { class DiffCallback<T : BaseModel> : DiffUtil.ItemCallback<T>() {
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean { override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {

View file

@ -3,13 +3,11 @@ package org.oxycblt.auxio.recycler.viewholders
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.recycler.ClickListener
// ViewHolder abstraction that automates some of the things that are common for all ViewHolders. // ViewHolder abstraction that automates some of the things that are common for all ViewHolders.
// TODO: Implement some kind of abstraction for BaseViewHolder.from()
abstract class BaseViewHolder<T : BaseModel>( abstract class BaseViewHolder<T : BaseModel>(
private val baseBinding: ViewDataBinding, private val baseBinding: ViewDataBinding,
protected val listener: ClickListener<T>? private val doOnClick: ((T) -> Unit)?
) : RecyclerView.ViewHolder(baseBinding.root) { ) : RecyclerView.ViewHolder(baseBinding.root) {
init { init {
baseBinding.root.layoutParams = RecyclerView.LayoutParams( baseBinding.root.layoutParams = RecyclerView.LayoutParams(
@ -18,10 +16,8 @@ abstract class BaseViewHolder<T : BaseModel>(
} }
fun bind(model: T) { fun bind(model: T) {
if (listener != null) { doOnClick?.let {
baseBinding.root.setOnClickListener { baseBinding.root.setOnClickListener { it(model) }
listener.onClick(model)
}
} }
onBind(model) onBind(model)

View file

@ -12,15 +12,14 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Header import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.recycler.ClickListener
// Shared ViewHolders for each ViewModel, providing basic information // Shared ViewHolders for each ViewModel, providing basic information
// All new instances should be created with from() instead of direct instantiation. // All new instances should be created with from() instead of direct instantiation.
class GenreViewHolder private constructor( class GenreViewHolder private constructor(
listener: ClickListener<Genre>, doOnClick: (Genre) -> Unit,
private val binding: ItemGenreBinding private val binding: ItemGenreBinding
) : BaseViewHolder<Genre>(binding, listener) { ) : BaseViewHolder<Genre>(binding, doOnClick) {
override fun onBind(model: Genre) { override fun onBind(model: Genre) {
binding.genre = model binding.genre = model
@ -30,9 +29,9 @@ class GenreViewHolder private constructor(
companion object { companion object {
const val ITEM_TYPE = 10 const val ITEM_TYPE = 10
fun from(context: Context, listener: ClickListener<Genre>): GenreViewHolder { fun from(context: Context, doOnClick: (Genre) -> Unit): GenreViewHolder {
return GenreViewHolder( return GenreViewHolder(
ClickListener { listener.onClick(it) }, doOnClick,
ItemGenreBinding.inflate(LayoutInflater.from(context)) ItemGenreBinding.inflate(LayoutInflater.from(context))
) )
} }
@ -40,9 +39,9 @@ class GenreViewHolder private constructor(
} }
class ArtistViewHolder private constructor( class ArtistViewHolder private constructor(
listener: ClickListener<Artist>, doOnClick: (Artist) -> Unit,
private val binding: ItemArtistBinding private val binding: ItemArtistBinding
) : BaseViewHolder<Artist>(binding, listener) { ) : BaseViewHolder<Artist>(binding, doOnClick) {
override fun onBind(model: Artist) { override fun onBind(model: Artist) {
binding.artist = model binding.artist = model
@ -52,9 +51,9 @@ class ArtistViewHolder private constructor(
companion object { companion object {
const val ITEM_TYPE = 11 const val ITEM_TYPE = 11
fun from(context: Context, listener: ClickListener<Artist>): ArtistViewHolder { fun from(context: Context, doOnClick: (Artist) -> Unit): ArtistViewHolder {
return ArtistViewHolder( return ArtistViewHolder(
ClickListener { listener.onClick(it) }, doOnClick,
ItemArtistBinding.inflate(LayoutInflater.from(context)) ItemArtistBinding.inflate(LayoutInflater.from(context))
) )
} }
@ -62,9 +61,9 @@ class ArtistViewHolder private constructor(
} }
class AlbumViewHolder private constructor( class AlbumViewHolder private constructor(
listener: ClickListener<Album>, doOnClick: (Album) -> Unit,
private val binding: ItemAlbumBinding private val binding: ItemAlbumBinding
) : BaseViewHolder<Album>(binding, listener) { ) : BaseViewHolder<Album>(binding, doOnClick) {
override fun onBind(model: Album) { override fun onBind(model: Album) {
binding.album = model binding.album = model
@ -74,9 +73,9 @@ class AlbumViewHolder private constructor(
companion object { companion object {
const val ITEM_TYPE = 12 const val ITEM_TYPE = 12
fun from(context: Context, listener: ClickListener<Album>): AlbumViewHolder { fun from(context: Context, doOnClick: (Album) -> Unit): AlbumViewHolder {
return AlbumViewHolder( return AlbumViewHolder(
listener, doOnClick,
ItemAlbumBinding.inflate(LayoutInflater.from(context)) ItemAlbumBinding.inflate(LayoutInflater.from(context))
) )
} }
@ -86,9 +85,9 @@ class AlbumViewHolder private constructor(
// TODO: Add indicators to song recycler items when they're being played. // TODO: Add indicators to song recycler items when they're being played.
class SongViewHolder private constructor( class SongViewHolder private constructor(
listener: ClickListener<Song>, doOnClick: (Song) -> Unit,
private val binding: ItemSongBinding private val binding: ItemSongBinding
) : BaseViewHolder<Song>(binding, listener) { ) : BaseViewHolder<Song>(binding, doOnClick) {
override fun onBind(model: Song) { override fun onBind(model: Song) {
binding.song = model binding.song = model
@ -100,9 +99,9 @@ class SongViewHolder private constructor(
companion object { companion object {
const val ITEM_TYPE = 13 const val ITEM_TYPE = 13
fun from(context: Context, listener: ClickListener<Song>): SongViewHolder { fun from(context: Context, doOnClick: (Song) -> Unit): SongViewHolder {
return SongViewHolder( return SongViewHolder(
listener, doOnClick,
ItemSongBinding.inflate(LayoutInflater.from(context)) ItemSongBinding.inflate(LayoutInflater.from(context))
) )
} }

View file

@ -3,18 +3,17 @@ package org.oxycblt.auxio.songs
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.recycler.viewholders.SongViewHolder import org.oxycblt.auxio.recycler.viewholders.SongViewHolder
class SongAdapter( class SongAdapter(
private val data: List<Song>, private val data: List<Song>,
private val listener: ClickListener<Song> private val doOnClick: (Song) -> Unit
) : RecyclerView.Adapter<SongViewHolder>() { ) : RecyclerView.Adapter<SongViewHolder>() {
override fun getItemCount(): Int = data.size override fun getItemCount(): Int = data.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongViewHolder {
return SongViewHolder.from(parent.context, listener) return SongViewHolder.from(parent.context, doOnClick)
} }
override fun onBindViewHolder(holder: SongViewHolder, position: Int) { override fun onBindViewHolder(holder: SongViewHolder, position: Int) {

View file

@ -10,7 +10,6 @@ import androidx.fragment.app.activityViewModels
import org.oxycblt.auxio.databinding.FragmentSongsBinding import org.oxycblt.auxio.databinding.FragmentSongsBinding
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
class SongsFragment : Fragment() { class SongsFragment : Fragment() {
@ -33,12 +32,9 @@ class SongsFragment : Fragment() {
// --- UI SETUP --- // --- UI SETUP ---
binding.songRecycler.apply { binding.songRecycler.apply {
adapter = SongAdapter( adapter = SongAdapter(musicModel.songs.value!!) {
musicModel.songs.value!!, playbackModel.updateSong(it)
ClickListener { song ->
playbackModel.updateSong(song)
} }
)
applyDivider() applyDivider()
setHasFixedSize(true) setHasFixedSize(true)
} }

View file

@ -19,25 +19,25 @@ import org.oxycblt.auxio.R
// Pairs of the base accent and its theme // Pairs of the base accent and its theme
private val ACCENTS = listOf( private val ACCENTS = listOf(
Pair(R.color.red, R.style.Theme_Red), Pair(R.color.red, R.style.Theme_Red), // 0
Pair(R.color.pink, R.style.Theme_Pink), Pair(R.color.pink, R.style.Theme_Pink), // 1
Pair(R.color.purple, R.style.Theme_Purple), Pair(R.color.purple, R.style.Theme_Purple), // 2
Pair(R.color.deep_purple, R.style.Theme_DeepPurple), Pair(R.color.deep_purple, R.style.Theme_DeepPurple), // 3
Pair(R.color.indigo, R.style.Theme_Indigo), Pair(R.color.indigo, R.style.Theme_Indigo), // 4
Pair(R.color.blue, R.style.Theme_Blue), Pair(R.color.blue, R.style.Theme_Blue), // 5
Pair(R.color.light_blue, R.style.Theme_Blue), Pair(R.color.light_blue, R.style.Theme_Blue), // 6
Pair(R.color.cyan, R.style.Theme_Cyan), Pair(R.color.cyan, R.style.Theme_Cyan), // 7
Pair(R.color.teal, R.style.Theme_Teal), Pair(R.color.teal, R.style.Theme_Teal), // 8
Pair(R.color.green, R.style.Theme_Green), Pair(R.color.green, R.style.Theme_Green), // 9
Pair(R.color.light_green, R.style.Theme_LightGreen), Pair(R.color.light_green, R.style.Theme_LightGreen), // 10
Pair(R.color.lime, R.style.Theme_Lime), Pair(R.color.lime, R.style.Theme_Lime), // 11
Pair(R.color.yellow, R.style.Theme_Yellow), Pair(R.color.yellow, R.style.Theme_Yellow), // 12
Pair(R.color.amber, R.style.Theme_Amber), Pair(R.color.amber, R.style.Theme_Amber), // 13
Pair(R.color.orange, R.style.Theme_Orange), Pair(R.color.orange, R.style.Theme_Orange), // 14
Pair(R.color.deep_orange, R.style.Theme_DeepOrange), Pair(R.color.deep_orange, R.style.Theme_DeepOrange), // 15
Pair(R.color.brown, R.style.Theme_Brown), Pair(R.color.brown, R.style.Theme_Brown), // 16
Pair(R.color.grey, R.style.Theme_Gray), Pair(R.color.grey, R.style.Theme_Gray), // 17
Pair(R.color.blue_grey, R.style.Theme_BlueGrey) Pair(R.color.blue_grey, R.style.Theme_BlueGrey) // 18
) )
val accent = ACCENTS[5] val accent = ACCENTS[5]

View file

@ -96,7 +96,7 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_play_to_pause" tools:src="@drawable/ic_play_to_pause"
tools:tint="@color/control_color" /> android:tint="@color/control_color" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout> </layout>

View file

@ -40,6 +40,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_margin="@dimen/margin_mid_large" android:layout_margin="@dimen/margin_mid_large"
android:elevation="4dp"
android:contentDescription="@{@string/description_album_cover(song.name)}" android:contentDescription="@{@string/description_album_cover(song.name)}"
app:coverArt="@{song}" app:coverArt="@{song}"
app:layout_constraintBottom_toTopOf="@+id/playback_song" app:layout_constraintBottom_toTopOf="@+id/playback_song"