detail: switch to sort dialogs

Use the new sort dialogs on in detail views.
This commit is contained in:
Alexander Capehart 2023-07-25 16:47:25 -06:00
parent ec7f2d979a
commit 3340914c51
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
13 changed files with 230 additions and 211 deletions

View file

@ -41,7 +41,6 @@ import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.ListViewModel
import org.oxycblt.auxio.list.Menu
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent
@ -196,30 +195,7 @@ class AlbumDetailFragment :
}
override fun onOpenSortMenu(anchor: View) {
openMenu(anchor, R.menu.sort_album) {
// Select the corresponding sort mode option
val sort = detailModel.albumSongSort
unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true
// Select the corresponding sort direction option
val directionItemId =
when (sort.direction) {
Sort.Direction.ASCENDING -> R.id.option_sort_asc
Sort.Direction.DESCENDING -> R.id.option_sort_dec
}
unlikelyToBeNull(menu.findItem(directionItemId)).isChecked = true
setOnMenuItemClickListener { item ->
item.isChecked = !item.isChecked
detailModel.albumSongSort =
when (item.itemId) {
// Sort direction options
R.id.option_sort_asc -> sort.withDirection(Sort.Direction.ASCENDING)
R.id.option_sort_dec -> sort.withDirection(Sort.Direction.DESCENDING)
// Any other option is a sort mode
else -> sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId)))
}
true
}
}
findNavController().navigateSafe(AlbumDetailFragmentDirections.sort())
}
override fun onNavigateToParentArtist() {

View file

@ -41,7 +41,6 @@ import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.ListViewModel
import org.oxycblt.auxio.list.Menu
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Music
@ -202,32 +201,7 @@ class ArtistDetailFragment :
}
override fun onOpenSortMenu(anchor: View) {
openMenu(anchor, R.menu.sort_artist) {
// Select the corresponding sort mode option
val sort = detailModel.artistSongSort
unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true
// Select the corresponding sort direction option
val directionItemId =
when (sort.direction) {
Sort.Direction.ASCENDING -> R.id.option_sort_asc
Sort.Direction.DESCENDING -> R.id.option_sort_dec
}
unlikelyToBeNull(menu.findItem(directionItemId)).isChecked = true
setOnMenuItemClickListener { item ->
item.isChecked = !item.isChecked
detailModel.artistSongSort =
when (item.itemId) {
// Sort direction options
R.id.option_sort_asc -> sort.withDirection(Sort.Direction.ASCENDING)
R.id.option_sort_dec -> sort.withDirection(Sort.Direction.DESCENDING)
// Any other option is a sort mode
else -> sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId)))
}
true
}
}
findNavController().navigateSafe(ArtistDetailFragmentDirections.sort())
}
private fun updateArtist(artist: Artist?) {

View file

@ -109,13 +109,8 @@ constructor(
get() = _albumInstructions
/** The current [Sort] used for [Song]s in [albumList]. */
var albumSongSort: Sort
val albumSongSort: Sort
get() = musicSettings.albumSongSort
set(value) {
musicSettings.albumSongSort = value
// Refresh the album list to reflect the new sort.
currentAlbum.value?.let { refreshAlbumList(it, true) }
}
/** The [PlaySong] instructions to use when playing a [Song] from [Album] details. */
val playInAlbumWith
@ -365,6 +360,11 @@ constructor(
}
}
fun applyAlbumSongSort(sort: Sort) {
musicSettings.albumSongSort = sort
_currentAlbum.value?.let { refreshAlbumList(it, true) }
}
/**
* Set a new [currentArtist] from it's [Music.UID]. [currentArtist] and [artistList] will be
* updated to align with the new [Artist].
@ -380,6 +380,11 @@ constructor(
}
}
fun applyArtistSongSort(sort: Sort) {
musicSettings.artistSongSort = sort
_currentArtist.value?.let { refreshArtistList(it, true) }
}
/**
* Set a new [currentGenre] from it's [Music.UID]. [currentGenre] and [genreList] will be
* updated to align with the new album.
@ -395,6 +400,11 @@ constructor(
}
}
fun applyGenreSongSort(sort: Sort) {
musicSettings.genreSongSort = sort
_currentGenre.value?.let { refreshGenreList(it, true) }
}
/**
* Set a new [currentPlaylist] from it's [Music.UID]. If the [Music.UID] differs,
* [currentPlaylist] and [currentPlaylist] will be updated to align with the new album.

View file

@ -41,7 +41,6 @@ import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.ListViewModel
import org.oxycblt.auxio.list.Menu
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music
@ -199,30 +198,7 @@ class GenreDetailFragment :
}
override fun onOpenSortMenu(anchor: View) {
openMenu(anchor, R.menu.sort_genre) {
// Select the corresponding sort mode option
val sort = detailModel.genreSongSort
unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true
// Select the corresponding sort direction option
val directionItemId =
when (sort.direction) {
Sort.Direction.ASCENDING -> R.id.option_sort_asc
Sort.Direction.DESCENDING -> R.id.option_sort_dec
}
unlikelyToBeNull(menu.findItem(directionItemId)).isChecked = true
setOnMenuItemClickListener { item ->
item.isChecked = !item.isChecked
detailModel.genreSongSort =
when (item.itemId) {
// Sort direction options
R.id.option_sort_asc -> sort.withDirection(Sort.Direction.ASCENDING)
R.id.option_sort_dec -> sort.withDirection(Sort.Direction.DESCENDING)
// Any other option is a sort mode
else -> sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId)))
}
true
}
}
findNavController().navigateSafe(GenreDetailFragmentDirections.sort())
}
private fun updatePlaylist(genre: Genre?) {

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2023 Auxio Project
* AlbumSongSortDialog.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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.detail.sort
import android.os.Bundle
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import org.oxycblt.auxio.databinding.DialogSortBinding
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.list.sort.SortDialog
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD
@AndroidEntryPoint
class AlbumSongSortDialog : SortDialog() {
private val detailModel: DetailViewModel by activityViewModels()
override fun onBindingCreated(binding: DialogSortBinding, savedInstanceState: Bundle?) {
super.onBindingCreated(binding, savedInstanceState)
// --- VIEWMODEL SETUP ---
collectImmediately(detailModel.currentAlbum, ::updateAlbum)
}
override fun getInitialSort() = detailModel.albumSongSort
override fun applyChosenSort(sort: Sort) {
detailModel.applyAlbumSongSort(sort)
}
override fun getModeChoices() = listOf(Sort.Mode.ByDisc, Sort.Mode.ByTrack)
private fun updateAlbum(album: Album?) {
if (album == null) {
logD("No album to sort, navigating away")
findNavController().navigateUp()
}
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2023 Auxio Project
* ArtistSongSortDialog.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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.detail.sort
import android.os.Bundle
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import org.oxycblt.auxio.databinding.DialogSortBinding
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.list.sort.SortDialog
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD
@AndroidEntryPoint
class ArtistSongSortDialog : SortDialog() {
private val detailModel: DetailViewModel by activityViewModels()
override fun onBindingCreated(binding: DialogSortBinding, savedInstanceState: Bundle?) {
super.onBindingCreated(binding, savedInstanceState)
// --- VIEWMODEL SETUP ---
collectImmediately(detailModel.currentArtist, ::updateArtist)
}
override fun getInitialSort() = detailModel.artistSongSort
override fun applyChosenSort(sort: Sort) {
detailModel.applyArtistSongSort(sort)
}
override fun getModeChoices() =
listOf(Sort.Mode.ByName, Sort.Mode.ByAlbum, Sort.Mode.ByDate, Sort.Mode.ByDuration)
private fun updateArtist(artist: Artist?) {
if (artist == null) {
logD("No artist to sort, navigating away")
findNavController().navigateUp()
}
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2023 Auxio Project
* GenreSongSortDialog.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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.detail.sort
import android.os.Bundle
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import org.oxycblt.auxio.databinding.DialogSortBinding
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.list.sort.SortDialog
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD
@AndroidEntryPoint
class GenreSongSortDialog : SortDialog() {
private val detailModel: DetailViewModel by activityViewModels()
override fun onBindingCreated(binding: DialogSortBinding, savedInstanceState: Bundle?) {
super.onBindingCreated(binding, savedInstanceState)
// --- VIEWMODEL SETUP ---
collectImmediately(detailModel.currentGenre, ::updateGenre)
}
override fun getInitialSort() = detailModel.genreSongSort
override fun applyChosenSort(sort: Sort) {
detailModel.applyGenreSongSort(sort)
}
override fun getModeChoices() =
listOf(
Sort.Mode.ByName,
Sort.Mode.ByArtist,
Sort.Mode.ByAlbum,
Sort.Mode.ByDate,
Sort.Mode.ByDuration)
private fun updateGenre(genre: Genre?) {
if (genre == null) {
logD("No genre to sort, navigating away")
findNavController().navigateUp()
}
}
}

View file

@ -18,14 +18,9 @@
package org.oxycblt.auxio.list
import android.view.View
import androidx.annotation.MenuRes
import androidx.appcompat.widget.PopupMenu
import androidx.core.view.MenuCompat
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.util.logD
/**
* A Fragment containing a selectable list.
@ -34,14 +29,6 @@ import org.oxycblt.auxio.util.logD
*/
abstract class ListFragment<in T : Music, VB : ViewBinding> :
SelectionFragment<VB>(), SelectableListListener<T> {
private var currentMenu: PopupMenu? = null
override fun onDestroyBinding(binding: VB) {
super.onDestroyBinding(binding)
currentMenu?.dismiss()
currentMenu = null
}
/**
* Called when [onClick] is called, but does not result in the item being selected. This more or
* less corresponds to an [onClick] implementation in a non-[ListFragment].
@ -63,30 +50,4 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
final override fun onSelect(item: T) {
listModel.select(item)
}
/**
* Open a menu. This menu will be managed by the Fragment and closed when the view is destroyed.
* If a menu is already opened, this call is ignored.
*
* @param anchor The [View] to anchor the menu to.
* @param menuRes The resource of the menu to load.
* @param block A block that is ran within [PopupMenu] that allows further configuration.
*/
protected fun openMenu(anchor: View, @MenuRes menuRes: Int, block: PopupMenu.() -> Unit) {
if (currentMenu != null) {
logD("Menu already present, not launching")
return
}
logD("Opening popup menu menu")
currentMenu =
PopupMenu(requireContext(), anchor).apply {
inflate(menuRes)
MenuCompat.setGroupDividerEnabled(menu, true)
block()
setOnDismissListener { currentMenu = null }
show()
}
}
}

View file

@ -19,6 +19,7 @@
package org.oxycblt.auxio.list
import androidx.annotation.IdRes
import java.lang.IllegalStateException
import kotlin.math.max
import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.R
@ -406,7 +407,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
get() = IntegerTable.SORT_BY_DISC
override val itemId: Int
get() = R.id.option_sort_disc
get() = throw IllegalStateException()
override val stringRes: Int
get() = R.string.lbl_disc
@ -428,7 +429,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
get() = IntegerTable.SORT_BY_TRACK
override val itemId: Int
get() = R.id.option_sort_track
get() = throw IllegalStateException()
override val stringRes: Int
get() = R.string.lbl_track

View file

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single"
android:id="@+id/sort_modes">
<item
android:id="@+id/option_sort_disc"
android:title="@string/lbl_disc" />
<item
android:id="@+id/option_sort_track"
android:title="@string/lbl_track" />
</group>
<group android:checkableBehavior="single"
android:id="@+id/sort_direction">
<item
android:id="@+id/option_sort_asc"
android:title="@string/lbl_sort_asc" />
<item
android:id="@+id/option_sort_dec"
android:title="@string/lbl_sort_dsc" />
</group>
</menu>

View file

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single"
android:id="@+id/sort_modes">
<item
android:id="@+id/option_sort_name"
android:title="@string/lbl_name" />
<item
android:id="@+id/option_sort_album"
android:title="@string/lbl_album" />
<item
android:id="@+id/option_sort_year"
android:title="@string/lbl_date" />
<item
android:id="@+id/option_sort_duration"
android:title="@string/lbl_duration" />
</group>
<group android:checkableBehavior="single"
android:id="@+id/sort_direction">
<item
android:id="@+id/option_sort_asc"
android:title="@string/lbl_sort_asc" />
<item
android:id="@+id/option_sort_dec"
android:title="@string/lbl_sort_dsc" />
</group>
</menu>

View file

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single"
android:id="@+id/sort_modes">
<item
android:id="@+id/option_sort_name"
android:title="@string/lbl_name" />
<item
android:id="@+id/option_sort_artist"
android:title="@string/lbl_artist" />
<item
android:id="@+id/option_sort_album"
android:title="@string/lbl_album" />
<item
android:id="@+id/option_sort_year"
android:title="@string/lbl_date" />
<item
android:id="@+id/option_sort_duration"
android:title="@string/lbl_duration" />
</group>
<group android:checkableBehavior="single"
android:id="@+id/sort_direction">
<item
android:id="@+id/option_sort_asc"
android:title="@string/lbl_sort_asc" />
<item
android:id="@+id/option_sort_dec"
android:title="@string/lbl_sort_dsc" />
</group>
</menu>

View file

@ -138,6 +138,9 @@
<argument
android:name="albumUid"
app:argType="org.oxycblt.auxio.music.Music$UID" />
<action
android:id="@+id/sort"
app:destination="@+id/album_song_sort_dialog" />
<action
android:id="@+id/show_song"
app:destination="@id/song_detail_dialog" />
@ -162,11 +165,14 @@
<action
android:id="@+id/play_from_genre"
app:destination="@id/play_from_genre_dialog" />
<action
android:id="@+id/sort"
app:destination="@id/sort_dialog" />
</fragment>
<dialog
android:id="@+id/album_song_sort_dialog"
android:name="org.oxycblt.auxio.detail.sort.AlbumSongSortDialog"
android:label="AlbumSongSortDialog"
tools:layout="@layout/dialog_sort" />
<fragment
android:id="@+id/artist_detail_fragment"
android:name="org.oxycblt.auxio.detail.ArtistDetailFragment"
@ -175,6 +181,9 @@
<argument
android:name="artistUid"
app:argType="org.oxycblt.auxio.music.Music$UID" />
<action
android:id="@+id/sort"
app:destination="@+id/artist_song_sort_dialog" />
<action
android:id="@+id/show_song"
app:destination="@id/song_detail_dialog" />
@ -198,6 +207,12 @@
app:destination="@id/play_from_genre_dialog" />
</fragment>
<dialog
android:id="@+id/artist_song_sort_dialog"
android:name="org.oxycblt.auxio.detail.sort.ArtistSongSortDialog"
android:label="ArtistSongSortDialog"
tools:layout="@layout/dialog_sort" />
<fragment
android:id="@+id/genre_detail_fragment"
android:name="org.oxycblt.auxio.detail.GenreDetailFragment"
@ -206,6 +221,9 @@
<argument
android:name="genreUid"
app:argType="org.oxycblt.auxio.music.Music$UID" />
<action
android:id="@+id/sort"
app:destination="@+id/genre_song_sort_dialog" />
<action
android:id="@+id/show_song"
app:destination="@id/song_detail_dialog" />
@ -232,6 +250,12 @@
app:destination="@id/play_from_artist_dialog" />
</fragment>
<dialog
android:id="@+id/genre_song_sort_dialog"
android:name="org.oxycblt.auxio.detail.sort.GenreSongSortDialog"
android:label="GenreSongSortDialog"
tools:layout="@layout/dialog_sort" />
<fragment
android:id="@+id/playlist_detail_fragment"
android:name="org.oxycblt.auxio.detail.PlaylistDetailFragment"
@ -391,10 +415,4 @@
android:name="parcel"
app:argType="org.oxycblt.auxio.list.Menu$ForPlaylist$Parcel" />
</dialog>
<dialog
android:id="@+id/sort_dialog"
android:name="org.oxycblt.auxio.list.sort.SortDialog"
android:label="sort_dialog"
tools:layout="@layout/dialog_sort" />
</navigation>