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:
parent
8536c3da31
commit
3c211a1d17
24 changed files with 217 additions and 151 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue