From 20feba024e36e43d91d19b3f8e7619b90440bdc8 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Sat, 15 Apr 2023 13:03:00 -0600 Subject: [PATCH 1/5] Merge pull request #419 from chrispalmeri/sorting Updates to intelligent sorting --- .../java/org/oxycblt/auxio/music/Music.kt | 50 ++++++------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/music/Music.kt b/app/src/main/java/org/oxycblt/auxio/music/Music.kt index 6df980346..1ccde597b 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Music.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Music.kt @@ -363,13 +363,14 @@ interface Genre : MusicParent { * @author Alexander Capehart (OxygenCobalt) */ class SortName(name: String, musicSettings: MusicSettings) : Comparable { - private val number: Int? private val collationKey: CollationKey val thumbString: String? init { var sortName = name if (musicSettings.intelligentSorting) { + sortName = sortName.replace(leadingPunctuation, "") + sortName = sortName.run { when { @@ -380,53 +381,34 @@ class SortName(name: String, musicSettings: MusicSettings) : Comparable 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(consecutiveDigits) { 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 leadingPunctuation: Regex = Regex("""^\p{Punct}+""") + val consecutiveDigits: Regex = Regex("""\d+""") } } From 89d599ae4ec77087623696a05b29f8e31ea5bc18 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Sat, 25 Mar 2023 15:54:06 -0600 Subject: [PATCH 2/5] widget: fix inconsistent cover corner radius Somehow the line that makes the corner sizes consistent was lost. --- .../main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt index 6069beed8..8bc813c48 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt @@ -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 { From b031adabeb6e5bfb657d4fabd514023479a238f7 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Sat, 15 Apr 2023 17:59:29 -0600 Subject: [PATCH 3/5] music: correctly bubble exceptions Correctly bubble failures in the music loading process. Do it the easy way and simply map to a result, then backl to an exception. I need to actually just make it fully bubble event --- .../org/oxycblt/auxio/music/system/Indexer.kt | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt b/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt index 321724d2c..c032299ef 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt @@ -23,17 +23,10 @@ import android.content.Context import android.content.pm.PackageManager import android.os.Build import androidx.core.content.ContextCompat +import kotlinx.coroutines.* 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 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 @@ -44,6 +37,8 @@ import org.oxycblt.auxio.music.storage.MediaStoreExtractor import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logE import org.oxycblt.auxio.util.logW +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext /** * Core music loading state class. @@ -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(Channel.UNLIMITED) val incompleteSongs = Channel(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() @@ -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,21 @@ 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 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 From e6b00b10254da064fa6f31ea7f55684a0ea908c2 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Sat, 15 Apr 2023 18:39:46 -0600 Subject: [PATCH 4/5] build: bump to 3.0.5 Bump the version to 3.0.5. --- CHANGELOG.md | 8 +++++++ README.md | 4 ++-- app/build.gradle | 4 ++-- .../java/org/oxycblt/auxio/music/Music.kt | 8 +++---- .../org/oxycblt/auxio/music/system/Indexer.kt | 24 +++++++++++-------- .../oxycblt/auxio/widgets/WidgetComponent.kt | 4 ++-- .../metadata/android/en-US/changelogs/29.txt | 3 +++ 7 files changed, 35 insertions(+), 20 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/29.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 0201041b3..fec2ba549 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## dev + +#### 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 diff --git a/README.md b/README.md index 373c5680d..ceb879047 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@

Auxio

A simple, rational music player for android.

- - Latest Version + + Latest Version Releases diff --git a/app/build.gradle b/app/build.gradle index 69e5a79a7..7ae1e5b25 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,8 +20,8 @@ android { defaultConfig { applicationId namespace - versionName "3.0.4" - versionCode 28 + versionName "3.0.5" + versionCode 29 minSdk 21 targetSdk 33 diff --git a/app/src/main/java/org/oxycblt/auxio/music/Music.kt b/app/src/main/java/org/oxycblt/auxio/music/Music.kt index 1ccde597b..baabe4ccf 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Music.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Music.kt @@ -369,7 +369,7 @@ class SortName(name: String, musicSettings: MusicSettings) : Comparable CoroutineScope.tryAsync(context: CoroutineContext = EmptyCoroutineContext, crossinline block: suspend () -> R) = async(context) { - try { - Result.success(block()) - } catch (e: Exception) { - Result.failure(e) + private inline fun 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 diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt index 8bc813c48..6c1e9e91b 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt @@ -98,8 +98,8 @@ constructor( builder .size(getSafeRemoteViewsImageSize(context, 10f)) .transformations( - SquareFrameTransform.INSTANCE, - RoundedCornersTransformation(cornerRadius.toFloat())) + SquareFrameTransform.INSTANCE, + RoundedCornersTransformation(cornerRadius.toFloat())) } else { builder.size(getSafeRemoteViewsImageSize(context)) } diff --git a/fastlane/metadata/android/en-US/changelogs/29.txt b/fastlane/metadata/android/en-US/changelogs/29.txt new file mode 100644 index 000000000..0eb6a89b9 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/29.txt @@ -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. \ No newline at end of file From d0f8291085355c70ad4c086c88b70efb9f1c2629 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Sun, 16 Apr 2023 17:55:02 -0600 Subject: [PATCH 5/5] music: fix incorrect sort punct removal Fix incorrect punctuation removal resulting in weird sorting behavior. --- CHANGELOG.md | 2 +- app/src/main/java/org/oxycblt/auxio/music/Music.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fec2ba549..937eb640e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## dev +## 3.0.5 #### What's Fixed - Fixed inconsistent corner radius on widget diff --git a/app/src/main/java/org/oxycblt/auxio/music/Music.kt b/app/src/main/java/org/oxycblt/auxio/music/Music.kt index baabe4ccf..abeb521ed 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Music.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Music.kt @@ -407,7 +407,7 @@ class SortName(name: String, musicSettings: MusicSettings) : Comparable