Merge branch 'master' into dev

This commit is contained in:
Alexander Capehart 2024-07-04 10:10:02 -06:00
commit 27fb1d1823
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
6 changed files with 232 additions and 437 deletions

View file

@ -13,6 +13,11 @@
- Music loader no longer spawns thousands of threads when scanning - Music loader no longer spawns thousands of threads when scanning
- Excessive CPU no longer spent showing music loading process - Excessive CPU no longer spent showing music loading process
## 3.5.1
#### What's Fixed
- Fixed music loading failure from improper sort systems
## 3.5.0 ## 3.5.0
#### What's New #### What's New

View file

@ -2,8 +2,8 @@
<h1 align="center"><b>Auxio</b></h1> <h1 align="center"><b>Auxio</b></h1>
<h4 align="center">A simple, rational music player for android.</h4> <h4 align="center">A simple, rational music player for android.</h4>
<p align="center"> <p align="center">
<a href="https://github.com/oxygencobalt/Auxio/releases/tag/v3.5.0"> <a href="https://github.com/oxygencobalt/Auxio/releases/tag/v3.5.1">
<img alt="Latest Version" src="https://img.shields.io/static/v1?label=tag&message=v3.5.0&color=64B5F6&style=flat"> <img alt="Latest Version" src="https://img.shields.io/static/v1?label=tag&message=v3.5.1&color=64B5F6&style=flat">
</a> </a>
<a href="https://github.com/oxygencobalt/Auxio/releases/"> <a href="https://github.com/oxygencobalt/Auxio/releases/">
<img alt="Releases" src="https://img.shields.io/github/downloads/OxygenCobalt/Auxio/total.svg?color=4B95DE&style=flat"> <img alt="Releases" src="https://img.shields.io/github/downloads/OxygenCobalt/Auxio/total.svg?color=4B95DE&style=flat">

View file

@ -21,8 +21,8 @@ android {
defaultConfig { defaultConfig {
applicationId namespace applicationId namespace
versionName "3.5.0" versionName "3.5.1"
versionCode 46 versionCode 47
minSdk 24 minSdk 24
targetSdk 34 targetSdk 34

View file

@ -20,17 +20,11 @@ package org.oxycblt.auxio.list.sort
import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.list.sort.Sort.Direction
import org.oxycblt.auxio.list.sort.Sort.Mode
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
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.info.Date
import org.oxycblt.auxio.music.info.Disc
import kotlin.math.max
/** /**
* A sorting method. * A sorting method.
@ -48,9 +42,9 @@ data class Sort(val mode: Mode, val direction: Direction) {
* @param songs The list of [Song]s. * @param songs The list of [Song]s.
* @return A new list of [Song]s sorted by this [Sort]'s configuration. * @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() val mutable = songs.toMutableList()
songsInPlace(mutable) mode.sortSongs(mutable, direction)
return mutable return mutable
} }
@ -60,9 +54,9 @@ data class Sort(val mode: Mode, val direction: Direction) {
* @param albums The list of [Album]s. * @param albums The list of [Album]s.
* @return A new list of [Album]s sorted by this [Sort]'s configuration. * @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() val mutable = albums.toMutableList()
albumsInPlace(mutable) mode.sortAlbums(mutable, direction)
return mutable return mutable
} }
@ -72,9 +66,9 @@ data class Sort(val mode: Mode, val direction: Direction) {
* @param artists The list of [Artist]s. * @param artists The list of [Artist]s.
* @return A new list of [Artist]s sorted by this [Sort]'s configuration. * @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() val mutable = artists.toMutableList()
artistsInPlace(mutable) mode.sortArtists(mutable, direction)
return mutable return mutable
} }
@ -84,9 +78,9 @@ data class Sort(val mode: Mode, val direction: Direction) {
* @param genres The list of [Genre]s. * @param genres The list of [Genre]s.
* @return A new list of [Genre]s sorted by this [Sort]'s configuration. * @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() val mutable = genres.toMutableList()
genresInPlace(mutable) mode.sortGenres(mutable, direction)
return mutable return mutable
} }
@ -96,37 +90,12 @@ data class Sort(val mode: Mode, val direction: Direction) {
* @param playlists The list of [Playlist]s. * @param playlists The list of [Playlist]s.
* @return A new list of [Playlist]s sorted by this [Sort]'s configuration * @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() val mutable = playlists.toMutableList()
playlistsInPlace(mutable) mode.sortPlaylists(mutable, direction)
return mutable 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. * The integer representation of this instance.
* *
@ -143,289 +112,270 @@ data class Sort(val mode: Mode, val direction: Direction) {
Direction.DESCENDING -> 0 Direction.DESCENDING -> 0
} }
/** Describes the type of data to sort with. */
sealed interface Mode { sealed interface Mode {
/** The integer representation of this sort mode. */
val intCode: Int val intCode: Int
/** The string resource of the human-readable name of this sort mode. */
val stringRes: Int val stringRes: Int
/** fun sortSongs(songs: MutableList<Song>, direction: Direction) {
* Get a [Comparator] that sorts [Song]s according to this [Mode]. throw NotImplementedError("Sorting songs is not supported for 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 sortAlbums(albums: MutableList<Album>, direction: Direction) {
* Get a [Comparator] that sorts [Album]s according to this [Mode]. throw NotImplementedError("Sorting albums is not supported for 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 sortArtists(artists: MutableList<Artist>, direction: Direction) {
* Return a [Comparator] that sorts [Artist]s according to this [Mode]. throw NotImplementedError("Sorting artists is not supported for 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 sortGenres(genres: MutableList<Genre>, direction: Direction) {
* Return a [Comparator] that sorts [Genre]s according to this [Mode]. throw NotImplementedError("Sorting genres is not supported for 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 sortPlaylists(playlists: MutableList<Playlist>, direction: Direction) {
* Return a [Comparator] that sorts [Playlist]s according to this [Mode]. throw NotImplementedError("Sorting playlists is not supported for 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
/**
* Sort by the item's name.
*
* @see Music.name
*/
data object ByName : Mode { data object ByName : Mode {
override val intCode: Int override val intCode = IntegerTable.SORT_BY_NAME
get() = IntegerTable.SORT_BY_NAME override val stringRes = R.string.lbl_name
override val stringRes: Int override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
get() = R.string.lbl_name when (direction) {
Direction.ASCENDING -> songs.sortBy { it.name }
override fun getSongComparator(direction: Direction) = Direction.DESCENDING -> songs.sortByDescending { it.name }
compareByDynamic(direction, BasicComparator.SONG) }
}
override fun getAlbumComparator(direction: Direction) =
compareByDynamic(direction, BasicComparator.ALBUM) override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
when (direction) {
override fun getArtistComparator(direction: Direction) = Direction.ASCENDING -> albums.sortBy { it.name }
compareByDynamic(direction, BasicComparator.ARTIST) Direction.DESCENDING -> albums.sortByDescending { it.name }
}
override fun getGenreComparator(direction: Direction) = }
compareByDynamic(direction, BasicComparator.GENRE)
override fun sortArtists(artists: MutableList<Artist>, direction: Direction) {
override fun getPlaylistComparator(direction: Direction) = when (direction) {
compareByDynamic(direction, BasicComparator.PLAYLIST) Direction.ASCENDING -> artists.sortBy { it.name }
Direction.DESCENDING -> artists.sortByDescending { it.name }
}
}
override fun sortGenres(genres: MutableList<Genre>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> genres.sortBy { it.name }
Direction.DESCENDING -> genres.sortByDescending { it.name }
}
}
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 { data object ByAlbum : Mode {
override val intCode: Int override val intCode = IntegerTable.SORT_BY_ALBUM
get() = IntegerTable.SORT_BY_ALBUM override val stringRes = R.string.lbl_album
override val stringRes: Int override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
get() = R.string.lbl_album songs.sortBy { it.name }
songs.sortBy { it.track }
override fun getSongComparator(direction: Direction): Comparator<Song> = songs.sortBy { it.disc }
MultiComparator( when (direction) {
compareByDynamic(direction, BasicComparator.ALBUM) { it.album }, Direction.ASCENDING -> songs.sortBy { it.album.name }
compareBy(NullableComparator.DISC) { it.disc }, Direction.DESCENDING -> songs.sortByDescending { it.album.name }
compareBy(NullableComparator.INT) { it.track }, }
compareBy(BasicComparator.SONG)) }
} }
/**
* Sort by the [Artist] name of an item. Only available for [Song] and [Album].
*
* @see Artist.name
*/
data object ByArtist : Mode { data object ByArtist : Mode {
override val intCode: Int override val intCode = IntegerTable.SORT_BY_ARTIST
get() = IntegerTable.SORT_BY_ARTIST override val stringRes = R.string.lbl_artist
override val stringRes: Int override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
get() = R.string.lbl_artist songs.sortBy { it.name }
songs.sortBy { it.track }
override fun getSongComparator(direction: Direction): Comparator<Song> = songs.sortBy { it.disc }
MultiComparator( songs.sortBy { it.album.name }
compareByDynamic(direction, ListComparator.ARTISTS) { it.artists }, songs.sortByDescending { it.album.dates }
compareByDescending(NullableComparator.DATE_RANGE) { it.album.dates }, when (direction) {
compareByDescending(BasicComparator.ALBUM) { it.album }, Direction.ASCENDING -> songs.sortBy { it.artists.firstOrNull()?.name }
compareBy(NullableComparator.DISC) { it.disc }, Direction.DESCENDING ->
compareBy(NullableComparator.INT) { it.track }, songs.sortByDescending { it.artists.firstOrNull()?.name }
compareBy(BasicComparator.SONG)) }
}
override fun getAlbumComparator(direction: Direction): Comparator<Album> =
MultiComparator( override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
compareByDynamic(direction, ListComparator.ARTISTS) { it.artists }, albums.sortBy { it.name }
compareByDescending(NullableComparator.DATE_RANGE) { it.dates }, albums.sortByDescending { it.dates }
compareBy(BasicComparator.ALBUM)) 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 { data object ByDate : Mode {
override val intCode: Int override val intCode = IntegerTable.SORT_BY_YEAR
get() = IntegerTable.SORT_BY_YEAR override val stringRes = R.string.lbl_date
override val stringRes: Int override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
get() = R.string.lbl_date songs.sortBy { it.name }
songs.sortBy { it.track }
override fun getSongComparator(direction: Direction): Comparator<Song> = songs.sortBy { it.disc }
MultiComparator( songs.sortByDescending { it.album.name }
compareByDynamic(direction, NullableComparator.DATE) { it.date }, when (direction) {
compareByDescending(BasicComparator.ALBUM) { it.album }, Direction.ASCENDING -> songs.sortBy { it.album.dates }
compareBy(NullableComparator.DISC) { it.disc }, Direction.DESCENDING -> songs.sortByDescending { it.album.dates }
compareBy(NullableComparator.INT) { it.track }, }
compareBy(BasicComparator.SONG)) }
override fun getAlbumComparator(direction: Direction): Comparator<Album> = override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
MultiComparator( albums.sortBy { it.name }
compareByDynamic(direction, NullableComparator.DATE_RANGE) { it.dates }, when (direction) {
compareBy(BasicComparator.ALBUM)) Direction.ASCENDING -> albums.sortBy { it.dates }
Direction.DESCENDING -> albums.sortByDescending { it.dates }
}
}
} }
/** Sort by the duration of an item. */
data object ByDuration : Mode { data object ByDuration : Mode {
override val intCode: Int override val intCode = IntegerTable.SORT_BY_DURATION
get() = IntegerTable.SORT_BY_DURATION override val stringRes = R.string.lbl_duration
override val stringRes: Int override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
get() = R.string.lbl_duration songs.sortBy { it.name }
when (direction) {
override fun getSongComparator(direction: Direction): Comparator<Song> = Direction.ASCENDING -> songs.sortBy { it.durationMs }
MultiComparator( Direction.DESCENDING -> songs.sortByDescending { it.durationMs }
compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.SONG)) }
}
override fun getAlbumComparator(direction: Direction): Comparator<Album> =
MultiComparator( override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.ALBUM)) albums.sortBy { it.name }
when (direction) {
override fun getArtistComparator(direction: Direction): Comparator<Artist> = Direction.ASCENDING -> albums.sortBy { it.durationMs }
MultiComparator( Direction.DESCENDING -> albums.sortByDescending { it.durationMs }
compareByDynamic(direction, NullableComparator.LONG) { it.durationMs }, }
compareBy(BasicComparator.ARTIST)) }
override fun getGenreComparator(direction: Direction): Comparator<Genre> = override fun sortArtists(artists: MutableList<Artist>, direction: Direction) {
MultiComparator( artists.sortBy { it.name }
compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.GENRE)) when (direction) {
Direction.ASCENDING -> artists.sortBy { it.durationMs }
override fun getPlaylistComparator(direction: Direction): Comparator<Playlist> = Direction.DESCENDING -> artists.sortByDescending { it.durationMs }
MultiComparator( }
compareByDynamic(direction) { it.durationMs }, }
compareBy(BasicComparator.PLAYLIST))
override fun sortGenres(genres: MutableList<Genre>, direction: Direction) {
genres.sortBy { it.name }
when (direction) {
Direction.ASCENDING -> genres.sortBy { it.durationMs }
Direction.DESCENDING -> genres.sortByDescending { it.durationMs }
}
}
override fun sortPlaylists(playlists: MutableList<Playlist>, direction: Direction) {
playlists.sortBy { it.name }
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 { data object ByCount : Mode {
override val intCode: Int override val intCode = IntegerTable.SORT_BY_COUNT
get() = IntegerTable.SORT_BY_COUNT override val stringRes = R.string.lbl_song_count
override val stringRes: Int override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
get() = R.string.lbl_song_count albums.sortBy { it.name }
when (direction) {
override fun getAlbumComparator(direction: Direction): Comparator<Album> = Direction.ASCENDING -> albums.sortBy { it.songs.size }
MultiComparator( Direction.DESCENDING -> albums.sortByDescending { it.songs.size }
compareByDynamic(direction) { it.songs.size }, compareBy(BasicComparator.ALBUM)) }
}
override fun getArtistComparator(direction: Direction): Comparator<Artist> =
MultiComparator( override fun sortArtists(artists: MutableList<Artist>, direction: Direction) {
compareByDynamic(direction, NullableComparator.INT) { it.songs.size }, artists.sortBy { it.name }
compareBy(BasicComparator.ARTIST)) when (direction) {
Direction.ASCENDING -> artists.sortBy { it.songs.size }
override fun getGenreComparator(direction: Direction): Comparator<Genre> = Direction.DESCENDING -> artists.sortByDescending { it.songs.size }
MultiComparator( }
compareByDynamic(direction) { it.songs.size }, compareBy(BasicComparator.GENRE)) }
override fun getPlaylistComparator(direction: Direction): Comparator<Playlist> = override fun sortGenres(genres: MutableList<Genre>, direction: Direction) {
MultiComparator( genres.sortBy { it.name }
compareByDynamic(direction) { it.songs.size }, when (direction) {
compareBy(BasicComparator.PLAYLIST)) Direction.ASCENDING -> genres.sortBy { it.songs.size }
Direction.DESCENDING -> genres.sortByDescending { it.songs.size }
}
}
override fun sortPlaylists(playlists: MutableList<Playlist>, direction: Direction) {
playlists.sortBy { it.name }
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 { data object ByDisc : Mode {
override val intCode: Int override val intCode = IntegerTable.SORT_BY_DISC
get() = IntegerTable.SORT_BY_DISC override val stringRes = R.string.lbl_disc
override val stringRes: Int override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
get() = R.string.lbl_disc songs.sortBy { it.name }
songs.sortBy { it.track }
override fun getSongComparator(direction: Direction): Comparator<Song> = when (direction) {
MultiComparator( Direction.ASCENDING -> songs.sortBy { it.disc }
compareByDynamic(direction, NullableComparator.DISC) { it.disc }, Direction.DESCENDING -> songs.sortByDescending { it.disc }
compareBy(NullableComparator.INT) { it.track }, }
compareBy(BasicComparator.SONG)) }
} }
/**
* Sort by the track number of an item. Only available for [Song]s.
*
* @see Song.track
*/
data object ByTrack : Mode { data object ByTrack : Mode {
override val intCode: Int override val intCode = IntegerTable.SORT_BY_TRACK
get() = IntegerTable.SORT_BY_TRACK override val stringRes = R.string.lbl_track
override val stringRes: Int override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
get() = R.string.lbl_track songs.sortBy { it.name }
when (direction) {
override fun getSongComparator(direction: Direction): Comparator<Song> = Direction.ASCENDING -> songs.sortBy { it.track }
MultiComparator( Direction.DESCENDING -> songs.sortByDescending { it.track }
compareBy(NullableComparator.DISC) { it.disc }, }
compareByDynamic(direction, NullableComparator.INT) { it.track }, songs.sortBy { it.disc }
compareBy(BasicComparator.SONG)) }
} }
/**
* 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 { data object ByDateAdded : Mode {
override val intCode: Int override val intCode = IntegerTable.SORT_BY_DATE_ADDED
get() = IntegerTable.SORT_BY_DATE_ADDED override val stringRes = R.string.lbl_date_added
override val stringRes: Int override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
get() = R.string.lbl_date_added songs.sortBy { it.name }
when (direction) {
Direction.ASCENDING -> songs.sortBy { it.dateAdded }
Direction.DESCENDING -> songs.sortByDescending { it.dateAdded }
}
}
override fun getSongComparator(direction: Direction): Comparator<Song> = override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
MultiComparator( albums.sortBy { it.name }
compareByDynamic(direction) { it.dateAdded }, compareBy(BasicComparator.SONG)) when (direction) {
Direction.ASCENDING -> albums.sortBy { it.dateAdded }
override fun getAlbumComparator(direction: Direction): Comparator<Album> = Direction.DESCENDING -> albums.sortByDescending { it.dateAdded }
MultiComparator( }
compareByDynamic(direction) { album -> album.dateAdded }, }
compareBy(BasicComparator.ALBUM))
} }
companion object { companion object {
/** fun fromIntCode(intCode: Int): Mode? =
* 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) =
when (intCode) { when (intCode) {
ByName.intCode -> ByName ByName.intCode -> ByName
ByArtist.intCode -> ByArtist ByArtist.intCode -> ByArtist
@ -465,169 +415,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>()
/** A re-usable instance configured for [Date]s. */
val DATE = NullableComparator<Date>()
}
}

View file

@ -438,7 +438,8 @@ constructor(
// to cascade to and cancel all other routines before finally bubbling up // to cascade to and cancel all other routines before finally bubbling up
// to the main extractor loop. // to the main extractor loop.
logE("MediaStore extraction failed: $e") logE("MediaStore extraction failed: $e")
incompleteSongs.close(Exception("MediaStore extraction failed: $e")) incompleteSongs.close(
Exception("MediaStore extraction failed: ${e.stackTraceToString()}"))
return@async return@async
} }
incompleteSongs.close() incompleteSongs.close()
@ -453,7 +454,8 @@ constructor(
tagExtractor.consume(incompleteSongs, completeSongs) tagExtractor.consume(incompleteSongs, completeSongs)
} catch (e: Exception) { } catch (e: Exception) {
logE("Tag extraction failed: $e") logE("Tag extraction failed: $e")
completeSongs.close(Exception("Tag extraction failed: $e")) completeSongs.close(
Exception("Tag extraction failed: ${e.stackTraceToString()}"))
return@async return@async
} }
completeSongs.close() completeSongs.close()
@ -470,7 +472,8 @@ constructor(
completeSongs, processedSongs, separators, nameFactory) completeSongs, processedSongs, separators, nameFactory)
} catch (e: Exception) { } catch (e: Exception) {
logE("DeviceLibrary creation failed: $e") logE("DeviceLibrary creation failed: $e")
processedSongs.close(Exception("DeviceLibrary creation failed: $e")) processedSongs.close(
Exception("DeviceLibrary creation failed: ${e.stackTraceToString()}"))
return@async Result.failure(e) return@async Result.failure(e)
} }
processedSongs.close() processedSongs.close()

View file

@ -0,0 +1,3 @@
Auxio 3.5.1 adds support for android auto alongside various playback and music quality of life improvements.
This release fixes a critical bug with the music loader.
For more information, see https://github.com/OxygenCobalt/Auxio/releases/tag/v3.5.0