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
|
||||
|
||||
## 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
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -20,8 +20,8 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
applicationId namespace
|
||||
versionName "3.0.4"
|
||||
versionCode 28
|
||||
versionName "3.0.5"
|
||||
versionCode 29
|
||||
|
||||
minSdk 21
|
||||
targetSdk 33
|
||||
|
|
|
@ -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+")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,11 +387,23 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -95,7 +95,9 @@ constructor(
|
|||
return if (cornerRadius > 0) {
|
||||
// If rounded, reduce the bitmap size further to obtain more pronounced
|
||||
// rounded corners.
|
||||
builder.transformations(
|
||||
builder
|
||||
.size(getSafeRemoteViewsImageSize(context, 10f))
|
||||
.transformations(
|
||||
SquareFrameTransform.INSTANCE,
|
||||
RoundedCornersTransformation(cornerRadius.toFloat()))
|
||||
} 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