Add artist songs list
Add a list of artist songs to ArtistDetailFragment. This moves the artist sort functionality to the song list instead of the album list.
This commit is contained in:
parent
cc72ebc251
commit
b9506bcbc3
17 changed files with 254 additions and 110 deletions
|
@ -65,7 +65,9 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
setupRecycler(detailAdapter)
|
||||
setupRecycler(detailAdapter) { pos ->
|
||||
pos == 0
|
||||
}
|
||||
|
||||
// -- DETAILVIEWMODEL SETUP ---
|
||||
|
||||
|
|
|
@ -6,19 +6,22 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.detail.adapters.ArtistDetailAdapter
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.ActionHeader
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.recycler.SortMode
|
||||
import org.oxycblt.auxio.ui.ActionMenu
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
|
||||
/**
|
||||
* The [DetailFragment] for an artist.
|
||||
* TODO: Show a list of songs?
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class ArtistDetailFragment : DetailFragment() {
|
||||
|
@ -42,14 +45,18 @@ class ArtistDetailFragment : DetailFragment() {
|
|||
}
|
||||
|
||||
val detailAdapter = ArtistDetailAdapter(
|
||||
detailModel, playbackModel, viewLifecycleOwner,
|
||||
doOnClick = { album ->
|
||||
if (!detailModel.isNavigating) {
|
||||
detailModel.setNavigating(true)
|
||||
playbackModel,
|
||||
doOnClick = { data ->
|
||||
if (data is Album) {
|
||||
if (!detailModel.isNavigating) {
|
||||
detailModel.setNavigating(true)
|
||||
|
||||
findNavController().navigate(
|
||||
ArtistDetailFragmentDirections.actionShowAlbum(album.id)
|
||||
)
|
||||
findNavController().navigate(
|
||||
ArtistDetailFragmentDirections.actionShowAlbum(data.id)
|
||||
)
|
||||
}
|
||||
} else if (data is Song) {
|
||||
playbackModel.playSong(data, PlaybackMode.IN_ARTIST)
|
||||
}
|
||||
},
|
||||
doOnLongClick = { view, data ->
|
||||
|
@ -57,24 +64,40 @@ class ArtistDetailFragment : DetailFragment() {
|
|||
}
|
||||
)
|
||||
|
||||
// We build the action header here since it's both more efficent to keep one action header
|
||||
// and it also prevents the header from being constantly refreshed when the sort is updated.
|
||||
|
||||
val songsHeader = ActionHeader(
|
||||
id = -2,
|
||||
name = getString(R.string.label_songs),
|
||||
icon = detailModel.artistSortMode.value!!.iconRes,
|
||||
) { btn ->
|
||||
detailModel.incrementArtistSortMode()
|
||||
|
||||
// We'll update the icon of this header object directly so that the state persists
|
||||
// after the viewholder is recycled.
|
||||
icon = detailModel.artistSortMode.value!!.iconRes
|
||||
btn.setImageResource(icon)
|
||||
}
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.lifecycleOwner = this
|
||||
|
||||
setupToolbar()
|
||||
setupRecycler(detailAdapter)
|
||||
setupRecycler(detailAdapter) { pos ->
|
||||
// If the item is an ActionHeader we need to also make the item full-width
|
||||
pos == 0 || detailAdapter.currentList.getOrNull(pos) is ActionHeader
|
||||
}
|
||||
|
||||
detailAdapter.submitList(createData(songsHeader, detailModel.artistSortMode.value!!))
|
||||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
detailModel.artistSortMode.observe(viewLifecycleOwner) { mode ->
|
||||
logD("Updating sort mode to $mode")
|
||||
|
||||
// Header detail data is always included
|
||||
val data = mutableListOf<BaseModel>(detailModel.currentArtist.value!!).also {
|
||||
it.addAll(mode.getSortedAlbumList(detailModel.currentArtist.value!!.albums))
|
||||
}
|
||||
|
||||
detailAdapter.submitList(data)
|
||||
detailAdapter.submitList(createData(songsHeader, mode))
|
||||
}
|
||||
|
||||
detailModel.navToItem.observe(viewLifecycleOwner) { item ->
|
||||
|
@ -105,9 +128,21 @@ class ArtistDetailFragment : DetailFragment() {
|
|||
// Highlight albums if they are being played
|
||||
playbackModel.parent.observe(viewLifecycleOwner) { parent ->
|
||||
if (parent is Album?) {
|
||||
detailAdapter.setCurrentAlbum(parent, binding.detailRecycler)
|
||||
detailAdapter.highlightAlbum(parent, binding.detailRecycler)
|
||||
} else {
|
||||
detailAdapter.setCurrentAlbum(null, binding.detailRecycler)
|
||||
detailAdapter.highlightAlbum(null, binding.detailRecycler)
|
||||
}
|
||||
}
|
||||
|
||||
// Highlight songs if they are being played
|
||||
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
||||
if (playbackModel.mode.value == PlaybackMode.IN_ARTIST &&
|
||||
playbackModel.parent.value?.id == detailModel.currentArtist.value!!.id
|
||||
) {
|
||||
detailAdapter.highlightSong(song, binding.detailRecycler)
|
||||
} else {
|
||||
// Clear the viewholders if the mode isn't ALL_SONGS
|
||||
detailAdapter.highlightSong(null, binding.detailRecycler)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,4 +150,15 @@ class ArtistDetailFragment : DetailFragment() {
|
|||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private fun createData(songHeader: ActionHeader, mode: SortMode): MutableList<BaseModel> {
|
||||
val artist = detailModel.currentArtist.value!!
|
||||
|
||||
val data = mutableListOf<BaseModel>(artist)
|
||||
data.addAll(SortMode.NUMERIC_DOWN.getSortedAlbumList(artist.albums))
|
||||
data.add(songHeader)
|
||||
data.addAll(mode.getSortedArtistSongList(artist.songs))
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,8 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.FragmentDetailBinding
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.fixAnimInfoLeak
|
||||
import org.oxycblt.auxio.ui.isLandscape
|
||||
|
@ -78,7 +76,10 @@ abstract class DetailFragment : Fragment() {
|
|||
/**
|
||||
* Shortcut method for recyclerview setup
|
||||
*/
|
||||
protected fun setupRecycler(detailAdapter: ListAdapter<BaseModel, RecyclerView.ViewHolder>) {
|
||||
protected fun setupRecycler(
|
||||
detailAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>,
|
||||
gridLookup: (Int) -> Boolean
|
||||
) {
|
||||
binding.detailRecycler.apply {
|
||||
adapter = detailAdapter
|
||||
setHasFixedSize(true)
|
||||
|
@ -88,7 +89,7 @@ abstract class DetailFragment : Fragment() {
|
|||
layoutManager = GridLayoutManager(requireContext(), 2).also {
|
||||
it.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return if (position == 0) 2 else 1
|
||||
return if (gridLookup(position)) 2 else 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,9 @@ class GenreDetailFragment : DetailFragment() {
|
|||
binding.lifecycleOwner = this
|
||||
|
||||
setupToolbar()
|
||||
setupRecycler(detailAdapter)
|
||||
setupRecycler(detailAdapter) { pos ->
|
||||
pos == 0
|
||||
}
|
||||
|
||||
// --- DETAILVIEWMODEL SETUP ---
|
||||
|
||||
|
|
|
@ -2,21 +2,23 @@ package org.oxycblt.auxio.detail.adapters
|
|||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.ItemArtistAlbumBinding
|
||||
import org.oxycblt.auxio.databinding.ItemArtistHeaderBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.databinding.ItemArtistSongBinding
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.ActionHeader
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.recycler.DiffCallback
|
||||
import org.oxycblt.auxio.recycler.viewholders.ActionHeaderViewHolder
|
||||
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||
import org.oxycblt.auxio.recycler.viewholders.Highlightable
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.disable
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
import org.oxycblt.auxio.ui.setTextColorResource
|
||||
|
||||
|
@ -25,19 +27,22 @@ import org.oxycblt.auxio.ui.setTextColorResource
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
class ArtistDetailAdapter(
|
||||
private val detailModel: DetailViewModel,
|
||||
private val playbackModel: PlaybackViewModel,
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val doOnClick: (data: Album) -> Unit,
|
||||
private val doOnLongClick: (view: View, data: Album) -> Unit,
|
||||
private val doOnClick: (data: BaseModel) -> Unit,
|
||||
private val doOnLongClick: (view: View, data: BaseModel) -> Unit,
|
||||
) : ListAdapter<BaseModel, RecyclerView.ViewHolder>(DiffCallback()) {
|
||||
private var currentAlbum: Album? = null
|
||||
private var lastHolder: Highlightable? = null
|
||||
private var currentAlbumHolder: Highlightable? = null
|
||||
|
||||
private var currentSong: Song? = null
|
||||
private var currentSongHolder: Highlightable? = null
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (getItem(position)) {
|
||||
is Artist -> ARTIST_HEADER_ITEM_TYPE
|
||||
is Album -> ARTIST_ALBUM_ITEM_TYPE
|
||||
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||
is Song -> ARTIST_SONG_ITEM_TYPE
|
||||
|
||||
else -> -1
|
||||
}
|
||||
|
@ -53,24 +58,43 @@ class ArtistDetailAdapter(
|
|||
ItemArtistAlbumBinding.inflate(parent.context.inflater)
|
||||
)
|
||||
|
||||
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
|
||||
|
||||
ARTIST_SONG_ITEM_TYPE -> ArtistSongViewHolder(
|
||||
ItemArtistSongBinding.inflate(parent.context.inflater)
|
||||
)
|
||||
|
||||
else -> error("Invalid ViewHolder item type $viewType")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (val item = getItem(position)) {
|
||||
val item = getItem(position)
|
||||
|
||||
when (item) {
|
||||
is Artist -> (holder as ArtistHeaderViewHolder).bind(item)
|
||||
is Album -> (holder as ArtistAlbumViewHolder).bind(item)
|
||||
is ActionHeader -> (holder as ActionHeaderViewHolder).bind(item)
|
||||
is Song -> (holder as ArtistSongViewHolder).bind(item)
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
if (currentAlbum != null && position > 0) {
|
||||
if (getItem(position).id == currentAlbum?.id) {
|
||||
// Reset the last ViewHolder before assigning the new, correct one to be highlighted
|
||||
lastHolder?.setHighlighted(false)
|
||||
lastHolder = (holder as Highlightable)
|
||||
holder.setHighlighted(true)
|
||||
} else {
|
||||
(holder as Highlightable).setHighlighted(false)
|
||||
if (holder is Highlightable) {
|
||||
when (item.id) {
|
||||
currentAlbum?.id -> {
|
||||
currentAlbumHolder?.setHighlighted(false)
|
||||
currentAlbumHolder = holder
|
||||
holder.setHighlighted(true)
|
||||
}
|
||||
|
||||
currentSong?.id -> {
|
||||
currentSongHolder?.setHighlighted(false)
|
||||
currentSongHolder = holder
|
||||
holder.setHighlighted(true)
|
||||
}
|
||||
|
||||
else -> holder.setHighlighted(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,11 +103,11 @@ class ArtistDetailAdapter(
|
|||
* Update the current [album] that this adapter should highlight
|
||||
* @param recycler The recyclerview the highlighting should act on.
|
||||
*/
|
||||
fun setCurrentAlbum(album: Album?, recycler: RecyclerView) {
|
||||
fun highlightAlbum(album: Album?, recycler: RecyclerView) {
|
||||
// Clear out the last ViewHolder as a song update usually signifies that this current
|
||||
// ViewHolder is likely invalid.
|
||||
lastHolder?.setHighlighted(false)
|
||||
lastHolder = null
|
||||
currentAlbumHolder?.setHighlighted(false)
|
||||
currentAlbumHolder = null
|
||||
|
||||
currentAlbum = album
|
||||
|
||||
|
@ -93,12 +117,46 @@ class ArtistDetailAdapter(
|
|||
item.name == album.name && item is Album
|
||||
}
|
||||
|
||||
logD(pos)
|
||||
|
||||
// Check if the ViewHolder if this album is visible, and highlight it if so.
|
||||
recycler.layoutManager?.findViewByPosition(pos)?.let { child ->
|
||||
recycler.getChildViewHolder(child)?.let {
|
||||
lastHolder = it as Highlightable
|
||||
currentAlbumHolder = it as Highlightable
|
||||
|
||||
lastHolder?.setHighlighted(true)
|
||||
currentAlbumHolder?.setHighlighted(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the [song] that this adapter should highlight
|
||||
* @param recycler The recyclerview the highlighting should act on.
|
||||
*/
|
||||
fun highlightSong(song: Song?, recycler: RecyclerView) {
|
||||
// Clear out the last ViewHolder as a song update usually signifies that this current
|
||||
// ViewHolder is likely invalid.
|
||||
currentSongHolder?.setHighlighted(false)
|
||||
currentSongHolder = null
|
||||
|
||||
currentSong = song
|
||||
|
||||
if (song != null) {
|
||||
// Use existing data instead of having to re-sort it.
|
||||
// We also have to account for the album count when searching for the viewholder
|
||||
val pos = currentList.indexOfFirst { item ->
|
||||
item.name == song.name && item is Song
|
||||
}
|
||||
|
||||
// Check if the ViewHolder for this song is visible, if it is then highlight it.
|
||||
// If the ViewHolder is not visible, then the adapter should take care of it if
|
||||
// it does become visible.
|
||||
recycler.layoutManager?.findViewByPosition(pos)?.let { child ->
|
||||
recycler.getChildViewHolder(child)?.let {
|
||||
currentSongHolder = it as Highlightable
|
||||
|
||||
currentSongHolder?.setHighlighted(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,13 +168,7 @@ class ArtistDetailAdapter(
|
|||
|
||||
override fun onBind(data: Artist) {
|
||||
binding.artist = data
|
||||
binding.detailModel = detailModel
|
||||
binding.playbackModel = playbackModel
|
||||
binding.lifecycleOwner = lifecycleOwner
|
||||
|
||||
if (data.albums.size < 2) {
|
||||
binding.artistSortButton.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,6 +185,8 @@ class ArtistDetailAdapter(
|
|||
}
|
||||
|
||||
override fun setHighlighted(isHighlighted: Boolean) {
|
||||
logD(isHighlighted)
|
||||
|
||||
if (isHighlighted) {
|
||||
binding.albumName.setTextColorResource(Accent.get().color)
|
||||
} else {
|
||||
|
@ -141,8 +195,29 @@ class ArtistDetailAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
inner class ArtistSongViewHolder(
|
||||
private val binding: ItemArtistSongBinding,
|
||||
) : BaseViewHolder<Song>(binding, doOnClick, doOnLongClick), Highlightable {
|
||||
private val normalTextColor = binding.songName.currentTextColor
|
||||
|
||||
override fun onBind(data: Song) {
|
||||
binding.song = data
|
||||
|
||||
binding.songName.requestLayout()
|
||||
}
|
||||
|
||||
override fun setHighlighted(isHighlighted: Boolean) {
|
||||
if (isHighlighted) {
|
||||
binding.songName.setTextColorResource(Accent.get().color)
|
||||
} else {
|
||||
binding.songName.setTextColor(normalTextColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ARTIST_HEADER_ITEM_TYPE = 0xA009
|
||||
const val ARTIST_ALBUM_ITEM_TYPE = 0xA00A
|
||||
const val ARTIST_SONG_ITEM_TYPE = 0xA00B
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ class GenreDetailAdapter(
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val GENRE_HEADER_ITEM_TYPE = 0xA00B
|
||||
const val GENRE_SONG_ITEM_TYPE = 0xA00C
|
||||
const val GENRE_HEADER_ITEM_TYPE = 0xA00C
|
||||
const val GENRE_SONG_ITEM_TYPE = 0xA00D
|
||||
}
|
||||
}
|
||||
|
|
|
@ -199,12 +199,12 @@ data class Header(
|
|||
|
||||
/**
|
||||
* A data object for a header with an action button. Inherits [BaseModel].
|
||||
* @property icon The icon ot apply for this header
|
||||
* @property icon The icon ot apply for this header. This can be changed to reflect any change.
|
||||
* @property action The callback that will be called when the action button is clicked.
|
||||
*/
|
||||
data class ActionHeader(
|
||||
override val id: Long = -1,
|
||||
override val name: String = "",
|
||||
@DrawableRes val icon: Int,
|
||||
val action: (button: ImageButton) -> Unit,
|
||||
@DrawableRes var icon: Int,
|
||||
val action: ActionHeader.(button: ImageButton) -> Unit,
|
||||
) : BaseModel()
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.view.ViewGroup
|
|||
import androidx.recyclerview.widget.AsyncListDiffer
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.ItemActionHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
||||
import org.oxycblt.auxio.logE
|
||||
import org.oxycblt.auxio.music.ActionHeader
|
||||
|
@ -47,9 +46,7 @@ class QueueAdapter(
|
|||
return when (viewType) {
|
||||
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
|
||||
|
||||
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder(
|
||||
ItemActionHeaderBinding.inflate(parent.context.inflater)
|
||||
)
|
||||
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
|
||||
|
||||
QUEUE_SONG_ITEM_TYPE -> QueueSongViewHolder(
|
||||
ItemQueueSongBinding.inflate(parent.context.inflater)
|
||||
|
|
|
@ -186,7 +186,7 @@ class ActionHeaderViewHolder(
|
|||
setImageResource(data.icon)
|
||||
|
||||
setOnClickListener {
|
||||
data.action(binding.headerButton)
|
||||
data.action(data, binding.headerButton)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -195,11 +195,11 @@ class ActionHeaderViewHolder(
|
|||
const val ITEM_TYPE = 0xA006
|
||||
|
||||
/**
|
||||
* Create an instance of [HeaderViewHolder]
|
||||
* Create an instance of [ActionHeaderViewHolder]
|
||||
*/
|
||||
fun from(context: Context): HeaderViewHolder {
|
||||
return HeaderViewHolder(
|
||||
ItemHeaderBinding.inflate(context.inflater)
|
||||
fun from(context: Context): ActionHeaderViewHolder {
|
||||
return ActionHeaderViewHolder(
|
||||
ItemActionHeaderBinding.inflate(context.inflater)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import org.oxycblt.auxio.music.BaseModel
|
|||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
|
||||
/**
|
||||
* Extension method for creating and showing a new [ActionMenu].
|
||||
|
@ -78,6 +77,7 @@ class ActionMenu(
|
|||
when (flag) {
|
||||
FLAG_NONE, FLAG_IN_GENRE -> R.menu.menu_song_actions
|
||||
FLAG_IN_ALBUM -> R.menu.menu_album_song_actions
|
||||
FLAG_IN_ARTIST -> R.menu.menu_artist_song_actions
|
||||
|
||||
else -> -1
|
||||
}
|
||||
|
@ -125,12 +125,6 @@ class ActionMenu(
|
|||
}
|
||||
}
|
||||
|
||||
R.id.action_play_artist -> {
|
||||
if (flag == FLAG_IN_ALBUM && data is Song) {
|
||||
playbackModel.playSong(data, PlaybackMode.IN_ARTIST)
|
||||
}
|
||||
}
|
||||
|
||||
R.id.action_queue_add -> {
|
||||
when (data) {
|
||||
is Song -> {
|
||||
|
|
|
@ -107,17 +107,6 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/artist_play_button" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/artist_sort_button"
|
||||
style="@style/HeaderAction"
|
||||
android:contentDescription="@string/description_sort_button"
|
||||
android:onClick="@{() -> detailModel.incrementArtistSortMode()}"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/artist_album_header"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/artist_album_header"
|
||||
app:sortIcon="@{detailModel.artistSortMode}"
|
||||
tools:src="@drawable/ic_sort_numeric_down" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
|
@ -107,17 +107,6 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/artist_play_button" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/artist_sort_button"
|
||||
style="@style/HeaderAction"
|
||||
android:contentDescription="@string/description_sort_button"
|
||||
android:onClick="@{() -> detailModel.incrementArtistSortMode()}"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/artist_album_header"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/artist_album_header"
|
||||
app:sortIcon="@{detailModel.artistSortMode}"
|
||||
tools:src="@drawable/ic_sort_numeric_down" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
|
@ -104,17 +104,6 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/artist_play_button" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/artist_sort_button"
|
||||
style="@style/HeaderAction"
|
||||
android:contentDescription="@string/description_sort_button"
|
||||
android:onClick="@{() -> detailModel.incrementArtistSortMode()}"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/artist_album_header"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/artist_album_header"
|
||||
app:sortIcon="@{detailModel.artistSortMode}"
|
||||
tools:src="@drawable/ic_sort_numeric_down" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
52
app/src/main/res/layout/item_artist_song.xml
Normal file
52
app/src/main/res/layout/item_artist_song.xml
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".recycler.viewholders.SongViewHolder">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="song"
|
||||
type="org.oxycblt.auxio.music.Song" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout style="@style/ItemSurroundings">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/album_cover"
|
||||
android:layout_width="@dimen/size_cover_compact"
|
||||
android:layout_height="@dimen/size_cover_compact"
|
||||
android:contentDescription="@{@string/description_album_cover(song.name)}"
|
||||
app:albumArt="@{song}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:src="@drawable/ic_song" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/song_name"
|
||||
style="@style/ItemText.Primary"
|
||||
android:layout_marginEnd="@dimen/spacing_medium"
|
||||
android:text="@{song.name}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/song_info"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="Song Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/song_info"
|
||||
style="@style/ItemText.Secondary"
|
||||
android:layout_marginEnd="@dimen/spacing_medium"
|
||||
android:text="@{song.album.name}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||
app:layout_constraintTop_toBottomOf="@+id/song_name"
|
||||
tools:text="Album" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -6,7 +6,4 @@
|
|||
<item
|
||||
android:id="@+id/action_go_artist"
|
||||
android:title="@string/label_go_artist" />
|
||||
<item
|
||||
android:id="@+id/action_play_artist"
|
||||
android:title="@string/label_play_artist" />
|
||||
</menu>
|
10
app/src/main/res/menu/menu_artist_song_actions.xml
Normal file
10
app/src/main/res/menu/menu_artist_song_actions.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_queue_add"
|
||||
android:title="@string/label_queue_add" />
|
||||
<item
|
||||
android:id="@+id/action_go_album"
|
||||
android:icon="@drawable/ic_album"
|
||||
android:title="@string/label_go_album" />
|
||||
</menu>
|
|
@ -50,8 +50,9 @@ To prevent any strange bugs, all integer representations must be unique. A table
|
|||
0xA008 | AlbumSongViewHolder
|
||||
0xA009 | ArtistHeaderViewHolder
|
||||
0xA00A | ArtistAlbumViewHolder
|
||||
0xA00B | GenreHeaderViewHolder
|
||||
0xA00C | GenreSongViewHolder
|
||||
0xA00B | ArtistSongViewHolder
|
||||
0xA00C | GenreHeaderViewHolder
|
||||
0xA00D | GenreSongViewHolder
|
||||
|
||||
0xA0A0 | Auxio notification code
|
||||
0xA0C0 | Auxio request code
|
||||
|
|
Loading…
Reference in a new issue