detail: improve disc header design
This commit is contained in:
parent
190abd5588
commit
97faa3f20e
4 changed files with 78 additions and 30 deletions
|
@ -49,8 +49,10 @@ object IntegerTable {
|
||||||
const val VIEW_TYPE_ARTIST_SONG = 0xA00A
|
const val VIEW_TYPE_ARTIST_SONG = 0xA00A
|
||||||
/** DiscHeaderViewHolder */
|
/** DiscHeaderViewHolder */
|
||||||
const val VIEW_TYPE_DISC_HEADER = 0xA00B
|
const val VIEW_TYPE_DISC_HEADER = 0xA00B
|
||||||
|
/** DiscHeaderViewHolder */
|
||||||
|
const val VIEW_TYPE_DISC_DIVIDER = 0xA00C
|
||||||
/** EditHeaderViewHolder */
|
/** EditHeaderViewHolder */
|
||||||
const val VIEW_TYPE_EDIT_HEADER = 0xA00C
|
const val VIEW_TYPE_EDIT_HEADER = 0xA00D
|
||||||
/** PlaylistSongViewHolder */
|
/** PlaylistSongViewHolder */
|
||||||
const val VIEW_TYPE_PLAYLIST_SONG = 0xA00E
|
const val VIEW_TYPE_PLAYLIST_SONG = 0xA00E
|
||||||
/** "Music playback" notification code */
|
/** "Music playback" notification code */
|
||||||
|
|
|
@ -29,6 +29,7 @@ import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.yield
|
import kotlinx.coroutines.yield
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.detail.list.DiscDivider
|
||||||
import org.oxycblt.auxio.detail.list.DiscHeader
|
import org.oxycblt.auxio.detail.list.DiscHeader
|
||||||
import org.oxycblt.auxio.detail.list.EditHeader
|
import org.oxycblt.auxio.detail.list.EditHeader
|
||||||
import org.oxycblt.auxio.detail.list.SortHeader
|
import org.oxycblt.auxio.detail.list.SortHeader
|
||||||
|
@ -554,7 +555,16 @@ constructor(
|
||||||
newList.add(Divider(header))
|
newList.add(Divider(header))
|
||||||
}
|
}
|
||||||
newList.add(header)
|
newList.add(header)
|
||||||
section.discs.flatMap { listOf(DiscHeader(it.key)) + it.value }
|
buildList<Item> {
|
||||||
|
for (entry in section.discs) {
|
||||||
|
val discHeader = DiscHeader(inner = entry.key)
|
||||||
|
if (isNotEmpty()) {
|
||||||
|
add(DiscDivider(discHeader))
|
||||||
|
}
|
||||||
|
add(discHeader)
|
||||||
|
addAll(entry.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Currently only the final section (songs, which can be sorted) are invalidatable
|
// Currently only the final section (songs, which can be sorted) are invalidatable
|
||||||
|
|
|
@ -24,6 +24,7 @@ import androidx.core.view.isGone
|
||||||
import androidx.core.view.isInvisible
|
import androidx.core.view.isInvisible
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.material.divider.MaterialDivider
|
||||||
import org.oxycblt.auxio.IntegerTable
|
import org.oxycblt.auxio.IntegerTable
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
|
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
|
||||||
|
@ -38,6 +39,7 @@ import org.oxycblt.auxio.music.info.Disc
|
||||||
import org.oxycblt.auxio.music.info.resolveNumber
|
import org.oxycblt.auxio.music.info.resolveNumber
|
||||||
import org.oxycblt.auxio.playback.formatDurationMs
|
import org.oxycblt.auxio.playback.formatDurationMs
|
||||||
import org.oxycblt.auxio.util.context
|
import org.oxycblt.auxio.util.context
|
||||||
|
import org.oxycblt.auxio.util.getAttrColorCompat
|
||||||
import org.oxycblt.auxio.util.inflater
|
import org.oxycblt.auxio.util.inflater
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,6 +54,7 @@ class AlbumDetailListAdapter(private val listener: Listener<Song>) :
|
||||||
when (getItem(position)) {
|
when (getItem(position)) {
|
||||||
// Support sub-headers for each disc, and special album songs.
|
// Support sub-headers for each disc, and special album songs.
|
||||||
is DiscHeader -> DiscHeaderViewHolder.VIEW_TYPE
|
is DiscHeader -> DiscHeaderViewHolder.VIEW_TYPE
|
||||||
|
is DiscDivider -> DiscDividerViewHolder.VIEW_TYPE
|
||||||
is Song -> AlbumSongViewHolder.VIEW_TYPE
|
is Song -> AlbumSongViewHolder.VIEW_TYPE
|
||||||
else -> super.getItemViewType(position)
|
else -> super.getItemViewType(position)
|
||||||
}
|
}
|
||||||
|
@ -59,6 +62,7 @@ class AlbumDetailListAdapter(private val listener: Listener<Song>) :
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||||
when (viewType) {
|
when (viewType) {
|
||||||
DiscHeaderViewHolder.VIEW_TYPE -> DiscHeaderViewHolder.from(parent)
|
DiscHeaderViewHolder.VIEW_TYPE -> DiscHeaderViewHolder.from(parent)
|
||||||
|
DiscDividerViewHolder.VIEW_TYPE -> DiscDividerViewHolder.from(parent)
|
||||||
AlbumSongViewHolder.VIEW_TYPE -> AlbumSongViewHolder.from(parent)
|
AlbumSongViewHolder.VIEW_TYPE -> AlbumSongViewHolder.from(parent)
|
||||||
else -> super.onCreateViewHolder(parent, viewType)
|
else -> super.onCreateViewHolder(parent, viewType)
|
||||||
}
|
}
|
||||||
|
@ -79,6 +83,8 @@ class AlbumDetailListAdapter(private val listener: Listener<Song>) :
|
||||||
when {
|
when {
|
||||||
oldItem is Disc && newItem is Disc ->
|
oldItem is Disc && newItem is Disc ->
|
||||||
DiscHeaderViewHolder.DIFF_CALLBACK.areContentsTheSame(oldItem, newItem)
|
DiscHeaderViewHolder.DIFF_CALLBACK.areContentsTheSame(oldItem, newItem)
|
||||||
|
oldItem is DiscDivider && newItem is DiscDivider ->
|
||||||
|
DiscDividerViewHolder.DIFF_CALLBACK.areContentsTheSame(oldItem, newItem)
|
||||||
oldItem is Song && newItem is Song ->
|
oldItem is Song && newItem is Song ->
|
||||||
AlbumSongViewHolder.DIFF_CALLBACK.areContentsTheSame(oldItem, newItem)
|
AlbumSongViewHolder.DIFF_CALLBACK.areContentsTheSame(oldItem, newItem)
|
||||||
|
|
||||||
|
@ -96,6 +102,8 @@ class AlbumDetailListAdapter(private val listener: Listener<Song>) :
|
||||||
*/
|
*/
|
||||||
data class DiscHeader(val inner: Disc?) : Item
|
data class DiscHeader(val inner: Disc?) : Item
|
||||||
|
|
||||||
|
data class DiscDivider(val anchor: DiscHeader?) : Item
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [RecyclerView.ViewHolder] that displays a [DiscHeader] to delimit different disc groups. Use
|
* A [RecyclerView.ViewHolder] that displays a [DiscHeader] to delimit different disc groups. Use
|
||||||
* [from] to create an instance.
|
* [from] to create an instance.
|
||||||
|
@ -140,6 +148,42 @@ private class DiscHeaderViewHolder(private val binding: ItemDiscHeaderBinding) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [RecyclerView.ViewHolder] that displays a [DiscHeader]. Use [from] to create an instance.
|
||||||
|
*
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
|
class DiscDividerViewHolder private constructor(divider: MaterialDivider) :
|
||||||
|
RecyclerView.ViewHolder(divider) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
divider.dividerColor =
|
||||||
|
divider.context
|
||||||
|
.getAttrColorCompat(com.google.android.material.R.attr.colorOutlineVariant)
|
||||||
|
.defaultColor
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/** Unique ID for this ViewHolder type. */
|
||||||
|
const val VIEW_TYPE = IntegerTable.VIEW_TYPE_DISC_DIVIDER
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*
|
||||||
|
* @param parent The parent to inflate this instance from.
|
||||||
|
* @return A new instance.
|
||||||
|
*/
|
||||||
|
fun from(parent: View) = DiscDividerViewHolder(MaterialDivider(parent.context))
|
||||||
|
|
||||||
|
/** A comparator that can be used with DiffUtil. */
|
||||||
|
val DIFF_CALLBACK =
|
||||||
|
object : SimpleDiffCallback<DiscDivider>() {
|
||||||
|
override fun areContentsTheSame(oldItem: DiscDivider, newItem: DiscDivider) =
|
||||||
|
oldItem.anchor == newItem.anchor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [RecyclerView.ViewHolder] that displays a [Song] in the context of an [Album]. Use [from] to
|
* A [RecyclerView.ViewHolder] that displays a [Song] in the context of an [Album]. Use [from] to
|
||||||
* create an instance.
|
* create an instance.
|
||||||
|
|
|
@ -6,57 +6,49 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingStart="@dimen/spacing_medium"
|
android:paddingStart="@dimen/spacing_medium"
|
||||||
android:paddingTop="@dimen/spacing_mid_medium"
|
android:paddingTop="@dimen/spacing_small"
|
||||||
android:paddingEnd="@dimen/spacing_medium"
|
android:paddingEnd="@dimen/spacing_medium"
|
||||||
android:paddingBottom="@dimen/spacing_mid_medium">
|
android:paddingBottom="@dimen/spacing_small">
|
||||||
|
|
||||||
<org.oxycblt.auxio.image.CoverView
|
|
||||||
android:id="@+id/disc_cover"
|
|
||||||
style="@style/Widget.Auxio.Image.Small"
|
|
||||||
android:scaleType="matrix"
|
|
||||||
app:enablePlaybackIndicator="false"
|
|
||||||
app:enableSelectionBadge="false"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:ignore="ContentDescription">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/disc_icon"
|
android:id="@+id/disc_icon"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="48dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="0dp"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
android:src="@drawable/ic_album_24"
|
android:src="@drawable/ic_album_24"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:tint="@color/sel_on_cover_bg"
|
app:tint="@color/sel_on_cover_bg"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
</org.oxycblt.auxio.image.CoverView>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/disc_number"
|
android:id="@+id/disc_number"
|
||||||
style="@style/Widget.Auxio.TextView.Item.Primary"
|
style="@style/Widget.Auxio.TextView.Primary.Compact"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="@dimen/spacing_mid_medium"
|
android:layout_marginEnd="@dimen/spacing_mid_medium"
|
||||||
|
android:layout_marginStart="@dimen/spacing_medium"
|
||||||
android:textColor="@color/sel_selectable_text_primary"
|
android:textColor="@color/sel_selectable_text_primary"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/disc_name"
|
app:layout_constraintBottom_toTopOf="@+id/disc_name"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/disc_cover"
|
app:layout_constraintStart_toEndOf="@+id/disc_icon"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="@+id/disc_icon"
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
tools:text="Disc 1" />
|
tools:text="Disc 1" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/disc_name"
|
android:id="@+id/disc_name"
|
||||||
style="@style/Widget.Auxio.TextView.Item.Secondary"
|
style="@style/Widget.Auxio.TextView.Secondary.Compact"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="@dimen/spacing_mid_medium"
|
android:layout_marginEnd="@dimen/spacing_mid_medium"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_marginStart="@dimen/spacing_medium"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/disc_icon"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/disc_cover"
|
app:layout_constraintStart_toEndOf="@+id/disc_icon"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/disc_number"
|
app:layout_constraintTop_toBottomOf="@+id/disc_number"
|
||||||
tools:text="Part 1"
|
tools:text="Part 1" />
|
||||||
tools:visibility="gone" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
Loading…
Reference in a new issue