list: make sorting direction explicit

Make sorting direction (ascending/descending) explicit in the UI and in
the code.

Instead of a boolean flag, two distinct "ascending" and "descending"
options are used instead. This should be much clearer.
This commit is contained in:
Alexander Capehart 2023-02-02 21:42:48 -07:00
parent 8536c3da31
commit 3c211a1d17
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
24 changed files with 217 additions and 151 deletions

View file

@ -8,6 +8,8 @@
#### What's Improved #### What's Improved
- Auxio will now accept zeroed track/disc numbers in the presence of non-zero total - Auxio will now accept zeroed track/disc numbers in the presence of non-zero total
track/disc fields track/disc fields
- Music loading has been made slightly faster
- Improved sort menu usability
#### What's Fixed #### What's Fixed
- Fixed non-functioning "repeat all" repeat mode - Fixed non-functioning "repeat all" repeat mode

View file

@ -40,7 +40,7 @@ import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.NavigationViewModel import org.oxycblt.auxio.ui.NavigationViewModel
import org.oxycblt.auxio.util.* import org.oxycblt.auxio.util.*
@ -151,14 +151,19 @@ class AlbumDetailFragment :
openMenu(anchor, R.menu.menu_album_sort) { openMenu(anchor, R.menu.menu_album_sort) {
val sort = detailModel.albumSongSort val sort = detailModel.albumSongSort
unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true
unlikelyToBeNull(menu.findItem(R.id.option_sort_asc)).isChecked = sort.isAscending 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 -> setOnMenuItemClickListener { item ->
item.isChecked = !item.isChecked item.isChecked = !item.isChecked
detailModel.albumSongSort = detailModel.albumSongSort =
if (item.itemId == R.id.option_sort_asc) { when (item.itemId) {
sort.withAscending(item.isChecked) R.id.option_sort_asc -> sort.withDirection(Sort.Direction.ASCENDING)
} else { R.id.option_sort_dec -> sort.withDirection(Sort.Direction.DESCENDING)
sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId))) else -> sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId)))
} }
true true
} }

View file

@ -39,7 +39,7 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.NavigationViewModel import org.oxycblt.auxio.ui.NavigationViewModel
import org.oxycblt.auxio.util.collect import org.oxycblt.auxio.util.collect
@ -167,15 +167,20 @@ class ArtistDetailFragment :
openMenu(anchor, R.menu.menu_artist_sort) { openMenu(anchor, R.menu.menu_artist_sort) {
val sort = detailModel.artistSongSort val sort = detailModel.artistSongSort
unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true
unlikelyToBeNull(menu.findItem(R.id.option_sort_asc)).isChecked = sort.isAscending 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 -> setOnMenuItemClickListener { item ->
item.isChecked = !item.isChecked item.isChecked = !item.isChecked
detailModel.artistSongSort = detailModel.artistSongSort =
if (item.itemId == R.id.option_sort_asc) { when (item.itemId) {
sort.withAscending(item.isChecked) R.id.option_sort_asc -> sort.withDirection(Sort.Direction.ASCENDING)
} else { R.id.option_sort_dec -> sort.withDirection(Sort.Direction.DESCENDING)
sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId))) else -> sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId)))
} }
true true

View file

@ -34,7 +34,7 @@ import org.oxycblt.auxio.list.BasicHeader
import org.oxycblt.auxio.list.Item import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.music.* import org.oxycblt.auxio.music.*
import org.oxycblt.auxio.music.library.Library import org.oxycblt.auxio.music.library.Library
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.music.metadata.AudioInfo import org.oxycblt.auxio.music.metadata.AudioInfo
import org.oxycblt.auxio.music.metadata.Disc import org.oxycblt.auxio.music.metadata.Disc
import org.oxycblt.auxio.music.metadata.ReleaseType import org.oxycblt.auxio.music.metadata.ReleaseType
@ -280,7 +280,7 @@ constructor(
private fun refreshArtistList(artist: Artist) { private fun refreshArtistList(artist: Artist) {
logD("Refreshing artist data") logD("Refreshing artist data")
val data = mutableListOf<Item>(artist) val data = mutableListOf<Item>(artist)
val albums = Sort(Sort.Mode.ByDate, false).albums(artist.albums) val albums = Sort(Sort.Mode.ByDate, Sort.Direction.DESCENDING).albums(artist.albums)
val byReleaseGroup = val byReleaseGroup =
albums.groupBy { albums.groupBy {

View file

@ -40,7 +40,7 @@ import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.NavigationViewModel import org.oxycblt.auxio.ui.NavigationViewModel
import org.oxycblt.auxio.util.collect import org.oxycblt.auxio.util.collect
@ -166,14 +166,19 @@ class GenreDetailFragment :
openMenu(anchor, R.menu.menu_genre_sort) { openMenu(anchor, R.menu.menu_genre_sort) {
val sort = detailModel.genreSongSort val sort = detailModel.genreSongSort
unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true
unlikelyToBeNull(menu.findItem(R.id.option_sort_asc)).isChecked = sort.isAscending 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 -> setOnMenuItemClickListener { item ->
item.isChecked = !item.isChecked item.isChecked = !item.isChecked
detailModel.genreSongSort = detailModel.genreSongSort =
if (item.itemId == R.id.option_sort_asc) { when (item.itemId) {
sort.withAscending(item.isChecked) R.id.option_sort_asc -> sort.withDirection(Sort.Direction.ASCENDING)
} else { R.id.option_sort_dec -> sort.withDirection(Sort.Direction.DESCENDING)
sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId))) else -> sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId)))
} }
true true
} }

View file

@ -23,6 +23,7 @@ import android.view.MenuItem
import android.view.View import android.view.View
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.MenuCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.iterator import androidx.core.view.iterator
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
@ -53,7 +54,7 @@ import org.oxycblt.auxio.list.selection.SelectionFragment
import org.oxycblt.auxio.list.selection.SelectionViewModel import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.* import org.oxycblt.auxio.music.*
import org.oxycblt.auxio.music.library.Library import org.oxycblt.auxio.music.library.Library
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.music.system.Indexer import org.oxycblt.auxio.music.system.Indexer
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.MainNavigationAction import org.oxycblt.auxio.ui.MainNavigationAction
@ -104,7 +105,10 @@ class HomeFragment :
// --- UI SETUP --- // --- UI SETUP ---
binding.homeAppbar.addOnOffsetChangedListener(this) binding.homeAppbar.addOnOffsetChangedListener(this)
binding.homeToolbar.setOnMenuItemClickListener(this) binding.homeToolbar.apply {
setOnMenuItemClickListener(this@HomeFragment)
MenuCompat.setGroupDividerEnabled(menu, true)
}
// Load the track color in manually as it's unclear whether the track actually supports // Load the track color in manually as it's unclear whether the track actually supports
// using a ColorStateList in the resources // using a ColorStateList in the resources
@ -213,11 +217,18 @@ class HomeFragment :
// Junk click event when opening the menu // Junk click event when opening the menu
} }
R.id.option_sort_asc -> { R.id.option_sort_asc -> {
item.isChecked = !item.isChecked item.isChecked = true
homeModel.setSortForCurrentTab( homeModel.setSortForCurrentTab(
homeModel homeModel
.getSortForTab(homeModel.currentTabMode.value) .getSortForTab(homeModel.currentTabMode.value)
.withAscending(item.isChecked)) .withDirection(Sort.Direction.ASCENDING))
}
R.id.option_sort_dec -> {
item.isChecked = true
homeModel.setSortForCurrentTab(
homeModel
.getSortForTab(homeModel.currentTabMode.value)
.withDirection(Sort.Direction.DESCENDING))
} }
else -> { else -> {
// Sorting option was selected, mark it as selected and update the mode // Sorting option was selected, mark it as selected and update the mode
@ -270,6 +281,7 @@ class HomeFragment :
// Only allow sorting by name, count, and duration for artists // Only allow sorting by name, count, and duration for artists
MusicMode.ARTISTS -> { id -> MusicMode.ARTISTS -> { id ->
id == R.id.option_sort_asc || id == R.id.option_sort_asc ||
id == R.id.option_sort_dec ||
id == R.id.option_sort_name || id == R.id.option_sort_name ||
id == R.id.option_sort_count || id == R.id.option_sort_count ||
id == R.id.option_sort_duration id == R.id.option_sort_duration
@ -277,6 +289,7 @@ class HomeFragment :
// Only allow sorting by name, count, and duration for genres // Only allow sorting by name, count, and duration for genres
MusicMode.GENRES -> { id -> MusicMode.GENRES -> { id ->
id == R.id.option_sort_asc || id == R.id.option_sort_asc ||
id == R.id.option_sort_dec ||
id == R.id.option_sort_name || id == R.id.option_sort_name ||
id == R.id.option_sort_count || id == R.id.option_sort_count ||
id == R.id.option_sort_duration id == R.id.option_sort_duration
@ -292,7 +305,10 @@ class HomeFragment :
// Check the ascending option and corresponding sort option to align with // Check the ascending option and corresponding sort option to align with
// the current sort of the tab. // the current sort of the tab.
if (option.itemId == toHighlight.mode.itemId || if (option.itemId == toHighlight.mode.itemId ||
(option.itemId == R.id.option_sort_asc && toHighlight.isAscending)) { (option.itemId == R.id.option_sort_asc &&
toHighlight.direction == Sort.Direction.ASCENDING) ||
(option.itemId == R.id.option_sort_dec &&
toHighlight.direction == Sort.Direction.DESCENDING)) {
option.isChecked = true option.isChecked = true
} }

View file

@ -25,7 +25,7 @@ import kotlinx.coroutines.flow.StateFlow
import org.oxycblt.auxio.home.tabs.Tab import org.oxycblt.auxio.home.tabs.Tab
import org.oxycblt.auxio.music.* import org.oxycblt.auxio.music.*
import org.oxycblt.auxio.music.library.Library import org.oxycblt.auxio.music.library.Library
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.playback.PlaybackSettings import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD

View file

@ -37,7 +37,7 @@ import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
import org.oxycblt.auxio.list.recycler.AlbumViewHolder import org.oxycblt.auxio.list.recycler.AlbumViewHolder
import org.oxycblt.auxio.list.selection.SelectionViewModel import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.* import org.oxycblt.auxio.music.*
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.formatDurationMs import org.oxycblt.auxio.playback.formatDurationMs
import org.oxycblt.auxio.playback.secsToMs import org.oxycblt.auxio.playback.secsToMs

View file

@ -38,7 +38,7 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.formatDurationMs import org.oxycblt.auxio.playback.formatDurationMs
import org.oxycblt.auxio.ui.NavigationViewModel import org.oxycblt.auxio.ui.NavigationViewModel

View file

@ -38,7 +38,7 @@ import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.formatDurationMs import org.oxycblt.auxio.playback.formatDurationMs
import org.oxycblt.auxio.ui.NavigationViewModel import org.oxycblt.auxio.ui.NavigationViewModel

View file

@ -40,7 +40,7 @@ import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.formatDurationMs import org.oxycblt.auxio.playback.formatDurationMs
import org.oxycblt.auxio.playback.secsToMs import org.oxycblt.auxio.playback.secsToMs

View file

@ -35,7 +35,7 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
/** /**
* A [Keyer] implementation for [Music] data. * A [Keyer] implementation for [Music] data.
@ -91,7 +91,7 @@ private constructor(
) : Fetcher { ) : Fetcher {
override suspend fun fetch(): FetchResult? { override suspend fun fetch(): FetchResult? {
// Pick the "most prominent" albums (i.e albums with the most songs) to show in the image. // Pick the "most prominent" albums (i.e albums with the most songs) to show in the image.
val albums = Sort(Sort.Mode.ByCount, false).albums(artist.albums) val albums = Sort(Sort.Mode.ByCount, Sort.Direction.DESCENDING).albums(artist.albums)
val results = albums.mapAtMostNotNull(4) { album -> Covers.fetch(context, album) } val results = albums.mapAtMostNotNull(4) { album -> Covers.fetch(context, album) }
return Images.createMosaic(context, results, size) return Images.createMosaic(context, results, size)
} }

View file

@ -21,6 +21,8 @@ import android.view.MenuItem
import android.view.View import android.view.View
import androidx.annotation.MenuRes import androidx.annotation.MenuRes
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.internal.view.SupportMenu
import androidx.core.view.MenuCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import org.oxycblt.auxio.MainFragmentDirections import org.oxycblt.auxio.MainFragmentDirections
@ -237,6 +239,8 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
currentMenu = currentMenu =
PopupMenu(requireContext(), anchor).apply { PopupMenu(requireContext(), anchor).apply {
inflate(menuRes) inflate(menuRes)
logD(menu is SupportMenu)
MenuCompat.setGroupDividerEnabled(menu, true)
block() block()
setOnDismissListener { currentMenu = null } setOnDismissListener { currentMenu = null }
show() show()

View file

@ -15,14 +15,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.oxycblt.auxio.music.library package org.oxycblt.auxio.list
import androidx.annotation.IdRes import androidx.annotation.IdRes
import kotlin.math.max import kotlin.math.max
import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.* import org.oxycblt.auxio.music.*
import org.oxycblt.auxio.music.library.Sort.Mode import org.oxycblt.auxio.list.Sort.Mode
import org.oxycblt.auxio.music.metadata.Date import org.oxycblt.auxio.music.metadata.Date
import org.oxycblt.auxio.music.metadata.Disc import org.oxycblt.auxio.music.metadata.Disc
@ -32,23 +32,23 @@ import org.oxycblt.auxio.music.metadata.Disc
* This can be used not only to sort items, but also represent a sorting mode within the UI. * This can be used not only to sort items, but also represent a sorting mode within the UI.
* *
* @param mode A [Mode] dictating how to sort the list. * @param mode A [Mode] dictating how to sort the list.
* @param isAscending Whether to sort in ascending or descending order. * @param direction The [Direction] to sort in.
* @author Alexander Capehart (OxygenCobalt) * @author Alexander Capehart (OxygenCobalt)
*/ */
data class Sort(val mode: Mode, val isAscending: Boolean) { data class Sort(val mode: Mode, val direction: Direction) {
/** /**
* Create a new [Sort] with the same [mode], but different [isAscending] value. * Create a new [Sort] with the same [mode], but a different [Direction].
* @param isAscending Whether the new sort should be in ascending order or not. * @param direction The new [Direction] to sort in.
* @return A new sort with the same mode, but with the new [isAscending] value applied. * @return A new sort with the same mode, but with the new [Direction] value applied.
*/ */
fun withAscending(isAscending: Boolean) = Sort(mode, isAscending) fun withDirection(direction: Direction) = Sort(mode, direction)
/** /**
* Create a new [Sort] with the same [isAscending] value, but different [mode] value. * Create a new [Sort] with the same [direction] value, but different [mode] value.
* @param mode Tbe new mode to use for the Sort. * @param mode Tbe new mode to use for the Sort.
* @return A new sort with the same [isAscending] value, but with the new [mode] applied. * @return A new sort with the same [direction] value, but with the new [mode] applied.
*/ */
fun withMode(mode: Mode) = Sort(mode, isAscending) fun withMode(mode: Mode) = Sort(mode, direction)
/** /**
* Sort a list of [Song]s. * Sort a list of [Song]s.
@ -99,7 +99,7 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
* @param songs The [Song]s to sort. * @param songs The [Song]s to sort.
*/ */
private fun songsInPlace(songs: MutableList<out Song>) { private fun songsInPlace(songs: MutableList<out Song>) {
songs.sortWith(mode.getSongComparator(isAscending)) songs.sortWith(mode.getSongComparator(direction))
} }
/** /**
@ -107,7 +107,7 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
* @param albums The [Album]s to sort. * @param albums The [Album]s to sort.
*/ */
private fun albumsInPlace(albums: MutableList<out Album>) { private fun albumsInPlace(albums: MutableList<out Album>) {
albums.sortWith(mode.getAlbumComparator(isAscending)) albums.sortWith(mode.getAlbumComparator(direction))
} }
/** /**
@ -115,7 +115,7 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
* @param artists The [Album]s to sort. * @param artists The [Album]s to sort.
*/ */
private fun artistsInPlace(artists: MutableList<out Artist>) { private fun artistsInPlace(artists: MutableList<out Artist>) {
artists.sortWith(mode.getArtistComparator(isAscending)) artists.sortWith(mode.getArtistComparator(direction))
} }
/** /**
@ -123,7 +123,7 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
* @param genres The [Genre]s to sort. * @param genres The [Genre]s to sort.
*/ */
private fun genresInPlace(genres: MutableList<out Genre>) { private fun genresInPlace(genres: MutableList<out Genre>) {
genres.sortWith(mode.getGenreComparator(isAscending)) genres.sortWith(mode.getGenreComparator(direction))
} }
/** /**
@ -134,8 +134,14 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
// Sort's integer representation is formatted as AMMMM, where A is a bitflag // Sort's integer representation is formatted as AMMMM, where A is a bitflag
// representing if the sort is in ascending or descending order, and M is the // representing if the sort is in ascending or descending order, and M is the
// integer representation of the sort mode. // integer representation of the sort mode.
get() = mode.intCode.shl(1) or if (isAscending) 1 else 0 get() =
mode.intCode.shl(1) or
when (direction) {
Direction.ASCENDING -> 1
Direction.DESCENDING -> 0
}
/** Describes the type of data to sort with. */
sealed class Mode { sealed class Mode {
/** The integer representation of this sort mode. */ /** The integer representation of this sort mode. */
abstract val intCode: Int abstract val intCode: Int
@ -144,37 +150,37 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
/** /**
* Get a [Comparator] that sorts [Song]s according to this [Mode]. * Get a [Comparator] that sorts [Song]s according to this [Mode].
* @param isAscending Whether to sort in ascending or descending order. * @param direction The direction to sort in.
* @return A [Comparator] that can be used to sort a [Song] list according to this [Mode]. * @return A [Comparator] that can be used to sort a [Song] list according to this [Mode].
*/ */
open fun getSongComparator(isAscending: Boolean): Comparator<Song> { open fun getSongComparator(direction: Direction): Comparator<Song> {
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
/** /**
* Get a [Comparator] that sorts [Album]s according to this [Mode]. * Get a [Comparator] that sorts [Album]s according to this [Mode].
* @param isAscending Whether to sort in ascending or descending order. * @param direction The direction to sort in.
* @return A [Comparator] that can be used to sort a [Album] list according to this [Mode]. * @return A [Comparator] that can be used to sort a [Album] list according to this [Mode].
*/ */
open fun getAlbumComparator(isAscending: Boolean): Comparator<Album> { open fun getAlbumComparator(direction: Direction): Comparator<Album> {
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
/** /**
* Return a [Comparator] that sorts [Artist]s according to this [Mode]. * Return a [Comparator] that sorts [Artist]s according to this [Mode].
* @param isAscending Whether to sort in ascending or descending order. * @param direction The direction to sort in.
* @return A [Comparator] that can be used to sort a [Artist] list according to this [Mode]. * @return A [Comparator] that can be used to sort a [Artist] list according to this [Mode].
*/ */
open fun getArtistComparator(isAscending: Boolean): Comparator<Artist> { open fun getArtistComparator(direction: Direction): Comparator<Artist> {
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
/** /**
* Return a [Comparator] that sorts [Genre]s according to this [Mode]. * Return a [Comparator] that sorts [Genre]s according to this [Mode].
* @param isAscending Whether to sort in ascending or descending order. * @param direction The direction to sort in.
* @return A [Comparator] that can be used to sort a [Genre] list according to this [Mode]. * @return A [Comparator] that can be used to sort a [Genre] list according to this [Mode].
*/ */
open fun getGenreComparator(isAscending: Boolean): Comparator<Genre> { open fun getGenreComparator(direction: Direction): Comparator<Genre> {
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
@ -189,17 +195,17 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
override val itemId: Int override val itemId: Int
get() = R.id.option_sort_name get() = R.id.option_sort_name
override fun getSongComparator(isAscending: Boolean) = override fun getSongComparator(direction: Direction) =
compareByDynamic(isAscending, BasicComparator.SONG) compareByDynamic(direction, BasicComparator.SONG)
override fun getAlbumComparator(isAscending: Boolean) = override fun getAlbumComparator(direction: Direction) =
compareByDynamic(isAscending, BasicComparator.ALBUM) compareByDynamic(direction, BasicComparator.ALBUM)
override fun getArtistComparator(isAscending: Boolean) = override fun getArtistComparator(direction: Direction) =
compareByDynamic(isAscending, BasicComparator.ARTIST) compareByDynamic(direction, BasicComparator.ARTIST)
override fun getGenreComparator(isAscending: Boolean) = override fun getGenreComparator(direction: Direction) =
compareByDynamic(isAscending, BasicComparator.GENRE) compareByDynamic(direction, BasicComparator.GENRE)
} }
/** /**
@ -213,9 +219,9 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
override val itemId: Int override val itemId: Int
get() = R.id.option_sort_album get() = R.id.option_sort_album
override fun getSongComparator(isAscending: Boolean): Comparator<Song> = override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending, BasicComparator.ALBUM) { it.album }, compareByDynamic(direction, BasicComparator.ALBUM) { it.album },
compareBy(NullableComparator.DISC) { it.disc }, compareBy(NullableComparator.DISC) { it.disc },
compareBy(NullableComparator.INT) { it.track }, compareBy(NullableComparator.INT) { it.track },
compareBy(BasicComparator.SONG)) compareBy(BasicComparator.SONG))
@ -232,18 +238,18 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
override val itemId: Int override val itemId: Int
get() = R.id.option_sort_artist get() = R.id.option_sort_artist
override fun getSongComparator(isAscending: Boolean): Comparator<Song> = override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending, ListComparator.ARTISTS) { it.artists }, compareByDynamic(direction, ListComparator.ARTISTS) { it.artists },
compareByDescending(NullableComparator.DATE_RANGE) { it.album.dates }, compareByDescending(NullableComparator.DATE_RANGE) { it.album.dates },
compareByDescending(BasicComparator.ALBUM) { it.album }, compareByDescending(BasicComparator.ALBUM) { it.album },
compareBy(NullableComparator.DISC) { it.disc }, compareBy(NullableComparator.DISC) { it.disc },
compareBy(NullableComparator.INT) { it.track }, compareBy(NullableComparator.INT) { it.track },
compareBy(BasicComparator.SONG)) compareBy(BasicComparator.SONG))
override fun getAlbumComparator(isAscending: Boolean): Comparator<Album> = override fun getAlbumComparator(direction: Direction): Comparator<Album> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending, ListComparator.ARTISTS) { it.artists }, compareByDynamic(direction, ListComparator.ARTISTS) { it.artists },
compareByDescending(NullableComparator.DATE_RANGE) { it.dates }, compareByDescending(NullableComparator.DATE_RANGE) { it.dates },
compareBy(BasicComparator.ALBUM)) compareBy(BasicComparator.ALBUM))
} }
@ -260,17 +266,17 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
override val itemId: Int override val itemId: Int
get() = R.id.option_sort_year get() = R.id.option_sort_year
override fun getSongComparator(isAscending: Boolean): Comparator<Song> = override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending, NullableComparator.DATE_RANGE) { it.album.dates }, compareByDynamic(direction, NullableComparator.DATE_RANGE) { it.album.dates },
compareByDescending(BasicComparator.ALBUM) { it.album }, compareByDescending(BasicComparator.ALBUM) { it.album },
compareBy(NullableComparator.DISC) { it.disc }, compareBy(NullableComparator.DISC) { it.disc },
compareBy(NullableComparator.INT) { it.track }, compareBy(NullableComparator.INT) { it.track },
compareBy(BasicComparator.SONG)) compareBy(BasicComparator.SONG))
override fun getAlbumComparator(isAscending: Boolean): Comparator<Album> = override fun getAlbumComparator(direction: Direction): Comparator<Album> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending, NullableComparator.DATE_RANGE) { it.dates }, compareByDynamic(direction, NullableComparator.DATE_RANGE) { it.dates },
compareBy(BasicComparator.ALBUM)) compareBy(BasicComparator.ALBUM))
} }
@ -282,25 +288,22 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
override val itemId: Int override val itemId: Int
get() = R.id.option_sort_duration get() = R.id.option_sort_duration
override fun getSongComparator(isAscending: Boolean): Comparator<Song> = override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending) { it.durationMs }, compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.SONG))
compareBy(BasicComparator.SONG))
override fun getAlbumComparator(isAscending: Boolean): Comparator<Album> = override fun getAlbumComparator(direction: Direction): Comparator<Album> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending) { it.durationMs }, compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.ALBUM))
compareBy(BasicComparator.ALBUM))
override fun getArtistComparator(isAscending: Boolean): Comparator<Artist> = override fun getArtistComparator(direction: Direction): Comparator<Artist> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending, NullableComparator.LONG) { it.durationMs }, compareByDynamic(direction, NullableComparator.LONG) { it.durationMs },
compareBy(BasicComparator.ARTIST)) compareBy(BasicComparator.ARTIST))
override fun getGenreComparator(isAscending: Boolean): Comparator<Genre> = override fun getGenreComparator(direction: Direction): Comparator<Genre> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending) { it.durationMs }, compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.GENRE))
compareBy(BasicComparator.GENRE))
} }
/** /**
@ -314,20 +317,18 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
override val itemId: Int override val itemId: Int
get() = R.id.option_sort_count get() = R.id.option_sort_count
override fun getAlbumComparator(isAscending: Boolean): Comparator<Album> = override fun getAlbumComparator(direction: Direction): Comparator<Album> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending) { it.songs.size }, compareByDynamic(direction) { it.songs.size }, compareBy(BasicComparator.ALBUM))
compareBy(BasicComparator.ALBUM))
override fun getArtistComparator(isAscending: Boolean): Comparator<Artist> = override fun getArtistComparator(direction: Direction): Comparator<Artist> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending, NullableComparator.INT) { it.songs.size }, compareByDynamic(direction, NullableComparator.INT) { it.songs.size },
compareBy(BasicComparator.ARTIST)) compareBy(BasicComparator.ARTIST))
override fun getGenreComparator(isAscending: Boolean): Comparator<Genre> = override fun getGenreComparator(direction: Direction): Comparator<Genre> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending) { it.songs.size }, compareByDynamic(direction) { it.songs.size }, compareBy(BasicComparator.GENRE))
compareBy(BasicComparator.GENRE))
} }
/** /**
@ -341,9 +342,9 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
override val itemId: Int override val itemId: Int
get() = R.id.option_sort_disc get() = R.id.option_sort_disc
override fun getSongComparator(isAscending: Boolean): Comparator<Song> = override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending, NullableComparator.DISC) { it.disc }, compareByDynamic(direction, NullableComparator.DISC) { it.disc },
compareBy(NullableComparator.INT) { it.track }, compareBy(NullableComparator.INT) { it.track },
compareBy(BasicComparator.SONG)) compareBy(BasicComparator.SONG))
} }
@ -359,10 +360,10 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
override val itemId: Int override val itemId: Int
get() = R.id.option_sort_track get() = R.id.option_sort_track
override fun getSongComparator(isAscending: Boolean): Comparator<Song> = override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator( MultiComparator(
compareBy(NullableComparator.DISC) { it.disc }, compareBy(NullableComparator.DISC) { it.disc },
compareByDynamic(isAscending, NullableComparator.INT) { it.track }, compareByDynamic(direction, NullableComparator.INT) { it.track },
compareBy(BasicComparator.SONG)) compareBy(BasicComparator.SONG))
} }
@ -378,48 +379,47 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
override val itemId: Int override val itemId: Int
get() = R.id.option_sort_date_added get() = R.id.option_sort_date_added
override fun getSongComparator(isAscending: Boolean): Comparator<Song> = override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending) { it.dateAdded }, compareBy(BasicComparator.SONG)) compareByDynamic(direction) { it.dateAdded }, compareBy(BasicComparator.SONG))
override fun getAlbumComparator(isAscending: Boolean): Comparator<Album> = override fun getAlbumComparator(direction: Direction): Comparator<Album> =
MultiComparator( MultiComparator(
compareByDynamic(isAscending) { album -> album.dateAdded }, compareByDynamic(direction) { album -> album.dateAdded },
compareBy(BasicComparator.ALBUM)) compareBy(BasicComparator.ALBUM))
} }
/** /**
* Utility function to create a [Comparator] in a dynamic way determined by [isAscending]. * Utility function to create a [Comparator] in a dynamic way determined by [direction].
* @param isAscending Whether to sort in ascending or descending order. * @param direction The [Direction] to sort in.
* @see compareBy * @see compareBy
* @see compareByDescending * @see compareByDescending
*/ */
protected inline fun <T : Music, K : Comparable<K>> compareByDynamic( protected inline fun <T : Music, K : Comparable<K>> compareByDynamic(
isAscending: Boolean, direction: Direction,
crossinline selector: (T) -> K crossinline selector: (T) -> K
) = ) =
if (isAscending) { when (direction) {
compareBy(selector) Direction.ASCENDING -> compareBy(selector)
} else { Direction.DESCENDING -> compareByDescending(selector)
compareByDescending(selector)
} }
/** /**
* Utility function to create a [Comparator] in a dynamic way determined by [isAscending] * Utility function to create a [Comparator] in a dynamic way determined by [direction]
* @param isAscending Whether to sort in ascending or descending order. * @param direction The [Direction] to sort in.
* @param comparator A [Comparator] to wrap. * @param comparator A [Comparator] to wrap.
* @return A new [Comparator] with the specified configuration. * @return A new [Comparator] with the specified configuration.
* @see compareBy * @see compareBy
* @see compareByDescending * @see compareByDescending
*/ */
protected fun <T : Music> compareByDynamic( protected fun <T : Music> compareByDynamic(
isAscending: Boolean, direction: Direction,
comparator: Comparator<in T> comparator: Comparator<in T>
): Comparator<T> = compareByDynamic(isAscending, comparator) { it } ): Comparator<T> = compareByDynamic(direction, comparator) { it }
/** /**
* Utility function to create a [Comparator] a dynamic way determined by [isAscending] * Utility function to create a [Comparator] a dynamic way determined by [direction]
* @param isAscending Whether to sort in ascending or descending order. * @param direction The [Direction] to sort in.
* @param comparator A [Comparator] to wrap. * @param comparator A [Comparator] to wrap.
* @param selector Called to obtain a specific attribute to sort by. * @param selector Called to obtain a specific attribute to sort by.
* @return A new [Comparator] with the specified configuration. * @return A new [Comparator] with the specified configuration.
@ -427,14 +427,13 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
* @see compareByDescending * @see compareByDescending
*/ */
protected inline fun <T : Music, K> compareByDynamic( protected inline fun <T : Music, K> compareByDynamic(
isAscending: Boolean, direction: Direction,
comparator: Comparator<in K>, comparator: Comparator<in K>,
crossinline selector: (T) -> K crossinline selector: (T) -> K
) = ) =
if (isAscending) { when (direction) {
compareBy(comparator, selector) Direction.ASCENDING -> compareBy(comparator, selector)
} else { Direction.DESCENDING -> compareByDescending(comparator, selector)
compareByDescending(comparator, selector)
} }
/** /**
@ -596,6 +595,12 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
} }
} }
/** The direction to sort items in. */
enum class Direction {
ASCENDING,
DESCENDING
}
companion object { companion object {
/** /**
* Convert a [Sort] integer representation into an instance. * Convert a [Sort] integer representation into an instance.
@ -607,9 +612,9 @@ data class Sort(val mode: Mode, val isAscending: Boolean) {
// Sort's integer representation is formatted as AMMMM, where A is a bitflag // Sort's integer representation is formatted as AMMMM, where A is a bitflag
// representing on if the mode is ascending or descending, and M is the integer // representing on if the mode is ascending or descending, and M is the integer
// representation of the sort mode. // representation of the sort mode.
val isAscending = (intCode and 1) == 1 val direction = if ((intCode and 1) == 1) Direction.ASCENDING else Direction.DESCENDING
val mode = Mode.fromIntCode(intCode.shr(1)) ?: return null val mode = Mode.fromIntCode(intCode.shr(1)) ?: return null
return Sort(mode, isAscending) return Sort(mode, direction)
} }
} }
} }

View file

@ -21,7 +21,7 @@ import android.content.Context
import android.os.storage.StorageManager import android.os.storage.StorageManager
import androidx.core.content.edit import androidx.core.content.edit
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.music.storage.Directory import org.oxycblt.auxio.music.storage.Directory
import org.oxycblt.auxio.music.storage.MusicDirectories import org.oxycblt.auxio.music.storage.MusicDirectories
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
@ -116,7 +116,7 @@ private class RealMusicSettings(context: Context) :
get() = get() =
Sort.fromIntCode( Sort.fromIntCode(
sharedPreferences.getInt(getString(R.string.set_key_songs_sort), Int.MIN_VALUE)) sharedPreferences.getInt(getString(R.string.set_key_songs_sort), Int.MIN_VALUE))
?: Sort(Sort.Mode.ByName, true) ?: Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
set(value) { set(value) {
sharedPreferences.edit { sharedPreferences.edit {
putInt(getString(R.string.set_key_songs_sort), value.intCode) putInt(getString(R.string.set_key_songs_sort), value.intCode)
@ -128,7 +128,7 @@ private class RealMusicSettings(context: Context) :
get() = get() =
Sort.fromIntCode( Sort.fromIntCode(
sharedPreferences.getInt(getString(R.string.set_key_albums_sort), Int.MIN_VALUE)) sharedPreferences.getInt(getString(R.string.set_key_albums_sort), Int.MIN_VALUE))
?: Sort(Sort.Mode.ByName, true) ?: Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
set(value) { set(value) {
sharedPreferences.edit { sharedPreferences.edit {
putInt(getString(R.string.set_key_albums_sort), value.intCode) putInt(getString(R.string.set_key_albums_sort), value.intCode)
@ -140,7 +140,7 @@ private class RealMusicSettings(context: Context) :
get() = get() =
Sort.fromIntCode( Sort.fromIntCode(
sharedPreferences.getInt(getString(R.string.set_key_artists_sort), Int.MIN_VALUE)) sharedPreferences.getInt(getString(R.string.set_key_artists_sort), Int.MIN_VALUE))
?: Sort(Sort.Mode.ByName, true) ?: Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
set(value) { set(value) {
sharedPreferences.edit { sharedPreferences.edit {
putInt(getString(R.string.set_key_artists_sort), value.intCode) putInt(getString(R.string.set_key_artists_sort), value.intCode)
@ -152,7 +152,7 @@ private class RealMusicSettings(context: Context) :
get() = get() =
Sort.fromIntCode( Sort.fromIntCode(
sharedPreferences.getInt(getString(R.string.set_key_genres_sort), Int.MIN_VALUE)) sharedPreferences.getInt(getString(R.string.set_key_genres_sort), Int.MIN_VALUE))
?: Sort(Sort.Mode.ByName, true) ?: Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
set(value) { set(value) {
sharedPreferences.edit { sharedPreferences.edit {
putInt(getString(R.string.set_key_genres_sort), value.intCode) putInt(getString(R.string.set_key_genres_sort), value.intCode)
@ -166,7 +166,7 @@ private class RealMusicSettings(context: Context) :
Sort.fromIntCode( Sort.fromIntCode(
sharedPreferences.getInt( sharedPreferences.getInt(
getString(R.string.set_key_album_songs_sort), Int.MIN_VALUE)) getString(R.string.set_key_album_songs_sort), Int.MIN_VALUE))
?: Sort(Sort.Mode.ByDisc, true) ?: Sort(Sort.Mode.ByDisc, Sort.Direction.ASCENDING)
// Correct legacy album sort modes to Disc // Correct legacy album sort modes to Disc
if (sort.mode is Sort.Mode.ByName) { if (sort.mode is Sort.Mode.ByName) {
@ -187,7 +187,7 @@ private class RealMusicSettings(context: Context) :
Sort.fromIntCode( Sort.fromIntCode(
sharedPreferences.getInt( sharedPreferences.getInt(
getString(R.string.set_key_artist_songs_sort), Int.MIN_VALUE)) getString(R.string.set_key_artist_songs_sort), Int.MIN_VALUE))
?: Sort(Sort.Mode.ByDate, false) ?: Sort(Sort.Mode.ByDate, Sort.Direction.DESCENDING)
set(value) { set(value) {
sharedPreferences.edit { sharedPreferences.edit {
putInt(getString(R.string.set_key_artist_songs_sort), value.intCode) putInt(getString(R.string.set_key_artist_songs_sort), value.intCode)
@ -200,7 +200,7 @@ private class RealMusicSettings(context: Context) :
Sort.fromIntCode( Sort.fromIntCode(
sharedPreferences.getInt( sharedPreferences.getInt(
getString(R.string.set_key_genre_songs_sort), Int.MIN_VALUE)) getString(R.string.set_key_genre_songs_sort), Int.MIN_VALUE))
?: Sort(Sort.Mode.ByName, true) ?: Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
set(value) { set(value) {
sharedPreferences.edit { sharedPreferences.edit {
putInt(getString(R.string.set_key_genre_songs_sort), value.intCode) putInt(getString(R.string.set_key_genre_songs_sort), value.intCode)

View file

@ -20,6 +20,7 @@ package org.oxycblt.auxio.music.library
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.provider.OpenableColumns import android.provider.OpenableColumns
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.music.* import org.oxycblt.auxio.music.*
import org.oxycblt.auxio.music.storage.contentResolverSafe import org.oxycblt.auxio.music.storage.contentResolverSafe
import org.oxycblt.auxio.music.storage.useQuery import org.oxycblt.auxio.music.storage.useQuery
@ -76,7 +77,7 @@ interface Library {
companion object { companion object {
/** /**
* Create a library-backed instance of [Library]. * Create an instance of [Library].
* @param rawSongs [RawSong]s to create the library out of. * @param rawSongs [RawSong]s to create the library out of.
* @param settings [MusicSettings] required. * @param settings [MusicSettings] required.
*/ */
@ -93,10 +94,10 @@ private class RealLibrary(rawSongs: List<RawSong>, settings: MusicSettings) : Li
// Use a mapping to make finding information based on it's UID much faster. // Use a mapping to make finding information based on it's UID much faster.
private val uidMap = buildMap { private val uidMap = buildMap {
songs.forEach { this[it.uid] = it.finalize() } songs.forEach { put(it.uid, it.finalize()) }
albums.forEach { this[it.uid] = it.finalize() } albums.forEach { put(it.uid, it.finalize()) }
artists.forEach { this[it.uid] = it.finalize() } artists.forEach { put(it.uid, it.finalize()) }
genres.forEach { this[it.uid] = it.finalize() } genres.forEach { put(it.uid, it.finalize()) }
} }
/** /**
@ -131,7 +132,8 @@ private class RealLibrary(rawSongs: List<RawSong>, settings: MusicSettings) : Li
* grouping. * grouping.
*/ */
private fun buildSongs(rawSongs: List<RawSong>, settings: MusicSettings) = private fun buildSongs(rawSongs: List<RawSong>, settings: MusicSettings) =
Sort(Sort.Mode.ByName, true).songs(rawSongs.map { RealSong(it, settings) }.distinct()) Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
.songs(rawSongs.map { RealSong(it, settings) }.distinct())
/** /**
* Build a list of [Album]s from the given [Song]s. * Build a list of [Album]s from the given [Song]s.

View file

@ -24,6 +24,7 @@ import java.text.CollationKey
import java.text.Collator import java.text.Collator
import kotlin.math.max import kotlin.math.max
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
@ -418,7 +419,7 @@ class RealArtist(private val rawArtist: RawArtist, songAlbums: List<Music>) : Ar
fun finalize(): Artist { fun finalize(): Artist {
check(songs.isNotEmpty() || albums.isNotEmpty()) { "Malformed artist: Empty" } check(songs.isNotEmpty() || albums.isNotEmpty()) { "Malformed artist: Empty" }
genres = genres =
Sort(Sort.Mode.ByName, true) Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
.genres(songs.flatMapTo(mutableSetOf()) { it.genres }) .genres(songs.flatMapTo(mutableSetOf()) { it.genres })
.sortedByDescending { genre -> songs.count { it.genres.contains(genre) } } .sortedByDescending { genre -> songs.count { it.genres.contains(genre) } }
return this return this
@ -458,10 +459,10 @@ class RealGenre(private val rawGenre: RawGenre, override val songs: List<RealSon
} }
albums = albums =
Sort(Sort.Mode.ByName, true).albums(distinctAlbums).sortedByDescending { album -> Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
album.songs.count { it.genres.contains(this) } .albums(distinctAlbums)
} .sortedByDescending { album -> album.songs.count { it.genres.contains(this) } }
artists = Sort(Sort.Mode.ByName, true).artists(distinctArtists) artists = Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING).artists(distinctArtists)
durationMs = totalDuration durationMs = totalDuration
} }

View file

@ -32,7 +32,7 @@ import org.oxycblt.auxio.list.BasicHeader
import org.oxycblt.auxio.list.Item import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.music.* import org.oxycblt.auxio.music.*
import org.oxycblt.auxio.music.library.Library import org.oxycblt.auxio.music.library.Library
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.playback.PlaybackSettings import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
@ -176,6 +176,6 @@ constructor(
} }
private companion object { private companion object {
val SORT = Sort(Sort.Mode.ByName, true) val SORT = Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
} }
} }

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single"> <group android:checkableBehavior="single"
android:id="@+id/sort_modes">
<item <item
android:id="@+id/option_sort_disc" android:id="@+id/option_sort_disc"
android:title="@string/lbl_sort_disc" /> android:title="@string/lbl_sort_disc" />
@ -8,9 +9,13 @@
android:id="@+id/option_sort_track" android:id="@+id/option_sort_track"
android:title="@string/lbl_sort_track" /> android:title="@string/lbl_sort_track" />
</group> </group>
<group android:checkableBehavior="all"> <group android:checkableBehavior="single"
android:id="@+id/sort_direction">
<item <item
android:id="@+id/option_sort_asc" android:id="@+id/option_sort_asc"
android:title="@string/lbl_sort_asc" /> android:title="@string/lbl_sort_asc" />
<item
android:id="@+id/option_sort_dec"
android:title="@string/lbl_sort_dec" />
</group> </group>
</menu> </menu>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single"> <group android:checkableBehavior="single"
android:id="@+id/sort_modes">
<item <item
android:id="@+id/option_sort_name" android:id="@+id/option_sort_name"
android:title="@string/lbl_sort_name" /> android:title="@string/lbl_sort_name" />
@ -14,9 +15,13 @@
android:id="@+id/option_sort_duration" android:id="@+id/option_sort_duration"
android:title="@string/lbl_sort_duration" /> android:title="@string/lbl_sort_duration" />
</group> </group>
<group android:checkableBehavior="all"> <group android:checkableBehavior="single"
android:id="@+id/sort_direction">
<item <item
android:id="@+id/option_sort_asc" android:id="@+id/option_sort_asc"
android:title="@string/lbl_sort_asc" /> android:title="@string/lbl_sort_asc" />
<item
android:id="@+id/option_sort_dec"
android:title="@string/lbl_sort_dec" />
</group> </group>
</menu> </menu>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single"> <group android:checkableBehavior="single"
android:id="@+id/sort_modes">
<item <item
android:id="@+id/option_sort_name" android:id="@+id/option_sort_name"
android:title="@string/lbl_sort_name" /> android:title="@string/lbl_sort_name" />
@ -17,9 +18,13 @@
android:id="@+id/option_sort_duration" android:id="@+id/option_sort_duration"
android:title="@string/lbl_sort_duration" /> android:title="@string/lbl_sort_duration" />
</group> </group>
<group android:checkableBehavior="all"> <group android:checkableBehavior="single"
android:id="@+id/sort_direction">
<item <item
android:id="@+id/option_sort_asc" android:id="@+id/option_sort_asc"
android:title="@string/lbl_sort_asc" /> android:title="@string/lbl_sort_asc" />
<item
android:id="@+id/option_sort_dec"
android:title="@string/lbl_sort_dec" />
</group> </group>
</menu> </menu>

View file

@ -14,7 +14,8 @@
android:title="@string/lbl_sort" android:title="@string/lbl_sort"
app:showAsAction="ifRoom"> app:showAsAction="ifRoom">
<menu> <menu>
<group android:checkableBehavior="single"> <group android:checkableBehavior="single"
android:id="@+id/sort_modes">
<item <item
android:id="@+id/option_sort_name" android:id="@+id/option_sort_name"
android:title="@string/lbl_sort_name" /> android:title="@string/lbl_sort_name" />
@ -37,10 +38,14 @@
android:id="@+id/option_sort_date_added" android:id="@+id/option_sort_date_added"
android:title="@string/lbl_sort_date_added" /> android:title="@string/lbl_sort_date_added" />
</group> </group>
<group android:checkableBehavior="all"> <group android:checkableBehavior="single"
android:id="@+id/sort_direction">
<item <item
android:id="@+id/option_sort_asc" android:id="@+id/option_sort_asc"
android:title="@string/lbl_sort_asc" /> android:title="@string/lbl_sort_asc" />
<item
android:id="@+id/option_sort_dec"
android:title="@string/lbl_sort_dec" />
</group> </group>
</menu> </menu>
</item> </item>

View file

@ -92,6 +92,7 @@
<string name="lbl_sort_track">Track</string> <string name="lbl_sort_track">Track</string>
<string name="lbl_sort_date_added">Date added</string> <string name="lbl_sort_date_added">Date added</string>
<string name="lbl_sort_asc">Ascending</string> <string name="lbl_sort_asc">Ascending</string>
<string name="lbl_sort_dec">Descending</string>
<string name="lbl_playback">Now playing</string> <string name="lbl_playback">Now playing</string>
<string name="lbl_equalizer">Equalizer</string> <string name="lbl_equalizer">Equalizer</string>

View file

@ -17,7 +17,7 @@
package org.oxycblt.auxio.music package org.oxycblt.auxio.music
import org.oxycblt.auxio.music.library.Sort import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.music.storage.MusicDirectories import org.oxycblt.auxio.music.storage.MusicDirectories
interface FakeMusicSettings : MusicSettings { interface FakeMusicSettings : MusicSettings {