diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt index 6c76ba584..f4d911a83 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/accent/Accent.kt b/app/src/main/java/org/oxycblt/auxio/accent/Accent.kt index 29a551434..0da1260b7 100644 --- a/app/src/main/java/org/oxycblt/auxio/accent/Accent.kt +++ b/app/src/main/java/org/oxycblt/auxio/accent/Accent.kt @@ -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 ) } diff --git a/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt b/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt index 871fcb717..cda029250 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt @@ -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) + } } /** diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt index 4d84d25fe..4a67bac6c 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -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(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 diff --git a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt index 0cedbd5fe..0aed4c8cc 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -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(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 { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt index 27053ea33..c38031d01 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt @@ -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() { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt index 15d4133c8..e5e12ce83 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -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() - val currentGenre: LiveData get() = mCurrentGenre + private val mCurGenre = MutableLiveData() + val curGenre: LiveData get() = mCurGenre - private val mCurrentArtist = MutableLiveData() - val currentArtist: LiveData get() = mCurrentArtist + private val mGenreData = MutableLiveData(listOf()) + val genreData: LiveData> = mGenreData - private val mCurrentAlbum = MutableLiveData() - val currentAlbum: LiveData get() = mCurrentAlbum + private val mCurArtist = MutableLiveData() + val curArtist: LiveData get() = mCurArtist - // --- SORT MODES --- + private val mArtistData = MutableLiveData(listOf()) + val artistData: LiveData> = mArtistData - private val mGenreSortMode = MutableLiveData(settingsManager.genreSortMode) - val genreSortMode: LiveData get() = mGenreSortMode + private val mCurAlbum = MutableLiveData() + val curAlbum: LiveData get() = mCurAlbum - private val mArtistSortMode = MutableLiveData(settingsManager.artistSortMode) - val albumSortMode: LiveData get() = mAlbumSortMode + private val mAlbumData = MutableLiveData(listOf()) + val albumData: LiveData> get() = mAlbumData - private val mAlbumSortMode = MutableLiveData(settingsManager.albumSortMode) - val artistSortMode: LiveData get() = mArtistSortMode - - private var mIsNavigating = false - val isNavigating: Boolean get() = mIsNavigating + var isNavigating = false + private set private val mNavToItem = MutableLiveData() /** Flag for unified navigation. Observe this to coordinate navigation to an item's UI. */ val navToItem: LiveData 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(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(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 - /** - * 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 + mCurAlbum.value = musicStore.albums.find { it.id == id } - else -> SortMode.ALPHA_DOWN - } + val data = mutableListOf(curAlbum.value!!) - mGenreSortMode.value = mode - settingsManager.genreSortMode = mode - } + 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 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 + data.addAll(LibSortMode.ASCENDING.sortAlbum(curAlbum.value!!)) - 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 } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt index 43f5ee30f..67533f1cf 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -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(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 { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/AlbumDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt similarity index 73% rename from app/src/main/java/org/oxycblt/auxio/detail/adapters/AlbumDetailAdapter.kt rename to app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt index d6b57aad5..275458cb2 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/AlbumDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt @@ -16,23 +16,26 @@ * along with this program. If not, see . */ -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(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(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) } } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt similarity index 80% rename from app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistDetailAdapter.kt rename to app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt index 2d441b587..8cf39e0b3 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt @@ -16,36 +16,37 @@ * along with this program. If not, see . */ -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(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(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
(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(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 } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt similarity index 76% rename from app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreDetailAdapter.kt rename to app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt index ebdea2cf5..7f3bda6e5 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt @@ -16,23 +16,25 @@ * along with this program. If not, see . */ -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(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(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) } } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/Highlightable.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/Highlightable.kt similarity index 95% rename from app/src/main/java/org/oxycblt/auxio/detail/adapters/Highlightable.kt rename to app/src/main/java/org/oxycblt/auxio/detail/recycler/Highlightable.kt index 4701b1e51..532d6040d 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/Highlightable.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/Highlightable.kt @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package org.oxycblt.auxio.detail.adapters +package org.oxycblt.auxio.detail.recycler /** * Interface that allows the highlighting of certain ViewHolders diff --git a/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedEntryAdapter.kt b/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedEntryAdapter.kt index ba27ac342..b91ad98cc 100644 --- a/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedEntryAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedEntryAdapter.kt @@ -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( diff --git a/app/src/main/java/org/oxycblt/auxio/home/LibSortMode.kt b/app/src/main/java/org/oxycblt/auxio/home/LibSortMode.kt index 071a76c5b..67ae8bfb9 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/LibSortMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/LibSortMode.kt @@ -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 { + return sortSongs(artist.songs) + } + + /** + * Sort the songs in a genre. + * @see sortSongs + */ + fun sortGenre(genre: Genre): List { + return sortSongs(genre.songs) + } + companion object { /** * Convert a menu [id] to an instance of [LibSortMode]. diff --git a/app/src/main/java/org/oxycblt/auxio/music/Models.kt b/app/src/main/java/org/oxycblt/auxio/music/Models.kt index ec14021e6..3859d0754 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt @@ -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() diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt index 5e905d785..f5ab0e5d7 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt @@ -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) -} diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt index fd5f0ad64..2eecf4684 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt @@ -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) } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt index dca6ffe6a..ff2b951bb 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt @@ -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() { private var data = mutableListOf() 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
(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 diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt index 38f0732f5..97783d7ac 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt index f810e1266..d41677afe 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt @@ -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) - } } diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt index 609e6487b..b4780c20b 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt index 5d3e8c4e0..5479f356d 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt @@ -130,7 +130,11 @@ class SearchViewModel : ViewModel() { */ private fun List.filterByOrNull(value: String): List? { 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) } diff --git a/app/src/main/java/org/oxycblt/auxio/ui/SortMode.kt b/app/src/main/java/org/oxycblt/auxio/ui/SortMode.kt index aad4dabc9..1fe65e9da 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/SortMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/SortMode.kt @@ -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): List { - 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): List { - 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): List { - 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 diff --git a/app/src/main/java/org/oxycblt/auxio/ui/ViewHolders.kt b/app/src/main/java/org/oxycblt/auxio/ui/ViewHolders.kt index 0844ee228..863fa6738 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/ViewHolders.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/ViewHolders.kt @@ -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(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)) + } + } +} diff --git a/app/src/main/res/drawable/ic_author.xml b/app/src/main/res/drawable/ic_author.xml deleted file mode 100644 index 7e80f2a7b..000000000 --- a/app/src/main/res/drawable/ic_author.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_clear.xml b/app/src/main/res/drawable/ic_clear.xml index 7d02a28b1..cac8f9fe7 100644 --- a/app/src/main/res/drawable/ic_clear.xml +++ b/app/src/main/res/drawable/ic_clear.xml @@ -2,7 +2,7 @@ - - - diff --git a/app/src/main/res/drawable/ic_sort_alpha_up.xml b/app/src/main/res/drawable/ic_sort_alpha_up.xml deleted file mode 100644 index 84a5d9965..000000000 --- a/app/src/main/res/drawable/ic_sort_alpha_up.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_sort_numeric_down.xml b/app/src/main/res/drawable/ic_sort_numeric_down.xml deleted file mode 100644 index 0bf7a4265..000000000 --- a/app/src/main/res/drawable/ic_sort_numeric_down.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_sort_numeric_up.xml b/app/src/main/res/drawable/ic_sort_numeric_up.xml deleted file mode 100644 index e01c65d35..000000000 --- a/app/src/main/res/drawable/ic_sort_numeric_up.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ui_header_dividers.xml b/app/src/main/res/drawable/ui_header_dividers.xml index b0db278e1..0f07bd2e6 100644 --- a/app/src/main/res/drawable/ui_header_dividers.xml +++ b/app/src/main/res/drawable/ui_header_dividers.xml @@ -7,7 +7,7 @@ + android:color="@color/mtrl_btn_stroke_color_selector" /> diff --git a/app/src/main/res/layout-land/item_album_header.xml b/app/src/main/res/layout-land/item_album_header.xml deleted file mode 100644 index 9317ad359..000000000 --- a/app/src/main/res/layout-land/item_album_header.xml +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout-land/item_artist_header.xml b/app/src/main/res/layout-land/item_artist_header.xml deleted file mode 100644 index a35893b69..000000000 --- a/app/src/main/res/layout-land/item_artist_header.xml +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout-land/item_detail.xml b/app/src/main/res/layout-land/item_detail.xml new file mode 100644 index 000000000..36eae3636 --- /dev/null +++ b/app/src/main/res/layout-land/item_detail.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/item_genre_header.xml b/app/src/main/res/layout-land/item_genre_header.xml deleted file mode 100644 index 6a14ed49b..000000000 --- a/app/src/main/res/layout-land/item_genre_header.xml +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-large/item_album_header.xml b/app/src/main/res/layout-large/item_album_header.xml deleted file mode 100644 index 964ebbc4c..000000000 --- a/app/src/main/res/layout-large/item_album_header.xml +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout-large/item_artist_header.xml b/app/src/main/res/layout-large/item_artist_header.xml deleted file mode 100644 index 141ef7022..000000000 --- a/app/src/main/res/layout-large/item_artist_header.xml +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout-large/item_detail.xml b/app/src/main/res/layout-large/item_detail.xml new file mode 100644 index 000000000..a5a7f4e9d --- /dev/null +++ b/app/src/main/res/layout-large/item_detail.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-large/item_genre_header.xml b/app/src/main/res/layout-large/item_genre_header.xml deleted file mode 100644 index 9e10c4786..000000000 --- a/app/src/main/res/layout-large/item_genre_header.xml +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml index 2dc703d66..d94a1ee30 100644 --- a/app/src/main/res/layout/fragment_about.xml +++ b/app/src/main/res/layout/fragment_about.xml @@ -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"> diff --git a/app/src/main/res/layout/fragment_compact_playback.xml b/app/src/main/res/layout/fragment_compact_playback.xml index f989fb5fc..eb8fa4872 100644 --- a/app/src/main/res/layout/fragment_compact_playback.xml +++ b/app/src/main/res/layout/fragment_compact_playback.xml @@ -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" diff --git a/app/src/main/res/layout/fragment_detail.xml b/app/src/main/res/layout/fragment_detail.xml index 0b109667b..3c61b55f7 100644 --- a/app/src/main/res/layout/fragment_detail.xml +++ b/app/src/main/res/layout/fragment_detail.xml @@ -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" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_action_header.xml b/app/src/main/res/layout/item_action_header.xml index e3219140c..89e6dd5c1 100644 --- a/app/src/main/res/layout/item_action_header.xml +++ b/app/src/main/res/layout/item_action_header.xml @@ -4,12 +4,11 @@ xmlns:tools="http://schemas.android.com/tools" tools:context=".ui.HeaderViewHolder"> - + type="org.oxycblt.auxio.music.ActionHeader" /> + tools:src="@drawable/ic_sort"/> + \ No newline at end of file diff --git a/app/src/main/res/layout/item_album.xml b/app/src/main/res/layout/item_album.xml index 755d96b2c..05e9ea78d 100644 --- a/app/src/main/res/layout/item_album.xml +++ b/app/src/main/res/layout/item_album.xml @@ -37,7 +37,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_album_song.xml b/app/src/main/res/layout/item_album_song.xml index 82c6dd232..f14e7d2b3 100644 --- a/app/src/main/res/layout/item_album_song.xml +++ b/app/src/main/res/layout/item_album_song.xml @@ -2,7 +2,7 @@ + tools:context=".detail.recycler.AlbumDetailAdapter.AlbumSongViewHolder"> diff --git a/app/src/main/res/layout/item_artist_album.xml b/app/src/main/res/layout/item_artist_album.xml index 786e96715..5b8694b2e 100644 --- a/app/src/main/res/layout/item_artist_album.xml +++ b/app/src/main/res/layout/item_artist_album.xml @@ -2,7 +2,7 @@ + tools:context=".detail.recycler.ArtistDetailAdapter.ArtistAlbumViewHolder"> @@ -38,7 +38,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_detail.xml b/app/src/main/res/layout/item_detail.xml new file mode 100644 index 000000000..94809adc7 --- /dev/null +++ b/app/src/main/res/layout/item_detail.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_genre_header.xml b/app/src/main/res/layout/item_genre_header.xml deleted file mode 100644 index e60f68153..000000000 --- a/app/src/main/res/layout/item_genre_header.xml +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_genre_song.xml b/app/src/main/res/layout/item_genre_song.xml index 833aac0d2..a6d0b04fc 100644 --- a/app/src/main/res/layout/item_genre_song.xml +++ b/app/src/main/res/layout/item_genre_song.xml @@ -2,7 +2,7 @@ + tools:context=".detail.recycler.GenreDetailAdapter.GenreSongViewHolder"> @@ -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" diff --git a/app/src/main/res/layout/item_queue_song.xml b/app/src/main/res/layout/item_queue_song.xml index 50ea63a26..fcc327bb4 100644 --- a/app/src/main/res/layout/item_queue_song.xml +++ b/app/src/main/res/layout/item_queue_song.xml @@ -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 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_home.xml b/app/src/main/res/menu/menu_home.xml index 2d9f36a20..3f43384dc 100644 --- a/app/src/main/res/menu/menu_home.xml +++ b/app/src/main/res/menu/menu_home.xml @@ -10,7 +10,7 @@ diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 9f7437553..0b0d6b98e 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -100,7 +100,7 @@ "Prohledat vaši knihovnu…" - "Změnit pořadí řazení" + "Změnit pořadí řazení" "Stopa %d" "Přehrát nebo pozastavit" "Přeskočit na další skladbu" diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 039d6dbc5..491ea7d72 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -103,7 +103,7 @@ Musikbibliothek durchsuchen… - Reihenfolge ändern + Reihenfolge ändern Titel %d Abspielen oder Pausieren diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 16ed3f926..e051c1f45 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -106,7 +106,7 @@ Busca en tu biblioteca… - Cambiar el orden de clasificación + Cambiar el orden de clasificación Pista %d Reproducir o Pausar diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index e53b010cd..cd999fa92 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -104,7 +104,7 @@ Zoek in uw bibliotheek… - Sorteervolgorde wijzigen + Sorteervolgorde wijzigen Nummer %d Afspelen/Pauzeren diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 593f6184c..c5fbbfa58 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -14,8 +14,8 @@ 48dp 56dp - 136dp - 264dp + 128dp + 256dp 1dp 2dp diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index a7035f9c8..a7f87b4e6 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -4,8 +4,8 @@ Auxio - %1$s • %2$s - %1$s • %2$s • %3$s - %1$s, %2$s - <b>%1$s</b>: %2$s + %1$s • %2$s + %1$s • %2$s • %3$s + %1$s, %2$s + <b>%1$s</b>: %2$s \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 13b5f5bd1..bad5996f3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -114,7 +114,6 @@ Search your library… - Change Sort Order Track %d Play or Pause diff --git a/app/src/main/res/values/styles_core.xml b/app/src/main/res/values/styles_core.xml index 38c4a0d18..d8ce9d0b3 100644 --- a/app/src/main/res/values/styles_core.xml +++ b/app/src/main/res/values/styles_core.xml @@ -11,7 +11,7 @@ - - - - \ No newline at end of file