detail: refactor module
Completely refactor the detail module. This is for a few reasons: - Prevent data regeneration every time a fragment re-creates. - Make DetailModel follow the customs of other ViewModels. - Simplify layouts into a single detail item to reduce code complexity. Currently sorting doesn't work, but that is still being worked out as the legacy SortMode continues to be phased out of Auxio.
This commit is contained in:
parent
d8c0037b10
commit
0e0be19e1d
65 changed files with 694 additions and 1740 deletions
|
@ -139,7 +139,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
|
systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
|
||||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||||
|
|
||||||
setOnApplyWindowInsetsListener { v, insets ->
|
setOnApplyWindowInsetsListener { _, insets ->
|
||||||
updatePadding(
|
updatePadding(
|
||||||
left = insets.systemWindowInsetLeft,
|
left = insets.systemWindowInsetLeft,
|
||||||
right = insets.systemWindowInsetRight
|
right = insets.systemWindowInsetRight
|
||||||
|
|
|
@ -106,7 +106,7 @@ data class Accent(
|
||||||
val hex = context.getString(color).uppercase()
|
val hex = context.getString(color).uppercase()
|
||||||
|
|
||||||
return HtmlCompat.fromHtml(
|
return HtmlCompat.fromHtml(
|
||||||
context.getString(R.string.format_accent_summary, name, hex),
|
context.getString(R.string.fmt_accent_desc, name, hex),
|
||||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,10 @@ fun ImageView.bindArtistImage(artist: Artist?) {
|
||||||
@BindingAdapter("genreImage")
|
@BindingAdapter("genreImage")
|
||||||
fun ImageView.bindGenreImage(genre: Genre?) {
|
fun ImageView.bindGenreImage(genre: Genre?) {
|
||||||
load(genre, R.drawable.ic_genre, MosaicFetcher(context))
|
load(genre, R.drawable.ic_genre, MosaicFetcher(context))
|
||||||
|
|
||||||
|
if (genre != null) {
|
||||||
|
contentDescription = context.getString(R.string.desc_genre_image, genre.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -27,11 +27,11 @@ import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.LinearSmoothScroller
|
import androidx.recyclerview.widget.LinearSmoothScroller
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.detail.adapters.AlbumDetailAdapter
|
import org.oxycblt.auxio.detail.recycler.AlbumDetailAdapter
|
||||||
|
import org.oxycblt.auxio.music.ActionHeader
|
||||||
import org.oxycblt.auxio.music.Album
|
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.Header
|
||||||
import org.oxycblt.auxio.music.MusicStore
|
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
import org.oxycblt.auxio.ui.ActionMenu
|
import org.oxycblt.auxio.ui.ActionMenu
|
||||||
|
@ -52,20 +52,10 @@ class AlbumDetailFragment : DetailFragment() {
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
// If DetailViewModel isn't already storing the album, get it from MusicStore
|
detailModel.setAlbum(args.albumId, requireContext())
|
||||||
// using the ID given by the navigation arguments.
|
|
||||||
if (detailModel.currentAlbum.value == null ||
|
|
||||||
detailModel.currentAlbum.value?.id != args.albumId
|
|
||||||
) {
|
|
||||||
detailModel.updateAlbum(
|
|
||||||
MusicStore.getInstance().albums.find {
|
|
||||||
it.id == args.albumId
|
|
||||||
}!!
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val detailAdapter = AlbumDetailAdapter(
|
val detailAdapter = AlbumDetailAdapter(
|
||||||
detailModel, playbackModel, viewLifecycleOwner,
|
playbackModel, detailModel,
|
||||||
doOnClick = { playbackModel.playSong(it, PlaybackMode.IN_ALBUM) },
|
doOnClick = { playbackModel.playSong(it, PlaybackMode.IN_ALBUM) },
|
||||||
doOnLongClick = { view, data -> newMenu(view, data, ActionMenu.FLAG_IN_ALBUM) }
|
doOnLongClick = { view, data -> newMenu(view, data, ActionMenu.FLAG_IN_ALBUM) }
|
||||||
)
|
)
|
||||||
|
@ -76,7 +66,7 @@ class AlbumDetailFragment : DetailFragment() {
|
||||||
|
|
||||||
setupToolbar(R.menu.menu_album_detail) { itemId ->
|
setupToolbar(R.menu.menu_album_detail) { itemId ->
|
||||||
if (itemId == R.id.action_queue_add) {
|
if (itemId == R.id.action_queue_add) {
|
||||||
playbackModel.addToUserQueue(detailModel.currentAlbum.value!!)
|
playbackModel.addToUserQueue(detailModel.curAlbum.value!!)
|
||||||
requireContext().showToast(R.string.lbl_queue_added)
|
requireContext().showToast(R.string.lbl_queue_added)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
@ -85,19 +75,13 @@ class AlbumDetailFragment : DetailFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
setupRecycler(detailAdapter) { pos ->
|
setupRecycler(detailAdapter) { pos ->
|
||||||
pos == 0
|
val item = detailAdapter.currentList[pos]
|
||||||
|
item is Header || item is ActionHeader || item is Album
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- DETAILVIEWMODEL SETUP ---
|
// -- DETAILVIEWMODEL SETUP ---
|
||||||
|
|
||||||
detailModel.albumSortMode.observe(viewLifecycleOwner) { mode ->
|
detailModel.albumData.observe(viewLifecycleOwner) { data ->
|
||||||
logD("Updating sort mode to $mode")
|
|
||||||
|
|
||||||
// Detail header data is included
|
|
||||||
val data = mutableListOf<BaseModel>(detailModel.currentAlbum.value!!).also {
|
|
||||||
it.addAll(mode.getSortedSongList(detailModel.currentAlbum.value!!.songs))
|
|
||||||
}
|
|
||||||
|
|
||||||
detailAdapter.submitList(data)
|
detailAdapter.submitList(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,10 +90,10 @@ class AlbumDetailFragment : DetailFragment() {
|
||||||
// Songs should be scrolled to if the album matches, or a new detail
|
// Songs should be scrolled to if the album matches, or a new detail
|
||||||
// fragment should be launched otherwise.
|
// fragment should be launched otherwise.
|
||||||
is Song -> {
|
is Song -> {
|
||||||
if (detailModel.currentAlbum.value!!.id == item.album.id) {
|
if (detailModel.curAlbum.value!!.id == item.album.id) {
|
||||||
scrollToItem(item.id)
|
scrollToItem(item.id, detailAdapter)
|
||||||
|
|
||||||
detailModel.doneWithNavToItem()
|
detailModel.finishNavToItem()
|
||||||
} else {
|
} else {
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
AlbumDetailFragmentDirections.actionShowAlbum(item.album.id)
|
AlbumDetailFragmentDirections.actionShowAlbum(item.album.id)
|
||||||
|
@ -120,9 +104,9 @@ class AlbumDetailFragment : DetailFragment() {
|
||||||
// If the album matches, no need to do anything. Otherwise launch a new
|
// If the album matches, no need to do anything. Otherwise launch a new
|
||||||
// detail fragment.
|
// detail fragment.
|
||||||
is Album -> {
|
is Album -> {
|
||||||
if (detailModel.currentAlbum.value!!.id == item.id) {
|
if (detailModel.curAlbum.value!!.id == item.id) {
|
||||||
binding.detailRecycler.scrollToPosition(0)
|
binding.detailRecycler.scrollToPosition(0)
|
||||||
detailModel.doneWithNavToItem()
|
detailModel.finishNavToItem()
|
||||||
} else {
|
} else {
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
AlbumDetailFragmentDirections.actionShowAlbum(item.id)
|
AlbumDetailFragmentDirections.actionShowAlbum(item.id)
|
||||||
|
@ -146,7 +130,7 @@ class AlbumDetailFragment : DetailFragment() {
|
||||||
|
|
||||||
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
||||||
if (playbackModel.mode.value == PlaybackMode.IN_ALBUM &&
|
if (playbackModel.mode.value == PlaybackMode.IN_ALBUM &&
|
||||||
playbackModel.parent.value?.id == detailModel.currentAlbum.value!!.id
|
playbackModel.parent.value?.id == detailModel.curAlbum.value!!.id
|
||||||
) {
|
) {
|
||||||
detailAdapter.highlightSong(song, binding.detailRecycler)
|
detailAdapter.highlightSong(song, binding.detailRecycler)
|
||||||
} else {
|
} else {
|
||||||
|
@ -169,17 +153,15 @@ class AlbumDetailFragment : DetailFragment() {
|
||||||
/**
|
/**
|
||||||
* Scroll to an song using its [id].
|
* Scroll to an song using its [id].
|
||||||
*/
|
*/
|
||||||
private fun scrollToItem(id: Long) {
|
private fun scrollToItem(id: Long, adapter: AlbumDetailAdapter) {
|
||||||
// Calculate where the item for the currently played song is
|
// Calculate where the item for the currently played song is
|
||||||
val pos = detailModel.albumSortMode.value!!.getSortedSongList(
|
val pos = adapter.currentList.indexOfFirst { it.id == id && it is Album }
|
||||||
detailModel.currentAlbum.value!!.songs
|
|
||||||
).indexOfFirst { it.id == id }
|
|
||||||
|
|
||||||
if (pos != -1) {
|
if (pos != -1) {
|
||||||
binding.detailRecycler.post {
|
binding.detailRecycler.post {
|
||||||
// Make sure to increment the position to make up for the detail header
|
// Make sure to increment the position to make up for the detail header
|
||||||
binding.detailRecycler.layoutManager?.startSmoothScroll(
|
binding.detailRecycler.layoutManager?.startSmoothScroll(
|
||||||
CenterSmoothScroller(requireContext(), pos.inc())
|
CenterSmoothScroller(requireContext(), pos)
|
||||||
)
|
)
|
||||||
|
|
||||||
// If the recyclerview can scroll, its certain that it will have to scroll to
|
// If the recyclerview can scroll, its certain that it will have to scroll to
|
||||||
|
|
|
@ -24,17 +24,14 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.detail.recycler.ArtistDetailAdapter
|
||||||
import org.oxycblt.auxio.detail.adapters.ArtistDetailAdapter
|
import org.oxycblt.auxio.music.ActionHeader
|
||||||
import org.oxycblt.auxio.music.Album
|
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.Header
|
import org.oxycblt.auxio.music.Header
|
||||||
import org.oxycblt.auxio.music.MusicStore
|
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
import org.oxycblt.auxio.ui.ActionMenu
|
import org.oxycblt.auxio.ui.ActionMenu
|
||||||
import org.oxycblt.auxio.ui.SortMode
|
|
||||||
import org.oxycblt.auxio.ui.newMenu
|
import org.oxycblt.auxio.ui.newMenu
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
|
|
||||||
|
@ -50,20 +47,10 @@ class ArtistDetailFragment : DetailFragment() {
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
// If DetailViewModel isn't already storing the artist, get it from MusicStore
|
detailModel.setArtist(args.artistId, requireContext())
|
||||||
// using the ID given by the navigation arguments
|
|
||||||
if (detailModel.currentArtist.value == null ||
|
|
||||||
detailModel.currentArtist.value?.id != args.artistId
|
|
||||||
) {
|
|
||||||
detailModel.updateArtist(
|
|
||||||
MusicStore.getInstance().artists.find {
|
|
||||||
it.id == args.artistId
|
|
||||||
}!!
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val detailAdapter = ArtistDetailAdapter(
|
val detailAdapter = ArtistDetailAdapter(
|
||||||
playbackModel, detailModel,
|
playbackModel,
|
||||||
doOnClick = { data ->
|
doOnClick = { data ->
|
||||||
if (!detailModel.isNavigating) {
|
if (!detailModel.isNavigating) {
|
||||||
detailModel.setNavigating(true)
|
detailModel.setNavigating(true)
|
||||||
|
@ -88,39 +75,22 @@ class ArtistDetailFragment : DetailFragment() {
|
||||||
setupToolbar()
|
setupToolbar()
|
||||||
setupRecycler(detailAdapter) { pos ->
|
setupRecycler(detailAdapter) { pos ->
|
||||||
// If the item is an ActionHeader we need to also make the item full-width
|
// If the item is an ActionHeader we need to also make the item full-width
|
||||||
pos == 0 || detailAdapter.currentList.getOrNull(pos) is Header
|
val item = detailAdapter.currentList[pos]
|
||||||
|
item is Header || item is ActionHeader || item is Artist
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
|
|
||||||
detailModel.artistSortMode.observe(viewLifecycleOwner) { mode ->
|
detailModel.artistData.observe(viewLifecycleOwner) { data ->
|
||||||
logD("Updating sort mode to $mode")
|
|
||||||
|
|
||||||
val artist = detailModel.currentArtist.value!!
|
|
||||||
|
|
||||||
val data = mutableListOf<BaseModel>(artist)
|
|
||||||
|
|
||||||
data.addAll(SortMode.NUMERIC_DOWN.getSortedAlbumList(artist.albums))
|
|
||||||
|
|
||||||
data.add(
|
|
||||||
Header(
|
|
||||||
id = -2,
|
|
||||||
name = getString(R.string.lbl_songs),
|
|
||||||
isAction = true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
data.addAll(mode.getSortedArtistSongList(artist.songs))
|
|
||||||
|
|
||||||
detailAdapter.submitList(data)
|
detailAdapter.submitList(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
detailModel.navToItem.observe(viewLifecycleOwner) { item ->
|
detailModel.navToItem.observe(viewLifecycleOwner) { item ->
|
||||||
when (item) {
|
when (item) {
|
||||||
is Artist -> {
|
is Artist -> {
|
||||||
if (item.id == detailModel.currentArtist.value!!.id) {
|
if (item.id == detailModel.curArtist.value?.id) {
|
||||||
binding.detailRecycler.scrollToPosition(0)
|
binding.detailRecycler.scrollToPosition(0)
|
||||||
detailModel.doneWithNavToItem()
|
detailModel.finishNavToItem()
|
||||||
} else {
|
} else {
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
ArtistDetailFragmentDirections.actionShowArtist(item.id)
|
ArtistDetailFragmentDirections.actionShowArtist(item.id)
|
||||||
|
@ -136,7 +106,8 @@ class ArtistDetailFragment : DetailFragment() {
|
||||||
ArtistDetailFragmentDirections.actionShowAlbum(item.album.id)
|
ArtistDetailFragmentDirections.actionShowAlbum(item.album.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> {}
|
else -> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +123,7 @@ class ArtistDetailFragment : DetailFragment() {
|
||||||
// Highlight songs if they are being played
|
// Highlight songs if they are being played
|
||||||
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
||||||
if (playbackModel.mode.value == PlaybackMode.IN_ARTIST &&
|
if (playbackModel.mode.value == PlaybackMode.IN_ARTIST &&
|
||||||
playbackModel.parent.value?.id == detailModel.currentArtist.value!!.id
|
playbackModel.parent.value?.id == detailModel.curArtist.value?.id
|
||||||
) {
|
) {
|
||||||
detailAdapter.highlightSong(song, binding.detailRecycler)
|
detailAdapter.highlightSong(song, binding.detailRecycler)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -36,9 +36,6 @@ import org.oxycblt.auxio.util.isLandscape
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Base [Fragment] implementing the base features shared across all detail fragments.
|
* A Base [Fragment] implementing the base features shared across all detail fragments.
|
||||||
* TODO: Want to implement something using CollapsingToolbarLayout. This would eliminate alot of
|
|
||||||
* the complexity from this ball of mud, but I have to do something to fix the scroll stopping
|
|
||||||
* issue.
|
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
abstract class DetailFragment : Fragment() {
|
abstract class DetailFragment : Fragment() {
|
||||||
|
|
|
@ -18,112 +18,135 @@
|
||||||
|
|
||||||
package org.oxycblt.auxio.detail
|
package org.oxycblt.auxio.detail
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.home.LibSortMode
|
||||||
|
import org.oxycblt.auxio.music.ActionHeader
|
||||||
import org.oxycblt.auxio.music.Album
|
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.settings.SettingsManager
|
import org.oxycblt.auxio.music.Header
|
||||||
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.ui.SortMode
|
import org.oxycblt.auxio.ui.SortMode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ViewModel that stores data for the [DetailFragment]s, such as what they're showing & what
|
* ViewModel that stores data for the [DetailFragment]s, such as what they're showing & what
|
||||||
* [SortMode] they are currently on.
|
* [SortMode] they are currently on.
|
||||||
* TODO: Redo sorting here
|
* TODO: Re-add sorting
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class DetailViewModel : ViewModel() {
|
class DetailViewModel : ViewModel() {
|
||||||
private val settingsManager = SettingsManager.getInstance()
|
|
||||||
|
|
||||||
// --- CURRENT VALUES ---
|
// --- CURRENT VALUES ---
|
||||||
|
|
||||||
private val mCurrentGenre = MutableLiveData<Genre?>()
|
private val mCurGenre = MutableLiveData<Genre?>()
|
||||||
val currentGenre: LiveData<Genre?> get() = mCurrentGenre
|
val curGenre: LiveData<Genre?> get() = mCurGenre
|
||||||
|
|
||||||
private val mCurrentArtist = MutableLiveData<Artist?>()
|
private val mGenreData = MutableLiveData(listOf<BaseModel>())
|
||||||
val currentArtist: LiveData<Artist?> get() = mCurrentArtist
|
val genreData: LiveData<List<BaseModel>> = mGenreData
|
||||||
|
|
||||||
private val mCurrentAlbum = MutableLiveData<Album?>()
|
private val mCurArtist = MutableLiveData<Artist?>()
|
||||||
val currentAlbum: LiveData<Album?> get() = mCurrentAlbum
|
val curArtist: LiveData<Artist?> get() = mCurArtist
|
||||||
|
|
||||||
// --- SORT MODES ---
|
private val mArtistData = MutableLiveData(listOf<BaseModel>())
|
||||||
|
val artistData: LiveData<List<BaseModel>> = mArtistData
|
||||||
|
|
||||||
private val mGenreSortMode = MutableLiveData(settingsManager.genreSortMode)
|
private val mCurAlbum = MutableLiveData<Album?>()
|
||||||
val genreSortMode: LiveData<SortMode> get() = mGenreSortMode
|
val curAlbum: LiveData<Album?> get() = mCurAlbum
|
||||||
|
|
||||||
private val mArtistSortMode = MutableLiveData(settingsManager.artistSortMode)
|
private val mAlbumData = MutableLiveData(listOf<BaseModel>())
|
||||||
val albumSortMode: LiveData<SortMode> get() = mAlbumSortMode
|
val albumData: LiveData<List<BaseModel>> get() = mAlbumData
|
||||||
|
|
||||||
private val mAlbumSortMode = MutableLiveData(settingsManager.albumSortMode)
|
var isNavigating = false
|
||||||
val artistSortMode: LiveData<SortMode> get() = mArtistSortMode
|
private set
|
||||||
|
|
||||||
private var mIsNavigating = false
|
|
||||||
val isNavigating: Boolean get() = mIsNavigating
|
|
||||||
|
|
||||||
private val mNavToItem = MutableLiveData<BaseModel?>()
|
private val mNavToItem = MutableLiveData<BaseModel?>()
|
||||||
|
|
||||||
/** Flag for unified navigation. Observe this to coordinate navigation to an item's UI. */
|
/** Flag for unified navigation. Observe this to coordinate navigation to an item's UI. */
|
||||||
val navToItem: LiveData<BaseModel?> get() = mNavToItem
|
val navToItem: LiveData<BaseModel?> get() = mNavToItem
|
||||||
|
|
||||||
fun updateGenre(genre: Genre) {
|
private val musicStore = MusicStore.getInstance()
|
||||||
mCurrentGenre.value = genre
|
|
||||||
|
fun setGenre(id: Long, context: Context) {
|
||||||
|
if (mCurGenre.value?.id == id) return
|
||||||
|
|
||||||
|
mCurGenre.value = musicStore.genres.find { it.id == id }
|
||||||
|
|
||||||
|
val data = mutableListOf<BaseModel>(curGenre.value!!)
|
||||||
|
|
||||||
|
data.add(
|
||||||
|
ActionHeader(
|
||||||
|
id = -2,
|
||||||
|
name = context.getString(R.string.lbl_songs),
|
||||||
|
icon = R.drawable.ic_sort,
|
||||||
|
desc = R.string.lbl_sort,
|
||||||
|
onClick = {
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
data.addAll(LibSortMode.ASCENDING.sortGenre(curGenre.value!!))
|
||||||
|
|
||||||
|
mGenreData.value = data
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateArtist(artist: Artist) {
|
fun setArtist(id: Long, context: Context) {
|
||||||
mCurrentArtist.value = artist
|
if (mCurArtist.value?.id == id) return
|
||||||
|
|
||||||
|
mCurArtist.value = musicStore.artists.find { it.id == id }
|
||||||
|
|
||||||
|
val artist = curArtist.value!!
|
||||||
|
val data = mutableListOf<BaseModel>(artist)
|
||||||
|
|
||||||
|
data.add(
|
||||||
|
Header(
|
||||||
|
id = -2,
|
||||||
|
name = context.getString(R.string.lbl_albums)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
data.addAll(LibSortMode.YEAR.sortAlbums(artist.albums))
|
||||||
|
|
||||||
|
data.add(
|
||||||
|
ActionHeader(
|
||||||
|
id = -3,
|
||||||
|
name = context.getString(R.string.lbl_songs),
|
||||||
|
icon = R.drawable.ic_sort,
|
||||||
|
desc = R.string.lbl_sort,
|
||||||
|
onClick = {
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
data.addAll(LibSortMode.YEAR.sortArtist(artist))
|
||||||
|
|
||||||
|
mArtistData.value = data.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateAlbum(album: Album) {
|
fun setAlbum(id: Long, context: Context) {
|
||||||
mCurrentAlbum.value = album
|
if (mCurAlbum.value?.id == id) return
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
mCurAlbum.value = musicStore.albums.find { it.id == id }
|
||||||
* Increment the sort mode of the genre artists
|
|
||||||
*/
|
|
||||||
fun incrementGenreSortMode() {
|
|
||||||
val mode = when (mGenreSortMode.value) {
|
|
||||||
SortMode.ALPHA_DOWN -> SortMode.ALPHA_UP
|
|
||||||
SortMode.ALPHA_UP -> SortMode.ALPHA_DOWN
|
|
||||||
|
|
||||||
else -> SortMode.ALPHA_DOWN
|
val data = mutableListOf<BaseModel>(curAlbum.value!!)
|
||||||
}
|
|
||||||
|
|
||||||
mGenreSortMode.value = mode
|
data.add(
|
||||||
settingsManager.genreSortMode = mode
|
ActionHeader(
|
||||||
}
|
id = -2,
|
||||||
|
name = context.getString(R.string.lbl_songs),
|
||||||
|
icon = R.drawable.ic_sort,
|
||||||
|
desc = R.string.lbl_sort,
|
||||||
|
onClick = {
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
data.addAll(LibSortMode.ASCENDING.sortAlbum(curAlbum.value!!))
|
||||||
* Increment the sort mode of the artist albums
|
|
||||||
*/
|
|
||||||
fun incrementArtistSortMode() {
|
|
||||||
val mode = when (mArtistSortMode.value) {
|
|
||||||
SortMode.NUMERIC_DOWN -> SortMode.NUMERIC_UP
|
|
||||||
SortMode.NUMERIC_UP -> SortMode.ALPHA_DOWN
|
|
||||||
SortMode.ALPHA_DOWN -> SortMode.ALPHA_UP
|
|
||||||
SortMode.ALPHA_UP -> SortMode.NUMERIC_DOWN
|
|
||||||
|
|
||||||
else -> SortMode.NUMERIC_DOWN
|
mAlbumData.value = data
|
||||||
}
|
|
||||||
|
|
||||||
mArtistSortMode.value = mode
|
|
||||||
settingsManager.artistSortMode = mode
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increment the sort mode of the album song
|
|
||||||
*/
|
|
||||||
fun incrementAlbumSortMode() {
|
|
||||||
val mode = when (mAlbumSortMode.value) {
|
|
||||||
SortMode.NUMERIC_DOWN -> SortMode.NUMERIC_UP
|
|
||||||
SortMode.NUMERIC_UP -> SortMode.NUMERIC_DOWN
|
|
||||||
|
|
||||||
else -> SortMode.NUMERIC_DOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
mAlbumSortMode.value = mode
|
|
||||||
settingsManager.albumSortMode = mAlbumSortMode.value!!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -136,14 +159,14 @@ class DetailViewModel : ViewModel() {
|
||||||
/**
|
/**
|
||||||
* Mark that the navigation process is done.
|
* Mark that the navigation process is done.
|
||||||
*/
|
*/
|
||||||
fun doneWithNavToItem() {
|
fun finishNavToItem() {
|
||||||
mNavToItem.value = null
|
mNavToItem.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the current navigation status to [isNavigating]
|
* Update the current navigation status to [isNavigating]
|
||||||
*/
|
*/
|
||||||
fun setNavigating(isNavigating: Boolean) {
|
fun setNavigating(navigating: Boolean) {
|
||||||
mIsNavigating = isNavigating
|
isNavigating = navigating
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,12 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import org.oxycblt.auxio.detail.adapters.GenreDetailAdapter
|
import org.oxycblt.auxio.detail.recycler.GenreDetailAdapter
|
||||||
|
import org.oxycblt.auxio.music.ActionHeader
|
||||||
import org.oxycblt.auxio.music.Album
|
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.Genre
|
||||||
import org.oxycblt.auxio.music.MusicStore
|
import org.oxycblt.auxio.music.Header
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
import org.oxycblt.auxio.ui.ActionMenu
|
import org.oxycblt.auxio.ui.ActionMenu
|
||||||
|
@ -47,20 +48,10 @@ class GenreDetailFragment : DetailFragment() {
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
// If DetailViewModel isn't already storing the genre, get it from MusicStore
|
detailModel.setGenre(args.genreId, requireContext())
|
||||||
// using the ID given by the navigation arguments
|
|
||||||
if (detailModel.currentGenre.value == null ||
|
|
||||||
detailModel.currentGenre.value?.id != args.genreId
|
|
||||||
) {
|
|
||||||
detailModel.updateGenre(
|
|
||||||
MusicStore.getInstance().genres.find {
|
|
||||||
it.id == args.genreId
|
|
||||||
}!!
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val detailAdapter = GenreDetailAdapter(
|
val detailAdapter = GenreDetailAdapter(
|
||||||
detailModel, playbackModel, viewLifecycleOwner,
|
playbackModel,
|
||||||
doOnClick = { song ->
|
doOnClick = { song ->
|
||||||
playbackModel.playSong(song, PlaybackMode.IN_GENRE)
|
playbackModel.playSong(song, PlaybackMode.IN_GENRE)
|
||||||
},
|
},
|
||||||
|
@ -75,19 +66,13 @@ class GenreDetailFragment : DetailFragment() {
|
||||||
|
|
||||||
setupToolbar()
|
setupToolbar()
|
||||||
setupRecycler(detailAdapter) { pos ->
|
setupRecycler(detailAdapter) { pos ->
|
||||||
pos == 0
|
val item = detailAdapter.currentList[pos]
|
||||||
|
item is Header || item is ActionHeader || item is Genre
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- DETAILVIEWMODEL SETUP ---
|
// --- DETAILVIEWMODEL SETUP ---
|
||||||
|
|
||||||
detailModel.genreSortMode.observe(viewLifecycleOwner) { mode ->
|
detailModel.genreData.observe(viewLifecycleOwner) { data ->
|
||||||
logD("Updating sort mode to $mode")
|
|
||||||
|
|
||||||
// Detail header data is included
|
|
||||||
val data = mutableListOf<BaseModel>(detailModel.currentGenre.value!!).also {
|
|
||||||
it.addAll(mode.getSortedSongList(detailModel.currentGenre.value!!.songs))
|
|
||||||
}
|
|
||||||
|
|
||||||
detailAdapter.submitList(data)
|
detailAdapter.submitList(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +91,8 @@ class GenreDetailFragment : DetailFragment() {
|
||||||
GenreDetailFragmentDirections.actionShowAlbum(item.album.id)
|
GenreDetailFragmentDirections.actionShowAlbum(item.album.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> {}
|
else -> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +100,7 @@ class GenreDetailFragment : DetailFragment() {
|
||||||
|
|
||||||
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
||||||
if (playbackModel.mode.value == PlaybackMode.IN_GENRE &&
|
if (playbackModel.mode.value == PlaybackMode.IN_GENRE &&
|
||||||
playbackModel.parent.value?.id == detailModel.currentGenre.value!!.id
|
playbackModel.parent.value?.id == detailModel.curGenre.value!!.id
|
||||||
) {
|
) {
|
||||||
detailAdapter.highlightSong(song, binding.detailRecycler)
|
detailAdapter.highlightSong(song, binding.detailRecycler)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,23 +16,26 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.oxycblt.auxio.detail.adapters
|
package org.oxycblt.auxio.detail.recycler
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.lifecycle.LifecycleOwner
|
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.databinding.ItemAlbumHeaderBinding
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.coil.bindAlbumArt
|
||||||
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
|
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
|
||||||
|
import org.oxycblt.auxio.databinding.ItemDetailBinding
|
||||||
import org.oxycblt.auxio.detail.DetailViewModel
|
import org.oxycblt.auxio.detail.DetailViewModel
|
||||||
|
import org.oxycblt.auxio.music.ActionHeader
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
import org.oxycblt.auxio.music.BaseModel
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
|
import org.oxycblt.auxio.ui.ActionHeaderViewHolder
|
||||||
import org.oxycblt.auxio.ui.BaseViewHolder
|
import org.oxycblt.auxio.ui.BaseViewHolder
|
||||||
import org.oxycblt.auxio.ui.DiffCallback
|
import org.oxycblt.auxio.ui.DiffCallback
|
||||||
import org.oxycblt.auxio.util.disable
|
import org.oxycblt.auxio.util.getPlural
|
||||||
import org.oxycblt.auxio.util.inflater
|
import org.oxycblt.auxio.util.inflater
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,9 +43,8 @@ import org.oxycblt.auxio.util.inflater
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class AlbumDetailAdapter(
|
class AlbumDetailAdapter(
|
||||||
private val detailModel: DetailViewModel,
|
|
||||||
private val playbackModel: PlaybackViewModel,
|
private val playbackModel: PlaybackViewModel,
|
||||||
private val lifecycleOwner: LifecycleOwner,
|
private val detailModel: DetailViewModel,
|
||||||
private val doOnClick: (data: Song) -> Unit,
|
private val doOnClick: (data: Song) -> Unit,
|
||||||
private val doOnLongClick: (view: View, data: Song) -> Unit
|
private val doOnLongClick: (view: View, data: Song) -> Unit
|
||||||
) : ListAdapter<BaseModel, RecyclerView.ViewHolder>(DiffCallback()) {
|
) : ListAdapter<BaseModel, RecyclerView.ViewHolder>(DiffCallback()) {
|
||||||
|
@ -52,20 +54,25 @@ class AlbumDetailAdapter(
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
return when (getItem(position)) {
|
return when (getItem(position)) {
|
||||||
is Album -> ALBUM_HEADER_ITEM_TYPE
|
is Album -> ALBUM_HEADER_ITEM_TYPE
|
||||||
|
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||||
is Song -> ALBUM_SONG_ITEM_TYPE
|
is Song -> ALBUM_SONG_ITEM_TYPE
|
||||||
|
|
||||||
else -> -1
|
else -> -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
ALBUM_HEADER_ITEM_TYPE -> AlbumHeaderViewHolder(
|
ALBUM_HEADER_ITEM_TYPE -> AlbumHeaderViewHolder(
|
||||||
ItemAlbumHeaderBinding.inflate(parent.context.inflater)
|
ItemDetailBinding.inflate(parent.context.inflater)
|
||||||
)
|
)
|
||||||
|
|
||||||
ALBUM_SONG_ITEM_TYPE -> AlbumSongViewHolder(
|
ALBUM_SONG_ITEM_TYPE -> AlbumSongViewHolder(
|
||||||
ItemAlbumSongBinding.inflate(parent.context.inflater)
|
ItemAlbumSongBinding.inflate(parent.context.inflater)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
|
||||||
|
|
||||||
else -> error("Invalid ViewHolder item type $viewType")
|
else -> error("Invalid ViewHolder item type $viewType")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,18 +83,20 @@ class AlbumDetailAdapter(
|
||||||
when (item) {
|
when (item) {
|
||||||
is Album -> (holder as AlbumHeaderViewHolder).bind(item)
|
is Album -> (holder as AlbumHeaderViewHolder).bind(item)
|
||||||
is Song -> (holder as AlbumSongViewHolder).bind(item)
|
is Song -> (holder as AlbumSongViewHolder).bind(item)
|
||||||
|
is ActionHeader -> (holder as ActionHeaderViewHolder).bind(item)
|
||||||
|
|
||||||
else -> {}
|
else -> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentSong != null && position > 0) {
|
if (holder is Highlightable) {
|
||||||
if (item.id == currentSong?.id) {
|
if (item.id == currentSong?.id) {
|
||||||
// Reset the last ViewHolder before assigning the new, correct one to be highlighted
|
// Reset the last ViewHolder before assigning the new, correct one to be highlighted
|
||||||
currentHolder?.setHighlighted(false)
|
currentHolder?.setHighlighted(false)
|
||||||
currentHolder = (holder as Highlightable)
|
currentHolder = holder
|
||||||
holder.setHighlighted(true)
|
holder.setHighlighted(true)
|
||||||
} else {
|
} else {
|
||||||
(holder as Highlightable).setHighlighted(false)
|
holder.setHighlighted(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,17 +133,41 @@ class AlbumDetailAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class AlbumHeaderViewHolder(
|
inner class AlbumHeaderViewHolder(
|
||||||
private val binding: ItemAlbumHeaderBinding
|
private val binding: ItemDetailBinding
|
||||||
) : BaseViewHolder<Album>(binding) {
|
) : BaseViewHolder<Album>(binding) {
|
||||||
|
|
||||||
override fun onBind(data: Album) {
|
override fun onBind(data: Album) {
|
||||||
binding.album = data
|
binding.detailCover.apply {
|
||||||
binding.detailModel = detailModel
|
bindAlbumArt(data)
|
||||||
binding.playbackModel = playbackModel
|
contentDescription = context.getString(R.string.desc_album_cover, data.name)
|
||||||
binding.lifecycleOwner = lifecycleOwner
|
}
|
||||||
|
|
||||||
if (data.songs.size < 2) {
|
binding.detailName.text = data.name
|
||||||
binding.albumSortButton.disable()
|
|
||||||
|
binding.detailSubhead.apply {
|
||||||
|
text = data.artist.name
|
||||||
|
|
||||||
|
setOnClickListener {
|
||||||
|
detailModel.navToItem(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.detailInfo.text = binding.detailInfo.context.getString(
|
||||||
|
R.string.fmt_three,
|
||||||
|
data.year.toString(),
|
||||||
|
binding.detailInfo.context.getPlural(
|
||||||
|
R.plurals.fmt_song_count,
|
||||||
|
data.songs.size
|
||||||
|
),
|
||||||
|
data.totalDuration
|
||||||
|
)
|
||||||
|
|
||||||
|
binding.detailPlayButton.setOnClickListener {
|
||||||
|
playbackModel.playAlbum(data, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.detailShuffleButton.setOnClickListener {
|
||||||
|
playbackModel.playAlbum(data, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,36 +16,37 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.oxycblt.auxio.detail.adapters
|
package org.oxycblt.auxio.detail.recycler
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.databinding.ItemActionHeaderBinding
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.coil.bindArtistImage
|
||||||
import org.oxycblt.auxio.databinding.ItemArtistAlbumBinding
|
import org.oxycblt.auxio.databinding.ItemArtistAlbumBinding
|
||||||
import org.oxycblt.auxio.databinding.ItemArtistHeaderBinding
|
|
||||||
import org.oxycblt.auxio.databinding.ItemArtistSongBinding
|
import org.oxycblt.auxio.databinding.ItemArtistSongBinding
|
||||||
import org.oxycblt.auxio.detail.DetailViewModel
|
import org.oxycblt.auxio.databinding.ItemDetailBinding
|
||||||
|
import org.oxycblt.auxio.music.ActionHeader
|
||||||
import org.oxycblt.auxio.music.Album
|
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.Header
|
import org.oxycblt.auxio.music.Header
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
|
import org.oxycblt.auxio.ui.ActionHeaderViewHolder
|
||||||
import org.oxycblt.auxio.ui.BaseViewHolder
|
import org.oxycblt.auxio.ui.BaseViewHolder
|
||||||
import org.oxycblt.auxio.ui.DiffCallback
|
import org.oxycblt.auxio.ui.DiffCallback
|
||||||
import org.oxycblt.auxio.util.disable
|
import org.oxycblt.auxio.ui.HeaderViewHolder
|
||||||
|
import org.oxycblt.auxio.util.getPlural
|
||||||
import org.oxycblt.auxio.util.inflater
|
import org.oxycblt.auxio.util.inflater
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An adapter for displaying the [Album]s and [Song]s of an artist.
|
* An adapter for displaying the [Album]s and [Song]s of an artist.
|
||||||
* This isnt the nicest implementation, but it works.
|
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class ArtistDetailAdapter(
|
class ArtistDetailAdapter(
|
||||||
private val playbackModel: PlaybackViewModel,
|
private val playbackModel: PlaybackViewModel,
|
||||||
private val detailModel: DetailViewModel,
|
|
||||||
private val doOnClick: (data: Album) -> Unit,
|
private val doOnClick: (data: Album) -> Unit,
|
||||||
private val doOnSongClick: (data: Song) -> Unit,
|
private val doOnSongClick: (data: Song) -> Unit,
|
||||||
private val doOnLongClick: (view: View, data: BaseModel) -> Unit,
|
private val doOnLongClick: (view: View, data: BaseModel) -> Unit,
|
||||||
|
@ -60,8 +61,9 @@ class ArtistDetailAdapter(
|
||||||
return when (getItem(position)) {
|
return when (getItem(position)) {
|
||||||
is Artist -> ARTIST_HEADER_ITEM_TYPE
|
is Artist -> ARTIST_HEADER_ITEM_TYPE
|
||||||
is Album -> ARTIST_ALBUM_ITEM_TYPE
|
is Album -> ARTIST_ALBUM_ITEM_TYPE
|
||||||
is Header -> ARTIST_SONG_HEADER_ITEM_TYPE
|
|
||||||
is Song -> ARTIST_SONG_ITEM_TYPE
|
is Song -> ARTIST_SONG_ITEM_TYPE
|
||||||
|
is Header -> HeaderViewHolder.ITEM_TYPE
|
||||||
|
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||||
|
|
||||||
else -> -1
|
else -> -1
|
||||||
}
|
}
|
||||||
|
@ -70,21 +72,21 @@ class ArtistDetailAdapter(
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
ARTIST_HEADER_ITEM_TYPE -> ArtistHeaderViewHolder(
|
ARTIST_HEADER_ITEM_TYPE -> ArtistHeaderViewHolder(
|
||||||
ItemArtistHeaderBinding.inflate(parent.context.inflater)
|
ItemDetailBinding.inflate(parent.context.inflater)
|
||||||
)
|
)
|
||||||
|
|
||||||
ARTIST_ALBUM_ITEM_TYPE -> ArtistAlbumViewHolder(
|
ARTIST_ALBUM_ITEM_TYPE -> ArtistAlbumViewHolder(
|
||||||
ItemArtistAlbumBinding.inflate(parent.context.inflater)
|
ItemArtistAlbumBinding.inflate(parent.context.inflater)
|
||||||
)
|
)
|
||||||
|
|
||||||
ARTIST_SONG_HEADER_ITEM_TYPE -> ArtistSongHeaderViewHolder(
|
|
||||||
ItemActionHeaderBinding.inflate(parent.context.inflater)
|
|
||||||
)
|
|
||||||
|
|
||||||
ARTIST_SONG_ITEM_TYPE -> ArtistSongViewHolder(
|
ARTIST_SONG_ITEM_TYPE -> ArtistSongViewHolder(
|
||||||
ItemArtistSongBinding.inflate(parent.context.inflater)
|
ItemArtistSongBinding.inflate(parent.context.inflater)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
|
||||||
|
|
||||||
|
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
|
||||||
|
|
||||||
else -> error("Invalid ViewHolder item type $viewType")
|
else -> error("Invalid ViewHolder item type $viewType")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,9 +97,9 @@ class ArtistDetailAdapter(
|
||||||
when (item) {
|
when (item) {
|
||||||
is Artist -> (holder as ArtistHeaderViewHolder).bind(item)
|
is Artist -> (holder as ArtistHeaderViewHolder).bind(item)
|
||||||
is Album -> (holder as ArtistAlbumViewHolder).bind(item)
|
is Album -> (holder as ArtistAlbumViewHolder).bind(item)
|
||||||
is Header -> (holder as ArtistSongHeaderViewHolder).bind(item)
|
|
||||||
is Song -> (holder as ArtistSongViewHolder).bind(item)
|
is Song -> (holder as ArtistSongViewHolder).bind(item)
|
||||||
|
is Header -> (holder as HeaderViewHolder).bind(item)
|
||||||
|
is ActionHeader -> (holder as ActionHeaderViewHolder).bind(item)
|
||||||
else -> {
|
else -> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,22 +182,43 @@ class ArtistDetailAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ArtistHeaderViewHolder(
|
inner class ArtistHeaderViewHolder(
|
||||||
private val binding: ItemArtistHeaderBinding
|
private val binding: ItemDetailBinding
|
||||||
) : BaseViewHolder<Artist>(binding) {
|
) : BaseViewHolder<Artist>(binding) {
|
||||||
|
|
||||||
override fun onBind(data: Artist) {
|
override fun onBind(data: Artist) {
|
||||||
binding.artist = data
|
val context = binding.root.context
|
||||||
binding.playbackModel = playbackModel
|
|
||||||
|
binding.detailCover.apply {
|
||||||
|
bindArtistImage(data)
|
||||||
|
contentDescription = context.getString(R.string.desc_artist_image, data.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.detailName.text = data.name
|
||||||
|
|
||||||
|
binding.detailSubhead.text = data.genre?.resolvedName
|
||||||
|
?: context.getString(R.string.def_genre)
|
||||||
|
|
||||||
|
binding.detailInfo.text = context.getString(
|
||||||
|
R.string.fmt_counts,
|
||||||
|
context.getPlural(R.plurals.fmt_album_count, data.albums.size),
|
||||||
|
context.getPlural(R.plurals.fmt_song_count, data.songs.size)
|
||||||
|
)
|
||||||
|
|
||||||
|
binding.detailPlayButton.setOnClickListener {
|
||||||
|
playbackModel.playArtist(data, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.detailShuffleButton.setOnClickListener {
|
||||||
|
playbackModel.playArtist(data, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic ViewHolder for a detail album
|
|
||||||
inner class ArtistAlbumViewHolder(
|
inner class ArtistAlbumViewHolder(
|
||||||
private val binding: ItemArtistAlbumBinding,
|
private val binding: ItemArtistAlbumBinding,
|
||||||
) : BaseViewHolder<Album>(binding, doOnClick, doOnLongClick), Highlightable {
|
) : BaseViewHolder<Album>(binding, doOnClick, doOnLongClick), Highlightable {
|
||||||
override fun onBind(data: Album) {
|
override fun onBind(data: Album) {
|
||||||
binding.album = data
|
binding.album = data
|
||||||
|
|
||||||
binding.albumName.requestLayout()
|
binding.albumName.requestLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,39 +227,11 @@ class ArtistDetailAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ArtistSongHeaderViewHolder(
|
|
||||||
private val binding: ItemActionHeaderBinding
|
|
||||||
) : BaseViewHolder<Header>(binding) {
|
|
||||||
|
|
||||||
override fun onBind(data: Header) {
|
|
||||||
binding.header = data
|
|
||||||
|
|
||||||
binding.headerButton.apply {
|
|
||||||
val sortMode = detailModel.artistSortMode
|
|
||||||
val artist = detailModel.currentArtist.value!!
|
|
||||||
|
|
||||||
setImageResource(sortMode.value!!.iconRes)
|
|
||||||
|
|
||||||
setOnClickListener {
|
|
||||||
detailModel.incrementArtistSortMode()
|
|
||||||
setImageResource(sortMode.value!!.iconRes)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (artist.songs.size < 2) {
|
|
||||||
disable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class ArtistSongViewHolder(
|
inner class ArtistSongViewHolder(
|
||||||
private val binding: ItemArtistSongBinding,
|
private val binding: ItemArtistSongBinding,
|
||||||
) : BaseViewHolder<Song>(binding, doOnSongClick, doOnLongClick), Highlightable {
|
) : BaseViewHolder<Song>(binding, doOnSongClick, doOnLongClick), Highlightable {
|
||||||
private val normalTextColor = binding.songName.currentTextColor
|
|
||||||
|
|
||||||
override fun onBind(data: Song) {
|
override fun onBind(data: Song) {
|
||||||
binding.song = data
|
binding.song = data
|
||||||
|
|
||||||
binding.songName.requestLayout()
|
binding.songName.requestLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +243,6 @@ class ArtistDetailAdapter(
|
||||||
companion object {
|
companion object {
|
||||||
const val ARTIST_HEADER_ITEM_TYPE = 0xA009
|
const val ARTIST_HEADER_ITEM_TYPE = 0xA009
|
||||||
const val ARTIST_ALBUM_ITEM_TYPE = 0xA00A
|
const val ARTIST_ALBUM_ITEM_TYPE = 0xA00A
|
||||||
const val ARTIST_SONG_HEADER_ITEM_TYPE = 0xA00B
|
|
||||||
const val ARTIST_SONG_ITEM_TYPE = 0xA00C
|
const val ARTIST_SONG_ITEM_TYPE = 0xA00C
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,23 +16,25 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.oxycblt.auxio.detail.adapters
|
package org.oxycblt.auxio.detail.recycler
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.lifecycle.LifecycleOwner
|
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.databinding.ItemGenreHeaderBinding
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.coil.bindGenreImage
|
||||||
|
import org.oxycblt.auxio.databinding.ItemDetailBinding
|
||||||
import org.oxycblt.auxio.databinding.ItemGenreSongBinding
|
import org.oxycblt.auxio.databinding.ItemGenreSongBinding
|
||||||
import org.oxycblt.auxio.detail.DetailViewModel
|
import org.oxycblt.auxio.music.ActionHeader
|
||||||
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.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
|
import org.oxycblt.auxio.ui.ActionHeaderViewHolder
|
||||||
import org.oxycblt.auxio.ui.BaseViewHolder
|
import org.oxycblt.auxio.ui.BaseViewHolder
|
||||||
import org.oxycblt.auxio.ui.DiffCallback
|
import org.oxycblt.auxio.ui.DiffCallback
|
||||||
import org.oxycblt.auxio.util.disable
|
import org.oxycblt.auxio.util.getPlural
|
||||||
import org.oxycblt.auxio.util.inflater
|
import org.oxycblt.auxio.util.inflater
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,9 +42,7 @@ import org.oxycblt.auxio.util.inflater
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class GenreDetailAdapter(
|
class GenreDetailAdapter(
|
||||||
private val detailModel: DetailViewModel,
|
|
||||||
private val playbackModel: PlaybackViewModel,
|
private val playbackModel: PlaybackViewModel,
|
||||||
private val lifecycleOwner: LifecycleOwner,
|
|
||||||
private val doOnClick: (data: Song) -> Unit,
|
private val doOnClick: (data: Song) -> Unit,
|
||||||
private val doOnLongClick: (view: View, data: Song) -> Unit
|
private val doOnLongClick: (view: View, data: Song) -> Unit
|
||||||
) : ListAdapter<BaseModel, RecyclerView.ViewHolder>(DiffCallback()) {
|
) : ListAdapter<BaseModel, RecyclerView.ViewHolder>(DiffCallback()) {
|
||||||
|
@ -52,6 +52,7 @@ class GenreDetailAdapter(
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
return when (getItem(position)) {
|
return when (getItem(position)) {
|
||||||
is Genre -> GENRE_HEADER_ITEM_TYPE
|
is Genre -> GENRE_HEADER_ITEM_TYPE
|
||||||
|
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||||
is Song -> GENRE_SONG_ITEM_TYPE
|
is Song -> GENRE_SONG_ITEM_TYPE
|
||||||
|
|
||||||
else -> -1
|
else -> -1
|
||||||
|
@ -61,13 +62,15 @@ class GenreDetailAdapter(
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
GENRE_HEADER_ITEM_TYPE -> GenreHeaderViewHolder(
|
GENRE_HEADER_ITEM_TYPE -> GenreHeaderViewHolder(
|
||||||
ItemGenreHeaderBinding.inflate(parent.context.inflater)
|
ItemDetailBinding.inflate(parent.context.inflater)
|
||||||
)
|
)
|
||||||
|
|
||||||
GENRE_SONG_ITEM_TYPE -> GenreSongViewHolder(
|
GENRE_SONG_ITEM_TYPE -> GenreSongViewHolder(
|
||||||
ItemGenreSongBinding.inflate(parent.context.inflater),
|
ItemGenreSongBinding.inflate(parent.context.inflater),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
|
||||||
|
|
||||||
else -> error("Bad viewholder item type $viewType")
|
else -> error("Bad viewholder item type $viewType")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,18 +80,19 @@ class GenreDetailAdapter(
|
||||||
|
|
||||||
when (item) {
|
when (item) {
|
||||||
is Genre -> (holder as GenreHeaderViewHolder).bind(item)
|
is Genre -> (holder as GenreHeaderViewHolder).bind(item)
|
||||||
|
is ActionHeader -> (holder as ActionHeaderViewHolder).bind(item)
|
||||||
is Song -> (holder as GenreSongViewHolder).bind(item)
|
is Song -> (holder as GenreSongViewHolder).bind(item)
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentSong != null && position > 0) {
|
if (holder is Highlightable) {
|
||||||
if (item.id == currentSong?.id) {
|
if (item.id == currentSong?.id) {
|
||||||
// Reset the last ViewHolder before assigning the new, correct one to be highlighted
|
// Reset the last ViewHolder before assigning the new, correct one to be highlighted
|
||||||
currentHolder?.setHighlighted(false)
|
currentHolder?.setHighlighted(false)
|
||||||
currentHolder = (holder as Highlightable)
|
currentHolder = holder
|
||||||
holder.setHighlighted(true)
|
holder.setHighlighted(true)
|
||||||
} else {
|
} else {
|
||||||
(holder as Highlightable).setHighlighted(false)
|
holder.setHighlighted(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,16 +129,30 @@ class GenreDetailAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class GenreHeaderViewHolder(
|
inner class GenreHeaderViewHolder(
|
||||||
private val binding: ItemGenreHeaderBinding
|
private val binding: ItemDetailBinding
|
||||||
) : BaseViewHolder<Genre>(binding) {
|
) : BaseViewHolder<Genre>(binding) {
|
||||||
override fun onBind(data: Genre) {
|
override fun onBind(data: Genre) {
|
||||||
binding.genre = data
|
val context = binding.root.context
|
||||||
binding.detailModel = detailModel
|
|
||||||
binding.playbackModel = playbackModel
|
|
||||||
binding.lifecycleOwner = lifecycleOwner
|
|
||||||
|
|
||||||
if (data.songs.size < 2) {
|
binding.detailCover.apply {
|
||||||
binding.genreSortButton.disable()
|
bindGenreImage(data)
|
||||||
|
contentDescription = context.getString(R.string.desc_artist_image, data.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.detailName.text = data.name
|
||||||
|
|
||||||
|
binding.detailSubhead.apply {
|
||||||
|
text = context.getPlural(R.plurals.fmt_song_count, data.songs.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.detailInfo.text = data.totalDuration
|
||||||
|
|
||||||
|
binding.detailPlayButton.setOnClickListener {
|
||||||
|
playbackModel.playGenre(data, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.detailShuffleButton.setOnClickListener {
|
||||||
|
playbackModel.playGenre(data, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.oxycblt.auxio.detail.adapters
|
package org.oxycblt.auxio.detail.recycler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that allows the highlighting of certain ViewHolders
|
* Interface that allows the highlighting of certain ViewHolders
|
|
@ -25,7 +25,7 @@ import org.oxycblt.auxio.databinding.ItemExcludedDirBinding
|
||||||
import org.oxycblt.auxio.util.inflater
|
import org.oxycblt.auxio.util.inflater
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter that shows the blacklist entries and their "Clear" button.
|
* Adapter that shows the excluded directories and their "Clear" button.
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class ExcludedEntryAdapter(
|
class ExcludedEntryAdapter(
|
||||||
|
|
|
@ -21,7 +21,9 @@ package org.oxycblt.auxio.home
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
|
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.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.ui.sliceArticle
|
import org.oxycblt.auxio.ui.sliceArticle
|
||||||
|
|
||||||
|
@ -130,6 +132,22 @@ enum class LibSortMode(@IdRes val itemId: Int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the songs in an artist.
|
||||||
|
* @see sortSongs
|
||||||
|
*/
|
||||||
|
fun sortArtist(artist: Artist): List<Song> {
|
||||||
|
return sortSongs(artist.songs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the songs in a genre.
|
||||||
|
* @see sortSongs
|
||||||
|
*/
|
||||||
|
fun sortGenre(genre: Genre): List<Song> {
|
||||||
|
return sortSongs(genre.songs)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Convert a menu [id] to an instance of [LibSortMode].
|
* Convert a menu [id] to an instance of [LibSortMode].
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
package org.oxycblt.auxio.music
|
package org.oxycblt.auxio.music
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.view.View
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
// --- MUSIC MODELS ---
|
// --- MUSIC MODELS ---
|
||||||
|
|
||||||
|
@ -218,11 +221,21 @@ data class Genre(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A data object used solely for the "Header" UI element. Inherits [BaseModel].
|
* A data object used solely for the "Header" UI element.
|
||||||
* @param isAction Whether this header corresponds to an action or not
|
|
||||||
*/
|
*/
|
||||||
data class Header(
|
data class Header(
|
||||||
override val id: Long,
|
override val id: Long,
|
||||||
override val name: String,
|
override val name: String,
|
||||||
val isAction: Boolean = false
|
) : BaseModel()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A data object used for an action header. Like [Header], but with a button.
|
||||||
|
* Inherits [BaseModel].
|
||||||
|
*/
|
||||||
|
data class ActionHeader(
|
||||||
|
override val id: Long,
|
||||||
|
override val name: String,
|
||||||
|
@DrawableRes val icon: Int,
|
||||||
|
@StringRes val desc: Int,
|
||||||
|
val onClick: (View) -> Unit
|
||||||
) : BaseModel()
|
) : BaseModel()
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
package org.oxycblt.auxio.music
|
package org.oxycblt.auxio.music
|
||||||
|
|
||||||
import android.content.ContentUris
|
import android.content.ContentUris
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
|
@ -122,67 +121,16 @@ fun Long.toDuration(): String {
|
||||||
return durationString
|
return durationString
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert an integer to its formatted year.
|
|
||||||
*/
|
|
||||||
fun Int.toYear(context: Context): String {
|
|
||||||
return if (this > 0) {
|
|
||||||
toString()
|
|
||||||
} else {
|
|
||||||
context.getString(R.string.def_date)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- BINDING ADAPTERS ---
|
// --- BINDING ADAPTERS ---
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind the most prominent artist genre
|
|
||||||
*/
|
|
||||||
@BindingAdapter("artistGenre")
|
|
||||||
fun TextView.bindArtistGenre(artist: Artist) {
|
|
||||||
text = artist.genre?.resolvedName ?: context.getString(R.string.def_genre)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind the album + song counts for an artist
|
* Bind the album + song counts for an artist
|
||||||
*/
|
*/
|
||||||
@BindingAdapter("artistCounts")
|
@BindingAdapter("artistCounts")
|
||||||
fun TextView.bindArtistCounts(artist: Artist) {
|
fun TextView.bindArtistCounts(artist: Artist) {
|
||||||
val albums = context.getPlural(R.plurals.fmt_album_count, artist.albums.size)
|
|
||||||
val songs = context.getPlural(R.plurals.fmt_song_count, artist.songs.size)
|
|
||||||
|
|
||||||
text = context.getString(R.string.format_double_counts, albums, songs)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all album information, used on [org.oxycblt.auxio.detail.AlbumDetailFragment]
|
|
||||||
*/
|
|
||||||
@BindingAdapter("albumDetails")
|
|
||||||
fun TextView.bindAllAlbumDetails(album: Album) {
|
|
||||||
text = context.getString(
|
text = context.getString(
|
||||||
R.string.format_double_info,
|
R.string.fmt_counts,
|
||||||
album.year.toYear(context),
|
context.getPlural(R.plurals.fmt_album_count, artist.albums.size),
|
||||||
context.getPlural(R.plurals.fmt_song_count, album.songs.size),
|
context.getPlural(R.plurals.fmt_song_count, artist.songs.size)
|
||||||
album.totalDuration
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get basic information about an album, used on album ViewHolders
|
|
||||||
*/
|
|
||||||
@BindingAdapter("albumInfo")
|
|
||||||
fun TextView.bindAlbumInfo(album: Album) {
|
|
||||||
text = context.getString(
|
|
||||||
R.string.format_info,
|
|
||||||
album.artist.name,
|
|
||||||
context.getPlural(R.plurals.fmt_song_count, album.songs.size),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind the year for an album.
|
|
||||||
*/
|
|
||||||
@BindingAdapter("albumYear")
|
|
||||||
fun TextView.bindAlbumYear(album: Album) {
|
|
||||||
text = album.year.toYear(context)
|
|
||||||
}
|
|
||||||
|
|
|
@ -129,11 +129,11 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) { nextQueue ->
|
playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) {
|
||||||
updateQueueIcon(queueItem)
|
updateQueueIcon(queueItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackModel.userQueue.observe(viewLifecycleOwner) { userQueue ->
|
playbackModel.userQueue.observe(viewLifecycleOwner) {
|
||||||
updateQueueIcon(queueItem)
|
updateQueueIcon(queueItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,17 +21,16 @@ package org.oxycblt.auxio.playback.queue
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.widget.TooltipCompat
|
|
||||||
import androidx.recyclerview.widget.AsyncListDiffer
|
import androidx.recyclerview.widget.AsyncListDiffer
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.R
|
|
||||||
import org.oxycblt.auxio.databinding.ItemActionHeaderBinding
|
|
||||||
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
||||||
|
import org.oxycblt.auxio.music.ActionHeader
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
import org.oxycblt.auxio.music.BaseModel
|
||||||
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.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
|
import org.oxycblt.auxio.ui.ActionHeaderViewHolder
|
||||||
import org.oxycblt.auxio.ui.BaseViewHolder
|
import org.oxycblt.auxio.ui.BaseViewHolder
|
||||||
import org.oxycblt.auxio.ui.DiffCallback
|
import org.oxycblt.auxio.ui.DiffCallback
|
||||||
import org.oxycblt.auxio.ui.HeaderViewHolder
|
import org.oxycblt.auxio.ui.HeaderViewHolder
|
||||||
|
@ -46,8 +45,7 @@ import org.oxycblt.auxio.util.logE
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class QueueAdapter(
|
class QueueAdapter(
|
||||||
private val touchHelper: ItemTouchHelper,
|
private val touchHelper: ItemTouchHelper
|
||||||
private val playbackModel: PlaybackViewModel
|
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
private var data = mutableListOf<BaseModel>()
|
private var data = mutableListOf<BaseModel>()
|
||||||
private var listDiffer = AsyncListDiffer(this, DiffCallback())
|
private var listDiffer = AsyncListDiffer(this, DiffCallback())
|
||||||
|
@ -55,14 +53,10 @@ class QueueAdapter(
|
||||||
override fun getItemCount(): Int = data.size
|
override fun getItemCount(): Int = data.size
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
return when (val item = data[position]) {
|
return when (data[position]) {
|
||||||
is Header -> if (item.isAction) {
|
|
||||||
USER_QUEUE_HEADER_ITEM_TYPE
|
|
||||||
} else {
|
|
||||||
HeaderViewHolder.ITEM_TYPE
|
|
||||||
}
|
|
||||||
|
|
||||||
is Song -> QUEUE_SONG_ITEM_TYPE
|
is Song -> QUEUE_SONG_ITEM_TYPE
|
||||||
|
is Header -> HeaderViewHolder.ITEM_TYPE
|
||||||
|
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||||
|
|
||||||
else -> -1
|
else -> -1
|
||||||
}
|
}
|
||||||
|
@ -70,29 +64,22 @@ class QueueAdapter(
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
|
|
||||||
|
|
||||||
USER_QUEUE_HEADER_ITEM_TYPE -> UserQueueHeaderViewHolder(
|
|
||||||
ItemActionHeaderBinding.inflate(parent.context.inflater)
|
|
||||||
)
|
|
||||||
|
|
||||||
QUEUE_SONG_ITEM_TYPE -> QueueSongViewHolder(
|
QUEUE_SONG_ITEM_TYPE -> QueueSongViewHolder(
|
||||||
ItemQueueSongBinding.inflate(parent.context.inflater)
|
ItemQueueSongBinding.inflate(parent.context.inflater)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
|
||||||
|
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
|
||||||
|
|
||||||
else -> error("Invalid viewholder item type $viewType.")
|
else -> error("Invalid viewholder item type $viewType.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
when (val item = data[position]) {
|
when (val item = data[position]) {
|
||||||
is Header -> if (item.isAction) {
|
|
||||||
(holder as UserQueueHeaderViewHolder).bind(item)
|
|
||||||
} else {
|
|
||||||
(holder as HeaderViewHolder).bind(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
is Song -> (holder as QueueSongViewHolder).bind(item)
|
is Song -> (holder as QueueSongViewHolder).bind(item)
|
||||||
|
is Header -> (holder as HeaderViewHolder).bind(item)
|
||||||
|
is ActionHeader -> (holder as ActionHeaderViewHolder).bind(item)
|
||||||
|
|
||||||
else -> logE("Bad data given to QueueAdapter.")
|
else -> logE("Bad data given to QueueAdapter.")
|
||||||
}
|
}
|
||||||
|
@ -181,29 +168,6 @@ class QueueAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* The viewholder for
|
|
||||||
*/
|
|
||||||
inner class UserQueueHeaderViewHolder(
|
|
||||||
private val binding: ItemActionHeaderBinding
|
|
||||||
) : BaseViewHolder<Header>(binding) {
|
|
||||||
|
|
||||||
override fun onBind(data: Header) {
|
|
||||||
binding.header = data
|
|
||||||
|
|
||||||
binding.headerButton.apply {
|
|
||||||
setImageResource(R.drawable.ic_clear)
|
|
||||||
|
|
||||||
contentDescription = context.getString(R.string.desc_clear_user_queue)
|
|
||||||
TooltipCompat.setTooltipText(this, contentDescription)
|
|
||||||
|
|
||||||
setOnClickListener {
|
|
||||||
playbackModel.clearUserQueue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val QUEUE_SONG_ITEM_TYPE = 0xA005
|
const val QUEUE_SONG_ITEM_TYPE = 0xA005
|
||||||
const val USER_QUEUE_HEADER_ITEM_TYPE = 0xA006
|
const val USER_QUEUE_HEADER_ITEM_TYPE = 0xA006
|
||||||
|
|
|
@ -90,6 +90,7 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc
|
||||||
// an elevation change.
|
// an elevation change.
|
||||||
// TODO: Maybe restrict the item from being drawn over the recycler bounds?
|
// TODO: Maybe restrict the item from being drawn over the recycler bounds?
|
||||||
// Seems like its possible with enough UI magic
|
// Seems like its possible with enough UI magic
|
||||||
|
// TODO: Add an accented BG to the removal action
|
||||||
|
|
||||||
val view = viewHolder.itemView
|
val view = viewHolder.itemView
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import kotlinx.coroutines.NonDisposableHandle.parent
|
import kotlinx.coroutines.NonDisposableHandle.parent
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
||||||
|
import org.oxycblt.auxio.music.ActionHeader
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
import org.oxycblt.auxio.music.BaseModel
|
||||||
import org.oxycblt.auxio.music.Header
|
import org.oxycblt.auxio.music.Header
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
|
@ -53,7 +54,7 @@ class QueueFragment : Fragment() {
|
||||||
val callback = QueueDragCallback(playbackModel)
|
val callback = QueueDragCallback(playbackModel)
|
||||||
|
|
||||||
val helper = ItemTouchHelper(callback)
|
val helper = ItemTouchHelper(callback)
|
||||||
val queueAdapter = QueueAdapter(helper, playbackModel)
|
val queueAdapter = QueueAdapter(helper)
|
||||||
var lastShuffle = playbackModel.isShuffling.value
|
var lastShuffle = playbackModel.isShuffling.value
|
||||||
|
|
||||||
callback.addQueueAdapter(queueAdapter)
|
callback.addQueueAdapter(queueAdapter)
|
||||||
|
@ -120,10 +121,12 @@ class QueueFragment : Fragment() {
|
||||||
val nextQueue = playbackModel.nextItemsInQueue.value!!
|
val nextQueue = playbackModel.nextItemsInQueue.value!!
|
||||||
|
|
||||||
if (userQueue.isNotEmpty()) {
|
if (userQueue.isNotEmpty()) {
|
||||||
queue += Header(
|
queue += ActionHeader(
|
||||||
id = -2,
|
id = -2,
|
||||||
name = getString(R.string.lbl_next_user_queue),
|
name = getString(R.string.lbl_next_user_queue),
|
||||||
isAction = true
|
icon = R.drawable.ic_clear,
|
||||||
|
desc = R.string.desc_clear_user_queue,
|
||||||
|
onClick = { playbackModel.clearUserQueue() }
|
||||||
)
|
)
|
||||||
|
|
||||||
queue += userQueue
|
queue += userQueue
|
||||||
|
@ -132,8 +135,10 @@ class QueueFragment : Fragment() {
|
||||||
if (nextQueue.isNotEmpty()) {
|
if (nextQueue.isNotEmpty()) {
|
||||||
queue += Header(
|
queue += Header(
|
||||||
id = -3,
|
id = -3,
|
||||||
name = getString(R.string.fmt_next_from, getParentName()),
|
name = getString(
|
||||||
isAction = false
|
R.string.fmt_next_from,
|
||||||
|
playbackModel.parent.value?.displayName ?: getString(R.string.lbl_all_songs)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
queue += nextQueue
|
queue += nextQueue
|
||||||
|
@ -141,8 +146,4 @@ class QueueFragment : Fragment() {
|
||||||
|
|
||||||
return queue
|
return queue
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getParentName(): String {
|
|
||||||
return playbackModel.parent.value?.displayName ?: getString(R.string.lbl_all_songs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentSearchBinding
|
import org.oxycblt.auxio.databinding.FragmentSearchBinding
|
||||||
import org.oxycblt.auxio.detail.DetailViewModel
|
import org.oxycblt.auxio.detail.DetailViewModel
|
||||||
|
@ -74,9 +73,6 @@ class SearchFragment : Fragment() {
|
||||||
},
|
},
|
||||||
::newMenu
|
::newMenu
|
||||||
)
|
)
|
||||||
|
|
||||||
val toolbarParams = binding.searchToolbar.layoutParams as AppBarLayout.LayoutParams
|
|
||||||
|
|
||||||
// --- UI SETUP --
|
// --- UI SETUP --
|
||||||
|
|
||||||
binding.lifecycleOwner = viewLifecycleOwner
|
binding.lifecycleOwner = viewLifecycleOwner
|
||||||
|
|
|
@ -130,7 +130,11 @@ class SearchViewModel : ViewModel() {
|
||||||
*/
|
*/
|
||||||
private fun List<BaseModel>.filterByOrNull(value: String): List<BaseModel>? {
|
private fun List<BaseModel>.filterByOrNull(value: String): List<BaseModel>? {
|
||||||
val filtered = filter {
|
val filtered = filter {
|
||||||
it.name.normalized().contains(value.normalized(), ignoreCase = true)
|
// First see if the normal item name will work. If that fails, try the "normalized"
|
||||||
|
// [e.g all accented/unicode chars become latin chars] instead. Hopefully this
|
||||||
|
// shouldn't break other language's search functionality.
|
||||||
|
it.name.contains(value, ignoreCase = true) ||
|
||||||
|
it.name.normalized().contains(value, ignoreCase = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (filtered.isNotEmpty()) filtered else null
|
return if (filtered.isNotEmpty()) filtered else null
|
||||||
|
@ -155,8 +159,8 @@ class SearchViewModel : ViewModel() {
|
||||||
idx += Character.charCount(cp)
|
idx += Character.charCount(cp)
|
||||||
|
|
||||||
when (Character.getType(cp)) {
|
when (Character.getType(cp)) {
|
||||||
Character.NON_SPACING_MARK.toInt(), Character.COMBINING_SPACING_MARK.toInt() ->
|
// Character.NON_SPACING_MARK and Character.COMBINING_SPACING_MARK
|
||||||
continue
|
6, 8 -> continue
|
||||||
|
|
||||||
else -> sb.appendCodePoint(cp)
|
else -> sb.appendCodePoint(cp)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,8 @@
|
||||||
|
|
||||||
package org.oxycblt.auxio.ui
|
package org.oxycblt.auxio.ui
|
||||||
|
|
||||||
import android.widget.ImageButton
|
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.annotation.IdRes
|
|
||||||
import androidx.databinding.BindingAdapter
|
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.music.Album
|
|
||||||
import org.oxycblt.auxio.music.Artist
|
|
||||||
import org.oxycblt.auxio.music.Genre
|
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,83 +29,11 @@ import org.oxycblt.auxio.music.Song
|
||||||
*/
|
*/
|
||||||
enum class SortMode(@DrawableRes val iconRes: Int) {
|
enum class SortMode(@DrawableRes val iconRes: Int) {
|
||||||
// Icons for each mode are assigned to the enums themselves
|
// Icons for each mode are assigned to the enums themselves
|
||||||
NONE(R.drawable.ic_sort_none),
|
NONE(R.drawable.ic_sort),
|
||||||
ALPHA_UP(R.drawable.ic_sort_alpha_up),
|
ALPHA_UP(R.drawable.ic_sort),
|
||||||
ALPHA_DOWN(R.drawable.ic_sort_alpha_down),
|
ALPHA_DOWN(R.drawable.ic_sort),
|
||||||
NUMERIC_UP(R.drawable.ic_sort_numeric_up),
|
NUMERIC_UP(R.drawable.ic_sort),
|
||||||
NUMERIC_DOWN(R.drawable.ic_sort_numeric_down);
|
NUMERIC_DOWN(R.drawable.ic_sort);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a sorted list of genres for a SortMode. Only supports alphabetic sorting.
|
|
||||||
* @param genres An unsorted list of genres.
|
|
||||||
* @return The sorted list of genres.
|
|
||||||
*/
|
|
||||||
fun getSortedGenreList(genres: List<Genre>): List<Genre> {
|
|
||||||
return when (this) {
|
|
||||||
ALPHA_UP -> genres.sortedWith(
|
|
||||||
compareByDescending(String.CASE_INSENSITIVE_ORDER) {
|
|
||||||
it.resolvedName.sliceArticle()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
ALPHA_DOWN -> genres.sortedWith(
|
|
||||||
compareBy(String.CASE_INSENSITIVE_ORDER) {
|
|
||||||
it.resolvedName.sliceArticle()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> genres
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a sorted list of artists for a SortMode. Only supports alphabetic sorting.
|
|
||||||
* @param artists An unsorted list of artists.
|
|
||||||
* @return The sorted list of artists.
|
|
||||||
*/
|
|
||||||
fun getSortedArtistList(artists: List<Artist>): List<Artist> {
|
|
||||||
return when (this) {
|
|
||||||
ALPHA_UP -> artists.sortedWith(
|
|
||||||
compareByDescending(String.CASE_INSENSITIVE_ORDER) {
|
|
||||||
it.name.sliceArticle()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
ALPHA_DOWN -> artists.sortedWith(
|
|
||||||
compareBy(String.CASE_INSENSITIVE_ORDER) {
|
|
||||||
it.name.sliceArticle()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> artists
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a sorted list of albums for a SortMode. Supports alpha + numeric sorting.
|
|
||||||
* @param albums An unsorted list of albums.
|
|
||||||
* @return The sorted list of albums.
|
|
||||||
*/
|
|
||||||
fun getSortedAlbumList(albums: List<Album>): List<Album> {
|
|
||||||
return when (this) {
|
|
||||||
ALPHA_UP -> albums.sortedWith(
|
|
||||||
compareByDescending(String.CASE_INSENSITIVE_ORDER) {
|
|
||||||
it.name.sliceArticle()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
ALPHA_DOWN -> albums.sortedWith(
|
|
||||||
compareBy(String.CASE_INSENSITIVE_ORDER) {
|
|
||||||
it.name.sliceArticle()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
NUMERIC_UP -> albums.sortedBy { it.year }
|
|
||||||
NUMERIC_DOWN -> albums.sortedByDescending { it.year }
|
|
||||||
|
|
||||||
else -> albums
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a sorted list of songs for a SortMode. Supports alpha + numeric sorting.
|
* Get a sorted list of songs for a SortMode. Supports alpha + numeric sorting.
|
||||||
|
@ -182,19 +104,6 @@ enum class SortMode(@DrawableRes val iconRes: Int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a sorting menu ID for this mode. Alphabetic only.
|
|
||||||
* @return The action id for this mode.
|
|
||||||
*/
|
|
||||||
@IdRes
|
|
||||||
fun toMenuId(): Int {
|
|
||||||
return when (this) {
|
|
||||||
ALPHA_UP -> R.id.option_sort_asc
|
|
||||||
ALPHA_DOWN -> R.id.option_sort_dsc
|
|
||||||
else -> R.id.option_sort_dsc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the constant for this mode. Used to write a compressed variant to SettingsManager
|
* Get the constant for this mode. Used to write a compressed variant to SettingsManager
|
||||||
* @return The int constant for this mode.
|
* @return The int constant for this mode.
|
||||||
|
@ -234,14 +143,6 @@ enum class SortMode(@DrawableRes val iconRes: Int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind the [SortMode] icon for an ImageButton.
|
|
||||||
*/
|
|
||||||
@BindingAdapter("sortIcon")
|
|
||||||
fun ImageButton.bindSortIcon(mode: SortMode) {
|
|
||||||
setImageResource(mode.iconRes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Slice a string so that any preceding articles like The/A(n) are truncated.
|
* Slice a string so that any preceding articles like The/A(n) are truncated.
|
||||||
* This is hilariously anglo-centric, but its mostly for MediaStore compat and hopefully
|
* This is hilariously anglo-centric, but its mostly for MediaStore compat and hopefully
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021 Auxio Project
|
* Copyright (c) 2021 Auxio Project
|
||||||
* ViewHolders.kt is part of Auxio.
|
* SortHeaderViewHolder.kt is part of Auxio.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,13 +20,16 @@ package org.oxycblt.auxio.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.appcompat.widget.TooltipCompat
|
||||||
import androidx.databinding.ViewDataBinding
|
import androidx.databinding.ViewDataBinding
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import org.oxycblt.auxio.databinding.ItemActionHeaderBinding
|
||||||
import org.oxycblt.auxio.databinding.ItemAlbumBinding
|
import org.oxycblt.auxio.databinding.ItemAlbumBinding
|
||||||
import org.oxycblt.auxio.databinding.ItemArtistBinding
|
import org.oxycblt.auxio.databinding.ItemArtistBinding
|
||||||
import org.oxycblt.auxio.databinding.ItemGenreBinding
|
import org.oxycblt.auxio.databinding.ItemGenreBinding
|
||||||
import org.oxycblt.auxio.databinding.ItemHeaderBinding
|
import org.oxycblt.auxio.databinding.ItemHeaderBinding
|
||||||
import org.oxycblt.auxio.databinding.ItemSongBinding
|
import org.oxycblt.auxio.databinding.ItemSongBinding
|
||||||
|
import org.oxycblt.auxio.music.ActionHeader
|
||||||
import org.oxycblt.auxio.music.Album
|
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
|
||||||
|
@ -245,3 +248,29 @@ class HeaderViewHolder private constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ActionHeaderViewHolder private constructor(
|
||||||
|
private val binding: ItemActionHeaderBinding
|
||||||
|
) : BaseViewHolder<ActionHeader>(binding) {
|
||||||
|
|
||||||
|
override fun onBind(data: ActionHeader) {
|
||||||
|
binding.header = data
|
||||||
|
|
||||||
|
binding.headerButton.apply {
|
||||||
|
TooltipCompat.setTooltipText(this, contentDescription)
|
||||||
|
|
||||||
|
setOnClickListener(data.onClick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ITEM_TYPE = 0xA999 // TODO: Give this an ID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of [ActionHeaderViewHolder]
|
||||||
|
*/
|
||||||
|
fun from(context: Context): ActionHeaderViewHolder {
|
||||||
|
return ActionHeaderViewHolder(ItemActionHeaderBinding.inflate(context.inflater))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM21.41 6.34l-3.75-3.75-2.53 2.54 3.75 3.75 2.53-2.54z" />
|
|
||||||
</vector>
|
|
|
@ -2,7 +2,7 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:tint="?attr/colorAccent"
|
android:tint="?attr/colorControlNormal"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?attr/colorAccent"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M3.694 17.992H0.976l4.142-12h3.27l4.136 12H9.806L6.8 8.734H6.706zm-0.17-4.717h6.422v1.98H3.524zm10.313 4.717v-1.506l5.988-8.402h-6V5.992h9.188v1.505L17.018 15.9h6.006v2.092z" />
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M15.5 20.535h-7l3.5 3.456 3.5-3.456z" />
|
|
||||||
</vector>
|
|
|
@ -1,13 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?attr/colorAccent"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M15.5 3.448h-7L12-0.008l3.5 3.456z" />
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M3.694 17.992H0.976l4.142-12h3.27l4.136 12H9.806L6.8 8.734H6.706zm-0.17-4.717h6.422v1.98H3.524zm10.313 4.717v-1.506l5.988-8.402h-6V5.992h9.188v1.505L17.018 15.9h6.006v2.092z" />
|
|
||||||
</vector>
|
|
|
@ -1,15 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?android:attr/colorAccent"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M6.542 18q-1.508-0.006-2.595-0.716-1.082-0.711-1.667-2.06-0.578-1.348-0.573-3.244 0-1.89 0.58-3.221 0.584-1.331 1.665-2.025 1.088-0.7 2.59-0.7 1.503 0 2.584 0.7 1.088 0.7 1.673 2.03 0.584 1.326 0.578 3.216 0 1.902-0.584 3.25-0.579 1.348-1.66 2.06Q8.05 18 6.542 18zm0-2.025q1.03 0 1.643-0.999 0.614-0.998 0.608-2.996 0-1.314-0.28-2.188-0.275-0.875-0.784-1.315-0.503-0.44-1.187-0.44-1.023 0-1.637 0.987-0.614 0.988-0.62 2.957 0 1.33 0.276 2.222 0.28 0.886 0.789 1.332 0.508 0.44 1.193 0.44zM17.51 6q0.924 0 1.777 0.3 0.86 0.293 1.532 0.953 0.679 0.66 1.07 1.749 0.398 1.083 0.404 2.668-0.006 1.472-0.351 2.629-0.34 1.15-0.976 1.958-0.638 0.806-1.538 1.23-0.895 0.417-2.005 0.417-1.199 0-2.117-0.446-0.918-0.45-1.479-1.224-0.555-0.779-0.672-1.749h2.496q0.146 0.632 0.614 0.982 0.468 0.344 1.158 0.344 1.169 0 1.777-0.982 0.608-0.987 0.614-2.702h-0.082Q19.463 12.635 19.007 13q-0.456 0.362-1.046 0.559-0.585 0.197-1.246 0.197-1.058 0-1.894-0.48-0.83-0.479-1.31-1.32-0.479-0.846-0.473-1.929-0.006-1.173 0.555-2.082 0.562-0.914 1.567-1.433Q16.172 5.994 17.51 6zm0.018 1.918q-0.59 0-1.053 0.271Q16.02 8.46 15.75 8.923q-0.263 0.462-0.257 1.038-0.006 0.575 0.252 1.032 0.263 0.457 0.719 0.728 0.456 0.265 1.04 0.265 0.439 0 0.807-0.158 0.374-0.158 0.65-0.434 0.28-0.282 0.438-0.65 0.158-0.372 0.163-0.795-0.005-0.558-0.269-1.02-0.263-0.463-0.725-0.734-0.461-0.277-1.04-0.277z"
|
|
||||||
tools:ignore="VectorPath" />
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M15.5 20.544h-7L12 24l3.5-3.456z" />
|
|
||||||
</vector>
|
|
|
@ -1,15 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?attr/colorAccent"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M6.542 18q-1.508-0.006-2.595-0.716-1.082-0.711-1.667-2.06-0.578-1.348-0.573-3.244 0-1.89 0.58-3.221 0.584-1.331 1.665-2.025 1.088-0.7 2.59-0.7 1.503 0 2.584 0.7 1.088 0.7 1.673 2.03 0.584 1.326 0.578 3.216 0 1.902-0.584 3.25-0.579 1.348-1.66 2.06Q8.05 18 6.542 18zm0-2.025q1.03 0 1.643-0.999 0.614-0.998 0.608-2.996 0-1.314-0.28-2.188-0.275-0.875-0.784-1.315-0.503-0.44-1.187-0.44-1.023 0-1.637 0.987-0.614 0.988-0.62 2.957 0 1.33 0.276 2.222 0.28 0.886 0.789 1.332 0.508 0.44 1.193 0.44zM17.51 6q0.924 0 1.777 0.3 0.86 0.293 1.532 0.953 0.679 0.66 1.07 1.749 0.398 1.083 0.404 2.668-0.006 1.472-0.351 2.629-0.34 1.15-0.976 1.958-0.638 0.806-1.538 1.23-0.895 0.417-2.005 0.417-1.199 0-2.117-0.446-0.918-0.45-1.479-1.224-0.555-0.779-0.672-1.749h2.496q0.146 0.632 0.614 0.982 0.468 0.344 1.158 0.344 1.169 0 1.777-0.982 0.608-0.987 0.614-2.702h-0.082Q19.463 12.635 19.007 13q-0.456 0.362-1.046 0.559-0.585 0.197-1.246 0.197-1.058 0-1.894-0.48-0.83-0.479-1.31-1.32-0.479-0.846-0.473-1.929-0.006-1.173 0.555-2.082 0.562-0.914 1.567-1.433Q16.172 5.994 17.51 6zm0.018 1.918q-0.59 0-1.053 0.271Q16.02 8.46 15.75 8.923q-0.263 0.462-0.257 1.038-0.006 0.575 0.252 1.032 0.263 0.457 0.719 0.728 0.456 0.265 1.04 0.265 0.439 0 0.807-0.158 0.374-0.158 0.65-0.434 0.28-0.282 0.438-0.65 0.158-0.372 0.163-0.795-0.005-0.558-0.269-1.02-0.263-0.463-0.725-0.734-0.461-0.277-1.04-0.277z"
|
|
||||||
tools:ignore="VectorPath" />
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M15.5 3.456h-7L12 0l3.5 3.456z" />
|
|
||||||
</vector>
|
|
|
@ -7,7 +7,7 @@
|
||||||
<shape android:shape="rectangle">
|
<shape android:shape="rectangle">
|
||||||
<stroke
|
<stroke
|
||||||
android:width="@dimen/size_stroke_small"
|
android:width="@dimen/size_stroke_small"
|
||||||
android:color="@color/overlay_divider" />
|
android:color="@color/mtrl_btn_stroke_color_selector" />
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
||||||
|
|
|
@ -1,137 +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">
|
|
||||||
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="album"
|
|
||||||
type="org.oxycblt.auxio.music.Album" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="detailModel"
|
|
||||||
type="org.oxycblt.auxio.detail.DetailViewModel" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="playbackModel"
|
|
||||||
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:animateLayoutChanges="true">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/album_cover"
|
|
||||||
style="@style/Widget.ImageView.Full"
|
|
||||||
android:layout_width="@dimen/size_cover_huge_land"
|
|
||||||
android:layout_height="@dimen/size_cover_huge_land"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
|
||||||
android:contentDescription="@{@string/desc_album_cover(album.name)}"
|
|
||||||
app:albumArt="@{album}"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:src="@drawable/ic_album" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/album_name"
|
|
||||||
style="@style/Widget.TextView.Detail"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_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="@+id/album_cover"
|
|
||||||
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/spacing_medium"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:onClick="@{() -> detailModel.navToItem(album.artist)}"
|
|
||||||
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/spacing_medium"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:albumDetails="@{album}"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/album_cover"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/album_artist"
|
|
||||||
tools:text="2020 / 10 Songs / 16:16" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/album_play_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Secondary"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
|
||||||
android:onClick="@{() -> playbackModel.playAlbum(album, false)}"
|
|
||||||
android:text="@string/lbl_play"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/album_shuffle_button"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/album_cover" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/album_shuffle_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Primary"
|
|
||||||
android:layout_marginStart="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
android:backgroundTint="?attr/colorAccent"
|
|
||||||
android:onClick="@{() -> playbackModel.playAlbum(album, true)}"
|
|
||||||
android:text="@string/lbl_shuffle"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/album_play_button"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/album_play_button"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/album_play_button" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/album_song_header"
|
|
||||||
style="@style/Widget.TextView.Header"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:background="@drawable/ui_header_dividers"
|
|
||||||
android:text="@string/lbl_songs"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/album_play_button" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/album_sort_button"
|
|
||||||
style="@style/Widget.Button.Unbounded.Small"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:contentDescription="@string/desc_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>
|
|
|
@ -1,114 +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.adapters.ArtistDetailAdapter.ArtistHeaderViewHolder">
|
|
||||||
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="artist"
|
|
||||||
type="org.oxycblt.auxio.music.Artist" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="playbackModel"
|
|
||||||
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/artist_image"
|
|
||||||
android:layout_width="@dimen/size_cover_huge_land"
|
|
||||||
android:layout_height="@dimen/size_cover_huge_land"
|
|
||||||
android:layout_margin="@dimen/spacing_medium"
|
|
||||||
android:contentDescription="@{@string/desc_artist_image(artist.name)}"
|
|
||||||
style="@style/Widget.ImageView.Full"
|
|
||||||
app:artistImage="@{artist}"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:src="@drawable/ic_artist" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/artist_name"
|
|
||||||
style="@style/Widget.TextView.Detail"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
android:text="@{artist.name}"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/artist_genre"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/artist_image"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/artist_image"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
tools:text="Artist Name" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/artist_genre"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:artistGenre="@{artist}"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/artist_counts"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/artist_image"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_name"
|
|
||||||
tools:text="Genre Name" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/artist_counts"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:artistCounts="@{artist}"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/artist_image"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/artist_image"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_genre"
|
|
||||||
tools:text="2 Albums, 20 Songs" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/artist_play_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Secondary"
|
|
||||||
android:onClick="@{() -> playbackModel.playArtist(artist, false)}"
|
|
||||||
android:text="@string/lbl_play"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/artist_shuffle_button"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_image" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/artist_shuffle_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Primary"
|
|
||||||
android:backgroundTint="?attr/colorAccent"
|
|
||||||
android:onClick="@{() -> playbackModel.playArtist(artist, true)}"
|
|
||||||
android:text="@string/lbl_shuffle"
|
|
||||||
android:layout_marginStart="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/artist_play_button"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/artist_play_button"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/artist_play_button" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/artist_album_header"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
style="@style/Widget.TextView.Header"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:text="@string/lbl_albums"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_play_button" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</layout>
|
|
87
app/src/main/res/layout-land/item_detail.xml
Normal file
87
app/src/main/res/layout-land/item_detail.xml
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- This layout is re-used across all detail fragments. Do not add databinding. -->
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="@dimen/spacing_medium">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/detail_cover"
|
||||||
|
style="@style/Widget.ImageView.Full"
|
||||||
|
android:layout_width="@dimen/size_cover_huge_land"
|
||||||
|
android:layout_height="@dimen/size_cover_huge_land"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@drawable/ic_artist"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detail_name"
|
||||||
|
style="@style/Widget.TextView.Detail"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/spacing_medium"
|
||||||
|
android:layout_marginEnd="@dimen/spacing_medium"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/detail_subhead"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/detail_cover"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/detail_cover"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="Name" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detail_subhead"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/spacing_medium"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/detail_info"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/detail_cover"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detail_name"
|
||||||
|
tools:text="Info A" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detail_info"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/spacing_medium"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/detail_cover"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/detail_cover"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detail_subhead"
|
||||||
|
tools:text="Info B" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/detail_play_button"
|
||||||
|
style="@style/Widget.Button.Vibrant.Secondary"
|
||||||
|
android:layout_marginTop="@dimen/spacing_small"
|
||||||
|
android:layout_marginEnd="@dimen/spacing_small"
|
||||||
|
android:text="@string/lbl_play"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/detail_shuffle_button"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detail_cover" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/detail_shuffle_button"
|
||||||
|
style="@style/Widget.Button.Vibrant.Primary"
|
||||||
|
android:layout_marginStart="@dimen/spacing_small"
|
||||||
|
android:backgroundTint="?attr/colorAccent"
|
||||||
|
android:text="@string/lbl_shuffle"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/detail_play_button"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/detail_play_button"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/detail_play_button" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</layout>
|
|
@ -1,131 +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.adapters.GenreDetailAdapter.GenreHeaderViewHolder">
|
|
||||||
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="genre"
|
|
||||||
type="org.oxycblt.auxio.music.Genre" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="detailModel"
|
|
||||||
type="org.oxycblt.auxio.detail.DetailViewModel" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="playbackModel"
|
|
||||||
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/genre_image"
|
|
||||||
android:layout_width="@dimen/size_cover_huge_land"
|
|
||||||
android:layout_height="@dimen/size_cover_huge_land"
|
|
||||||
android:layout_margin="@dimen/spacing_medium"
|
|
||||||
android:contentDescription="@{@string/desc_genre_image(genre.name)}"
|
|
||||||
style="@style/Widget.ImageView.Full"
|
|
||||||
app:genreImage="@{genre}"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:src="@drawable/ic_genre" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/genre_name"
|
|
||||||
style="@style/Widget.TextView.Detail"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
android:text="@{genre.resolvedName}"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/genre_song_count"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/genre_image"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/genre_image"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
tools:text="Genre Name" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/genre_song_count"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:text="@{@plurals/fmt_song_count(genre.songs.size(), genre.songs.size())}"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/genre_duration"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/genre_image"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_name"
|
|
||||||
tools:text="2 Artists, 4 Albums" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/genre_play_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Secondary"
|
|
||||||
android:onClick="@{() -> playbackModel.playGenre(genre, false)}"
|
|
||||||
android:text="@string/lbl_play"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/genre_shuffle_button"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_image" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/genre_shuffle_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Primary"
|
|
||||||
android:backgroundTint="?attr/colorAccent"
|
|
||||||
android:onClick="@{() -> playbackModel.playGenre(genre, true)}"
|
|
||||||
android:text="@string/lbl_shuffle"
|
|
||||||
android:layout_marginStart="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/genre_play_button"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/genre_play_button"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/genre_play_button" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/genre_duration"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:text="@{genre.totalDuration}"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/genre_image"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/genre_image"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_song_count"
|
|
||||||
tools:text="16:16" />
|
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/genre_song_header"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/Widget.TextView.Header"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:text="@string/lbl_songs"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_play_button" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/genre_sort_button"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/Widget.Button.Unbounded.Small"
|
|
||||||
android:contentDescription="@string/desc_sort_button"
|
|
||||||
android:onClick="@{() -> detailModel.incrementGenreSortMode()}"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/genre_song_header"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/genre_song_header"
|
|
||||||
app:sortIcon="@{detailModel.genreSortMode}"
|
|
||||||
tools:src="@drawable/ic_sort_alpha_down" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</layout>
|
|
|
@ -1,136 +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">
|
|
||||||
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="album"
|
|
||||||
type="org.oxycblt.auxio.music.Album" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="detailModel"
|
|
||||||
type="org.oxycblt.auxio.detail.DetailViewModel" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="playbackModel"
|
|
||||||
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
|
|
||||||
</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_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
|
||||||
android:contentDescription="@{@string/desc_album_cover(album.name)}"
|
|
||||||
style="@style/Widget.ImageView.Full"
|
|
||||||
app:albumArt="@{album}"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:src="@drawable/ic_album" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/album_name"
|
|
||||||
style="@style/Widget.TextView.Detail"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_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="@+id/album_cover"
|
|
||||||
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/spacing_medium"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:onClick="@{() -> detailModel.navToItem(album.artist)}"
|
|
||||||
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/spacing_medium"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:albumDetails="@{album}"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/album_cover"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/album_artist"
|
|
||||||
tools:text="2020 / 10 Songs / 16:16" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/album_play_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Secondary"
|
|
||||||
android:onClick="@{() -> playbackModel.playAlbum(album, false)}"
|
|
||||||
android:text="@string/lbl_play"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/album_shuffle_button"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/album_cover" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/album_shuffle_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Primary"
|
|
||||||
android:backgroundTint="?attr/colorAccent"
|
|
||||||
android:onClick="@{() -> playbackModel.playAlbum(album, true)}"
|
|
||||||
android:text="@string/lbl_shuffle"
|
|
||||||
android:layout_marginStart="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/album_play_button"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/album_play_button"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/album_play_button" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/album_song_header"
|
|
||||||
style="@style/Widget.TextView.Header"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:background="@drawable/ui_header_dividers"
|
|
||||||
android:text="@string/lbl_songs"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/album_play_button" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/album_sort_button"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/Widget.Button.Unbounded.Small"
|
|
||||||
android:contentDescription="@string/desc_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>
|
|
|
@ -1,114 +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.adapters.ArtistDetailAdapter.ArtistHeaderViewHolder">
|
|
||||||
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="artist"
|
|
||||||
type="org.oxycblt.auxio.music.Artist" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="playbackModel"
|
|
||||||
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/artist_image"
|
|
||||||
android:layout_width="@dimen/size_cover_huge"
|
|
||||||
android:layout_height="@dimen/size_cover_huge"
|
|
||||||
android:layout_margin="@dimen/spacing_medium"
|
|
||||||
android:contentDescription="@{@string/desc_artist_image(artist.name)}"
|
|
||||||
style="@style/Widget.ImageView.Full"
|
|
||||||
app:artistImage="@{artist}"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:src="@drawable/ic_artist" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/artist_name"
|
|
||||||
style="@style/Widget.TextView.Detail"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
android:text="@{artist.name}"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/artist_genre"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/artist_image"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/artist_image"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
tools:text="Artist Name" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/artist_genre"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:artistGenre="@{artist}"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/artist_counts"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/artist_image"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_name"
|
|
||||||
tools:text="Genre Name" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/artist_counts"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:artistCounts="@{artist}"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/artist_image"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/artist_image"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_genre"
|
|
||||||
tools:text="2 Albums, 20 Songs" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/artist_play_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Secondary"
|
|
||||||
android:onClick="@{() -> playbackModel.playArtist(artist, false)}"
|
|
||||||
android:text="@string/lbl_play"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/artist_shuffle_button"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_image" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/artist_shuffle_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Primary"
|
|
||||||
android:backgroundTint="?attr/colorAccent"
|
|
||||||
android:onClick="@{() -> playbackModel.playArtist(artist, true)}"
|
|
||||||
android:text="@string/lbl_shuffle"
|
|
||||||
android:layout_marginStart="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/artist_play_button"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/artist_play_button"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/artist_play_button" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/artist_album_header"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
style="@style/Widget.TextView.Header"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:text="@string/lbl_albums"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_play_button" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</layout>
|
|
84
app/src/main/res/layout-large/item_detail.xml
Normal file
84
app/src/main/res/layout-large/item_detail.xml
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<?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">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="@dimen/spacing_medium">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/detail_cover"
|
||||||
|
android:layout_width="@dimen/size_cover_huge"
|
||||||
|
android:layout_height="@dimen/size_cover_huge"
|
||||||
|
style="@style/Widget.ImageView.Full"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@drawable/ic_artist"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detail_name"
|
||||||
|
style="@style/Widget.TextView.Detail"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/spacing_medium"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/detail_subhead"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/detail_cover"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/detail_cover"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="Name" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detail_subhead"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/spacing_medium"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/detail_info"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/detail_cover"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detail_name"
|
||||||
|
tools:text="Info A" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detail_info"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/spacing_medium"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/detail_cover"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/detail_cover"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detail_subhead"
|
||||||
|
tools:text="Info B" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/detail_play_button"
|
||||||
|
style="@style/Widget.Button.Vibrant.Secondary"
|
||||||
|
android:text="@string/lbl_play"
|
||||||
|
android:layout_marginTop="@dimen/spacing_small"
|
||||||
|
android:layout_marginEnd="@dimen/spacing_small"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/detail_shuffle_button"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detail_cover" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/detail_shuffle_button"
|
||||||
|
style="@style/Widget.Button.Vibrant.Primary"
|
||||||
|
android:backgroundTint="?attr/colorAccent"
|
||||||
|
android:text="@string/lbl_shuffle"
|
||||||
|
android:layout_marginStart="@dimen/spacing_small"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/detail_play_button"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/detail_play_button"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/detail_play_button" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</layout>
|
|
@ -1,129 +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.adapters.GenreDetailAdapter.GenreHeaderViewHolder">
|
|
||||||
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="genre"
|
|
||||||
type="org.oxycblt.auxio.music.Genre" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="detailModel"
|
|
||||||
type="org.oxycblt.auxio.detail.DetailViewModel" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="playbackModel"
|
|
||||||
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/genre_image"
|
|
||||||
android:layout_width="@dimen/size_cover_huge"
|
|
||||||
android:layout_height="@dimen/size_cover_huge"
|
|
||||||
android:layout_margin="@dimen/spacing_medium"
|
|
||||||
android:contentDescription="@{@string/desc_genre_image(genre.name)}"
|
|
||||||
style="@style/Widget.ImageView.Full"
|
|
||||||
app:genreImage="@{genre}"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:src="@drawable/ic_genre" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/genre_name"
|
|
||||||
style="@style/Widget.TextView.Detail"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
android:text="@{genre.resolvedName}"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/genre_song_count"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/genre_image"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/genre_image"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
tools:text="Genre Name" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/genre_song_count"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:text="@{@plurals/fmt_song_count(genre.songs.size(), genre.songs.size())}"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/genre_duration"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/genre_image"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_name"
|
|
||||||
tools:text="2 Artists, 4 Albums" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/genre_play_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Secondary"
|
|
||||||
android:onClick="@{() -> playbackModel.playGenre(genre, false)}"
|
|
||||||
android:text="@string/lbl_play"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/genre_shuffle_button"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_image" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/genre_shuffle_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Primary"
|
|
||||||
android:backgroundTint="?attr/colorAccent"
|
|
||||||
android:onClick="@{() -> playbackModel.playGenre(genre, true)}"
|
|
||||||
android:text="@string/lbl_shuffle"
|
|
||||||
android:layout_marginStart="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/genre_play_button"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/genre_play_button"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/genre_play_button" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/genre_duration"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:text="@{genre.totalDuration}"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/genre_image"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/genre_image"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_song_count"
|
|
||||||
tools:text="16:16" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/genre_song_header"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/Widget.TextView.Header"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:text="@string/lbl_songs"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_play_button" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/genre_sort_button"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/Widget.Button.Unbounded.Small"
|
|
||||||
android:contentDescription="@string/desc_sort_button"
|
|
||||||
android:onClick="@{() -> detailModel.incrementGenreSortMode()}"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/genre_song_header"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/genre_song_header"
|
|
||||||
app:sortIcon="@{detailModel.genreSortMode}"
|
|
||||||
tools:src="@drawable/ic_sort_alpha_down" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</layout>
|
|
|
@ -29,6 +29,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:theme="@style/Theme.Neutral"
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
@ -38,7 +39,7 @@
|
||||||
app:cardBackgroundColor="?attr/colorSurface"
|
app:cardBackgroundColor="?attr/colorSurface"
|
||||||
app:cardCornerRadius="0dp"
|
app:cardCornerRadius="0dp"
|
||||||
app:cardElevation="0dp"
|
app:cardElevation="0dp"
|
||||||
app:strokeColor="@color/overlay_divider"
|
app:strokeColor="@color/mtrl_btn_stroke_color_selector"
|
||||||
app:strokeWidth="1dp">
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
@ -158,7 +159,6 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/about_song_count"
|
android:id="@+id/about_song_count"
|
||||||
style="@style/Widget.TextView.Icon"
|
style="@style/Widget.TextView.Icon"
|
||||||
android:theme="@style/Theme.Neutral"
|
|
||||||
app:drawableStartCompat="@drawable/ic_song"
|
app:drawableStartCompat="@drawable/ic_song"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
@ -169,7 +169,7 @@
|
||||||
android:id="@+id/about_author"
|
android:id="@+id/about_author"
|
||||||
style="@style/Widget.TextView.Icon"
|
style="@style/Widget.TextView.Icon"
|
||||||
android:text="@string/lbl_author"
|
android:text="@string/lbl_author"
|
||||||
app:drawableStartCompat="@drawable/ic_author"
|
app:drawableStartCompat="@drawable/ic_artist"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/about_song_count" />
|
app:layout_constraintTop_toBottomOf="@+id/about_song_count" />
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
style="@style/Widget.TextView.Compact.Secondary"
|
style="@style/Widget.TextView.Compact.Secondary"
|
||||||
android:layout_marginStart="@dimen/spacing_small"
|
android:layout_marginStart="@dimen/spacing_small"
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
android:layout_marginEnd="@dimen/spacing_small"
|
||||||
android:text="@{@string/format_info(song.album.artist.name, song.album.name)}"
|
android:text="@{@string/fmt_two(song.album.artist.name, song.album.name)}"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/playback_cover"
|
app:layout_constraintBottom_toBottomOf="@+id/playback_cover"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
|
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
|
||||||
app:layout_constraintStart_toEndOf="@+id/playback_cover"
|
app:layout_constraintStart_toEndOf="@+id/playback_cover"
|
||||||
|
|
|
@ -28,8 +28,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||||
tools:listitem="@layout/item_artist_header"
|
tools:listitem="@layout/item_detail" />
|
||||||
tools:layout_marginTop="56dp"/>
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
</layout>
|
</layout>
|
|
@ -4,12 +4,11 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".ui.HeaderViewHolder">
|
tools:context=".ui.HeaderViewHolder">
|
||||||
|
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="header"
|
name="header"
|
||||||
type="org.oxycblt.auxio.music.Header" />
|
type="org.oxycblt.auxio.music.ActionHeader" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
@ -32,10 +31,12 @@
|
||||||
style="@style/Widget.Button.Unbounded.Small"
|
style="@style/Widget.Button.Unbounded.Small"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@{context.getString(header.desc)}"
|
||||||
|
android:src="@{context.getDrawable(header.icon)}"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:ignore="ContentDescription"
|
tools:src="@drawable/ic_sort"/>
|
||||||
tools:src="@drawable/ic_clear" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</layout>
|
</layout>
|
|
@ -37,7 +37,7 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/album_info"
|
android:id="@+id/album_info"
|
||||||
style="@style/Widget.TextView.Item.Secondary"
|
style="@style/Widget.TextView.Item.Secondary"
|
||||||
app:albumInfo="@{album}"
|
android:text="@{@string/fmt_two(album.artist.name, @plurals/fmt_song_count(album.songs.size, album.songs.size))}"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||||
|
|
|
@ -1,128 +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">
|
|
||||||
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="album"
|
|
||||||
type="org.oxycblt.auxio.music.Album" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="detailModel"
|
|
||||||
type="org.oxycblt.auxio.detail.DetailViewModel" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="playbackModel"
|
|
||||||
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:animateLayoutChanges="true">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/album_cover"
|
|
||||||
style="@style/Widget.ImageView.Full"
|
|
||||||
android:layout_width="@dimen/size_cover_huge"
|
|
||||||
android:layout_height="@dimen/size_cover_huge"
|
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
|
||||||
android:contentDescription="@{@string/desc_album_cover(album.name)}"
|
|
||||||
app:albumArt="@{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/Widget.TextView.Detail"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_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/spacing_medium"
|
|
||||||
android:onClick="@{() -> detailModel.navToItem(album.artist)}"
|
|
||||||
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/spacing_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" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/album_play_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Secondary"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
|
||||||
android:onClick="@{() -> playbackModel.playAlbum(album, false)}"
|
|
||||||
android:text="@string/lbl_play"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/album_shuffle_button"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/album_details" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/album_shuffle_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Primary"
|
|
||||||
android:layout_marginStart="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
android:backgroundTint="?attr/colorAccent"
|
|
||||||
android:onClick="@{() -> playbackModel.playAlbum(album, true)}"
|
|
||||||
android:text="@string/lbl_shuffle"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/album_play_button"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/album_play_button"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/album_play_button" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/album_song_header"
|
|
||||||
style="@style/Widget.TextView.Header"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:background="@drawable/ui_header_dividers"
|
|
||||||
android:text="@string/lbl_songs"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/album_play_button" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/album_sort_button"
|
|
||||||
style="@style/Widget.Button.Unbounded.Small"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:contentDescription="@string/desc_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>
|
|
|
@ -2,7 +2,7 @@
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".detail.adapters.AlbumDetailAdapter.AlbumSongViewHolder">
|
tools:context=".detail.recycler.AlbumDetailAdapter.AlbumSongViewHolder">
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".detail.adapters.ArtistDetailAdapter.ArtistAlbumViewHolder">
|
tools:context=".detail.recycler.ArtistDetailAdapter.ArtistAlbumViewHolder">
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/album_year"
|
android:id="@+id/album_year"
|
||||||
style="@style/Widget.TextView.Item.Secondary"
|
style="@style/Widget.TextView.Item.Secondary"
|
||||||
app:albumYear="@{album}"
|
android:text="@{album.year != 0 ? String.valueOf(album.year) : @string/def_date}"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||||
|
|
|
@ -1,114 +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.adapters.ArtistDetailAdapter.ArtistHeaderViewHolder">
|
|
||||||
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="artist"
|
|
||||||
type="org.oxycblt.auxio.music.Artist" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="playbackModel"
|
|
||||||
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/artist_image"
|
|
||||||
style="@style/Widget.ImageView.Full"
|
|
||||||
android:layout_width="@dimen/size_cover_huge"
|
|
||||||
android:layout_height="@dimen/size_cover_huge"
|
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
|
||||||
android:contentDescription="@{@string/desc_artist_image(artist.name)}"
|
|
||||||
app:artistImage="@{artist}"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:src="@drawable/ic_artist" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/artist_name"
|
|
||||||
style="@style/Widget.TextView.Detail"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
android:text="@{artist.name}"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_image"
|
|
||||||
tools:text="Artist Name" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/artist_genre"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:artistGenre="@{artist}"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_name"
|
|
||||||
tools:text="Genre Name" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/artist_counts"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:artistCounts="@{artist}"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_genre"
|
|
||||||
tools:text="2 Albums, 20 Songs" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/artist_play_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Secondary"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
|
||||||
android:onClick="@{() -> playbackModel.playArtist(artist, false)}"
|
|
||||||
android:text="@string/lbl_play"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/artist_shuffle_button"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_counts" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/artist_shuffle_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Primary"
|
|
||||||
android:layout_marginStart="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
android:backgroundTint="?attr/colorAccent"
|
|
||||||
android:onClick="@{() -> playbackModel.playArtist(artist, true)}"
|
|
||||||
android:text="@string/lbl_shuffle"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/artist_play_button"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/artist_play_button"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/artist_play_button" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/artist_song_header"
|
|
||||||
style="@style/Widget.TextView.Header"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:background="@drawable/ui_header_dividers"
|
|
||||||
android:text="@string/lbl_albums"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_play_button" />
|
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</layout>
|
|
78
app/src/main/res/layout/item_detail.xml
Normal file
78
app/src/main/res/layout/item_detail.xml
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- This layout is re-used across all detail fragments. Do not add databinding. -->
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="@dimen/spacing_medium">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/detail_cover"
|
||||||
|
style="@style/Widget.ImageView.Full"
|
||||||
|
android:layout_width="@dimen/size_cover_huge"
|
||||||
|
android:layout_height="@dimen/size_cover_huge"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@drawable/ic_artist"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detail_name"
|
||||||
|
style="@style/Widget.TextView.Detail"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_medium"
|
||||||
|
android:layout_marginEnd="@dimen/spacing_medium"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detail_cover"
|
||||||
|
tools:text="Name" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detail_subhead"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detail_name"
|
||||||
|
tools:text="Info A" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detail_info"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detail_subhead"
|
||||||
|
tools:text="Info B" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/detail_play_button"
|
||||||
|
style="@style/Widget.Button.Vibrant.Secondary"
|
||||||
|
android:layout_marginTop="@dimen/spacing_small"
|
||||||
|
android:layout_marginEnd="@dimen/spacing_small"
|
||||||
|
android:text="@string/lbl_play"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/detail_shuffle_button"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/detail_info" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/detail_shuffle_button"
|
||||||
|
style="@style/Widget.Button.Vibrant.Primary"
|
||||||
|
android:layout_marginStart="@dimen/spacing_small"
|
||||||
|
android:backgroundTint="?attr/colorAccent"
|
||||||
|
android:text="@string/lbl_shuffle"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/detail_play_button"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/detail_play_button"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/detail_play_button" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</layout>
|
|
@ -1,126 +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.adapters.GenreDetailAdapter.GenreHeaderViewHolder">
|
|
||||||
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="genre"
|
|
||||||
type="org.oxycblt.auxio.music.Genre" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="detailModel"
|
|
||||||
type="org.oxycblt.auxio.detail.DetailViewModel" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="playbackModel"
|
|
||||||
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
|
|
||||||
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/genre_image"
|
|
||||||
style="@style/Widget.ImageView.Full"
|
|
||||||
android:layout_width="@dimen/size_cover_huge"
|
|
||||||
android:layout_height="@dimen/size_cover_huge"
|
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
|
||||||
android:contentDescription="@{@string/desc_genre_image(genre.name)}"
|
|
||||||
app:genreImage="@{genre}"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:src="@drawable/ic_genre" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/genre_name"
|
|
||||||
style="@style/Widget.TextView.Detail"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
android:text="@{genre.resolvedName}"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_image"
|
|
||||||
tools:text="Genre Name" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/genre_song_count"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:text="@{@plurals/fmt_song_count(genre.songs.size(), genre.songs.size())}"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_name"
|
|
||||||
tools:text="80 Songs" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/genre_duration"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:text="@{genre.totalDuration}"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_song_count"
|
|
||||||
tools:text="16:16" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/genre_play_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Secondary"
|
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
|
||||||
android:onClick="@{() -> playbackModel.playGenre(genre, false)}"
|
|
||||||
android:text="@string/lbl_play"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/genre_shuffle_button"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_duration" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/genre_shuffle_button"
|
|
||||||
style="@style/Widget.Button.Vibrant.Primary"
|
|
||||||
android:backgroundTint="?attr/colorAccent"
|
|
||||||
android:onClick="@{() -> playbackModel.playGenre(genre, true)}"
|
|
||||||
android:text="@string/lbl_shuffle"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/genre_play_button"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/genre_play_button"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/genre_play_button" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/genre_song_header"
|
|
||||||
style="@style/Widget.TextView.Header"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
|
||||||
android:text="@string/lbl_songs"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/genre_play_button" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/genre_sort_button"
|
|
||||||
style="@style/Widget.Button.Unbounded.Small"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:contentDescription="@string/desc_sort_button"
|
|
||||||
android:onClick="@{() -> detailModel.incrementGenreSortMode()}"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/genre_song_header"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/genre_song_header"
|
|
||||||
app:sortIcon="@{detailModel.genreSortMode}"
|
|
||||||
tools:src="@drawable/ic_sort_alpha_down" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</layout>
|
|
|
@ -2,7 +2,7 @@
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".detail.adapters.GenreDetailAdapter.GenreSongViewHolder">
|
tools:context=".detail.recycler.GenreDetailAdapter.GenreSongViewHolder">
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
android:id="@+id/song_info"
|
android:id="@+id/song_info"
|
||||||
style="@style/Widget.TextView.Item.Secondary"
|
style="@style/Widget.TextView.Item.Secondary"
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
android:layout_marginEnd="@dimen/spacing_medium"
|
||||||
android:text="@{@string/format_info(song.album.artist.name, song.album.name)}"
|
android:text="@{@string/fmt_two(song.album.artist.name, song.album.name)}"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/song_duration"
|
app:layout_constraintEnd_toStartOf="@+id/song_duration"
|
||||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
android:id="@+id/song_info"
|
android:id="@+id/song_info"
|
||||||
style="@style/Widget.TextView.Item.Secondary"
|
style="@style/Widget.TextView.Item.Secondary"
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
android:layout_marginEnd="@dimen/spacing_medium"
|
||||||
android:text="@{@string/format_info(song.album.artist.name, song.album.name)}"
|
android:text="@{@string/fmt_two(song.album.artist.name, song.album.name)}"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/song_drag_handle"
|
app:layout_constraintEnd_toStartOf="@+id/song_drag_handle"
|
||||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||||
|
@ -52,13 +52,14 @@
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/song_drag_handle"
|
android:id="@+id/song_drag_handle"
|
||||||
android:layout_width="@dimen/size_btn_small"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="@dimen/size_btn_small"
|
android:layout_height="@dimen/size_btn_small"
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:contentDescription="@string/desc_queue_handle"
|
android:contentDescription="@string/desc_queue_handle"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
|
android:paddingStart="@dimen/spacing_medium"
|
||||||
|
android:paddingEnd="@dimen/spacing_medium"
|
||||||
android:src="@drawable/ic_handle"
|
android:src="@drawable/ic_handle"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/album_cover"
|
app:layout_constraintBottom_toBottomOf="@+id/album_cover"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
android:id="@+id/song_info"
|
android:id="@+id/song_info"
|
||||||
style="@style/Widget.TextView.Item.Secondary"
|
style="@style/Widget.TextView.Item.Secondary"
|
||||||
android:layout_marginEnd="@dimen/spacing_medium"
|
android:layout_marginEnd="@dimen/spacing_medium"
|
||||||
android:text="@{@string/format_info(song.album.artist.name, song.album.name)}"
|
android:text="@{@string/fmt_two(song.album.artist.name, song.album.name)}"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||||
|
|
20
app/src/main/res/menu/menu_detail.xml
Normal file
20
app/src/main/res/menu/menu_detail.xml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<group android:checkableBehavior="single">
|
||||||
|
<item
|
||||||
|
android:id="@+id/option_sort_asc"
|
||||||
|
android:title="@string/lbl_sort_asc" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/option_sort_dsc"
|
||||||
|
android:title="@string/lbl_sort_dsc" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/option_sort_artist"
|
||||||
|
android:title="@string/lbl_sort_artist" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/option_sort_album"
|
||||||
|
android:title="@string/lbl_sort_album" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/option_sort_year"
|
||||||
|
android:title="@string/lbl_sort_year" />
|
||||||
|
</group>
|
||||||
|
</menu>
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/submenu_sorting"
|
android:id="@+id/submenu_sorting"
|
||||||
android:icon="@drawable/ic_sort_none"
|
android:icon="@drawable/ic_sort"
|
||||||
android:title="@string/lbl_sort"
|
android:title="@string/lbl_sort"
|
||||||
app:showAsAction="ifRoom">
|
app:showAsAction="ifRoom">
|
||||||
<menu>
|
<menu>
|
||||||
|
|
|
@ -100,7 +100,7 @@
|
||||||
<string name="hint_search_library">"Prohledat vaši knihovnu…"</string>
|
<string name="hint_search_library">"Prohledat vaši knihovnu…"</string>
|
||||||
|
|
||||||
<!-- Description Namespace | Accessibility Strings -->
|
<!-- Description Namespace | Accessibility Strings -->
|
||||||
<string name="desc_sort_button">"Změnit pořadí řazení"</string>
|
<string name="desc_sort">"Změnit pořadí řazení"</string>
|
||||||
<string name="desc_track_number">"Stopa %d"</string>
|
<string name="desc_track_number">"Stopa %d"</string>
|
||||||
<string name="desc_play_pause">"Přehrát nebo pozastavit"</string>
|
<string name="desc_play_pause">"Přehrát nebo pozastavit"</string>
|
||||||
<string name="desc_skip_next">"Přeskočit na další skladbu"</string>
|
<string name="desc_skip_next">"Přeskočit na další skladbu"</string>
|
||||||
|
|
|
@ -103,7 +103,7 @@
|
||||||
<string name="hint_search_library">Musikbibliothek durchsuchen…</string>
|
<string name="hint_search_library">Musikbibliothek durchsuchen…</string>
|
||||||
|
|
||||||
<!-- Description Namespace | Accessibility Strings -->
|
<!-- Description Namespace | Accessibility Strings -->
|
||||||
<string name="desc_sort_button">Reihenfolge ändern</string>
|
<string name="desc_sort">Reihenfolge ändern</string>
|
||||||
<string name="desc_track_number">Titel %d</string>
|
<string name="desc_track_number">Titel %d</string>
|
||||||
|
|
||||||
<string name="desc_play_pause">Abspielen oder Pausieren</string>
|
<string name="desc_play_pause">Abspielen oder Pausieren</string>
|
||||||
|
|
|
@ -106,7 +106,7 @@
|
||||||
<string name="hint_search_library">Busca en tu biblioteca…</string>
|
<string name="hint_search_library">Busca en tu biblioteca…</string>
|
||||||
|
|
||||||
<!-- Description Namespace | Accessibility Strings -->
|
<!-- Description Namespace | Accessibility Strings -->
|
||||||
<string name="desc_sort_button">Cambiar el orden de clasificación</string>
|
<string name="desc_sort">Cambiar el orden de clasificación</string>
|
||||||
<string name="desc_track_number">Pista %d</string>
|
<string name="desc_track_number">Pista %d</string>
|
||||||
|
|
||||||
<string name="desc_play_pause">Reproducir o Pausar</string>
|
<string name="desc_play_pause">Reproducir o Pausar</string>
|
||||||
|
|
|
@ -104,7 +104,7 @@
|
||||||
<string name="hint_search_library">Zoek in uw bibliotheek…</string>
|
<string name="hint_search_library">Zoek in uw bibliotheek…</string>
|
||||||
|
|
||||||
<!-- Description Namespace | Accessibility Strings -->
|
<!-- Description Namespace | Accessibility Strings -->
|
||||||
<string name="desc_sort_button">Sorteervolgorde wijzigen</string>
|
<string name="desc_sort">Sorteervolgorde wijzigen</string>
|
||||||
<string name="desc_track_number">Nummer %d</string>
|
<string name="desc_track_number">Nummer %d</string>
|
||||||
|
|
||||||
<string name="desc_play_pause">Afspelen/Pauzeren</string>
|
<string name="desc_play_pause">Afspelen/Pauzeren</string>
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
|
|
||||||
<dimen name="size_cover_compact">48dp</dimen>
|
<dimen name="size_cover_compact">48dp</dimen>
|
||||||
<dimen name="size_cover_normal">56dp</dimen>
|
<dimen name="size_cover_normal">56dp</dimen>
|
||||||
<dimen name="size_cover_huge_land">136dp</dimen>
|
<dimen name="size_cover_huge_land">128dp</dimen>
|
||||||
<dimen name="size_cover_huge">264dp</dimen>
|
<dimen name="size_cover_huge">256dp</dimen>
|
||||||
|
|
||||||
<dimen name="size_stroke_small">1dp</dimen>
|
<dimen name="size_stroke_small">1dp</dimen>
|
||||||
<dimen name="size_stroke_large">2dp</dimen>
|
<dimen name="size_stroke_large">2dp</dimen>
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
<string name="info_app_name" translatable="false">Auxio</string>
|
<string name="info_app_name" translatable="false">Auxio</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="format_info" translatable="false">%1$s • %2$s</string>
|
<string name="fmt_two" translatable="false">%1$s • %2$s</string>
|
||||||
<string name="format_double_info" translatable="false">%1$s • %2$s • %3$s</string>
|
<string name="fmt_three" translatable="false">%1$s • %2$s • %3$s</string>
|
||||||
<string name="format_double_counts" translatable="false">%1$s, %2$s</string>
|
<string name="fmt_counts" translatable="false">%1$s, %2$s</string>
|
||||||
<string name="format_accent_summary" translatable="false"><b>%1$s</b>: %2$s</string>
|
<string name="fmt_accent_desc" translatable="false"><b>%1$s</b>: %2$s</string>
|
||||||
</resources>
|
</resources>
|
|
@ -114,7 +114,6 @@
|
||||||
<string name="hint_search_library">Search your library…</string>
|
<string name="hint_search_library">Search your library…</string>
|
||||||
|
|
||||||
<!-- Description Namespace | Accessibility Strings -->
|
<!-- Description Namespace | Accessibility Strings -->
|
||||||
<string name="desc_sort_button">Change Sort Order</string>
|
|
||||||
<string name="desc_track_number">Track %d</string>
|
<string name="desc_track_number">Track %d</string>
|
||||||
|
|
||||||
<string name="desc_play_pause">Play or Pause</string>
|
<string name="desc_play_pause">Play or Pause</string>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<style name="Theme.Base" parent="Theme.Splash">
|
<style name="Theme.Base" parent="Theme.Splash">
|
||||||
<!-- Colors -->
|
<!-- Colors -->
|
||||||
<item name="colorSurface">@color/surface</item>
|
<item name="colorSurface">@color/surface</item>
|
||||||
<item name="colorAccent">@color/design_default_color_primary</item>
|
<item name="colorAccent">@color/blue</item>
|
||||||
|
|
||||||
<item name="colorPrimary">?attr/colorAccent</item>
|
<item name="colorPrimary">?attr/colorAccent</item>
|
||||||
<item name="colorOnPrimary">?attr/colorSurface</item>
|
<item name="colorOnPrimary">?attr/colorSurface</item>
|
||||||
|
|
|
@ -197,26 +197,24 @@
|
||||||
<item name="android:background">@drawable/ui_small_unbounded_ripple</item>
|
<item name="android:background">@drawable/ui_small_unbounded_ripple</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Widget.Button.Vibrant.Base" parent="@style/Widget.MaterialComponents.Button.TextButton">
|
<style name="Widget.Button.Vibrant.Primary" parent="@style/Widget.MaterialComponents.Button.TextButton">
|
||||||
<item name="android:layout_width">0dp</item>
|
<item name="android:layout_width">0dp</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:clickable">true</item>
|
<item name="android:letterSpacing">0.05</item>
|
||||||
<item name="android:focusable">true</item>
|
<item name="android:textAllCaps">false</item>
|
||||||
<item name="fontFamily">@font/inter_semibold</item>
|
|
||||||
<item name="android:textSize">@dimen/text_size_small</item>
|
<item name="android:textSize">@dimen/text_size_small</item>
|
||||||
<item name="textAllCaps">false</item>
|
<item name="android:textColor">?attr/colorSurface</item>
|
||||||
|
<item name="fontFamily">@font/inter_semibold</item>
|
||||||
<item name="cornerRadius">0dp</item>
|
<item name="cornerRadius">0dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Widget.Button.Vibrant.Primary" parent="@style/Widget.Button.Vibrant.Base">
|
<style name="Widget.Button.Vibrant.Secondary" parent="@style/Widget.MaterialComponents.Button.OutlinedButton">
|
||||||
<item name="android:textColor">?attr/colorSurface</item>
|
<item name="android:layout_width">0dp</item>
|
||||||
<item name="backgroundTint">?attr/colorAccent</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="rippleColor">@color/mtrl_btn_ripple_color</item>
|
<item name="android:letterSpacing">0.05</item>
|
||||||
</style>
|
<item name="android:textAllCaps">false</item>
|
||||||
|
<item name="android:textSize">@dimen/text_size_small</item>
|
||||||
<style name="Widget.Button.Vibrant.Secondary" parent="@style/Widget.Button.Vibrant.Base">
|
<item name="fontFamily">@font/inter_semibold</item>
|
||||||
<item name="strokeColor">@color/overlay_divider</item>
|
<item name="cornerRadius">0dp</item>
|
||||||
<item name="strokeWidth">@dimen/size_stroke_small</item>
|
|
||||||
<item name="rippleColor">?attr/colorControlHighlight</item>
|
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in a new issue