Refactor album detail layout
Change the album detail layout to rely on a RecyclerView entirely instead of a NestedScrollView, at the cost of some functionality I'll need to re-add.
This commit is contained in:
parent
7ef4eb5fde
commit
46fa300252
26 changed files with 418 additions and 532 deletions
|
@ -14,7 +14,7 @@
|
|||
|
||||
## About
|
||||
|
||||
Auxio is a local music player for android partially inspired by both Spotify and other FOSS music players such as [Music Player GO](https://github.com/enricocid/Music-Player-GO) and [Phonograph](https://github.com/kabouzeid/Phonograph), albeit with a heavy emphasis on a simple and sensible, however customizable UI/UX.
|
||||
Auxio is a local music player for android partially inspired by both Spotify and other FOSS music players such as [Music Player GO](https://github.com/enricocid/Music-Player-GO) and [Phonograph](https://github.com/kabouzeid/Phonograph), albeit with a heavy emphasis on a simple and straightfoward, however customizable UI/UX.
|
||||
|
||||
Unlike other music players, Auxio is based off of [ExoPlayer](https://exoplayer.dev/), allowing for much better listening experience compared to the native [MediaPlayer](https://developer.android.com/guide/topics/media/mediaplayer) API. Auxio's codebase is also designed to be extendable, allowing for the addition of features that are not included in the main app.
|
||||
|
||||
|
@ -53,7 +53,7 @@ Unlike other music players, Auxio is based off of [ExoPlayer](https://exoplayer.
|
|||
|
||||
- Better music loading system
|
||||
- Improved genre/artist/album UIs
|
||||
- Dedicated search tab
|
||||
- New search setup
|
||||
- Swipe-to-next-track function
|
||||
- Artist Images
|
||||
- Black theme
|
||||
|
|
|
@ -97,7 +97,7 @@ dependencies {
|
|||
implementation "com.google.android.exoplayer:extension-mediasession:$exoplayer_version"
|
||||
|
||||
// Image loading
|
||||
implementation 'io.coil-kt:coil:0.13.0'
|
||||
implementation 'io.coil-kt:coil:1.1.0'
|
||||
|
||||
// Material
|
||||
implementation 'com.google.android.material:material:1.3.0-beta01'
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.oxycblt.auxio">
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
@ -10,17 +9,17 @@
|
|||
<queries />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="@xml/backup_descriptor"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:allowBackup="true"
|
||||
android:theme="@style/Theme.Base"
|
||||
android:fullBackupContent="@xml/backup_descriptor">
|
||||
android:theme="@style/Theme.Base">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleInstance"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:launchMode="singleInstance"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:windowSoftInputMode="adjustPan">
|
||||
|
||||
|
@ -32,12 +31,12 @@
|
|||
</activity>
|
||||
<service
|
||||
android:name=".playback.PlaybackService"
|
||||
android:description="@string/label_service_playback"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:exported="false"
|
||||
android:enabled="true"
|
||||
android:description="@string/label_service_playback"
|
||||
android:stopWithTask="false" />
|
||||
</application>
|
||||
</manifest>
|
|
@ -5,20 +5,19 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding
|
||||
import org.oxycblt.auxio.detail.adapters.AlbumSongAdapter
|
||||
import org.oxycblt.auxio.databinding.FragmentDetailBinding
|
||||
import org.oxycblt.auxio.detail.adapters.AlbumDetailAdapter
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.recycler.LinearCenterScroller
|
||||
import org.oxycblt.auxio.ui.createToast
|
||||
import org.oxycblt.auxio.ui.disable
|
||||
import org.oxycblt.auxio.ui.setupAlbumSongActions
|
||||
|
||||
/**
|
||||
|
@ -26,17 +25,13 @@ import org.oxycblt.auxio.ui.setupAlbumSongActions
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
class AlbumDetailFragment : DetailFragment() {
|
||||
|
||||
private val args: AlbumDetailFragmentArgs by navArgs()
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val binding = FragmentAlbumDetailBinding.inflate(inflater)
|
||||
|
||||
// If DetailViewModel isn't already storing the album, get it from MusicStore
|
||||
// using the ID given by the navigation arguments.
|
||||
if (detailModel.currentAlbum.value == null ||
|
||||
|
@ -49,7 +44,8 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
)
|
||||
}
|
||||
|
||||
val songAdapter = AlbumSongAdapter(
|
||||
val detailAdapter = AlbumDetailAdapter(
|
||||
detailModel, viewLifecycleOwner,
|
||||
doOnClick = { playbackModel.playSong(it, PlaybackMode.IN_ALBUM) },
|
||||
doOnLongClick = { data, view ->
|
||||
PopupMenu(requireContext(), view).setupAlbumSongActions(
|
||||
|
@ -58,22 +54,12 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
}
|
||||
)
|
||||
|
||||
var lastHolder: AlbumSongAdapter.ViewHolder? = null
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.lifecycleOwner = this
|
||||
binding.detailModel = detailModel
|
||||
binding.playbackModel = playbackModel
|
||||
binding.album = detailModel.currentAlbum.value!!
|
||||
|
||||
binding.albumToolbar.apply {
|
||||
setNavigationOnClickListener {
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
setupToolbar(R.menu.menu_album_actions) {
|
||||
when (it) {
|
||||
R.id.action_shuffle -> {
|
||||
playbackModel.playAlbum(
|
||||
detailModel.currentAlbum.value!!, true
|
||||
|
@ -91,7 +77,7 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
|
||||
R.id.action_queue_add -> {
|
||||
playbackModel.addToUserQueue(detailModel.currentAlbum.value!!)
|
||||
context.getString(R.string.label_queue_added).createToast(requireContext())
|
||||
getString(R.string.label_queue_added).createToast(requireContext())
|
||||
|
||||
true
|
||||
}
|
||||
|
@ -99,18 +85,12 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.albumSongRecycler.apply {
|
||||
adapter = songAdapter
|
||||
binding.detailRecycler.apply {
|
||||
adapter = detailAdapter
|
||||
setHasFixedSize(true)
|
||||
}
|
||||
|
||||
// Don't enable the sort button if there's only one song [or less]
|
||||
if (detailModel.currentAlbum.value!!.songs.size < 2) {
|
||||
binding.albumSortButton.disable()
|
||||
}
|
||||
|
||||
// If this fragment was created in order to nav to an item, then snap scroll to that item.
|
||||
playbackModel.navToItem.value?.let {
|
||||
if (it is Song) {
|
||||
|
@ -123,13 +103,11 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
detailModel.albumSortMode.observe(viewLifecycleOwner) { mode ->
|
||||
logD("Updating sort mode to $mode")
|
||||
|
||||
// Update the current sort icon
|
||||
binding.albumSortButton.setImageResource(mode.iconRes)
|
||||
val data = mutableListOf<BaseModel>(detailModel.currentAlbum.value!!).also {
|
||||
it.addAll(mode.getSortedSongList(detailModel.currentAlbum.value!!.songs))
|
||||
}
|
||||
|
||||
// Then update the sort mode of the album adapter.
|
||||
songAdapter.submitList(
|
||||
mode.getSortedSongList(detailModel.currentAlbum.value!!.songs)
|
||||
)
|
||||
detailAdapter.submitList(data)
|
||||
}
|
||||
|
||||
detailModel.doneWithNavToParent()
|
||||
|
@ -148,32 +126,6 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
||||
if (song != null) {
|
||||
val pos = detailModel.albumSortMode.value!!.getSortedSongList(
|
||||
detailModel.currentAlbum.value!!.songs
|
||||
).indexOfFirst { it.id == song.id }
|
||||
|
||||
if (pos != -1) {
|
||||
binding.albumSongRecycler.post {
|
||||
lastHolder?.removePlaying()
|
||||
|
||||
lastHolder = binding.albumSongRecycler.getChildViewHolder(
|
||||
binding.albumSongRecycler.getChildAt(pos)
|
||||
) as AlbumSongAdapter.ViewHolder
|
||||
|
||||
lastHolder?.setPlaying(requireContext())
|
||||
}
|
||||
|
||||
return@observe
|
||||
} else {
|
||||
lastHolder?.removePlaying()
|
||||
}
|
||||
}
|
||||
|
||||
lastHolder?.removePlaying()
|
||||
}
|
||||
|
||||
playbackModel.navToItem.observe(viewLifecycleOwner) {
|
||||
if (it != null) {
|
||||
if (it is Song) {
|
||||
|
@ -196,22 +148,18 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
* @param binding The binding required
|
||||
* @param smooth Whether to scroll smoothly or not, true for yes, false for no.
|
||||
*/
|
||||
private fun scrollToPlayingItem(binding: FragmentAlbumDetailBinding, smooth: Boolean) {
|
||||
private fun scrollToPlayingItem(binding: FragmentDetailBinding, smooth: Boolean) {
|
||||
// Calculate where the item for the currently played song is, and scroll to there
|
||||
val pos = detailModel.albumSortMode.value!!.getSortedSongList(
|
||||
detailModel.currentAlbum.value!!.songs
|
||||
).indexOf(playbackModel.song.value)
|
||||
|
||||
if (pos != -1) {
|
||||
binding.albumSongRecycler.post {
|
||||
val y = binding.albumSongRecycler.y +
|
||||
binding.albumSongRecycler.getChildAt(pos).y
|
||||
|
||||
if (smooth) {
|
||||
binding.nestedScroll.smoothScrollTo(0, y.toInt())
|
||||
} else {
|
||||
binding.nestedScroll.scrollTo(0, y.toInt())
|
||||
}
|
||||
// TODO: Re-add snap scrolling.
|
||||
binding.detailRecycler.post {
|
||||
binding.detailRecycler.layoutManager?.startSmoothScroll(
|
||||
LinearCenterScroller(pos)
|
||||
)
|
||||
}
|
||||
|
||||
playbackModel.doneWithNavToItem()
|
||||
|
|
|
@ -5,17 +5,14 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentDetailBinding
|
||||
import org.oxycblt.auxio.detail.adapters.ArtistAlbumAdapter
|
||||
import org.oxycblt.auxio.detail.adapters.ArtistDetailAdapter
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.setupAlbumActions
|
||||
|
||||
/**
|
||||
|
@ -24,15 +21,12 @@ import org.oxycblt.auxio.ui.setupAlbumActions
|
|||
*/
|
||||
class ArtistDetailFragment : DetailFragment() {
|
||||
private val args: ArtistDetailFragmentArgs by navArgs()
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val binding = FragmentDetailBinding.inflate(inflater)
|
||||
|
||||
// If DetailViewModel isn't already storing the artist, get it from MusicStore
|
||||
// using the ID given by the navigation arguments
|
||||
if (detailModel.currentArtist.value == null ||
|
||||
|
@ -45,7 +39,7 @@ class ArtistDetailFragment : DetailFragment() {
|
|||
)
|
||||
}
|
||||
|
||||
val albumAdapter = ArtistAlbumAdapter(
|
||||
val detailAdapter = ArtistDetailAdapter(
|
||||
detailModel, viewLifecycleOwner,
|
||||
doOnClick = {
|
||||
if (!detailModel.isNavigating) {
|
||||
|
@ -67,15 +61,8 @@ class ArtistDetailFragment : DetailFragment() {
|
|||
|
||||
binding.lifecycleOwner = this
|
||||
|
||||
binding.detailToolbar.apply {
|
||||
inflateMenu(R.menu.menu_artist_detail)
|
||||
|
||||
setNavigationOnClickListener {
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
setupToolbar(R.menu.menu_artist_actions) {
|
||||
when (it) {
|
||||
R.id.action_shuffle -> {
|
||||
playbackModel.playArtist(
|
||||
detailModel.currentArtist.value!!,
|
||||
|
@ -96,10 +83,9 @@ class ArtistDetailFragment : DetailFragment() {
|
|||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.detailRecycler.apply {
|
||||
adapter = albumAdapter
|
||||
adapter = detailAdapter
|
||||
setHasFixedSize(true)
|
||||
}
|
||||
|
||||
|
@ -112,7 +98,7 @@ class ArtistDetailFragment : DetailFragment() {
|
|||
it.addAll(mode.getSortedAlbumList(detailModel.currentArtist.value!!.albums))
|
||||
}
|
||||
|
||||
albumAdapter.submitList(data)
|
||||
detailAdapter.submitList(data)
|
||||
}
|
||||
|
||||
playbackModel.navToItem.observe(viewLifecycleOwner) {
|
||||
|
|
|
@ -3,20 +3,26 @@ package org.oxycblt.auxio.detail
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.annotation.MenuRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.oxycblt.auxio.databinding.FragmentDetailBinding
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.memberBinding
|
||||
|
||||
/**
|
||||
* A Base [Fragment] implementing a [OnBackPressedCallback] so that Auxio will navigate upwards
|
||||
* instead of out of the app if a Detail Fragment is currently open. Also carries the
|
||||
* multi-navigation fix.
|
||||
* TODO: Migrate to a more powerful/efficient CoordinatorLayout instead of NestedScrollView
|
||||
* A Base [Fragment] implementing the base features shared across all detail fragments.
|
||||
* TODO: Add custom artist images
|
||||
* TODO: Add playing item highlighting
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
abstract class DetailFragment : Fragment() {
|
||||
protected val detailModel: DetailViewModel by activityViewModels()
|
||||
protected val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
protected val binding: FragmentDetailBinding by memberBinding(
|
||||
FragmentDetailBinding::inflate
|
||||
)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)
|
||||
|
@ -34,6 +40,25 @@ abstract class DetailFragment : Fragment() {
|
|||
callback.isEnabled = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut method for doing setup of the detail toolbar.
|
||||
*/
|
||||
protected fun setupToolbar(@MenuRes menu: Int, onMenuClick: (id: Int) -> Boolean) {
|
||||
binding.detailToolbar.apply {
|
||||
inflateMenu(menu)
|
||||
|
||||
setNavigationOnClickListener {
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
|
||||
setOnMenuItemClickListener {
|
||||
onMenuClick(it.itemId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override the back button so that going back will only exit the detail fragments instead of
|
||||
// the entire app.
|
||||
private val callback = object : OnBackPressedCallback(false) {
|
||||
|
||||
override fun handleOnBackPressed() {
|
||||
|
|
|
@ -5,17 +5,13 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentDetailBinding
|
||||
import org.oxycblt.auxio.detail.adapters.GenreSongAdapter
|
||||
import org.oxycblt.auxio.detail.adapters.GenreDetailAdapter
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.ui.isLandscape
|
||||
import org.oxycblt.auxio.ui.setupGenreSongActions
|
||||
|
@ -25,17 +21,13 @@ import org.oxycblt.auxio.ui.setupGenreSongActions
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
class GenreDetailFragment : DetailFragment() {
|
||||
|
||||
private val args: GenreDetailFragmentArgs by navArgs()
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val binding = FragmentDetailBinding.inflate(inflater)
|
||||
|
||||
// If DetailViewModel isn't already storing the genre, get it from MusicStore
|
||||
// using the ID given by the navigation arguments
|
||||
if (detailModel.currentGenre.value == null ||
|
||||
|
@ -48,7 +40,7 @@ class GenreDetailFragment : DetailFragment() {
|
|||
)
|
||||
}
|
||||
|
||||
val songAdapter = GenreSongAdapter(
|
||||
val detailAdapter = GenreDetailAdapter(
|
||||
detailModel, viewLifecycleOwner,
|
||||
doOnClick = {
|
||||
playbackModel.playSong(it, PlaybackMode.IN_GENRE)
|
||||
|
@ -64,14 +56,8 @@ class GenreDetailFragment : DetailFragment() {
|
|||
|
||||
binding.lifecycleOwner = this
|
||||
|
||||
binding.detailToolbar.apply {
|
||||
inflateMenu(R.menu.menu_songs)
|
||||
setNavigationOnClickListener {
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
setupToolbar(R.menu.menu_genre_actions) {
|
||||
when (it) {
|
||||
R.id.action_shuffle -> {
|
||||
playbackModel.playGenre(
|
||||
detailModel.currentGenre.value!!,
|
||||
|
@ -84,10 +70,9 @@ class GenreDetailFragment : DetailFragment() {
|
|||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.detailRecycler.apply {
|
||||
adapter = songAdapter
|
||||
adapter = detailAdapter
|
||||
setHasFixedSize(true)
|
||||
|
||||
if (isLandscape(resources)) {
|
||||
|
@ -110,7 +95,7 @@ class GenreDetailFragment : DetailFragment() {
|
|||
it.addAll(mode.getSortedSongList(detailModel.currentGenre.value!!.songs))
|
||||
}
|
||||
|
||||
songAdapter.submitList(data)
|
||||
detailAdapter.submitList(data)
|
||||
}
|
||||
|
||||
logD("Fragment created.")
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package org.oxycblt.auxio.detail.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
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.ItemAlbumHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.recycler.DiffCallback
|
||||
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||
import org.oxycblt.auxio.ui.disable
|
||||
|
||||
/**
|
||||
* An adapter for displaying the details and [Song]s of an [Album]
|
||||
*/
|
||||
class AlbumDetailAdapter(
|
||||
private val detailModel: DetailViewModel,
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val doOnClick: (data: Song) -> Unit,
|
||||
private val doOnLongClick: (data: Song, view: View) -> Unit
|
||||
) : ListAdapter<BaseModel, RecyclerView.ViewHolder>(DiffCallback()) {
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (getItem(position)) {
|
||||
is Album -> ALBUM_HEADER_ITEM_TYPE
|
||||
is Song -> ALBUM_SONG_ITEM_TYPE
|
||||
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ALBUM_HEADER_ITEM_TYPE -> AlbumHeaderViewHolder(
|
||||
ItemAlbumHeaderBinding.inflate(LayoutInflater.from(parent.context))
|
||||
)
|
||||
ALBUM_SONG_ITEM_TYPE -> AlbumSongViewHolder(
|
||||
ItemAlbumSongBinding.inflate(LayoutInflater.from(parent.context))
|
||||
)
|
||||
|
||||
else -> error("Invalid ViewHolder item type $viewType")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (val item = getItem(position)) {
|
||||
is Album -> (holder as AlbumHeaderViewHolder).bind(item)
|
||||
is Song -> (holder as AlbumSongViewHolder).bind(item)
|
||||
}
|
||||
}
|
||||
|
||||
inner class AlbumHeaderViewHolder(
|
||||
private val binding: ItemAlbumHeaderBinding
|
||||
) : BaseViewHolder<Album>(binding, null, null) {
|
||||
|
||||
override fun onBind(data: Album) {
|
||||
binding.album = data
|
||||
binding.detailModel = detailModel
|
||||
binding.lifecycleOwner = lifecycleOwner
|
||||
|
||||
if (data.songs.size < 2) {
|
||||
binding.albumSortButton.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class AlbumSongViewHolder(
|
||||
private val binding: ItemAlbumSongBinding,
|
||||
) : BaseViewHolder<Song>(binding, doOnClick, doOnLongClick) {
|
||||
override fun onBind(data: Song) {
|
||||
binding.song = data
|
||||
|
||||
binding.songName.requestLayout()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ALBUM_HEADER_ITEM_TYPE = 0xA024
|
||||
const val ALBUM_SONG_ITEM_TYPE = 0xA025
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
package org.oxycblt.auxio.detail.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.recycler.DiffCallback
|
||||
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||
import org.oxycblt.auxio.ui.accent
|
||||
import org.oxycblt.auxio.ui.toColor
|
||||
|
||||
/**
|
||||
* An adapter for displaying the [Song]s of an album.
|
||||
*/
|
||||
class AlbumSongAdapter(
|
||||
private val doOnClick: (data: Song) -> Unit,
|
||||
private val doOnLongClick: (data: Song, view: View) -> Unit
|
||||
) : ListAdapter<Song, AlbumSongAdapter.ViewHolder>(DiffCallback()) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
ItemAlbumSongBinding.inflate(LayoutInflater.from(parent.context))
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(getItem(position))
|
||||
}
|
||||
|
||||
// Generic ViewHolder for a song
|
||||
inner class ViewHolder(
|
||||
private val binding: ItemAlbumSongBinding,
|
||||
) : BaseViewHolder<Song>(binding, doOnClick, doOnLongClick) {
|
||||
private val normalColor = binding.songName.currentTextColor
|
||||
private val inactiveColor = binding.songTrack.currentTextColor
|
||||
|
||||
override fun onBind(data: Song) {
|
||||
binding.song = data
|
||||
|
||||
binding.songName.requestLayout()
|
||||
}
|
||||
|
||||
fun setPlaying(context: Context) {
|
||||
val accentColor = accent.first.toColor(context)
|
||||
|
||||
binding.songName.setTextColor(accentColor)
|
||||
binding.songTrack.setTextColor(accentColor)
|
||||
}
|
||||
|
||||
fun removePlaying() {
|
||||
binding.songName.setTextColor(normalColor)
|
||||
binding.songTrack.setTextColor(inactiveColor)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ import org.oxycblt.auxio.ui.disable
|
|||
/**
|
||||
* An adapter for displaying the [Album]s of an artist.
|
||||
*/
|
||||
class ArtistAlbumAdapter(
|
||||
class ArtistDetailAdapter(
|
||||
private val detailModel: DetailViewModel,
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val doOnClick: (data: Album) -> Unit,
|
|
@ -19,7 +19,7 @@ import org.oxycblt.auxio.ui.disable
|
|||
/**
|
||||
* An adapter for displaying the [Song]s of a genre.
|
||||
*/
|
||||
class GenreSongAdapter(
|
||||
class GenreDetailAdapter(
|
||||
private val detailModel: DetailViewModel,
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val doOnClick: (data: Song) -> Unit,
|
|
@ -9,7 +9,6 @@ import android.widget.ImageButton
|
|||
import android.widget.TextView
|
||||
import androidx.databinding.BindingAdapter
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.recycler.SortMode
|
||||
|
||||
/**
|
||||
|
@ -174,7 +173,6 @@ fun TextView.bindAlbumYear(album: Album) {
|
|||
* Bind the [SortMode] icon for an ImageButton.
|
||||
*/
|
||||
@BindingAdapter("sortIcon")
|
||||
fun ImageButton.bindSortIcon(data: SortMode) {
|
||||
logD("YOU STUPID FUCKING RETARD JUST FUNCITON")
|
||||
setImageResource(data.iconRes)
|
||||
fun ImageButton.bindSortIcon(mode: SortMode) {
|
||||
setImageResource(mode.iconRes)
|
||||
}
|
||||
|
|
|
@ -196,7 +196,7 @@ fun PopupMenu.setupAlbumActions(
|
|||
else -> false
|
||||
}
|
||||
}
|
||||
inflateAndShow(R.menu.menu_album_detail)
|
||||
inflateAndShow(R.menu.menu_album_actions)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -220,7 +220,7 @@ fun PopupMenu.setupArtistActions(artist: Artist, playbackModel: PlaybackViewMode
|
|||
else -> false
|
||||
}
|
||||
}
|
||||
inflateAndShow(R.menu.menu_artist_detail)
|
||||
inflateAndShow(R.menu.menu_artist_actions)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -239,7 +239,7 @@ fun PopupMenu.setupGenreActions(genre: Genre, playbackModel: PlaybackViewModel)
|
|||
else -> false
|
||||
}
|
||||
}
|
||||
inflateAndShow(R.menu.menu_genre_detail)
|
||||
inflateAndShow(R.menu.menu_genre_actions)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
<?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=".detail.AlbumDetailFragment">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="detailModel"
|
||||
type="org.oxycblt.auxio.detail.DetailViewModel" />
|
||||
|
||||
<variable
|
||||
name="playbackModel"
|
||||
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
|
||||
|
||||
<variable
|
||||
name="album"
|
||||
type="org.oxycblt.auxio.music.Album" />
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/album_toolbar"
|
||||
style="@style/Toolbar.Style.Icon"
|
||||
android:background="?android:attr/windowBackground"
|
||||
android:elevation="@dimen/elevation_normal"
|
||||
app:menu="@menu/menu_album_detail"
|
||||
app:title="@string/label_library" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/nested_scroll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/album_cover"
|
||||
android:layout_width="@dimen/size_cover_mid_huge"
|
||||
android:layout_height="@dimen/size_cover_mid_huge"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:contentDescription="@{@string/description_album_cover(album.name)}"
|
||||
app:coverArt="@{album}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_album" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_name"
|
||||
style="@style/DetailTitleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:layout_marginEnd="@dimen/margin_medium"
|
||||
android:text="@{album.name}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/album_artist"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="Album Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_artist"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:background="@drawable/ui_ripple"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:onClick="@{() -> detailModel.doNavToParent()}"
|
||||
android:text="@{album.artist.name}"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:layout_constraintBottom_toTopOf="@+id/album_details"
|
||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_name"
|
||||
tools:text="Artist Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_details"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:albumDetails="@{album}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/album_song_header"
|
||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_artist"
|
||||
tools:text="2020 / 10 Songs / 16:16" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_song_header"
|
||||
style="@style/HeaderText"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:text="@string/label_songs"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_cover" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/album_sort_button"
|
||||
style="@style/HeaderAction"
|
||||
android:contentDescription="@string/description_sort_button"
|
||||
android:onClick="@{() -> detailModel.incrementAlbumSortMode()}"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/album_song_header"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/album_song_header"
|
||||
tools:src="@drawable/ic_sort_numeric_down" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/album_song_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:overScrollMode="never"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_song_header"
|
||||
app:spanCount="2"
|
||||
tools:itemCount="6"
|
||||
tools:listitem="@layout/item_album_song" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
||||
</layout>
|
99
app/src/main/res/layout-land/item_album_header.xml
Normal file
99
app/src/main/res/layout-land/item_album_header.xml
Normal file
|
@ -0,0 +1,99 @@
|
|||
<?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">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="detailModel"
|
||||
type="org.oxycblt.auxio.detail.DetailViewModel" />
|
||||
|
||||
<variable
|
||||
name="album"
|
||||
type="org.oxycblt.auxio.music.Album" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/album_cover"
|
||||
android:layout_width="@dimen/size_cover_mid_huge"
|
||||
android:layout_height="@dimen/size_cover_mid_huge"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:contentDescription="@{@string/description_album_cover(album.name)}"
|
||||
app:coverArt="@{album}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_album" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_name"
|
||||
style="@style/DetailTitleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:layout_marginEnd="@dimen/margin_medium"
|
||||
android:text="@{album.name}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/album_artist"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="Album Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_artist"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:background="@drawable/ui_ripple"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:onClick="@{() -> detailModel.doNavToParent()}"
|
||||
android:text="@{album.artist.name}"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:layout_constraintBottom_toTopOf="@+id/album_details"
|
||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_name"
|
||||
tools:text="Artist Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_details"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:albumDetails="@{album}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/album_song_header"
|
||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_artist"
|
||||
tools:text="2020 / 10 Songs / 16:16" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_song_header"
|
||||
style="@style/HeaderText"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:text="@string/label_songs"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_cover" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/album_sort_button"
|
||||
style="@style/HeaderAction"
|
||||
android:contentDescription="@string/description_sort_button"
|
||||
android:onClick="@{() -> detailModel.incrementAlbumSortMode()}"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/album_song_header"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/album_song_header"
|
||||
app:sortIcon="@{detailModel.albumSortMode}"
|
||||
tools:src="@drawable/ic_sort_numeric_down" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -84,10 +84,10 @@
|
|||
style="@style/HeaderAction"
|
||||
android:contentDescription="@string/description_sort_button"
|
||||
android:onClick="@{() -> detailModel.incrementArtistSortMode()}"
|
||||
app:sortIcon="@{detailModel.artistSortMode}"
|
||||
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>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<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">
|
||||
|
||||
<data>
|
||||
|
||||
|
@ -17,9 +17,7 @@
|
|||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/genre_image"
|
||||
|
@ -53,9 +51,9 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:text="@{@plurals/format_song_count(genre.songs.size(), genre.songs.size())}"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:text="@{@plurals/format_song_count(genre.songs.size(), genre.songs.size())}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/genre_duration"
|
||||
app:layout_constraintStart_toEndOf="@+id/genre_image"
|
||||
app:layout_constraintTop_toBottomOf="@+id/genre_name"
|
||||
|
@ -89,8 +87,8 @@
|
|||
android:onClick="@{() -> detailModel.incrementGenreSortMode()}"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/genre_song_header"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:sortIcon="@{detailModel.genreSortMode}"
|
||||
app:layout_constraintTop_toTopOf="@+id/genre_song_header"
|
||||
app:sortIcon="@{detailModel.genreSortMode}"
|
||||
tools:src="@drawable/ic_sort_alpha_down" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
<?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=".detail.AlbumDetailFragment">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="detailModel"
|
||||
type="org.oxycblt.auxio.detail.DetailViewModel" />
|
||||
|
||||
<variable
|
||||
name="playbackModel"
|
||||
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
|
||||
|
||||
<variable
|
||||
name="album"
|
||||
type="org.oxycblt.auxio.music.Album" />
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/album_toolbar"
|
||||
style="@style/Toolbar.Style.Icon"
|
||||
android:background="?android:attr/windowBackground"
|
||||
android:elevation="@dimen/elevation_normal"
|
||||
app:menu="@menu/menu_album_detail"
|
||||
app:title="@string/label_library" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/nested_scroll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/album_cover"
|
||||
android:layout_width="@dimen/size_cover_huge"
|
||||
android:layout_height="@dimen/size_cover_huge"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:contentDescription="@{@string/description_album_cover(album.name)}"
|
||||
app:coverArt="@{album}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_album" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_name"
|
||||
style="@style/DetailTitleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:layout_marginEnd="@dimen/margin_medium"
|
||||
android:text="@{album.name}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_cover"
|
||||
tools:text="Album Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_artist"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:background="@drawable/ui_ripple"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:onClick="@{() -> detailModel.doNavToParent()}"
|
||||
android:text="@{album.artist.name}"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_name"
|
||||
tools:text="Artist Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_details"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:albumDetails="@{album}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_artist"
|
||||
tools:text="2020 / 10 Songs / 16:16" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_song_header"
|
||||
style="@style/HeaderText"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:text="@string/label_songs"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_details" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/album_sort_button"
|
||||
style="@style/HeaderAction"
|
||||
android:contentDescription="@string/description_sort_button"
|
||||
android:onClick="@{() -> detailModel.incrementAlbumSortMode()}"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/album_song_header"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/album_song_header"
|
||||
tools:src="@drawable/ic_sort_numeric_down" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/album_song_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:overScrollMode="never"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_song_header"
|
||||
tools:itemCount="4"
|
||||
tools:listitem="@layout/item_album_song" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
||||
</layout>
|
|
@ -14,9 +14,9 @@
|
|||
style="@style/Toolbar.Style.Icon"
|
||||
android:background="?android:attr/windowBackground"
|
||||
android:elevation="@dimen/elevation_normal"
|
||||
app:title="@string/label_library"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
tools:menu="@menu/menu_artist_detail"/>
|
||||
app:title="@string/label_library"
|
||||
tools:menu="@menu/menu_artist_actions" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/detail_recycler"
|
||||
|
|
96
app/src/main/res/layout/item_album_header.xml
Normal file
96
app/src/main/res/layout/item_album_header.xml
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?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">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="detailModel"
|
||||
type="org.oxycblt.auxio.detail.DetailViewModel" />
|
||||
|
||||
<variable
|
||||
name="album"
|
||||
type="org.oxycblt.auxio.music.Album" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/album_cover"
|
||||
android:layout_width="@dimen/size_cover_huge"
|
||||
android:layout_height="@dimen/size_cover_huge"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:contentDescription="@{@string/description_album_cover(album.name)}"
|
||||
app:coverArt="@{album}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_album" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_name"
|
||||
style="@style/DetailTitleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:layout_marginEnd="@dimen/margin_medium"
|
||||
android:text="@{album.name}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_cover"
|
||||
tools:text="Album Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_artist"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:background="@drawable/ui_ripple"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:onClick="@{() -> detailModel.doNavToParent()}"
|
||||
android:text="@{album.artist.name}"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_name"
|
||||
tools:text="Artist Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_details"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_medium"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:albumDetails="@{album}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_artist"
|
||||
tools:text="2020 / 10 Songs / 16:16" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_song_header"
|
||||
style="@style/HeaderText"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:text="@string/label_songs"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_details" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/album_sort_button"
|
||||
style="@style/HeaderAction"
|
||||
android:contentDescription="@string/description_sort_button"
|
||||
android:onClick="@{() -> detailModel.incrementAlbumSortMode()}"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/album_song_header"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/album_song_header"
|
||||
app:sortIcon="@{detailModel.albumSortMode}"
|
||||
tools:src="@drawable/ic_sort_numeric_down" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -82,10 +82,10 @@
|
|||
style="@style/HeaderAction"
|
||||
android:contentDescription="@string/description_sort_button"
|
||||
android:onClick="@{() -> detailModel.incrementArtistSortMode()}"
|
||||
app:sortIcon="@{detailModel.artistSortMode}"
|
||||
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>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<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">
|
||||
|
||||
<data>
|
||||
|
||||
|
@ -17,9 +17,7 @@
|
|||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/genre_image"
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
android:id="@+id/album_detail_fragment"
|
||||
android:name="org.oxycblt.auxio.detail.AlbumDetailFragment"
|
||||
android:label="AlbumDetailFragment"
|
||||
tools:layout="@layout/fragment_album_detail">
|
||||
tools:layout="@layout/fragment_detail">
|
||||
<argument
|
||||
android:name="albumId"
|
||||
app:argType="long" />
|
||||
|
|
Loading…
Reference in a new issue