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
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
|
||||
setOnApplyWindowInsetsListener { v, insets ->
|
||||
setOnApplyWindowInsetsListener { _, insets ->
|
||||
updatePadding(
|
||||
left = insets.systemWindowInsetLeft,
|
||||
right = insets.systemWindowInsetRight
|
||||
|
|
|
@ -106,7 +106,7 @@ data class Accent(
|
|||
val hex = context.getString(color).uppercase()
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -67,6 +67,10 @@ fun ImageView.bindArtistImage(artist: Artist?) {
|
|||
@BindingAdapter("genreImage")
|
||||
fun ImageView.bindGenreImage(genre: Genre?) {
|
||||
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.recyclerview.widget.LinearSmoothScroller
|
||||
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.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Header
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.ui.ActionMenu
|
||||
|
@ -52,20 +52,10 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
// If DetailViewModel isn't already storing the album, get it from MusicStore
|
||||
// using the ID given by the navigation arguments.
|
||||
if (detailModel.currentAlbum.value == null ||
|
||||
detailModel.currentAlbum.value?.id != args.albumId
|
||||
) {
|
||||
detailModel.updateAlbum(
|
||||
MusicStore.getInstance().albums.find {
|
||||
it.id == args.albumId
|
||||
}!!
|
||||
)
|
||||
}
|
||||
detailModel.setAlbum(args.albumId, requireContext())
|
||||
|
||||
val detailAdapter = AlbumDetailAdapter(
|
||||
detailModel, playbackModel, viewLifecycleOwner,
|
||||
playbackModel, detailModel,
|
||||
doOnClick = { playbackModel.playSong(it, PlaybackMode.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 ->
|
||||
if (itemId == R.id.action_queue_add) {
|
||||
playbackModel.addToUserQueue(detailModel.currentAlbum.value!!)
|
||||
playbackModel.addToUserQueue(detailModel.curAlbum.value!!)
|
||||
requireContext().showToast(R.string.lbl_queue_added)
|
||||
true
|
||||
} else {
|
||||
|
@ -85,19 +75,13 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
}
|
||||
|
||||
setupRecycler(detailAdapter) { pos ->
|
||||
pos == 0
|
||||
val item = detailAdapter.currentList[pos]
|
||||
item is Header || item is ActionHeader || item is Album
|
||||
}
|
||||
|
||||
// -- DETAILVIEWMODEL SETUP ---
|
||||
|
||||
detailModel.albumSortMode.observe(viewLifecycleOwner) { mode ->
|
||||
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))
|
||||
}
|
||||
|
||||
detailModel.albumData.observe(viewLifecycleOwner) { data ->
|
||||
detailAdapter.submitList(data)
|
||||
}
|
||||
|
||||
|
@ -106,10 +90,10 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
// Songs should be scrolled to if the album matches, or a new detail
|
||||
// fragment should be launched otherwise.
|
||||
is Song -> {
|
||||
if (detailModel.currentAlbum.value!!.id == item.album.id) {
|
||||
scrollToItem(item.id)
|
||||
if (detailModel.curAlbum.value!!.id == item.album.id) {
|
||||
scrollToItem(item.id, detailAdapter)
|
||||
|
||||
detailModel.doneWithNavToItem()
|
||||
detailModel.finishNavToItem()
|
||||
} else {
|
||||
findNavController().navigate(
|
||||
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
|
||||
// detail fragment.
|
||||
is Album -> {
|
||||
if (detailModel.currentAlbum.value!!.id == item.id) {
|
||||
if (detailModel.curAlbum.value!!.id == item.id) {
|
||||
binding.detailRecycler.scrollToPosition(0)
|
||||
detailModel.doneWithNavToItem()
|
||||
detailModel.finishNavToItem()
|
||||
} else {
|
||||
findNavController().navigate(
|
||||
AlbumDetailFragmentDirections.actionShowAlbum(item.id)
|
||||
|
@ -146,7 +130,7 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
|
||||
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
||||
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)
|
||||
} else {
|
||||
|
@ -169,17 +153,15 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
/**
|
||||
* 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
|
||||
val pos = detailModel.albumSortMode.value!!.getSortedSongList(
|
||||
detailModel.currentAlbum.value!!.songs
|
||||
).indexOfFirst { it.id == id }
|
||||
val pos = adapter.currentList.indexOfFirst { it.id == id && it is Album }
|
||||
|
||||
if (pos != -1) {
|
||||
binding.detailRecycler.post {
|
||||
// Make sure to increment the position to make up for the detail header
|
||||
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
|
||||
|
|
|
@ -24,17 +24,14 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.detail.adapters.ArtistDetailAdapter
|
||||
import org.oxycblt.auxio.detail.recycler.ArtistDetailAdapter
|
||||
import org.oxycblt.auxio.music.ActionHeader
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Header
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.ui.ActionMenu
|
||||
import org.oxycblt.auxio.ui.SortMode
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
||||
|
@ -50,20 +47,10 @@ class ArtistDetailFragment : DetailFragment() {
|
|||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
// If DetailViewModel isn't already storing the artist, get it from MusicStore
|
||||
// using the ID given by the navigation arguments
|
||||
if (detailModel.currentArtist.value == null ||
|
||||
detailModel.currentArtist.value?.id != args.artistId
|
||||
) {
|
||||
detailModel.updateArtist(
|
||||
MusicStore.getInstance().artists.find {
|
||||
it.id == args.artistId
|
||||
}!!
|
||||
)
|
||||
}
|
||||
detailModel.setArtist(args.artistId, requireContext())
|
||||
|
||||
val detailAdapter = ArtistDetailAdapter(
|
||||
playbackModel, detailModel,
|
||||
playbackModel,
|
||||
doOnClick = { data ->
|
||||
if (!detailModel.isNavigating) {
|
||||
detailModel.setNavigating(true)
|
||||
|
@ -88,39 +75,22 @@ class ArtistDetailFragment : DetailFragment() {
|
|||
setupToolbar()
|
||||
setupRecycler(detailAdapter) { pos ->
|
||||
// If the item is an ActionHeader we need to also make the item full-width
|
||||
pos == 0 || detailAdapter.currentList.getOrNull(pos) is Header
|
||||
val item = detailAdapter.currentList[pos]
|
||||
item is Header || item is ActionHeader || item is Artist
|
||||
}
|
||||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
detailModel.artistSortMode.observe(viewLifecycleOwner) { mode ->
|
||||
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))
|
||||
|
||||
detailModel.artistData.observe(viewLifecycleOwner) { data ->
|
||||
detailAdapter.submitList(data)
|
||||
}
|
||||
|
||||
detailModel.navToItem.observe(viewLifecycleOwner) { item ->
|
||||
when (item) {
|
||||
is Artist -> {
|
||||
if (item.id == detailModel.currentArtist.value!!.id) {
|
||||
if (item.id == detailModel.curArtist.value?.id) {
|
||||
binding.detailRecycler.scrollToPosition(0)
|
||||
detailModel.doneWithNavToItem()
|
||||
detailModel.finishNavToItem()
|
||||
} else {
|
||||
findNavController().navigate(
|
||||
ArtistDetailFragmentDirections.actionShowArtist(item.id)
|
||||
|
@ -136,7 +106,8 @@ class ArtistDetailFragment : DetailFragment() {
|
|||
ArtistDetailFragmentDirections.actionShowAlbum(item.album.id)
|
||||
)
|
||||
|
||||
else -> {}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,7 +123,7 @@ class ArtistDetailFragment : DetailFragment() {
|
|||
// Highlight songs if they are being played
|
||||
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
||||
if (playbackModel.mode.value == PlaybackMode.IN_ARTIST &&
|
||||
playbackModel.parent.value?.id == detailModel.currentArtist.value!!.id
|
||||
playbackModel.parent.value?.id == detailModel.curArtist.value?.id
|
||||
) {
|
||||
detailAdapter.highlightSong(song, binding.detailRecycler)
|
||||
} else {
|
||||
|
|
|
@ -36,9 +36,6 @@ import org.oxycblt.auxio.util.isLandscape
|
|||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
abstract class DetailFragment : Fragment() {
|
||||
|
|
|
@ -18,112 +18,135 @@
|
|||
|
||||
package org.oxycblt.auxio.detail
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
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.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
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
|
||||
|
||||
/**
|
||||
* ViewModel that stores data for the [DetailFragment]s, such as what they're showing & what
|
||||
* [SortMode] they are currently on.
|
||||
* TODO: Redo sorting here
|
||||
* TODO: Re-add sorting
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class DetailViewModel : ViewModel() {
|
||||
private val settingsManager = SettingsManager.getInstance()
|
||||
|
||||
// --- CURRENT VALUES ---
|
||||
|
||||
private val mCurrentGenre = MutableLiveData<Genre?>()
|
||||
val currentGenre: LiveData<Genre?> get() = mCurrentGenre
|
||||
private val mCurGenre = MutableLiveData<Genre?>()
|
||||
val curGenre: LiveData<Genre?> get() = mCurGenre
|
||||
|
||||
private val mCurrentArtist = MutableLiveData<Artist?>()
|
||||
val currentArtist: LiveData<Artist?> get() = mCurrentArtist
|
||||
private val mGenreData = MutableLiveData(listOf<BaseModel>())
|
||||
val genreData: LiveData<List<BaseModel>> = mGenreData
|
||||
|
||||
private val mCurrentAlbum = MutableLiveData<Album?>()
|
||||
val currentAlbum: LiveData<Album?> get() = mCurrentAlbum
|
||||
private val mCurArtist = MutableLiveData<Artist?>()
|
||||
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)
|
||||
val genreSortMode: LiveData<SortMode> get() = mGenreSortMode
|
||||
private val mCurAlbum = MutableLiveData<Album?>()
|
||||
val curAlbum: LiveData<Album?> get() = mCurAlbum
|
||||
|
||||
private val mArtistSortMode = MutableLiveData(settingsManager.artistSortMode)
|
||||
val albumSortMode: LiveData<SortMode> get() = mAlbumSortMode
|
||||
private val mAlbumData = MutableLiveData(listOf<BaseModel>())
|
||||
val albumData: LiveData<List<BaseModel>> get() = mAlbumData
|
||||
|
||||
private val mAlbumSortMode = MutableLiveData(settingsManager.albumSortMode)
|
||||
val artistSortMode: LiveData<SortMode> get() = mArtistSortMode
|
||||
|
||||
private var mIsNavigating = false
|
||||
val isNavigating: Boolean get() = mIsNavigating
|
||||
var isNavigating = false
|
||||
private set
|
||||
|
||||
private val mNavToItem = MutableLiveData<BaseModel?>()
|
||||
|
||||
/** Flag for unified navigation. Observe this to coordinate navigation to an item's UI. */
|
||||
val navToItem: LiveData<BaseModel?> get() = mNavToItem
|
||||
|
||||
fun updateGenre(genre: Genre) {
|
||||
mCurrentGenre.value = genre
|
||||
private val musicStore = MusicStore.getInstance()
|
||||
|
||||
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) {
|
||||
mCurrentArtist.value = artist
|
||||
fun setArtist(id: Long, context: Context) {
|
||||
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) {
|
||||
mCurrentAlbum.value = album
|
||||
fun setAlbum(id: Long, context: Context) {
|
||||
if (mCurAlbum.value?.id == id) return
|
||||
|
||||
mCurAlbum.value = musicStore.albums.find { it.id == id }
|
||||
|
||||
val data = mutableListOf<BaseModel>(curAlbum.value!!)
|
||||
|
||||
data.add(
|
||||
ActionHeader(
|
||||
id = -2,
|
||||
name = context.getString(R.string.lbl_songs),
|
||||
icon = R.drawable.ic_sort,
|
||||
desc = R.string.lbl_sort,
|
||||
onClick = {
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* 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
|
||||
data.addAll(LibSortMode.ASCENDING.sortAlbum(curAlbum.value!!))
|
||||
|
||||
else -> SortMode.ALPHA_DOWN
|
||||
}
|
||||
|
||||
mGenreSortMode.value = mode
|
||||
settingsManager.genreSortMode = mode
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
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!!
|
||||
mAlbumData.value = data
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,14 +159,14 @@ class DetailViewModel : ViewModel() {
|
|||
/**
|
||||
* Mark that the navigation process is done.
|
||||
*/
|
||||
fun doneWithNavToItem() {
|
||||
fun finishNavToItem() {
|
||||
mNavToItem.value = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current navigation status to [isNavigating]
|
||||
*/
|
||||
fun setNavigating(isNavigating: Boolean) {
|
||||
mIsNavigating = isNavigating
|
||||
fun setNavigating(navigating: Boolean) {
|
||||
isNavigating = navigating
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,11 +24,12 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.navigation.fragment.findNavController
|
||||
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.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Header
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.ui.ActionMenu
|
||||
|
@ -47,20 +48,10 @@ class GenreDetailFragment : DetailFragment() {
|
|||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
// If DetailViewModel isn't already storing the genre, get it from MusicStore
|
||||
// using the ID given by the navigation arguments
|
||||
if (detailModel.currentGenre.value == null ||
|
||||
detailModel.currentGenre.value?.id != args.genreId
|
||||
) {
|
||||
detailModel.updateGenre(
|
||||
MusicStore.getInstance().genres.find {
|
||||
it.id == args.genreId
|
||||
}!!
|
||||
)
|
||||
}
|
||||
detailModel.setGenre(args.genreId, requireContext())
|
||||
|
||||
val detailAdapter = GenreDetailAdapter(
|
||||
detailModel, playbackModel, viewLifecycleOwner,
|
||||
playbackModel,
|
||||
doOnClick = { song ->
|
||||
playbackModel.playSong(song, PlaybackMode.IN_GENRE)
|
||||
},
|
||||
|
@ -75,19 +66,13 @@ class GenreDetailFragment : DetailFragment() {
|
|||
|
||||
setupToolbar()
|
||||
setupRecycler(detailAdapter) { pos ->
|
||||
pos == 0
|
||||
val item = detailAdapter.currentList[pos]
|
||||
item is Header || item is ActionHeader || item is Genre
|
||||
}
|
||||
|
||||
// --- DETAILVIEWMODEL SETUP ---
|
||||
|
||||
detailModel.genreSortMode.observe(viewLifecycleOwner) { mode ->
|
||||
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))
|
||||
}
|
||||
|
||||
detailModel.genreData.observe(viewLifecycleOwner) { data ->
|
||||
detailAdapter.submitList(data)
|
||||
}
|
||||
|
||||
|
@ -106,7 +91,8 @@ class GenreDetailFragment : DetailFragment() {
|
|||
GenreDetailFragmentDirections.actionShowAlbum(item.album.id)
|
||||
)
|
||||
|
||||
else -> {}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +100,7 @@ class GenreDetailFragment : DetailFragment() {
|
|||
|
||||
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
||||
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)
|
||||
} else {
|
||||
|
|
|
@ -16,23 +16,26 @@
|
|||
* 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.ViewGroup
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.ItemAlbumHeaderBinding
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.coil.bindAlbumArt
|
||||
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
|
||||
import org.oxycblt.auxio.databinding.ItemDetailBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.music.ActionHeader
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.ActionHeaderViewHolder
|
||||
import org.oxycblt.auxio.ui.BaseViewHolder
|
||||
import org.oxycblt.auxio.ui.DiffCallback
|
||||
import org.oxycblt.auxio.util.disable
|
||||
import org.oxycblt.auxio.util.getPlural
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
|
||||
/**
|
||||
|
@ -40,9 +43,8 @@ import org.oxycblt.auxio.util.inflater
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
class AlbumDetailAdapter(
|
||||
private val detailModel: DetailViewModel,
|
||||
private val playbackModel: PlaybackViewModel,
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val detailModel: DetailViewModel,
|
||||
private val doOnClick: (data: Song) -> Unit,
|
||||
private val doOnLongClick: (view: View, data: Song) -> Unit
|
||||
) : ListAdapter<BaseModel, RecyclerView.ViewHolder>(DiffCallback()) {
|
||||
|
@ -52,20 +54,25 @@ class AlbumDetailAdapter(
|
|||
override fun getItemViewType(position: Int): Int {
|
||||
return when (getItem(position)) {
|
||||
is Album -> ALBUM_HEADER_ITEM_TYPE
|
||||
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||
is Song -> ALBUM_SONG_ITEM_TYPE
|
||||
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ALBUM_HEADER_ITEM_TYPE -> AlbumHeaderViewHolder(
|
||||
ItemAlbumHeaderBinding.inflate(parent.context.inflater)
|
||||
ItemDetailBinding.inflate(parent.context.inflater)
|
||||
)
|
||||
|
||||
ALBUM_SONG_ITEM_TYPE -> AlbumSongViewHolder(
|
||||
ItemAlbumSongBinding.inflate(parent.context.inflater)
|
||||
)
|
||||
|
||||
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
|
||||
|
||||
else -> error("Invalid ViewHolder item type $viewType")
|
||||
}
|
||||
}
|
||||
|
@ -76,18 +83,20 @@ class AlbumDetailAdapter(
|
|||
when (item) {
|
||||
is Album -> (holder as AlbumHeaderViewHolder).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) {
|
||||
// Reset the last ViewHolder before assigning the new, correct one to be highlighted
|
||||
currentHolder?.setHighlighted(false)
|
||||
currentHolder = (holder as Highlightable)
|
||||
currentHolder = holder
|
||||
holder.setHighlighted(true)
|
||||
} else {
|
||||
(holder as Highlightable).setHighlighted(false)
|
||||
holder.setHighlighted(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -124,17 +133,41 @@ class AlbumDetailAdapter(
|
|||
}
|
||||
|
||||
inner class AlbumHeaderViewHolder(
|
||||
private val binding: ItemAlbumHeaderBinding
|
||||
private val binding: ItemDetailBinding
|
||||
) : BaseViewHolder<Album>(binding) {
|
||||
|
||||
override fun onBind(data: Album) {
|
||||
binding.album = data
|
||||
binding.detailModel = detailModel
|
||||
binding.playbackModel = playbackModel
|
||||
binding.lifecycleOwner = lifecycleOwner
|
||||
binding.detailCover.apply {
|
||||
bindAlbumArt(data)
|
||||
contentDescription = context.getString(R.string.desc_album_cover, data.name)
|
||||
}
|
||||
|
||||
if (data.songs.size < 2) {
|
||||
binding.albumSortButton.disable()
|
||||
binding.detailName.text = data.name
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.detail.adapters
|
||||
package org.oxycblt.auxio.detail.recycler
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
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.ItemArtistHeaderBinding
|
||||
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.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Header
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.ActionHeaderViewHolder
|
||||
import org.oxycblt.auxio.ui.BaseViewHolder
|
||||
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
|
||||
|
||||
/**
|
||||
* An adapter for displaying the [Album]s and [Song]s of an artist.
|
||||
* This isnt the nicest implementation, but it works.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class ArtistDetailAdapter(
|
||||
private val playbackModel: PlaybackViewModel,
|
||||
private val detailModel: DetailViewModel,
|
||||
private val doOnClick: (data: Album) -> Unit,
|
||||
private val doOnSongClick: (data: Song) -> Unit,
|
||||
private val doOnLongClick: (view: View, data: BaseModel) -> Unit,
|
||||
|
@ -60,8 +61,9 @@ class ArtistDetailAdapter(
|
|||
return when (getItem(position)) {
|
||||
is Artist -> ARTIST_HEADER_ITEM_TYPE
|
||||
is Album -> ARTIST_ALBUM_ITEM_TYPE
|
||||
is Header -> ARTIST_SONG_HEADER_ITEM_TYPE
|
||||
is Song -> ARTIST_SONG_ITEM_TYPE
|
||||
is Header -> HeaderViewHolder.ITEM_TYPE
|
||||
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||
|
||||
else -> -1
|
||||
}
|
||||
|
@ -70,21 +72,21 @@ class ArtistDetailAdapter(
|
|||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ARTIST_HEADER_ITEM_TYPE -> ArtistHeaderViewHolder(
|
||||
ItemArtistHeaderBinding.inflate(parent.context.inflater)
|
||||
ItemDetailBinding.inflate(parent.context.inflater)
|
||||
)
|
||||
|
||||
ARTIST_ALBUM_ITEM_TYPE -> ArtistAlbumViewHolder(
|
||||
ItemArtistAlbumBinding.inflate(parent.context.inflater)
|
||||
)
|
||||
|
||||
ARTIST_SONG_HEADER_ITEM_TYPE -> ArtistSongHeaderViewHolder(
|
||||
ItemActionHeaderBinding.inflate(parent.context.inflater)
|
||||
)
|
||||
|
||||
ARTIST_SONG_ITEM_TYPE -> ArtistSongViewHolder(
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
@ -95,9 +97,9 @@ class ArtistDetailAdapter(
|
|||
when (item) {
|
||||
is Artist -> (holder as ArtistHeaderViewHolder).bind(item)
|
||||
is Album -> (holder as ArtistAlbumViewHolder).bind(item)
|
||||
is Header -> (holder as ArtistSongHeaderViewHolder).bind(item)
|
||||
is Song -> (holder as ArtistSongViewHolder).bind(item)
|
||||
|
||||
is Header -> (holder as HeaderViewHolder).bind(item)
|
||||
is ActionHeader -> (holder as ActionHeaderViewHolder).bind(item)
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
@ -180,22 +182,43 @@ class ArtistDetailAdapter(
|
|||
}
|
||||
|
||||
inner class ArtistHeaderViewHolder(
|
||||
private val binding: ItemArtistHeaderBinding
|
||||
private val binding: ItemDetailBinding
|
||||
) : BaseViewHolder<Artist>(binding) {
|
||||
|
||||
override fun onBind(data: Artist) {
|
||||
binding.artist = data
|
||||
binding.playbackModel = playbackModel
|
||||
val context = binding.root.context
|
||||
|
||||
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(
|
||||
private val binding: ItemArtistAlbumBinding,
|
||||
) : BaseViewHolder<Album>(binding, doOnClick, doOnLongClick), Highlightable {
|
||||
override fun onBind(data: Album) {
|
||||
binding.album = data
|
||||
|
||||
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(
|
||||
private val binding: ItemArtistSongBinding,
|
||||
) : BaseViewHolder<Song>(binding, doOnSongClick, doOnLongClick), Highlightable {
|
||||
private val normalTextColor = binding.songName.currentTextColor
|
||||
|
||||
override fun onBind(data: Song) {
|
||||
binding.song = data
|
||||
|
||||
binding.songName.requestLayout()
|
||||
}
|
||||
|
||||
|
@ -248,7 +243,6 @@ class ArtistDetailAdapter(
|
|||
companion object {
|
||||
const val ARTIST_HEADER_ITEM_TYPE = 0xA009
|
||||
const val ARTIST_ALBUM_ITEM_TYPE = 0xA00A
|
||||
const val ARTIST_SONG_HEADER_ITEM_TYPE = 0xA00B
|
||||
const val ARTIST_SONG_ITEM_TYPE = 0xA00C
|
||||
}
|
||||
}
|
|
@ -16,23 +16,25 @@
|
|||
* 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.ViewGroup
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
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.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.music.ActionHeader
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.ActionHeaderViewHolder
|
||||
import org.oxycblt.auxio.ui.BaseViewHolder
|
||||
import org.oxycblt.auxio.ui.DiffCallback
|
||||
import org.oxycblt.auxio.util.disable
|
||||
import org.oxycblt.auxio.util.getPlural
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
|
||||
/**
|
||||
|
@ -40,9 +42,7 @@ import org.oxycblt.auxio.util.inflater
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
class GenreDetailAdapter(
|
||||
private val detailModel: DetailViewModel,
|
||||
private val playbackModel: PlaybackViewModel,
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val doOnClick: (data: Song) -> Unit,
|
||||
private val doOnLongClick: (view: View, data: Song) -> Unit
|
||||
) : ListAdapter<BaseModel, RecyclerView.ViewHolder>(DiffCallback()) {
|
||||
|
@ -52,6 +52,7 @@ class GenreDetailAdapter(
|
|||
override fun getItemViewType(position: Int): Int {
|
||||
return when (getItem(position)) {
|
||||
is Genre -> GENRE_HEADER_ITEM_TYPE
|
||||
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||
is Song -> GENRE_SONG_ITEM_TYPE
|
||||
|
||||
else -> -1
|
||||
|
@ -61,13 +62,15 @@ class GenreDetailAdapter(
|
|||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
GENRE_HEADER_ITEM_TYPE -> GenreHeaderViewHolder(
|
||||
ItemGenreHeaderBinding.inflate(parent.context.inflater)
|
||||
ItemDetailBinding.inflate(parent.context.inflater)
|
||||
)
|
||||
|
||||
GENRE_SONG_ITEM_TYPE -> GenreSongViewHolder(
|
||||
ItemGenreSongBinding.inflate(parent.context.inflater),
|
||||
)
|
||||
|
||||
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
|
||||
|
||||
else -> error("Bad viewholder item type $viewType")
|
||||
}
|
||||
}
|
||||
|
@ -77,18 +80,19 @@ class GenreDetailAdapter(
|
|||
|
||||
when (item) {
|
||||
is Genre -> (holder as GenreHeaderViewHolder).bind(item)
|
||||
is ActionHeader -> (holder as ActionHeaderViewHolder).bind(item)
|
||||
is Song -> (holder as GenreSongViewHolder).bind(item)
|
||||
else -> {}
|
||||
}
|
||||
|
||||
if (currentSong != null && position > 0) {
|
||||
if (holder is Highlightable) {
|
||||
if (item.id == currentSong?.id) {
|
||||
// Reset the last ViewHolder before assigning the new, correct one to be highlighted
|
||||
currentHolder?.setHighlighted(false)
|
||||
currentHolder = (holder as Highlightable)
|
||||
currentHolder = holder
|
||||
holder.setHighlighted(true)
|
||||
} else {
|
||||
(holder as Highlightable).setHighlighted(false)
|
||||
holder.setHighlighted(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,16 +129,30 @@ class GenreDetailAdapter(
|
|||
}
|
||||
|
||||
inner class GenreHeaderViewHolder(
|
||||
private val binding: ItemGenreHeaderBinding
|
||||
private val binding: ItemDetailBinding
|
||||
) : BaseViewHolder<Genre>(binding) {
|
||||
override fun onBind(data: Genre) {
|
||||
binding.genre = data
|
||||
binding.detailModel = detailModel
|
||||
binding.playbackModel = playbackModel
|
||||
binding.lifecycleOwner = lifecycleOwner
|
||||
val context = binding.root.context
|
||||
|
||||
if (data.songs.size < 2) {
|
||||
binding.genreSortButton.disable()
|
||||
binding.detailCover.apply {
|
||||
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/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.detail.adapters
|
||||
package org.oxycblt.auxio.detail.recycler
|
||||
|
||||
/**
|
||||
* Interface that allows the highlighting of certain ViewHolders
|
|
@ -25,7 +25,7 @@ import org.oxycblt.auxio.databinding.ItemExcludedDirBinding
|
|||
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
|
||||
*/
|
||||
class ExcludedEntryAdapter(
|
||||
|
|
|
@ -21,7 +21,9 @@ package org.oxycblt.auxio.home
|
|||
import androidx.annotation.IdRes
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
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 {
|
||||
/**
|
||||
* Convert a menu [id] to an instance of [LibSortMode].
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
package org.oxycblt.auxio.music
|
||||
|
||||
import android.net.Uri
|
||||
import android.view.View
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
|
||||
// --- MUSIC MODELS ---
|
||||
|
||||
|
@ -218,11 +221,21 @@ data class Genre(
|
|||
}
|
||||
|
||||
/**
|
||||
* A data object used solely for the "Header" UI element. Inherits [BaseModel].
|
||||
* @param isAction Whether this header corresponds to an action or not
|
||||
* A data object used solely for the "Header" UI element.
|
||||
*/
|
||||
data class Header(
|
||||
override val id: Long,
|
||||
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()
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
package org.oxycblt.auxio.music
|
||||
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.MediaStore
|
||||
import android.text.format.DateUtils
|
||||
|
@ -122,67 +121,16 @@ fun Long.toDuration(): String {
|
|||
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 ---
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@BindingAdapter("artistCounts")
|
||||
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(
|
||||
R.string.format_double_info,
|
||||
album.year.toYear(context),
|
||||
context.getPlural(R.plurals.fmt_song_count, album.songs.size),
|
||||
album.totalDuration
|
||||
R.string.fmt_counts,
|
||||
context.getPlural(R.plurals.fmt_album_count, artist.albums.size),
|
||||
context.getPlural(R.plurals.fmt_song_count, artist.songs.size)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
playbackModel.userQueue.observe(viewLifecycleOwner) { userQueue ->
|
||||
playbackModel.userQueue.observe(viewLifecycleOwner) {
|
||||
updateQueueIcon(queueItem)
|
||||
}
|
||||
|
||||
|
|
|
@ -21,17 +21,16 @@ package org.oxycblt.auxio.playback.queue
|
|||
import android.annotation.SuppressLint
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.TooltipCompat
|
||||
import androidx.recyclerview.widget.AsyncListDiffer
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
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.music.ActionHeader
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Header
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.ActionHeaderViewHolder
|
||||
import org.oxycblt.auxio.ui.BaseViewHolder
|
||||
import org.oxycblt.auxio.ui.DiffCallback
|
||||
import org.oxycblt.auxio.ui.HeaderViewHolder
|
||||
|
@ -46,8 +45,7 @@ import org.oxycblt.auxio.util.logE
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
class QueueAdapter(
|
||||
private val touchHelper: ItemTouchHelper,
|
||||
private val playbackModel: PlaybackViewModel
|
||||
private val touchHelper: ItemTouchHelper
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
private var data = mutableListOf<BaseModel>()
|
||||
private var listDiffer = AsyncListDiffer(this, DiffCallback())
|
||||
|
@ -55,14 +53,10 @@ class QueueAdapter(
|
|||
override fun getItemCount(): Int = data.size
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (val item = data[position]) {
|
||||
is Header -> if (item.isAction) {
|
||||
USER_QUEUE_HEADER_ITEM_TYPE
|
||||
} else {
|
||||
HeaderViewHolder.ITEM_TYPE
|
||||
}
|
||||
|
||||
return when (data[position]) {
|
||||
is Song -> QUEUE_SONG_ITEM_TYPE
|
||||
is Header -> HeaderViewHolder.ITEM_TYPE
|
||||
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||
|
||||
else -> -1
|
||||
}
|
||||
|
@ -70,29 +64,22 @@ class QueueAdapter(
|
|||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
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(
|
||||
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.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
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 Header -> (holder as HeaderViewHolder).bind(item)
|
||||
is ActionHeader -> (holder as ActionHeaderViewHolder).bind(item)
|
||||
|
||||
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 {
|
||||
const val QUEUE_SONG_ITEM_TYPE = 0xA005
|
||||
const val USER_QUEUE_HEADER_ITEM_TYPE = 0xA006
|
||||
|
|
|
@ -90,6 +90,7 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc
|
|||
// an elevation change.
|
||||
// TODO: Maybe restrict the item from being drawn over the recycler bounds?
|
||||
// Seems like its possible with enough UI magic
|
||||
// TODO: Add an accented BG to the removal action
|
||||
|
||||
val view = viewHolder.itemView
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
|||
import kotlinx.coroutines.NonDisposableHandle.parent
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
||||
import org.oxycblt.auxio.music.ActionHeader
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Header
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
|
@ -53,7 +54,7 @@ class QueueFragment : Fragment() {
|
|||
val callback = QueueDragCallback(playbackModel)
|
||||
|
||||
val helper = ItemTouchHelper(callback)
|
||||
val queueAdapter = QueueAdapter(helper, playbackModel)
|
||||
val queueAdapter = QueueAdapter(helper)
|
||||
var lastShuffle = playbackModel.isShuffling.value
|
||||
|
||||
callback.addQueueAdapter(queueAdapter)
|
||||
|
@ -120,10 +121,12 @@ class QueueFragment : Fragment() {
|
|||
val nextQueue = playbackModel.nextItemsInQueue.value!!
|
||||
|
||||
if (userQueue.isNotEmpty()) {
|
||||
queue += Header(
|
||||
queue += ActionHeader(
|
||||
id = -2,
|
||||
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
|
||||
|
@ -132,8 +135,10 @@ class QueueFragment : Fragment() {
|
|||
if (nextQueue.isNotEmpty()) {
|
||||
queue += Header(
|
||||
id = -3,
|
||||
name = getString(R.string.fmt_next_from, getParentName()),
|
||||
isAction = false
|
||||
name = getString(
|
||||
R.string.fmt_next_from,
|
||||
playbackModel.parent.value?.displayName ?: getString(R.string.lbl_all_songs)
|
||||
)
|
||||
)
|
||||
|
||||
queue += nextQueue
|
||||
|
@ -141,8 +146,4 @@ class QueueFragment : Fragment() {
|
|||
|
||||
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.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentSearchBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
|
@ -74,9 +73,6 @@ class SearchFragment : Fragment() {
|
|||
},
|
||||
::newMenu
|
||||
)
|
||||
|
||||
val toolbarParams = binding.searchToolbar.layoutParams as AppBarLayout.LayoutParams
|
||||
|
||||
// --- UI SETUP --
|
||||
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
|
|
|
@ -130,7 +130,11 @@ class SearchViewModel : ViewModel() {
|
|||
*/
|
||||
private fun List<BaseModel>.filterByOrNull(value: String): List<BaseModel>? {
|
||||
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
|
||||
|
@ -155,8 +159,8 @@ class SearchViewModel : ViewModel() {
|
|||
idx += Character.charCount(cp)
|
||||
|
||||
when (Character.getType(cp)) {
|
||||
Character.NON_SPACING_MARK.toInt(), Character.COMBINING_SPACING_MARK.toInt() ->
|
||||
continue
|
||||
// Character.NON_SPACING_MARK and Character.COMBINING_SPACING_MARK
|
||||
6, 8 -> continue
|
||||
|
||||
else -> sb.appendCodePoint(cp)
|
||||
}
|
||||
|
|
|
@ -18,14 +18,8 @@
|
|||
|
||||
package org.oxycblt.auxio.ui
|
||||
|
||||
import android.widget.ImageButton
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.databinding.BindingAdapter
|
||||
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
|
||||
|
||||
/**
|
||||
|
@ -35,83 +29,11 @@ import org.oxycblt.auxio.music.Song
|
|||
*/
|
||||
enum class SortMode(@DrawableRes val iconRes: Int) {
|
||||
// Icons for each mode are assigned to the enums themselves
|
||||
NONE(R.drawable.ic_sort_none),
|
||||
ALPHA_UP(R.drawable.ic_sort_alpha_up),
|
||||
ALPHA_DOWN(R.drawable.ic_sort_alpha_down),
|
||||
NUMERIC_UP(R.drawable.ic_sort_numeric_up),
|
||||
NUMERIC_DOWN(R.drawable.ic_sort_numeric_down);
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
NONE(R.drawable.ic_sort),
|
||||
ALPHA_UP(R.drawable.ic_sort),
|
||||
ALPHA_DOWN(R.drawable.ic_sort),
|
||||
NUMERIC_UP(R.drawable.ic_sort),
|
||||
NUMERIC_DOWN(R.drawable.ic_sort);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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.
|
||||
* This is hilariously anglo-centric, but its mostly for MediaStore compat and hopefully
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* 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
|
||||
* 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.view.View
|
||||
import androidx.appcompat.widget.TooltipCompat
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.ItemActionHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemAlbumBinding
|
||||
import org.oxycblt.auxio.databinding.ItemArtistBinding
|
||||
import org.oxycblt.auxio.databinding.ItemGenreBinding
|
||||
import org.oxycblt.auxio.databinding.ItemHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemSongBinding
|
||||
import org.oxycblt.auxio.music.ActionHeader
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
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"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorAccent"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<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">
|
||||
<stroke
|
||||
android:width="@dimen/size_stroke_small"
|
||||
android:color="@color/overlay_divider" />
|
||||
android:color="@color/mtrl_btn_stroke_color_selector" />
|
||||
</shape>
|
||||
</item>
|
||||
</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_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:theme="@style/Theme.Neutral"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
|
@ -38,7 +39,7 @@
|
|||
app:cardBackgroundColor="?attr/colorSurface"
|
||||
app:cardCornerRadius="0dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="@color/overlay_divider"
|
||||
app:strokeColor="@color/mtrl_btn_stroke_color_selector"
|
||||
app:strokeWidth="1dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
@ -158,7 +159,6 @@
|
|||
<TextView
|
||||
android:id="@+id/about_song_count"
|
||||
style="@style/Widget.TextView.Icon"
|
||||
android:theme="@style/Theme.Neutral"
|
||||
app:drawableStartCompat="@drawable/ic_song"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
@ -169,7 +169,7 @@
|
|||
android:id="@+id/about_author"
|
||||
style="@style/Widget.TextView.Icon"
|
||||
android:text="@string/lbl_author"
|
||||
app:drawableStartCompat="@drawable/ic_author"
|
||||
app:drawableStartCompat="@drawable/ic_artist"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/about_song_count" />
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
style="@style/Widget.TextView.Compact.Secondary"
|
||||
android:layout_marginStart="@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_constraintEnd_toStartOf="@+id/playback_play_pause"
|
||||
app:layout_constraintStart_toEndOf="@+id/playback_cover"
|
||||
|
|
|
@ -28,8 +28,7 @@
|
|||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
tools:listitem="@layout/item_artist_header"
|
||||
tools:layout_marginTop="56dp"/>
|
||||
tools:listitem="@layout/item_detail" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</layout>
|
|
@ -4,12 +4,11 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".ui.HeaderViewHolder">
|
||||
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="header"
|
||||
type="org.oxycblt.auxio.music.Header" />
|
||||
type="org.oxycblt.auxio.music.ActionHeader" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
@ -32,10 +31,12 @@
|
|||
style="@style/Widget.Button.Unbounded.Small"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@{context.getString(header.desc)}"
|
||||
android:src="@{context.getDrawable(header.icon)}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:src="@drawable/ic_clear" />
|
||||
tools:src="@drawable/ic_sort"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -37,7 +37,7 @@
|
|||
<TextView
|
||||
android:id="@+id/album_info"
|
||||
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_constraintEnd_toEndOf="parent"
|
||||
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"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".detail.adapters.AlbumDetailAdapter.AlbumSongViewHolder">
|
||||
tools:context=".detail.recycler.AlbumDetailAdapter.AlbumSongViewHolder">
|
||||
|
||||
<data>
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<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.ArtistAlbumViewHolder">
|
||||
tools:context=".detail.recycler.ArtistDetailAdapter.ArtistAlbumViewHolder">
|
||||
|
||||
<data>
|
||||
|
||||
|
@ -38,7 +38,7 @@
|
|||
<TextView
|
||||
android:id="@+id/album_year"
|
||||
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_constraintEnd_toEndOf="parent"
|
||||
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"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".detail.adapters.GenreDetailAdapter.GenreSongViewHolder">
|
||||
tools:context=".detail.recycler.GenreDetailAdapter.GenreSongViewHolder">
|
||||
|
||||
<data>
|
||||
|
||||
|
@ -40,7 +40,7 @@
|
|||
android:id="@+id/song_info"
|
||||
style="@style/Widget.TextView.Item.Secondary"
|
||||
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_constraintEnd_toStartOf="@+id/song_duration"
|
||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
android:id="@+id/song_info"
|
||||
style="@style/Widget.TextView.Item.Secondary"
|
||||
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_constraintEnd_toStartOf="@+id/song_drag_handle"
|
||||
app:layout_constraintStart_toEndOf="@+id/album_cover"
|
||||
|
@ -52,13 +52,14 @@
|
|||
|
||||
<ImageView
|
||||
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_marginEnd="@dimen/spacing_small"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/desc_queue_handle"
|
||||
android:focusable="true"
|
||||
android:scaleType="center"
|
||||
android:paddingStart="@dimen/spacing_medium"
|
||||
android:paddingEnd="@dimen/spacing_medium"
|
||||
android:src="@drawable/ic_handle"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/album_cover"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
android:id="@+id/song_info"
|
||||
style="@style/Widget.TextView.Item.Secondary"
|
||||
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_constraintEnd_toEndOf="parent"
|
||||
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
|
||||
android:id="@+id/submenu_sorting"
|
||||
android:icon="@drawable/ic_sort_none"
|
||||
android:icon="@drawable/ic_sort"
|
||||
android:title="@string/lbl_sort"
|
||||
app:showAsAction="ifRoom">
|
||||
<menu>
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
<string name="hint_search_library">"Prohledat vaši knihovnu…"</string>
|
||||
|
||||
<!-- 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_play_pause">"Přehrát nebo pozastavit"</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>
|
||||
|
||||
<!-- 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_play_pause">Abspielen oder Pausieren</string>
|
||||
|
|
|
@ -106,7 +106,7 @@
|
|||
<string name="hint_search_library">Busca en tu biblioteca…</string>
|
||||
|
||||
<!-- 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_play_pause">Reproducir o Pausar</string>
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
<string name="hint_search_library">Zoek in uw bibliotheek…</string>
|
||||
|
||||
<!-- 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_play_pause">Afspelen/Pauzeren</string>
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
|
||||
<dimen name="size_cover_compact">48dp</dimen>
|
||||
<dimen name="size_cover_normal">56dp</dimen>
|
||||
<dimen name="size_cover_huge_land">136dp</dimen>
|
||||
<dimen name="size_cover_huge">264dp</dimen>
|
||||
<dimen name="size_cover_huge_land">128dp</dimen>
|
||||
<dimen name="size_cover_huge">256dp</dimen>
|
||||
|
||||
<dimen name="size_stroke_small">1dp</dimen>
|
||||
<dimen name="size_stroke_large">2dp</dimen>
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
<string name="info_app_name" translatable="false">Auxio</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting/plurals -->
|
||||
<string name="format_info" translatable="false">%1$s • %2$s</string>
|
||||
<string name="format_double_info" translatable="false">%1$s • %2$s • %3$s</string>
|
||||
<string name="format_double_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_two" translatable="false">%1$s • %2$s</string>
|
||||
<string name="fmt_three" translatable="false">%1$s • %2$s • %3$s</string>
|
||||
<string name="fmt_counts" translatable="false">%1$s, %2$s</string>
|
||||
<string name="fmt_accent_desc" translatable="false"><b>%1$s</b>: %2$s</string>
|
||||
</resources>
|
|
@ -114,7 +114,6 @@
|
|||
<string name="hint_search_library">Search your library…</string>
|
||||
|
||||
<!-- Description Namespace | Accessibility Strings -->
|
||||
<string name="desc_sort_button">Change Sort Order</string>
|
||||
<string name="desc_track_number">Track %d</string>
|
||||
|
||||
<string name="desc_play_pause">Play or Pause</string>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<style name="Theme.Base" parent="Theme.Splash">
|
||||
<!-- Colors -->
|
||||
<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="colorOnPrimary">?attr/colorSurface</item>
|
||||
|
|
|
@ -197,26 +197,24 @@
|
|||
<item name="android:background">@drawable/ui_small_unbounded_ripple</item>
|
||||
</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_height">wrap_content</item>
|
||||
<item name="android:clickable">true</item>
|
||||
<item name="android:focusable">true</item>
|
||||
<item name="fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:letterSpacing">0.05</item>
|
||||
<item name="android:textAllCaps">false</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>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Button.Vibrant.Primary" parent="@style/Widget.Button.Vibrant.Base">
|
||||
<item name="android:textColor">?attr/colorSurface</item>
|
||||
<item name="backgroundTint">?attr/colorAccent</item>
|
||||
<item name="rippleColor">@color/mtrl_btn_ripple_color</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Button.Vibrant.Secondary" parent="@style/Widget.Button.Vibrant.Base">
|
||||
<item name="strokeColor">@color/overlay_divider</item>
|
||||
<item name="strokeWidth">@dimen/size_stroke_small</item>
|
||||
<item name="rippleColor">?attr/colorControlHighlight</item>
|
||||
<style name="Widget.Button.Vibrant.Secondary" parent="@style/Widget.MaterialComponents.Button.OutlinedButton">
|
||||
<item name="android:layout_width">0dp</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:letterSpacing">0.05</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
<item name="android:textSize">@dimen/text_size_small</item>
|
||||
<item name="fontFamily">@font/inter_semibold</item>
|
||||
<item name="cornerRadius">0dp</item>
|
||||
</style>
|
||||
</resources>
|
Loading…
Reference in a new issue