Merge pull request #425 from OxygenCobalt/3.0.5-cherrypick

Version 3.0.5
This commit is contained in:
Alexander Capehart 2023-04-17 00:03:14 +00:00 committed by GitHub
commit 78d9f9f613
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 60 additions and 57 deletions

View file

@ -1,5 +1,13 @@
# 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
#### What's New

View file

@ -2,8 +2,8 @@
<h1 align="center"><b>Auxio</b></h1>
<h4 align="center">A simple, rational music player for android.</h4>
<p align="center">
<a href="https://github.com/oxygencobalt/Auxio/releases/tag/v3.0.4">
<img alt="Latest Version" src="https://img.shields.io/static/v1?label=tag&message=v3.0.4&color=64B5F6&style=flat">
<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.5&color=64B5F6&style=flat">
</a>
<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">

View file

@ -20,8 +20,8 @@ android {
defaultConfig {
applicationId namespace
versionName "3.0.4"
versionCode 28
versionName "3.0.5"
versionCode 29
minSdk 21
targetSdk 33

View file

@ -363,13 +363,14 @@ interface Genre : MusicParent {
* @author Alexander Capehart (OxygenCobalt)
*/
class SortName(name: String, musicSettings: MusicSettings) : Comparable<SortName> {
private val number: Int?
private val collationKey: CollationKey
val thumbString: String?
init {
var sortName = name
if (musicSettings.intelligentSorting) {
sortName = sortName.replace(LEADING_PUNCTUATION_REGEX, "")
sortName =
sortName.run {
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.
when (val numericEnd = sortName.indexOfFirst { !it.isDigit() }) {
// 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
// Zero pad all numbers to six digits for better sorting
sortName = sortName.replace(CONSECUTIVE_DIGITS_REGEX) { it.value.padStart(6, '0') }
}
collationKey = COLLATOR.getCollationKey(sortName)
// Keep track of a string to use in the thumb view.
// Simply show '#' for everything before 'A'
// 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) =
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 compareTo(other: SortName) = collationKey.compareTo(other.collationKey)
override fun equals(other: Any?) =
other is SortName && number == other.number && collationKey == other.collationKey
override fun equals(other: Any?) = other is SortName && collationKey == other.collationKey
override fun hashCode(): Int {
var hashCode = collationKey.hashCode()
if (number != null) hashCode = 31 * hashCode + number
return hashCode
}
override fun hashCode(): Int = collationKey.hashCode()
private companion object {
val COLLATOR: Collator = Collator.getInstance().apply { strength = Collator.PRIMARY }
val LEADING_PUNCTUATION_REGEX = Regex("[\\p{Punct}+]")
val CONSECUTIVE_DIGITS_REGEX = Regex("\\d+")
}
}

View file

@ -25,15 +25,10 @@ import android.os.Build
import androidx.core.content.ContextCompat
import java.util.LinkedList
import javax.inject.Inject
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.*
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.music.*
import org.oxycblt.auxio.music.cache.CacheRepository
@ -351,14 +346,15 @@ constructor(
// Do the initial query of the cache and media databases in parallel.
logD("Starting queries")
val mediaStoreQueryJob = scope.async { mediaStoreExtractor.query() }
val mediaStoreQueryJob = scope.tryAsync { mediaStoreExtractor.query() }
val cache =
if (withCache) {
cacheRepository.readCache()
} else {
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
// 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 incompleteSongs = Channel<RawSong>(Channel.UNLIMITED)
val mediaStoreJob =
scope.async {
scope.tryAsync {
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.
val rawSongs = LinkedList<RawSong>()
@ -379,8 +375,8 @@ constructor(
emitIndexing(Indexer.Indexing.Songs(rawSongs.size, query.projectedTotal))
}
// These should be no-ops
mediaStoreJob.await()
metadataJob.await()
mediaStoreJob.await().getOrThrow()
metadataJob.await().getOrThrow()
if (rawSongs.isEmpty()) {
logE("Music library was empty")
@ -391,13 +387,25 @@ constructor(
// parallel.
logD("Discovered ${rawSongs.size} songs, starting finalization")
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) {
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)
}
}
/**
* Emit a new [Indexer.State.Indexing] state. This can be used to signal the current state of
* the music loading process to external code. Assumes that the callee has already checked if

View file

@ -95,9 +95,11 @@ constructor(
return if (cornerRadius > 0) {
// If rounded, reduce the bitmap size further to obtain more pronounced
// rounded corners.
builder.transformations(
SquareFrameTransform.INSTANCE,
RoundedCornersTransformation(cornerRadius.toFloat()))
builder
.size(getSafeRemoteViewsImageSize(context, 10f))
.transformations(
SquareFrameTransform.INSTANCE,
RoundedCornersTransformation(cornerRadius.toFloat()))
} else {
builder.size(getSafeRemoteViewsImageSize(context))
}

View 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.