list: dont abuse comparators for sort
Likely causing crashes with how they are set up.
This commit is contained in:
parent
c8fa389267
commit
4c92ac0f85
2 changed files with 203 additions and 425 deletions
|
@ -1,5 +1,10 @@
|
|||
# Changelog
|
||||
|
||||
## dev
|
||||
|
||||
#### What's Fixed
|
||||
- Fixed music loading failure from improper sort systems
|
||||
|
||||
## 3.5.0
|
||||
|
||||
#### What's New
|
||||
|
|
|
@ -18,17 +18,13 @@
|
|||
|
||||
package org.oxycblt.auxio.list.sort
|
||||
|
||||
import kotlin.math.max
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.info.Date
|
||||
import org.oxycblt.auxio.music.info.Disc
|
||||
|
||||
/**
|
||||
* A sorting method.
|
||||
|
@ -46,9 +42,9 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
* @param songs The list of [Song]s.
|
||||
* @return A new list of [Song]s sorted by this [Sort]'s configuration.
|
||||
*/
|
||||
fun <T : Song> songs(songs: Collection<T>): List<T> {
|
||||
fun songs(songs: Collection<Song>): List<Song> {
|
||||
val mutable = songs.toMutableList()
|
||||
songsInPlace(mutable)
|
||||
mode.sortSongs(mutable, direction)
|
||||
return mutable
|
||||
}
|
||||
|
||||
|
@ -58,9 +54,9 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
* @param albums The list of [Album]s.
|
||||
* @return A new list of [Album]s sorted by this [Sort]'s configuration.
|
||||
*/
|
||||
fun <T : Album> albums(albums: Collection<T>): List<T> {
|
||||
fun albums(albums: Collection<Album>): List<Album> {
|
||||
val mutable = albums.toMutableList()
|
||||
albumsInPlace(mutable)
|
||||
mode.sortAlbums(mutable, direction)
|
||||
return mutable
|
||||
}
|
||||
|
||||
|
@ -70,9 +66,9 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
* @param artists The list of [Artist]s.
|
||||
* @return A new list of [Artist]s sorted by this [Sort]'s configuration.
|
||||
*/
|
||||
fun <T : Artist> artists(artists: Collection<T>): List<T> {
|
||||
fun artists(artists: Collection<Artist>): List<Artist> {
|
||||
val mutable = artists.toMutableList()
|
||||
artistsInPlace(mutable)
|
||||
mode.sortArtists(mutable, direction)
|
||||
return mutable
|
||||
}
|
||||
|
||||
|
@ -82,9 +78,9 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
* @param genres The list of [Genre]s.
|
||||
* @return A new list of [Genre]s sorted by this [Sort]'s configuration.
|
||||
*/
|
||||
fun <T : Genre> genres(genres: Collection<T>): List<T> {
|
||||
fun genres(genres: Collection<Genre>): List<Genre> {
|
||||
val mutable = genres.toMutableList()
|
||||
genresInPlace(mutable)
|
||||
mode.sortGenres(mutable, direction)
|
||||
return mutable
|
||||
}
|
||||
|
||||
|
@ -94,37 +90,12 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
* @param playlists The list of [Playlist]s.
|
||||
* @return A new list of [Playlist]s sorted by this [Sort]'s configuration
|
||||
*/
|
||||
fun <T : Playlist> playlists(playlists: Collection<T>): List<T> {
|
||||
fun playlists(playlists: Collection<Playlist>): List<Playlist> {
|
||||
val mutable = playlists.toMutableList()
|
||||
playlistsInPlace(mutable)
|
||||
mode.sortPlaylists(mutable, direction)
|
||||
return mutable
|
||||
}
|
||||
|
||||
private fun songsInPlace(songs: MutableList<out Song>) {
|
||||
val comparator = mode.getSongComparator(direction) ?: return
|
||||
songs.sortWith(comparator)
|
||||
}
|
||||
|
||||
private fun albumsInPlace(albums: MutableList<out Album>) {
|
||||
val comparator = mode.getAlbumComparator(direction) ?: return
|
||||
albums.sortWith(comparator)
|
||||
}
|
||||
|
||||
private fun artistsInPlace(artists: MutableList<out Artist>) {
|
||||
val comparator = mode.getArtistComparator(direction) ?: return
|
||||
artists.sortWith(comparator)
|
||||
}
|
||||
|
||||
private fun genresInPlace(genres: MutableList<out Genre>) {
|
||||
val comparator = mode.getGenreComparator(direction) ?: return
|
||||
genres.sortWith(comparator)
|
||||
}
|
||||
|
||||
private fun playlistsInPlace(playlists: MutableList<out Playlist>) {
|
||||
val comparator = mode.getPlaylistComparator(direction) ?: return
|
||||
playlists.sortWith(comparator)
|
||||
}
|
||||
|
||||
/**
|
||||
* The integer representation of this instance.
|
||||
*
|
||||
|
@ -141,289 +112,254 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
Direction.DESCENDING -> 0
|
||||
}
|
||||
|
||||
/** Describes the type of data to sort with. */
|
||||
sealed interface Mode {
|
||||
/** The integer representation of this sort mode. */
|
||||
val intCode: Int
|
||||
/** The string resource of the human-readable name of this sort mode. */
|
||||
val stringRes: Int
|
||||
|
||||
/**
|
||||
* Get a [Comparator] that sorts [Song]s according to this [Mode].
|
||||
*
|
||||
* @param direction The direction to sort in.
|
||||
* @return A [Comparator] that can be used to sort a [Song] list according to this [Mode],
|
||||
* or null to not sort at all.
|
||||
*/
|
||||
fun getSongComparator(direction: Direction): Comparator<Song>? = null
|
||||
fun sortSongs(songs: MutableList<Song>, direction: Direction) {
|
||||
throw NotImplementedError("Sorting songs is not supported for this mode")
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a [Comparator] that sorts [Album]s according to this [Mode].
|
||||
*
|
||||
* @param direction The direction to sort in.
|
||||
* @return A [Comparator] that can be used to sort a [Album] list according to this [Mode],
|
||||
* or null to not sort at all.
|
||||
*/
|
||||
fun getAlbumComparator(direction: Direction): Comparator<Album>? = null
|
||||
fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
|
||||
throw NotImplementedError("Sorting albums is not supported for this mode")
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a [Comparator] that sorts [Artist]s according to this [Mode].
|
||||
*
|
||||
* @param direction The direction to sort in.
|
||||
* @return A [Comparator] that can be used to sort a [Artist] list according to this [Mode].
|
||||
* or null to not sort at all.
|
||||
*/
|
||||
fun getArtistComparator(direction: Direction): Comparator<Artist>? = null
|
||||
fun sortArtists(artists: MutableList<Artist>, direction: Direction) {
|
||||
throw NotImplementedError("Sorting artists is not supported for this mode")
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a [Comparator] that sorts [Genre]s according to this [Mode].
|
||||
*
|
||||
* @param direction The direction to sort in.
|
||||
* @return A [Comparator] that can be used to sort a [Genre] list according to this [Mode].
|
||||
* or null to not sort at all.
|
||||
*/
|
||||
fun getGenreComparator(direction: Direction): Comparator<Genre>? = null
|
||||
fun sortGenres(genres: MutableList<Genre>, direction: Direction) {
|
||||
throw NotImplementedError("Sorting genres is not supported for this mode")
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a [Comparator] that sorts [Playlist]s according to this [Mode].
|
||||
*
|
||||
* @param direction The direction to sort in.
|
||||
* @return A [Comparator] that can be used to sort a [Genre] list according to this [Mode].
|
||||
* or null to not sort at all.
|
||||
*/
|
||||
fun getPlaylistComparator(direction: Direction): Comparator<Playlist>? = null
|
||||
fun sortPlaylists(playlists: MutableList<Playlist>, direction: Direction) {
|
||||
throw NotImplementedError("Sorting playlists is not supported for this mode")
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by the item's name.
|
||||
*
|
||||
* @see Music.name
|
||||
*/
|
||||
data object ByName : Mode {
|
||||
override val intCode: Int
|
||||
get() = IntegerTable.SORT_BY_NAME
|
||||
override val intCode = IntegerTable.SORT_BY_NAME
|
||||
override val stringRes = R.string.lbl_name
|
||||
|
||||
override val stringRes: Int
|
||||
get() = R.string.lbl_name
|
||||
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> songs.sortBy { it.name }
|
||||
Direction.DESCENDING -> songs.sortByDescending { it.name }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSongComparator(direction: Direction) =
|
||||
compareByDynamic(direction, BasicComparator.SONG)
|
||||
override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> albums.sortBy { it.name }
|
||||
Direction.DESCENDING -> albums.sortByDescending { it.name }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAlbumComparator(direction: Direction) =
|
||||
compareByDynamic(direction, BasicComparator.ALBUM)
|
||||
override fun sortArtists(artists: MutableList<Artist>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> artists.sortBy { it.name }
|
||||
Direction.DESCENDING -> artists.sortByDescending { it.name }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getArtistComparator(direction: Direction) =
|
||||
compareByDynamic(direction, BasicComparator.ARTIST)
|
||||
override fun sortGenres(genres: MutableList<Genre>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> genres.sortBy { it.name }
|
||||
Direction.DESCENDING -> genres.sortByDescending { it.name }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getGenreComparator(direction: Direction) =
|
||||
compareByDynamic(direction, BasicComparator.GENRE)
|
||||
|
||||
override fun getPlaylistComparator(direction: Direction) =
|
||||
compareByDynamic(direction, BasicComparator.PLAYLIST)
|
||||
override fun sortPlaylists(playlists: MutableList<Playlist>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> playlists.sortBy { it.name }
|
||||
Direction.DESCENDING -> playlists.sortByDescending { it.name }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by the [Album] of an item. Only available for [Song]s.
|
||||
*
|
||||
* @see Album.name
|
||||
*/
|
||||
data object ByAlbum : Mode {
|
||||
override val intCode: Int
|
||||
get() = IntegerTable.SORT_BY_ALBUM
|
||||
override val intCode = IntegerTable.SORT_BY_ALBUM
|
||||
override val stringRes = R.string.lbl_album
|
||||
|
||||
override val stringRes: Int
|
||||
get() = R.string.lbl_album
|
||||
|
||||
override fun getSongComparator(direction: Direction): Comparator<Song> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction, BasicComparator.ALBUM) { it.album },
|
||||
compareBy(NullableComparator.DISC) { it.disc },
|
||||
compareBy(NullableComparator.INT) { it.track },
|
||||
compareBy(BasicComparator.SONG))
|
||||
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
|
||||
songs.sortBy { it.track }
|
||||
songs.sortBy { it.disc }
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> songs.sortBy { it.album.name }
|
||||
Direction.DESCENDING -> songs.sortByDescending { it.album.name }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by the [Artist] name of an item. Only available for [Song] and [Album].
|
||||
*
|
||||
* @see Artist.name
|
||||
*/
|
||||
data object ByArtist : Mode {
|
||||
override val intCode: Int
|
||||
get() = IntegerTable.SORT_BY_ARTIST
|
||||
override val intCode = IntegerTable.SORT_BY_ARTIST
|
||||
override val stringRes = R.string.lbl_artist
|
||||
|
||||
override val stringRes: Int
|
||||
get() = R.string.lbl_artist
|
||||
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
|
||||
songs.sortByDescending { it.album.dates }
|
||||
songs.sortByDescending { it.album.name }
|
||||
songs.sortBy { it.disc }
|
||||
songs.sortBy { it.track }
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> songs.sortBy { it.artists.firstOrNull()?.name }
|
||||
Direction.DESCENDING ->
|
||||
songs.sortByDescending { it.artists.firstOrNull()?.name }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSongComparator(direction: Direction): Comparator<Song> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction, ListComparator.ARTISTS) { it.artists },
|
||||
compareByDescending(NullableComparator.DATE_RANGE) { it.album.dates },
|
||||
compareByDescending(BasicComparator.ALBUM) { it.album },
|
||||
compareBy(NullableComparator.DISC) { it.disc },
|
||||
compareBy(NullableComparator.INT) { it.track },
|
||||
compareBy(BasicComparator.SONG))
|
||||
|
||||
override fun getAlbumComparator(direction: Direction): Comparator<Album> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction, ListComparator.ARTISTS) { it.artists },
|
||||
compareByDescending(NullableComparator.DATE_RANGE) { it.dates },
|
||||
compareBy(BasicComparator.ALBUM))
|
||||
override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
|
||||
albums.sortByDescending { it.dates }
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> albums.sortBy { it.artists.firstOrNull()?.name }
|
||||
Direction.DESCENDING ->
|
||||
albums.sortByDescending { it.artists.firstOrNull()?.name }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by the [Date] of an item. Only available for [Song] and [Album].
|
||||
*
|
||||
* @see Song.date
|
||||
* @see Album.dates
|
||||
*/
|
||||
data object ByDate : Mode {
|
||||
override val intCode: Int
|
||||
get() = IntegerTable.SORT_BY_YEAR
|
||||
override val intCode = IntegerTable.SORT_BY_YEAR
|
||||
override val stringRes = R.string.lbl_date
|
||||
|
||||
override val stringRes: Int
|
||||
get() = R.string.lbl_date
|
||||
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
|
||||
songs.sortByDescending { it.album.name }
|
||||
songs.sortBy { it.disc }
|
||||
songs.sortBy { it.track }
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> songs.sortBy { it.album.dates }
|
||||
Direction.DESCENDING -> songs.sortByDescending { it.album.dates }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSongComparator(direction: Direction): Comparator<Song> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction, NullableComparator.DATE_RANGE) { it.album.dates },
|
||||
compareByDescending(BasicComparator.ALBUM) { it.album },
|
||||
compareBy(NullableComparator.DISC) { it.disc },
|
||||
compareBy(NullableComparator.INT) { it.track },
|
||||
compareBy(BasicComparator.SONG))
|
||||
|
||||
override fun getAlbumComparator(direction: Direction): Comparator<Album> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction, NullableComparator.DATE_RANGE) { it.dates },
|
||||
compareBy(BasicComparator.ALBUM))
|
||||
override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> albums.sortBy { it.dates }
|
||||
Direction.DESCENDING -> albums.sortByDescending { it.dates }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Sort by the duration of an item. */
|
||||
data object ByDuration : Mode {
|
||||
override val intCode: Int
|
||||
get() = IntegerTable.SORT_BY_DURATION
|
||||
override val intCode = IntegerTable.SORT_BY_DURATION
|
||||
override val stringRes = R.string.lbl_duration
|
||||
|
||||
override val stringRes: Int
|
||||
get() = R.string.lbl_duration
|
||||
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> songs.sortBy { it.durationMs }
|
||||
Direction.DESCENDING -> songs.sortByDescending { it.durationMs }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSongComparator(direction: Direction): Comparator<Song> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.SONG))
|
||||
override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> albums.sortBy { it.durationMs }
|
||||
Direction.DESCENDING -> albums.sortByDescending { it.durationMs }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAlbumComparator(direction: Direction): Comparator<Album> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.ALBUM))
|
||||
override fun sortArtists(artists: MutableList<Artist>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> artists.sortBy { it.durationMs }
|
||||
Direction.DESCENDING -> artists.sortByDescending { it.durationMs }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getArtistComparator(direction: Direction): Comparator<Artist> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction, NullableComparator.LONG) { it.durationMs },
|
||||
compareBy(BasicComparator.ARTIST))
|
||||
override fun sortGenres(genres: MutableList<Genre>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> genres.sortBy { it.durationMs }
|
||||
Direction.DESCENDING -> genres.sortByDescending { it.durationMs }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getGenreComparator(direction: Direction): Comparator<Genre> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.GENRE))
|
||||
|
||||
override fun getPlaylistComparator(direction: Direction): Comparator<Playlist> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction) { it.durationMs },
|
||||
compareBy(BasicComparator.PLAYLIST))
|
||||
override fun sortPlaylists(playlists: MutableList<Playlist>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> playlists.sortBy { it.durationMs }
|
||||
Direction.DESCENDING -> playlists.sortByDescending { it.durationMs }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Sort by the amount of songs an item contains. Only available for MusicParents. */
|
||||
data object ByCount : Mode {
|
||||
override val intCode: Int
|
||||
get() = IntegerTable.SORT_BY_COUNT
|
||||
override val intCode = IntegerTable.SORT_BY_COUNT
|
||||
override val stringRes = R.string.lbl_song_count
|
||||
|
||||
override val stringRes: Int
|
||||
get() = R.string.lbl_song_count
|
||||
override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> albums.sortBy { it.songs.size }
|
||||
Direction.DESCENDING -> albums.sortByDescending { it.songs.size }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAlbumComparator(direction: Direction): Comparator<Album> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction) { it.songs.size }, compareBy(BasicComparator.ALBUM))
|
||||
override fun sortArtists(artists: MutableList<Artist>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> artists.sortBy { it.songs.size }
|
||||
Direction.DESCENDING -> artists.sortByDescending { it.songs.size }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getArtistComparator(direction: Direction): Comparator<Artist> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction, NullableComparator.INT) { it.songs.size },
|
||||
compareBy(BasicComparator.ARTIST))
|
||||
override fun sortGenres(genres: MutableList<Genre>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> genres.sortBy { it.songs.size }
|
||||
Direction.DESCENDING -> genres.sortByDescending { it.songs.size }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getGenreComparator(direction: Direction): Comparator<Genre> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction) { it.songs.size }, compareBy(BasicComparator.GENRE))
|
||||
|
||||
override fun getPlaylistComparator(direction: Direction): Comparator<Playlist> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction) { it.songs.size },
|
||||
compareBy(BasicComparator.PLAYLIST))
|
||||
override fun sortPlaylists(playlists: MutableList<Playlist>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> playlists.sortBy { it.songs.size }
|
||||
Direction.DESCENDING -> playlists.sortByDescending { it.songs.size }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by the disc number of an item. Only available for [Song]s.
|
||||
*
|
||||
* @see Song.disc
|
||||
*/
|
||||
data object ByDisc : Mode {
|
||||
override val intCode: Int
|
||||
get() = IntegerTable.SORT_BY_DISC
|
||||
override val intCode = IntegerTable.SORT_BY_DISC
|
||||
override val stringRes = R.string.lbl_disc
|
||||
|
||||
override val stringRes: Int
|
||||
get() = R.string.lbl_disc
|
||||
|
||||
override fun getSongComparator(direction: Direction): Comparator<Song> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction, NullableComparator.DISC) { it.disc },
|
||||
compareBy(NullableComparator.INT) { it.track },
|
||||
compareBy(BasicComparator.SONG))
|
||||
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
|
||||
songs.sortBy { it.track }
|
||||
songs.sortBy { it.name }
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> songs.sortBy { it.disc }
|
||||
Direction.DESCENDING -> songs.sortByDescending { it.disc }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by the track number of an item. Only available for [Song]s.
|
||||
*
|
||||
* @see Song.track
|
||||
*/
|
||||
data object ByTrack : Mode {
|
||||
override val intCode: Int
|
||||
get() = IntegerTable.SORT_BY_TRACK
|
||||
override val intCode = IntegerTable.SORT_BY_TRACK
|
||||
override val stringRes = R.string.lbl_track
|
||||
|
||||
override val stringRes: Int
|
||||
get() = R.string.lbl_track
|
||||
|
||||
override fun getSongComparator(direction: Direction): Comparator<Song> =
|
||||
MultiComparator(
|
||||
compareBy(NullableComparator.DISC) { it.disc },
|
||||
compareByDynamic(direction, NullableComparator.INT) { it.track },
|
||||
compareBy(BasicComparator.SONG))
|
||||
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
|
||||
songs.sortBy { it.name }
|
||||
songs.sortBy { it.disc }
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> songs.sortBy { it.track }
|
||||
Direction.DESCENDING -> songs.sortByDescending { it.track }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by the date an item was added. Only supported by [Song]s and [Album]s.
|
||||
*
|
||||
* @see Song.dateAdded
|
||||
* @see Album.dates
|
||||
*/
|
||||
data object ByDateAdded : Mode {
|
||||
override val intCode: Int
|
||||
get() = IntegerTable.SORT_BY_DATE_ADDED
|
||||
override val intCode = IntegerTable.SORT_BY_DATE_ADDED
|
||||
override val stringRes = R.string.lbl_date_added
|
||||
|
||||
override val stringRes: Int
|
||||
get() = R.string.lbl_date_added
|
||||
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> songs.sortBy { it.dateAdded }
|
||||
Direction.DESCENDING -> songs.sortByDescending { it.dateAdded }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSongComparator(direction: Direction): Comparator<Song> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction) { it.dateAdded }, compareBy(BasicComparator.SONG))
|
||||
|
||||
override fun getAlbumComparator(direction: Direction): Comparator<Album> =
|
||||
MultiComparator(
|
||||
compareByDynamic(direction) { album -> album.dateAdded },
|
||||
compareBy(BasicComparator.ALBUM))
|
||||
override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
|
||||
when (direction) {
|
||||
Direction.ASCENDING -> albums.sortBy { it.dateAdded }
|
||||
Direction.DESCENDING -> albums.sortByDescending { it.dateAdded }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Convert a [Mode] integer representation into an instance.
|
||||
*
|
||||
* @param intCode An integer representation of a [Mode]
|
||||
* @return The corresponding [Mode], or null if the [Mode] is invalid.
|
||||
* @see intCode
|
||||
*/
|
||||
fun fromIntCode(intCode: Int) =
|
||||
fun fromIntCode(intCode: Int): Mode? =
|
||||
when (intCode) {
|
||||
ByName.intCode -> ByName
|
||||
ByArtist.intCode -> ByArtist
|
||||
|
@ -463,166 +399,3 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to create a [Comparator] in a dynamic way determined by [direction].
|
||||
*
|
||||
* @param direction The [Sort.Direction] to sort in.
|
||||
* @see compareBy
|
||||
* @see compareByDescending
|
||||
*/
|
||||
private inline fun <T : Music, K : Comparable<K>> compareByDynamic(
|
||||
direction: Sort.Direction,
|
||||
crossinline selector: (T) -> K
|
||||
) =
|
||||
when (direction) {
|
||||
Sort.Direction.ASCENDING -> compareBy(selector)
|
||||
Sort.Direction.DESCENDING -> compareByDescending(selector)
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to create a [Comparator] in a dynamic way determined by [direction]
|
||||
*
|
||||
* @param direction The [Sort.Direction] to sort in.
|
||||
* @param comparator A [Comparator] to wrap.
|
||||
* @return A new [Comparator] with the specified configuration.
|
||||
* @see compareBy
|
||||
* @see compareByDescending
|
||||
*/
|
||||
private fun <T : Music> compareByDynamic(
|
||||
direction: Sort.Direction,
|
||||
comparator: Comparator<in T>
|
||||
): Comparator<T> = compareByDynamic(direction, comparator) { it }
|
||||
|
||||
/**
|
||||
* Utility function to create a [Comparator] a dynamic way determined by [direction]
|
||||
*
|
||||
* @param direction The [Sort.Direction] to sort in.
|
||||
* @param comparator A [Comparator] to wrap.
|
||||
* @param selector Called to obtain a specific attribute to sort by.
|
||||
* @return A new [Comparator] with the specified configuration.
|
||||
* @see compareBy
|
||||
* @see compareByDescending
|
||||
*/
|
||||
private inline fun <T : Music, K> compareByDynamic(
|
||||
direction: Sort.Direction,
|
||||
comparator: Comparator<in K>,
|
||||
crossinline selector: (T) -> K
|
||||
) =
|
||||
when (direction) {
|
||||
Sort.Direction.ASCENDING -> compareBy(comparator, selector)
|
||||
Sort.Direction.DESCENDING -> compareByDescending(comparator, selector)
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to create a [Comparator] that sorts in ascending order based on the given
|
||||
* [Comparator], with a selector based on the item itself.
|
||||
*
|
||||
* @param comparator The [Comparator] to wrap.
|
||||
* @return A new [Comparator] with the specified configuration.
|
||||
* @see compareBy
|
||||
*/
|
||||
private fun <T : Music> compareBy(comparator: Comparator<T>): Comparator<T> =
|
||||
compareBy(comparator) { it }
|
||||
|
||||
/**
|
||||
* A [Comparator] that chains several other [Comparator]s together to form one comparison.
|
||||
*
|
||||
* @param comparators The [Comparator]s to chain. These will be iterated through in order during a
|
||||
* comparison, with the first non-equal result becoming the result.
|
||||
*/
|
||||
private class MultiComparator<T>(vararg comparators: Comparator<T>) : Comparator<T> {
|
||||
private val _comparators = comparators
|
||||
|
||||
override fun compare(a: T?, b: T?): Int {
|
||||
for (comparator in _comparators) {
|
||||
val result = comparator.compare(a, b)
|
||||
if (result != 0) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a [Comparator], extending it to compare two lists.
|
||||
*
|
||||
* @param inner The [Comparator] to use.
|
||||
*/
|
||||
private class ListComparator<T>(private val inner: Comparator<T>) : Comparator<List<T>> {
|
||||
override fun compare(a: List<T>, b: List<T>): Int {
|
||||
for (i in 0 until max(a.size, b.size)) {
|
||||
val ai = a.getOrNull(i)
|
||||
val bi = b.getOrNull(i)
|
||||
when {
|
||||
ai != null && bi != null -> {
|
||||
val result = inner.compare(ai, bi)
|
||||
if (result != 0) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
ai == null && bi != null -> return -1 // a < b
|
||||
ai == null && bi == null -> return 0 // a = b
|
||||
else -> return 1 // a < b
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** A re-usable configured for [Artist]s.. */
|
||||
val ARTISTS: Comparator<List<Artist>> = ListComparator(BasicComparator.ARTIST)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A [Comparator] that compares abstract [Music] values. Internally, this is similar to
|
||||
* [NullableComparator], however comparing [Music.name] instead of [Comparable].
|
||||
*
|
||||
* @see NullableComparator
|
||||
* @see Music.name
|
||||
*/
|
||||
private class BasicComparator<T : Music> private constructor() : Comparator<T> {
|
||||
override fun compare(a: T, b: T) = a.name.compareTo(b.name)
|
||||
|
||||
companion object {
|
||||
/** A re-usable instance configured for [Song]s. */
|
||||
val SONG: Comparator<Song> = BasicComparator()
|
||||
/** A re-usable instance configured for [Album]s. */
|
||||
val ALBUM: Comparator<Album> = BasicComparator()
|
||||
/** A re-usable instance configured for [Artist]s. */
|
||||
val ARTIST: Comparator<Artist> = BasicComparator()
|
||||
/** A re-usable instance configured for [Genre]s. */
|
||||
val GENRE: Comparator<Genre> = BasicComparator()
|
||||
/** A re-usable instance configured for [Playlist]s. */
|
||||
val PLAYLIST: Comparator<Playlist> = BasicComparator()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A [Comparator] that compares two possibly null values. Values will be considered lesser if they
|
||||
* are null, and greater if they are non-null.
|
||||
*/
|
||||
private class NullableComparator<T : Comparable<T>> private constructor() : Comparator<T?> {
|
||||
override fun compare(a: T?, b: T?) =
|
||||
when {
|
||||
a != null && b != null -> a.compareTo(b)
|
||||
a == null && b != null -> -1 // a < b
|
||||
a == null && b == null -> 0 // a = b
|
||||
else -> 1 // a < b
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** A re-usable instance configured for [Int]s. */
|
||||
val INT = NullableComparator<Int>()
|
||||
/** A re-usable instance configured for [Long]s. */
|
||||
val LONG = NullableComparator<Long>()
|
||||
/** A re-usable instance configured for [Disc]s */
|
||||
val DISC = NullableComparator<Disc>()
|
||||
/** A re-usable instance configured for [Date.Range]s. */
|
||||
val DATE_RANGE = NullableComparator<Date.Range>()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue