Merge pull request #425 from OxygenCobalt/3.0.5-cherrypick
Version 3.0.5
This commit is contained in:
commit
78d9f9f613
7 changed files with 60 additions and 57 deletions
|
@ -1,5 +1,13 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 3.0.5
|
||||||
|
|
||||||
|
#### What's Fixed
|
||||||
|
- Fixed inconsistent corner radius on widget
|
||||||
|
- Fixed crash that would occur due to intelligent sort name functionality
|
||||||
|
- Fixed crashing on music loading failures that should route to an error
|
||||||
|
screen
|
||||||
|
|
||||||
## 3.0.4
|
## 3.0.4
|
||||||
|
|
||||||
#### What's New
|
#### What's New
|
||||||
|
|
|
@ -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.0.4">
|
<a href="https://github.com/oxygencobalt/Auxio/releases/tag/v3.0.5">
|
||||||
<img alt="Latest Version" src="https://img.shields.io/static/v1?label=tag&message=v3.0.4&color=64B5F6&style=flat">
|
<img alt="Latest Version" src="https://img.shields.io/static/v1?label=tag&message=v3.0.5&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">
|
||||||
|
|
|
@ -20,8 +20,8 @@ android {
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId namespace
|
applicationId namespace
|
||||||
versionName "3.0.4"
|
versionName "3.0.5"
|
||||||
versionCode 28
|
versionCode 29
|
||||||
|
|
||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
|
|
|
@ -363,13 +363,14 @@ interface Genre : MusicParent {
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
class SortName(name: String, musicSettings: MusicSettings) : Comparable<SortName> {
|
class SortName(name: String, musicSettings: MusicSettings) : Comparable<SortName> {
|
||||||
private val number: Int?
|
|
||||||
private val collationKey: CollationKey
|
private val collationKey: CollationKey
|
||||||
val thumbString: String?
|
val thumbString: String?
|
||||||
|
|
||||||
init {
|
init {
|
||||||
var sortName = name
|
var sortName = name
|
||||||
if (musicSettings.intelligentSorting) {
|
if (musicSettings.intelligentSorting) {
|
||||||
|
sortName = sortName.replace(LEADING_PUNCTUATION_REGEX, "")
|
||||||
|
|
||||||
sortName =
|
sortName =
|
||||||
sortName.run {
|
sortName.run {
|
||||||
when {
|
when {
|
||||||
|
@ -380,53 +381,34 @@ class SortName(name: String, musicSettings: MusicSettings) : Comparable<SortName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse out numeric portions of the title and use those for sorting, if applicable.
|
// Zero pad all numbers to six digits for better sorting
|
||||||
when (val numericEnd = sortName.indexOfFirst { !it.isDigit() }) {
|
sortName = sortName.replace(CONSECUTIVE_DIGITS_REGEX) { it.value.padStart(6, '0') }
|
||||||
// No numeric component.
|
|
||||||
0 -> number = null
|
|
||||||
// Whole title is numeric.
|
|
||||||
-1 -> {
|
|
||||||
number = sortName.toIntOrNull()
|
|
||||||
sortName = ""
|
|
||||||
}
|
|
||||||
// Part of the title is numeric.
|
|
||||||
else -> {
|
|
||||||
number = sortName.slice(0 until numericEnd).toIntOrNull()
|
|
||||||
sortName = sortName.slice(numericEnd until sortName.length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
number = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
collationKey = COLLATOR.getCollationKey(sortName)
|
collationKey = COLLATOR.getCollationKey(sortName)
|
||||||
|
|
||||||
// Keep track of a string to use in the thumb view.
|
// Keep track of a string to use in the thumb view.
|
||||||
|
// Simply show '#' for everything before 'A'
|
||||||
// TODO: This needs to be moved elsewhere.
|
// TODO: This needs to be moved elsewhere.
|
||||||
thumbString = (number?.toString() ?: collationKey?.run { sourceString.first().uppercase() })
|
thumbString =
|
||||||
|
collationKey?.run {
|
||||||
|
val thumbChar = sourceString.firstOrNull()
|
||||||
|
if (thumbChar?.isLetter() == true) thumbChar.uppercase() else "#"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String = number?.toString() ?: collationKey.sourceString
|
override fun toString(): String = collationKey.sourceString
|
||||||
|
|
||||||
override fun compareTo(other: SortName) =
|
override fun compareTo(other: SortName) = collationKey.compareTo(other.collationKey)
|
||||||
when {
|
|
||||||
number != null && other.number != null -> number.compareTo(other.number)
|
|
||||||
number != null && other.number == null -> -1 // a < b
|
|
||||||
number == null && other.number != null -> 1 // a > b
|
|
||||||
else -> collationKey.compareTo(other.collationKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?) =
|
override fun equals(other: Any?) = other is SortName && collationKey == other.collationKey
|
||||||
other is SortName && number == other.number && collationKey == other.collationKey
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int = collationKey.hashCode()
|
||||||
var hashCode = collationKey.hashCode()
|
|
||||||
if (number != null) hashCode = 31 * hashCode + number
|
|
||||||
return hashCode
|
|
||||||
}
|
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
val COLLATOR: Collator = Collator.getInstance().apply { strength = Collator.PRIMARY }
|
val COLLATOR: Collator = Collator.getInstance().apply { strength = Collator.PRIMARY }
|
||||||
|
val LEADING_PUNCTUATION_REGEX = Regex("[\\p{Punct}+]")
|
||||||
|
val CONSECUTIVE_DIGITS_REGEX = Regex("\\d+")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,15 +25,10 @@ import android.os.Build
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import kotlinx.coroutines.yield
|
|
||||||
import org.oxycblt.auxio.BuildConfig
|
import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.auxio.music.*
|
import org.oxycblt.auxio.music.*
|
||||||
import org.oxycblt.auxio.music.cache.CacheRepository
|
import org.oxycblt.auxio.music.cache.CacheRepository
|
||||||
|
@ -351,14 +346,15 @@ constructor(
|
||||||
|
|
||||||
// Do the initial query of the cache and media databases in parallel.
|
// Do the initial query of the cache and media databases in parallel.
|
||||||
logD("Starting queries")
|
logD("Starting queries")
|
||||||
val mediaStoreQueryJob = scope.async { mediaStoreExtractor.query() }
|
val mediaStoreQueryJob = scope.tryAsync { mediaStoreExtractor.query() }
|
||||||
val cache =
|
val cache =
|
||||||
if (withCache) {
|
if (withCache) {
|
||||||
cacheRepository.readCache()
|
cacheRepository.readCache()
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
val query = mediaStoreQueryJob.await()
|
// TODO: Stupid, actually bubble results properly
|
||||||
|
val query = mediaStoreQueryJob.await().getOrThrow()
|
||||||
|
|
||||||
// Now start processing the queried song information in parallel. Songs that can't be
|
// Now start processing the queried song information in parallel. Songs that can't be
|
||||||
// received from the cache are consisted incomplete and pushed to a separate channel
|
// received from the cache are consisted incomplete and pushed to a separate channel
|
||||||
|
@ -367,10 +363,10 @@ constructor(
|
||||||
val completeSongs = Channel<RawSong>(Channel.UNLIMITED)
|
val completeSongs = Channel<RawSong>(Channel.UNLIMITED)
|
||||||
val incompleteSongs = Channel<RawSong>(Channel.UNLIMITED)
|
val incompleteSongs = Channel<RawSong>(Channel.UNLIMITED)
|
||||||
val mediaStoreJob =
|
val mediaStoreJob =
|
||||||
scope.async {
|
scope.tryAsync {
|
||||||
mediaStoreExtractor.consume(query, cache, incompleteSongs, completeSongs)
|
mediaStoreExtractor.consume(query, cache, incompleteSongs, completeSongs)
|
||||||
}
|
}
|
||||||
val metadataJob = scope.async { tagExtractor.consume(incompleteSongs, completeSongs) }
|
val metadataJob = scope.tryAsync { tagExtractor.consume(incompleteSongs, completeSongs) }
|
||||||
|
|
||||||
// Await completed raw songs as they are processed.
|
// Await completed raw songs as they are processed.
|
||||||
val rawSongs = LinkedList<RawSong>()
|
val rawSongs = LinkedList<RawSong>()
|
||||||
|
@ -379,8 +375,8 @@ constructor(
|
||||||
emitIndexing(Indexer.Indexing.Songs(rawSongs.size, query.projectedTotal))
|
emitIndexing(Indexer.Indexing.Songs(rawSongs.size, query.projectedTotal))
|
||||||
}
|
}
|
||||||
// These should be no-ops
|
// These should be no-ops
|
||||||
mediaStoreJob.await()
|
mediaStoreJob.await().getOrThrow()
|
||||||
metadataJob.await()
|
metadataJob.await().getOrThrow()
|
||||||
|
|
||||||
if (rawSongs.isEmpty()) {
|
if (rawSongs.isEmpty()) {
|
||||||
logE("Music library was empty")
|
logE("Music library was empty")
|
||||||
|
@ -391,11 +387,23 @@ constructor(
|
||||||
// parallel.
|
// parallel.
|
||||||
logD("Discovered ${rawSongs.size} songs, starting finalization")
|
logD("Discovered ${rawSongs.size} songs, starting finalization")
|
||||||
emitIndexing(Indexer.Indexing.Indeterminate)
|
emitIndexing(Indexer.Indexing.Indeterminate)
|
||||||
val libraryJob = scope.async(Dispatchers.Main) { Library.from(rawSongs, musicSettings) }
|
val libraryJob = scope.tryAsync(Dispatchers.Main) { Library.from(rawSongs, musicSettings) }
|
||||||
if (cache == null || cache.invalidated) {
|
if (cache == null || cache.invalidated) {
|
||||||
cacheRepository.writeCache(rawSongs)
|
cacheRepository.writeCache(rawSongs)
|
||||||
}
|
}
|
||||||
return libraryJob.await()
|
return libraryJob.await().getOrThrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <R> CoroutineScope.tryAsync(
|
||||||
|
context: CoroutineContext = EmptyCoroutineContext,
|
||||||
|
crossinline block: suspend () -> R
|
||||||
|
) =
|
||||||
|
async(context) {
|
||||||
|
try {
|
||||||
|
Result.success(block())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Result.failure(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -95,7 +95,9 @@ constructor(
|
||||||
return if (cornerRadius > 0) {
|
return if (cornerRadius > 0) {
|
||||||
// If rounded, reduce the bitmap size further to obtain more pronounced
|
// If rounded, reduce the bitmap size further to obtain more pronounced
|
||||||
// rounded corners.
|
// rounded corners.
|
||||||
builder.transformations(
|
builder
|
||||||
|
.size(getSafeRemoteViewsImageSize(context, 10f))
|
||||||
|
.transformations(
|
||||||
SquareFrameTransform.INSTANCE,
|
SquareFrameTransform.INSTANCE,
|
||||||
RoundedCornersTransformation(cornerRadius.toFloat()))
|
RoundedCornersTransformation(cornerRadius.toFloat()))
|
||||||
} else {
|
} else {
|
||||||
|
|
3
fastlane/metadata/android/en-US/changelogs/29.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/29.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Auxio 3.0.0 massively improves the music library experience, with a new advanced music loader, a new unified artist model, and a new selection system that makes enqueueing music much simpler.
|
||||||
|
This release fixes critical issues.
|
||||||
|
For more information, see https://github.com/OxygenCobalt/Auxio/releases/tag/v3.0.5.
|
Loading…
Reference in a new issue