From 7a00c3c6aa9b62f639f1958b56fbd5199437805f Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Wed, 14 Aug 2024 18:46:52 -0600 Subject: [PATCH 01/19] music: parse singular spaced artist tags On ID3 and Vorbis. --- .../oxycblt/auxio/music/metadata/TagWorker.kt | 68 ++++++++++--------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/music/metadata/TagWorker.kt b/app/src/main/java/org/oxycblt/auxio/music/metadata/TagWorker.kt index d30e5324e..7db8ef662 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/metadata/TagWorker.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/metadata/TagWorker.kt @@ -153,8 +153,9 @@ private class TagWorkerImpl( private fun populateWithId3v2(textFrames: Map>) { // Song + logD(textFrames) (textFrames["TXXX:musicbrainz release track id"] - ?: textFrames["TXXX:musicbrainz_releasetrackid"]) + ?: textFrames["TXXX:musicbrainz_releasetrackid"]) ?.let { rawSong.musicBrainzId = it.first() } textFrames["TIT2"]?.let { rawSong.name = it.first() } textFrames["TSOT"]?.let { rawSong.sortName = it.first() } @@ -179,9 +180,9 @@ private class TagWorkerImpl( // TODO: Handle dates that are in "January" because the actual specific release date // isn't known? (textFrames["TDOR"]?.run { Date.from(first()) } - ?: textFrames["TDRC"]?.run { Date.from(first()) } - ?: textFrames["TDRL"]?.run { Date.from(first()) } - ?: parseId3v23Date(textFrames)) + ?: textFrames["TDRC"]?.run { Date.from(first()) } + ?: textFrames["TDRL"]?.run { Date.from(first()) } + ?: parseId3v23Date(textFrames)) ?.let { rawSong.date = it } // Album @@ -191,34 +192,36 @@ private class TagWorkerImpl( textFrames["TALB"]?.let { rawSong.albumName = it.first() } textFrames["TSOA"]?.let { rawSong.albumSortName = it.first() } (textFrames["TXXX:musicbrainz album type"] - ?: textFrames["TXXX:releasetype"] ?: - // This is a non-standard iTunes extension - textFrames["GRP1"]) + ?: textFrames["TXXX:releasetype"] ?: + // This is a non-standard iTunes extension + textFrames["GRP1"]) ?.let { rawSong.releaseTypes = it } // Artist (textFrames["TXXX:musicbrainz artist id"] ?: textFrames["TXXX:musicbrainz_artistid"])?.let { rawSong.artistMusicBrainzIds = it } - (textFrames["TXXX:artists"] ?: textFrames["TPE1"])?.let { rawSong.artistNames = it } + (textFrames["TXXX:artists"] ?: textFrames["TPE1"] + ?: textFrames["TXXX:artist"])?.let { rawSong.artistNames = it } (textFrames["TXXX:artistssort"] - ?: textFrames["TXXX:artists_sort"] ?: textFrames["TXXX:artists sort"] - ?: textFrames["TSOP"]) + ?: textFrames["TXXX:artists_sort"] ?: textFrames["TXXX:artists sort"] + ?: textFrames["TSOP"] ?: textFrames["artistsort"] ?: textFrames["TXXX:artist sort"]) ?.let { rawSong.artistSortNames = it } // Album artist (textFrames["TXXX:musicbrainz album artist id"] - ?: textFrames["TXXX:musicbrainz_albumartistid"]) + ?: textFrames["TXXX:musicbrainz_albumartistid"]) ?.let { rawSong.albumArtistMusicBrainzIds = it } (textFrames["TXXX:albumartists"] - ?: textFrames["TXXX:album_artists"] ?: textFrames["TXXX:album artists"] - ?: textFrames["TPE2"]) + ?: textFrames["TXXX:album_artists"] ?: textFrames["TXXX:album artists"] + ?: textFrames["TPE2"] ?: textFrames["TXXX:albumartist"] + ?: textFrames["TXXX:album artist"]) ?.let { rawSong.albumArtistNames = it } (textFrames["TXXX:albumartistssort"] - ?: textFrames["TXXX:albumartists_sort"] ?: textFrames["TXXX:albumartists sort"] - ?: textFrames["TXXX:albumartistsort"] - // This is a non-standard iTunes extension - ?: textFrames["TSO2"]) + ?: textFrames["TXXX:albumartists_sort"] ?: textFrames["TXXX:albumartists sort"] + ?: textFrames["TXXX:albumartistsort"] + // This is a non-standard iTunes extension + ?: textFrames["TSO2"] ?: textFrames["TXXX:album artist sort"]) ?.let { rawSong.albumArtistSortNames = it } // Genre @@ -226,7 +229,7 @@ private class TagWorkerImpl( // Compilation Flag (textFrames["TCMP"] // This is a non-standard itunes extension - ?: textFrames["TXXX:compilation"] ?: textFrames["TXXX:itunescompilation"]) + ?: textFrames["TXXX:compilation"] ?: textFrames["TXXX:itunescompilation"]) ?.let { // Ignore invalid instances of this tag if (it.size != 1 || it[0] != "1") return@let @@ -288,14 +291,16 @@ private class TagWorkerImpl( // Track. parseVorbisPositionField( - comments["tracknumber"]?.first(), - (comments["totaltracks"] ?: comments["tracktotal"] ?: comments["trackc"])?.first()) + comments["tracknumber"]?.first(), + (comments["totaltracks"] ?: comments["tracktotal"] ?: comments["trackc"])?.first() + ) ?.let { rawSong.track = it } // Disc and it's subtitle name. parseVorbisPositionField( - comments["discnumber"]?.first(), - (comments["totaldiscs"] ?: comments["disctotal"] ?: comments["discc"])?.first()) + comments["discnumber"]?.first(), + (comments["totaldiscs"] ?: comments["disctotal"] ?: comments["discc"])?.first() + ) ?.let { rawSong.disc = it } comments["discsubtitle"]?.let { rawSong.subtitle = it.first() } @@ -306,8 +311,8 @@ private class TagWorkerImpl( // 3. Year, as old vorbis tags tended to use this (I know this because it's the only // date tag that android supports, so it must be 15 years old or more!) (comments["originaldate"]?.run { Date.from(first()) } - ?: comments["date"]?.run { Date.from(first()) } - ?: comments["year"]?.run { Date.from(first()) }) + ?: comments["date"]?.run { Date.from(first()) } + ?: comments["year"]?.run { Date.from(first()) }) ?.let { rawSong.date = it } // Album @@ -326,7 +331,8 @@ private class TagWorkerImpl( } (comments["artists"] ?: comments["artist"])?.let { rawSong.artistNames = it } (comments["artistssort"] - ?: comments["artists_sort"] ?: comments["artists sort"] ?: comments["artistsort"]) + ?: comments["artists_sort"] ?: comments["artists sort"] ?: comments["artistsort"] + ?: comments["artist sort"]) ?.let { rawSong.artistSortNames = it } // Album artist @@ -334,12 +340,12 @@ private class TagWorkerImpl( rawSong.albumArtistMusicBrainzIds = it } (comments["albumartists"] - ?: comments["album_artists"] ?: comments["album artists"] - ?: comments["albumartist"]) + ?: comments["album_artists"] ?: comments["album artists"] + ?: comments["albumartist"] ?: comments["album artist"]) ?.let { rawSong.albumArtistNames = it } (comments["albumartistssort"] - ?: comments["albumartists_sort"] ?: comments["albumartists sort"] - ?: comments["albumartistsort"]) + ?: comments["albumartists_sort"] ?: comments["albumartists sort"] + ?: comments["albumartistsort"] ?: comments["album artist sort"]) ?.let { rawSong.albumArtistSortNames = it } // Genre @@ -363,10 +369,10 @@ private class TagWorkerImpl( // normally the only tag used for opus files, but some software still writes replay gain // tags anyway. (comments["r128_track_gain"]?.parseR128Adjustment() - ?: comments["replaygain_track_gain"]?.parseReplayGainAdjustment()) + ?: comments["replaygain_track_gain"]?.parseReplayGainAdjustment()) ?.let { rawSong.replayGainTrackAdjustment = it } (comments["r128_album_gain"]?.parseR128Adjustment() - ?: comments["replaygain_album_gain"]?.parseReplayGainAdjustment()) + ?: comments["replaygain_album_gain"]?.parseReplayGainAdjustment()) ?.let { rawSong.replayGainAlbumAdjustment = it } } From d10f84efa870768253d616e4f283aa7ee33d6e21 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Wed, 7 Aug 2024 21:23:31 -0600 Subject: [PATCH 02/19] widgets: move size fixing into a transform --- .../extractor/RoundedRectTransformation.kt | 5 +- .../widgets/WidgetBitmapTransformation.kt | 56 +++++++++++++++++++ .../oxycblt/auxio/widgets/WidgetComponent.kt | 24 ++++---- .../org/oxycblt/auxio/widgets/WidgetUtil.kt | 19 ------- 4 files changed, 70 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/org/oxycblt/auxio/widgets/WidgetBitmapTransformation.kt diff --git a/app/src/main/java/org/oxycblt/auxio/image/extractor/RoundedRectTransformation.kt b/app/src/main/java/org/oxycblt/auxio/image/extractor/RoundedRectTransformation.kt index c8d3ee145..0d32d20de 100644 --- a/app/src/main/java/org/oxycblt/auxio/image/extractor/RoundedRectTransformation.kt +++ b/app/src/main/java/org/oxycblt/auxio/image/extractor/RoundedRectTransformation.kt @@ -107,7 +107,10 @@ class RoundedRectTransformation( } private fun calculateOutputSize(input: Bitmap, size: Size): Pair { - // MODIFICATION: Remove short-circuiting for original size and input size + if (size == Size.ORIGINAL) { + // This path only runs w/the widget code, which already normalizes widget sizes + return input.width to input.height + } val multiplier = DecodeUtils.computeSizeMultiplier( srcWidth = input.width, diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetBitmapTransformation.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetBitmapTransformation.kt new file mode 100644 index 000000000..ac44e418d --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetBitmapTransformation.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 Auxio Project + * WidgetBitmapTransformation.kt is part of Auxio. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.oxycblt.auxio.widgets + +import android.content.res.Resources +import android.graphics.Bitmap +import coil.size.Size +import coil.transform.Transformation +import kotlin.math.sqrt + +class WidgetBitmapTransformation(private val reduce: Float) : Transformation { + private val metrics = Resources.getSystem().displayMetrics + private val sw = metrics.widthPixels + private val sh = metrics.heightPixels + // Cap memory usage at 1.5 times the size of the display + // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h + // https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java + // Of course since OEMs randomly patch this check, we give a lot of slack. + private val maxBitmapArea = (1.5 * sw * sh / reduce).toInt() + + override val cacheKey: String + get() = "WidgetBitmapTransformation:${maxBitmapArea}" + + override suspend fun transform(input: Bitmap, size: Size): Bitmap { + if (size !== Size.ORIGINAL) { + // The widget loading stack basically discards the size parameter since there's no + // sane value from the get-go, all this transform does is actually dynamically apply + // the size cap so this transform must always be zero. + throw IllegalArgumentException("WidgetBitmapTransformation requires original size.") + } + val inputArea = input.width * input.height + if (inputArea != maxBitmapArea) { + val scale = sqrt(maxBitmapArea / inputArea.toDouble()) + val newWidth = (input.width * scale).toInt() + val newHeight = (input.height * scale).toInt() + return Bitmap.createScaledBitmap(input, newWidth, newHeight, true) + } + return input + } +} 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 09ac5e8f1..8631845a5 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt @@ -22,6 +22,7 @@ import android.content.Context import android.graphics.Bitmap import android.os.Build import coil.request.ImageRequest +import coil.size.Size import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import org.oxycblt.auxio.R @@ -96,24 +97,19 @@ constructor( 0 } - return if (cornerRadius > 0) { - // If rounded, reduce the bitmap size further to obtain more pronounced - // rounded corners. - builder.size(getSafeRemoteViewsImageSize(context, 10f)) - val cornersTransformation = - RoundedRectTransformation(cornerRadius.toFloat()) + val transformations = buildList { if (imageSettings.forceSquareCovers) { - builder.transformations( - SquareCropTransformation.INSTANCE, cornersTransformation) + add(SquareCropTransformation.INSTANCE) + } + if (cornerRadius > 0) { + add(WidgetBitmapTransformation(10f)) + add(RoundedRectTransformation(cornerRadius.toFloat())) } else { - builder.transformations(cornersTransformation) + add(WidgetBitmapTransformation(2f)) } - } else { - if (imageSettings.forceSquareCovers) { - builder.transformations(SquareCropTransformation.INSTANCE) - } - builder.size(getSafeRemoteViewsImageSize(context)) } + + return builder.size(Size.ORIGINAL).transformations(transformations) } override fun onCompleted(bitmap: Bitmap?) { diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetUtil.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetUtil.kt index 799aa8a67..953af14c8 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetUtil.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetUtil.kt @@ -27,7 +27,6 @@ import android.widget.RemoteViews import androidx.annotation.DrawableRes import androidx.annotation.IdRes import androidx.annotation.LayoutRes -import kotlin.math.sqrt import org.oxycblt.auxio.util.isLandscape import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.newMainPendingIntent @@ -46,24 +45,6 @@ fun newRemoteViews(context: Context, @LayoutRes layoutRes: Int): RemoteViews { return views } -/** - * Get an image size guaranteed to not exceed the [RemoteViews] bitmap memory limit, assuming that - * there is only one image. - * - * @param context [Context] required to perform calculation. - * @param reduce Optional multiplier to reduce the image size. Recommended value is 2 to avoid - * device-specific variations in memory limit. - * @return The dimension of a bitmap that can be safely used in [RemoteViews]. - */ -fun getSafeRemoteViewsImageSize(context: Context, reduce: Float = 2f): Int { - val metrics = context.resources.displayMetrics - val sw = metrics.widthPixels - val sh = metrics.heightPixels - // Maximum size is 1/3 total screen area * 4 bytes per pixel. Reverse - // that to obtain the image size. - return sqrt((6f / 4f / reduce) * sw * sh).toInt() -} - /** * Set the background resource of a [RemoteViews] View. * From ba46895ad182d582545696dc3f8e46f7788fc3d7 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Thu, 8 Aug 2024 19:10:37 -0600 Subject: [PATCH 03/19] widget: increase bitmap reduction --- .../main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 8631845a5..90f2add86 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt @@ -102,10 +102,10 @@ constructor( add(SquareCropTransformation.INSTANCE) } if (cornerRadius > 0) { - add(WidgetBitmapTransformation(10f)) + add(WidgetBitmapTransformation(20f)) add(RoundedRectTransformation(cornerRadius.toFloat())) } else { - add(WidgetBitmapTransformation(2f)) + add(WidgetBitmapTransformation(4f)) } } From 67e51ab54c1d6de8f41f8afa9ed66e88d5aa2483 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Fri, 9 Aug 2024 21:27:26 -0600 Subject: [PATCH 04/19] widgets: decrease bitmap reduction --- .../main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 90f2add86..1e95eb6f4 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt @@ -102,10 +102,10 @@ constructor( add(SquareCropTransformation.INSTANCE) } if (cornerRadius > 0) { - add(WidgetBitmapTransformation(20f)) + add(WidgetBitmapTransformation(15f)) add(RoundedRectTransformation(cornerRadius.toFloat())) } else { - add(WidgetBitmapTransformation(4f)) + add(WidgetBitmapTransformation(3f)) } } From dad0d75d977bdbb4c4d4702ca1b5ea795faa4e44 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Wed, 14 Aug 2024 18:53:04 -0600 Subject: [PATCH 05/19] music: avoid foreground crash from early loading --- app/src/main/java/org/oxycblt/auxio/AuxioService.kt | 7 ++++--- .../main/java/org/oxycblt/auxio/music/MusicRepository.kt | 3 --- .../oxycblt/auxio/music/service/IndexerServiceFragment.kt | 6 ++++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/AuxioService.kt b/app/src/main/java/org/oxycblt/auxio/AuxioService.kt index 64121e6d1..6dc66bd76 100644 --- a/app/src/main/java/org/oxycblt/auxio/AuxioService.kt +++ b/app/src/main/java/org/oxycblt/auxio/AuxioService.kt @@ -43,19 +43,20 @@ class AuxioService : MediaLibraryService(), ForegroundListener { } override fun onBind(intent: Intent?): IBinder? { - handleIntent(intent) + onHandleForeground(intent) return super.onBind(intent) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // TODO: Start command occurring from a foreign service basically implies a detached // service, we might need more handling here. - handleIntent(intent) + onHandleForeground(intent) return super.onStartCommand(intent, flags, startId) } - private fun handleIntent(intent: Intent?) { + private fun onHandleForeground(intent: Intent?) { val nativeStart = intent?.getBooleanExtra(INTENT_KEY_NATIVE_START, false) ?: false + indexingFragment.start() if (!nativeStart) { // Some foreign code started us, no guarantees about foreground stability. Figure // out what to do. diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt index b8a3d2b26..7d60f825d 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt @@ -280,9 +280,6 @@ constructor( } logD("Registering worker $worker") indexingWorker = worker - if (indexingState == null) { - worker.requestIndex(true) - } } @Synchronized diff --git a/app/src/main/java/org/oxycblt/auxio/music/service/IndexerServiceFragment.kt b/app/src/main/java/org/oxycblt/auxio/music/service/IndexerServiceFragment.kt index 6362def6b..571e96ca7 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/service/IndexerServiceFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/service/IndexerServiceFragment.kt @@ -79,6 +79,12 @@ constructor( foregroundListener = null } + fun start() { + if (musicRepository.indexingState == null) { + requestIndex(true) + } + } + fun createNotification(post: (IndexerNotification?) -> Unit) { val state = musicRepository.indexingState if (state is IndexingState.Indexing) { From 5c779f6d8929bd32ba8fd7e1307f52e43edfa41b Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Wed, 14 Aug 2024 18:58:52 -0600 Subject: [PATCH 06/19] info: update changelog --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 906d311b5..d953459b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 3.5.3 + +#### What's Improved +- Added support for informal singular-spaced tags like `album artist` in +file metadata + +#### What's Fixed +- Fix "Foregroud not allowed" music loading crash from starting too early +- Fixed widget not loading on some devices due to the cover being too large + ## 3.5.2 #### What's Fixed From aa140bebaa344c064a219d99e460f570b6a147f9 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Wed, 14 Aug 2024 18:58:59 -0600 Subject: [PATCH 07/19] all: reformat --- .../java/org/oxycblt/auxio/music/info/Name.kt | 7 +- .../oxycblt/auxio/music/metadata/TagWorker.kt | 72 +++++++++---------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/music/info/Name.kt b/app/src/main/java/org/oxycblt/auxio/music/info/Name.kt index 3f5662b8d..30f4564c8 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/info/Name.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/info/Name.kt @@ -70,9 +70,10 @@ sealed interface Name : Comparable { final override fun compareTo(other: Name) = when (other) { is Known -> { - val result = sortTokens.zip(other.sortTokens).fold(0) { acc, (token, otherToken) -> - acc.takeIf { it != 0 } ?: token.compareTo(otherToken) - } + val result = + sortTokens.zip(other.sortTokens).fold(0) { acc, (token, otherToken) -> + acc.takeIf { it != 0 } ?: token.compareTo(otherToken) + } if (result != 0) result else sortTokens.size.compareTo(other.sortTokens.size) } is Unknown -> 1 diff --git a/app/src/main/java/org/oxycblt/auxio/music/metadata/TagWorker.kt b/app/src/main/java/org/oxycblt/auxio/music/metadata/TagWorker.kt index 7db8ef662..b0e23b0a2 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/metadata/TagWorker.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/metadata/TagWorker.kt @@ -155,7 +155,7 @@ private class TagWorkerImpl( // Song logD(textFrames) (textFrames["TXXX:musicbrainz release track id"] - ?: textFrames["TXXX:musicbrainz_releasetrackid"]) + ?: textFrames["TXXX:musicbrainz_releasetrackid"]) ?.let { rawSong.musicBrainzId = it.first() } textFrames["TIT2"]?.let { rawSong.name = it.first() } textFrames["TSOT"]?.let { rawSong.sortName = it.first() } @@ -180,9 +180,9 @@ private class TagWorkerImpl( // TODO: Handle dates that are in "January" because the actual specific release date // isn't known? (textFrames["TDOR"]?.run { Date.from(first()) } - ?: textFrames["TDRC"]?.run { Date.from(first()) } - ?: textFrames["TDRL"]?.run { Date.from(first()) } - ?: parseId3v23Date(textFrames)) + ?: textFrames["TDRC"]?.run { Date.from(first()) } + ?: textFrames["TDRL"]?.run { Date.from(first()) } + ?: parseId3v23Date(textFrames)) ?.let { rawSong.date = it } // Album @@ -192,36 +192,38 @@ private class TagWorkerImpl( textFrames["TALB"]?.let { rawSong.albumName = it.first() } textFrames["TSOA"]?.let { rawSong.albumSortName = it.first() } (textFrames["TXXX:musicbrainz album type"] - ?: textFrames["TXXX:releasetype"] ?: - // This is a non-standard iTunes extension - textFrames["GRP1"]) + ?: textFrames["TXXX:releasetype"] ?: + // This is a non-standard iTunes extension + textFrames["GRP1"]) ?.let { rawSong.releaseTypes = it } // Artist (textFrames["TXXX:musicbrainz artist id"] ?: textFrames["TXXX:musicbrainz_artistid"])?.let { rawSong.artistMusicBrainzIds = it } - (textFrames["TXXX:artists"] ?: textFrames["TPE1"] - ?: textFrames["TXXX:artist"])?.let { rawSong.artistNames = it } + (textFrames["TXXX:artists"] ?: textFrames["TPE1"] ?: textFrames["TXXX:artist"])?.let { + rawSong.artistNames = it + } (textFrames["TXXX:artistssort"] - ?: textFrames["TXXX:artists_sort"] ?: textFrames["TXXX:artists sort"] - ?: textFrames["TSOP"] ?: textFrames["artistsort"] ?: textFrames["TXXX:artist sort"]) + ?: textFrames["TXXX:artists_sort"] ?: textFrames["TXXX:artists sort"] + ?: textFrames["TSOP"] ?: textFrames["artistsort"] + ?: textFrames["TXXX:artist sort"]) ?.let { rawSong.artistSortNames = it } // Album artist (textFrames["TXXX:musicbrainz album artist id"] - ?: textFrames["TXXX:musicbrainz_albumartistid"]) + ?: textFrames["TXXX:musicbrainz_albumartistid"]) ?.let { rawSong.albumArtistMusicBrainzIds = it } (textFrames["TXXX:albumartists"] - ?: textFrames["TXXX:album_artists"] ?: textFrames["TXXX:album artists"] - ?: textFrames["TPE2"] ?: textFrames["TXXX:albumartist"] - ?: textFrames["TXXX:album artist"]) + ?: textFrames["TXXX:album_artists"] ?: textFrames["TXXX:album artists"] + ?: textFrames["TPE2"] ?: textFrames["TXXX:albumartist"] + ?: textFrames["TXXX:album artist"]) ?.let { rawSong.albumArtistNames = it } (textFrames["TXXX:albumartistssort"] - ?: textFrames["TXXX:albumartists_sort"] ?: textFrames["TXXX:albumartists sort"] - ?: textFrames["TXXX:albumartistsort"] - // This is a non-standard iTunes extension - ?: textFrames["TSO2"] ?: textFrames["TXXX:album artist sort"]) + ?: textFrames["TXXX:albumartists_sort"] ?: textFrames["TXXX:albumartists sort"] + ?: textFrames["TXXX:albumartistsort"] + // This is a non-standard iTunes extension + ?: textFrames["TSO2"] ?: textFrames["TXXX:album artist sort"]) ?.let { rawSong.albumArtistSortNames = it } // Genre @@ -229,7 +231,7 @@ private class TagWorkerImpl( // Compilation Flag (textFrames["TCMP"] // This is a non-standard itunes extension - ?: textFrames["TXXX:compilation"] ?: textFrames["TXXX:itunescompilation"]) + ?: textFrames["TXXX:compilation"] ?: textFrames["TXXX:itunescompilation"]) ?.let { // Ignore invalid instances of this tag if (it.size != 1 || it[0] != "1") return@let @@ -291,16 +293,14 @@ private class TagWorkerImpl( // Track. parseVorbisPositionField( - comments["tracknumber"]?.first(), - (comments["totaltracks"] ?: comments["tracktotal"] ?: comments["trackc"])?.first() - ) + comments["tracknumber"]?.first(), + (comments["totaltracks"] ?: comments["tracktotal"] ?: comments["trackc"])?.first()) ?.let { rawSong.track = it } // Disc and it's subtitle name. parseVorbisPositionField( - comments["discnumber"]?.first(), - (comments["totaldiscs"] ?: comments["disctotal"] ?: comments["discc"])?.first() - ) + comments["discnumber"]?.first(), + (comments["totaldiscs"] ?: comments["disctotal"] ?: comments["discc"])?.first()) ?.let { rawSong.disc = it } comments["discsubtitle"]?.let { rawSong.subtitle = it.first() } @@ -311,8 +311,8 @@ private class TagWorkerImpl( // 3. Year, as old vorbis tags tended to use this (I know this because it's the only // date tag that android supports, so it must be 15 years old or more!) (comments["originaldate"]?.run { Date.from(first()) } - ?: comments["date"]?.run { Date.from(first()) } - ?: comments["year"]?.run { Date.from(first()) }) + ?: comments["date"]?.run { Date.from(first()) } + ?: comments["year"]?.run { Date.from(first()) }) ?.let { rawSong.date = it } // Album @@ -331,8 +331,8 @@ private class TagWorkerImpl( } (comments["artists"] ?: comments["artist"])?.let { rawSong.artistNames = it } (comments["artistssort"] - ?: comments["artists_sort"] ?: comments["artists sort"] ?: comments["artistsort"] - ?: comments["artist sort"]) + ?: comments["artists_sort"] ?: comments["artists sort"] ?: comments["artistsort"] + ?: comments["artist sort"]) ?.let { rawSong.artistSortNames = it } // Album artist @@ -340,12 +340,12 @@ private class TagWorkerImpl( rawSong.albumArtistMusicBrainzIds = it } (comments["albumartists"] - ?: comments["album_artists"] ?: comments["album artists"] - ?: comments["albumartist"] ?: comments["album artist"]) + ?: comments["album_artists"] ?: comments["album artists"] ?: comments["albumartist"] + ?: comments["album artist"]) ?.let { rawSong.albumArtistNames = it } (comments["albumartistssort"] - ?: comments["albumartists_sort"] ?: comments["albumartists sort"] - ?: comments["albumartistsort"] ?: comments["album artist sort"]) + ?: comments["albumartists_sort"] ?: comments["albumartists sort"] + ?: comments["albumartistsort"] ?: comments["album artist sort"]) ?.let { rawSong.albumArtistSortNames = it } // Genre @@ -369,10 +369,10 @@ private class TagWorkerImpl( // normally the only tag used for opus files, but some software still writes replay gain // tags anyway. (comments["r128_track_gain"]?.parseR128Adjustment() - ?: comments["replaygain_track_gain"]?.parseReplayGainAdjustment()) + ?: comments["replaygain_track_gain"]?.parseReplayGainAdjustment()) ?.let { rawSong.replayGainTrackAdjustment = it } (comments["r128_album_gain"]?.parseR128Adjustment() - ?: comments["replaygain_album_gain"]?.parseReplayGainAdjustment()) + ?: comments["replaygain_album_gain"]?.parseReplayGainAdjustment()) ?.let { rawSong.replayGainAlbumAdjustment = it } } From 3fa5628a1efb5443afc37c8a46b79babaf25ee37 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Sat, 17 Aug 2024 18:10:55 -0600 Subject: [PATCH 08/19] playback: introduce foreground-safe restores - Allow DeferredPlayback.RestoreState to force-start playback - Allow DeferredPlayback.RestoreState to specify a fallback action guaranteed to succeed --- .../auxio/playback/service/ExoPlaybackStateHolder.kt | 12 ++++++++++-- .../auxio/playback/state/PlaybackStateHolder.kt | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/service/ExoPlaybackStateHolder.kt b/app/src/main/java/org/oxycblt/auxio/playback/service/ExoPlaybackStateHolder.kt index 058b33403..804470a35 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/service/ExoPlaybackStateHolder.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/service/ExoPlaybackStateHolder.kt @@ -163,10 +163,18 @@ class ExoPlaybackStateHolder( is DeferredPlayback.RestoreState -> { logD("Restoring playback state") restoreScope.launch { - persistenceRepository.readState()?.let { + val state = persistenceRepository.readState() + if (state != null) { // Apply the saved state on the main thread to prevent code expecting // state updates on the main thread from crashing. - withContext(Dispatchers.Main) { playbackManager.applySavedState(it, false) } + withContext(Dispatchers.Main) { + playbackManager.applySavedState(state, false) + if (action.play) { + playbackManager.playing(true) + } + } + } else if (action.fallback != null) { + playbackManager.playDeferred(action.fallback) } } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt index 857ac6898..471a6498c 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt @@ -276,7 +276,7 @@ data class QueueChange(val type: Type, val instructions: UpdateInstructions) { /** Possible long-running background tasks handled by the background playback task. */ sealed interface DeferredPlayback { /** Restore the previously saved playback state. */ - data object RestoreState : DeferredPlayback + data class RestoreState(val play: Boolean, val fallback: DeferredPlayback? = null) : DeferredPlayback /** * Start shuffled playback of the entire music library. Analogous to the "Shuffle All" shortcut. From ea9c5d3c88c7e7a3d4b716af22f51618a0809486 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Sat, 17 Aug 2024 18:21:39 -0600 Subject: [PATCH 09/19] tasker: add start action Add a tasker action to start AuxioService in a HIGHLY limited ammner. Resolves #754. --- app/build.gradle | 3 + app/src/main/AndroidManifest.xml | 10 +++ .../java/org/oxycblt/auxio/AuxioService.kt | 16 +++-- .../java/org/oxycblt/auxio/IntegerTable.kt | 7 +- .../java/org/oxycblt/auxio/MainActivity.kt | 4 +- .../service/MediaSessionServiceFragment.kt | 16 ++++- .../playback/state/PlaybackStateHolder.kt | 3 +- .../java/org/oxycblt/auxio/tasker/Start.kt | 70 +++++++++++++++++++ 8 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/org/oxycblt/auxio/tasker/Start.kt diff --git a/app/build.gradle b/app/build.gradle index fd4bfacc9..15206897f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -151,6 +151,9 @@ dependencies { // Speed dial implementation "com.leinardi.android:speed-dial:3.3.0" + // Tasker integration + implementation 'com.joaomgcd:taskerpluginlibrary:0.4.10' + // Testing debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12' testImplementation "junit:junit:4.13.2" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 960d2c8ae..564220423 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -135,5 +135,15 @@ android:resource="@xml/widget_info" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/org/oxycblt/auxio/AuxioService.kt b/app/src/main/java/org/oxycblt/auxio/AuxioService.kt index 6dc66bd76..f0352d523 100644 --- a/app/src/main/java/org/oxycblt/auxio/AuxioService.kt +++ b/app/src/main/java/org/oxycblt/auxio/AuxioService.kt @@ -55,13 +55,9 @@ class AuxioService : MediaLibraryService(), ForegroundListener { } private fun onHandleForeground(intent: Intent?) { - val nativeStart = intent?.getBooleanExtra(INTENT_KEY_NATIVE_START, false) ?: false + val startId = intent?.getIntExtra(INTENT_KEY_START_ID, -1) ?: -1 indexingFragment.start() - if (!nativeStart) { - // Some foreign code started us, no guarantees about foreground stability. Figure - // out what to do. - mediaSessionFragment.handleNonNativeStart() - } + mediaSessionFragment.start(startId) } override fun onTaskRemoved(rootIntent: Intent?) { @@ -87,6 +83,7 @@ class AuxioService : MediaLibraryService(), ForegroundListener { if (change == ForegroundListener.Change.MEDIA_SESSION) { mediaSessionFragment.createNotification { startForeground(it.notificationId, it.notification) + isForeground = true } } // Nothing changed, but don't show anything music related since we can always @@ -95,16 +92,21 @@ class AuxioService : MediaLibraryService(), ForegroundListener { indexingFragment.createNotification { if (it != null) { startForeground(it.code, it.build()) + isForeground = true } else { ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) + isForeground = false } } } } companion object { + var isForeground = false + private set + // This is only meant for Auxio to internally ensure that it's state management will work. - const val INTENT_KEY_NATIVE_START = BuildConfig.APPLICATION_ID + ".service.NATIVE_START" + const val INTENT_KEY_START_ID = BuildConfig.APPLICATION_ID + ".service.START_ID" } } diff --git a/app/src/main/java/org/oxycblt/auxio/IntegerTable.kt b/app/src/main/java/org/oxycblt/auxio/IntegerTable.kt index 86f3d1984..eb1a27d53 100644 --- a/app/src/main/java/org/oxycblt/auxio/IntegerTable.kt +++ b/app/src/main/java/org/oxycblt/auxio/IntegerTable.kt @@ -59,6 +59,10 @@ object IntegerTable { const val INDEXER_NOTIFICATION_CODE = 0xA0A1 /** MainActivity Intent request code */ const val REQUEST_CODE = 0xA0C0 + /** Activity AuxioService Start ID */ + const val START_ID_ACTIVITY = 0xA050 + /** Tasker AuxioService Start ID */ + const val START_ID_TASKER = 0xA051 /** RepeatMode.NONE */ const val REPEAT_MODE_NONE = 0xA100 /** RepeatMode.ALL */ @@ -133,7 +137,4 @@ object IntegerTable { const val PLAY_SONG_FROM_PLAYLIST = 0xA123 /** PlaySong.ByItself */ const val PLAY_SONG_BY_ITSELF = 0xA124 - const val PLAYER_COMMAND_INC_REPEAT_MODE = 0xA125 - const val PLAYER_COMMAND_TOGGLE_SHUFFLE = 0xA126 - const val PLAYER_COMMAND_EXIT = 0xA127 } diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt index 42ad2a134..ab5474c9c 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt @@ -71,11 +71,11 @@ class MainActivity : AppCompatActivity() { startService( Intent(this, AuxioService::class.java) - .putExtra(AuxioService.INTENT_KEY_NATIVE_START, true)) + .putExtra(AuxioService.INTENT_KEY_START_ID, IntegerTable.START_ID_ACTIVITY)) if (!startIntentAction(intent)) { // No intent action to do, just restore the previously saved state. - playbackModel.playDeferred(DeferredPlayback.RestoreState) + playbackModel.playDeferred(DeferredPlayback.RestoreState(false)) } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/service/MediaSessionServiceFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/service/MediaSessionServiceFragment.kt index 69f2aab6d..ad5102477 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/service/MediaSessionServiceFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/service/MediaSessionServiceFragment.kt @@ -109,11 +109,23 @@ constructor( } } - fun handleNonNativeStart() { + fun start(startedBy: Int) { // At minimum we want to ensure an active playback state. // TODO: Possibly also force to go foreground? logD("Handling non-native start.") - playbackManager.playDeferred(DeferredPlayback.RestoreState) + val action = + when (startedBy) { + IntegerTable.START_ID_ACTIVITY -> null + IntegerTable.START_ID_TASKER -> + DeferredPlayback.RestoreState( + play = true, fallback = DeferredPlayback.ShuffleAll) + // External services using Auxio better know what they are doing. + else -> DeferredPlayback.RestoreState(play = false) + } + if (action != null) { + logD("Initing service fragment using action $action") + playbackManager.playDeferred(action) + } } fun hasNotification(): Boolean = exoHolder.sessionOngoing diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt index 471a6498c..c2c51f0ce 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt @@ -276,7 +276,8 @@ data class QueueChange(val type: Type, val instructions: UpdateInstructions) { /** Possible long-running background tasks handled by the background playback task. */ sealed interface DeferredPlayback { /** Restore the previously saved playback state. */ - data class RestoreState(val play: Boolean, val fallback: DeferredPlayback? = null) : DeferredPlayback + data class RestoreState(val play: Boolean, val fallback: DeferredPlayback? = null) : + DeferredPlayback /** * Start shuffled playback of the entire music library. Analogous to the "Shuffle All" shortcut. diff --git a/app/src/main/java/org/oxycblt/auxio/tasker/Start.kt b/app/src/main/java/org/oxycblt/auxio/tasker/Start.kt new file mode 100644 index 000000000..b146ec443 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/tasker/Start.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024 Auxio Project + * Start.kt is part of Auxio. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.oxycblt.auxio.tasker + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.core.content.ContextCompat +import com.joaomgcd.taskerpluginlibrary.action.TaskerPluginRunnerActionNoOutputOrInput +import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfig +import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfigHelperNoOutputOrInput +import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfigNoInput +import com.joaomgcd.taskerpluginlibrary.input.TaskerInput +import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResult +import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResultSucess +import org.oxycblt.auxio.AuxioService +import org.oxycblt.auxio.IntegerTable + +class StartActionHelper(config: TaskerPluginConfig) : + TaskerPluginConfigHelperNoOutputOrInput(config) { + override val runnerClass: Class + get() = StartActionRunner::class.java + + override fun addToStringBlurb(input: TaskerInput, blurbBuilder: StringBuilder) { + blurbBuilder.append( + "Starts Auxio using the previously saved state. If no saved state is available, all songs will be shuffled. Playback will start immediately. Be careful controlling this service, if you close it and then try to use it again, you will probably crash the app.") + } +} + +class ActivityConfigStartAction : Activity(), TaskerPluginConfigNoInput { + override val context + get() = applicationContext + + private val taskerHelper by lazy { StartActionHelper(this) } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + taskerHelper.finishForTasker() + } +} + +class StartActionRunner : TaskerPluginRunnerActionNoOutputOrInput() { + override fun run(context: Context, input: TaskerInput): TaskerPluginResult { + ContextCompat.startForegroundService( + context, + Intent(context, AuxioService::class.java) + .putExtra(AuxioService.INTENT_KEY_START_ID, IntegerTable.START_ID_TASKER)) + while (!AuxioService.isForeground) { + Thread.sleep(100) + } + return TaskerPluginResultSucess() + } +} From b8a652d6f2aab887c7a1c9cbb8871e95be3386d4 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Wed, 21 Aug 2024 13:57:17 -0600 Subject: [PATCH 10/19] tasker: fix activity --- app/src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 564220423..8cce5eacb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -137,7 +137,7 @@ From 27e378ae2a241afa0bd66da19ef90a4f7845fc4a Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Wed, 21 Aug 2024 13:57:40 -0600 Subject: [PATCH 11/19] tasker: give start action real name Instead of the template. --- app/src/main/AndroidManifest.xml | 2 +- app/src/main/res/values/strings.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8cce5eacb..cbf7ea304 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -140,7 +140,7 @@ android:name=".tasker.ActivityConfigStartAction" android:exported="true" android:icon="@mipmap/ic_launcher" - android:label="My Tasker Action"> + android:label="@string/lbl_start_playback"> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e4431dacd..a006fca33 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -153,6 +153,7 @@ Shuffle Shuffle all + Start playback OK Cancel From cc7f9ba5390466f1d937515636ce58f406de3be7 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Wed, 21 Aug 2024 13:58:01 -0600 Subject: [PATCH 12/19] tasker: fix player main thread bugs on restore --- .../auxio/playback/service/ExoPlaybackStateHolder.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/service/ExoPlaybackStateHolder.kt b/app/src/main/java/org/oxycblt/auxio/playback/service/ExoPlaybackStateHolder.kt index 804470a35..59ac16d95 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/service/ExoPlaybackStateHolder.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/service/ExoPlaybackStateHolder.kt @@ -164,17 +164,17 @@ class ExoPlaybackStateHolder( logD("Restoring playback state") restoreScope.launch { val state = persistenceRepository.readState() - if (state != null) { - // Apply the saved state on the main thread to prevent code expecting - // state updates on the main thread from crashing. - withContext(Dispatchers.Main) { + withContext(Dispatchers.Main) { + if (state != null) { + // Apply the saved state on the main thread to prevent code expecting + // state updates on the main thread from crashing. playbackManager.applySavedState(state, false) if (action.play) { playbackManager.playing(true) } + } else if (action.fallback != null) { + playbackManager.playDeferred(action.fallback) } - } else if (action.fallback != null) { - playbackManager.playDeferred(action.fallback) } } } From 2c976374f37195d5e927f4a5edfc71c417a858db Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Wed, 21 Aug 2024 13:58:19 -0600 Subject: [PATCH 13/19] tasker: use translated tasker action description --- app/src/main/java/org/oxycblt/auxio/tasker/Start.kt | 4 ++-- app/src/main/res/values/strings.xml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/tasker/Start.kt b/app/src/main/java/org/oxycblt/auxio/tasker/Start.kt index b146ec443..1dd5c8997 100644 --- a/app/src/main/java/org/oxycblt/auxio/tasker/Start.kt +++ b/app/src/main/java/org/oxycblt/auxio/tasker/Start.kt @@ -32,6 +32,7 @@ import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResult import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResultSucess import org.oxycblt.auxio.AuxioService import org.oxycblt.auxio.IntegerTable +import org.oxycblt.auxio.R class StartActionHelper(config: TaskerPluginConfig) : TaskerPluginConfigHelperNoOutputOrInput(config) { @@ -39,8 +40,7 @@ class StartActionHelper(config: TaskerPluginConfig) : get() = StartActionRunner::class.java override fun addToStringBlurb(input: TaskerInput, blurbBuilder: StringBuilder) { - blurbBuilder.append( - "Starts Auxio using the previously saved state. If no saved state is available, all songs will be shuffled. Playback will start immediately. Be careful controlling this service, if you close it and then try to use it again, you will probably crash the app.") + blurbBuilder.append(context.getString(R.string.lng_tasker_start)) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a006fca33..886e5ce55 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -213,6 +213,10 @@ Donate to the project to get your name added here! Search your library… + + Starts Auxio using the previously saved state. If no saved state is available, all songs will be shuffled. Playback will start immediately. + \n\nWARNING: Be careful controlling this service, if you close it and then try to use it again, you will probably crash the app. + From e1f75bb33770d61b9898977d588e21c002db9a41 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Thu, 22 Aug 2024 10:18:45 -0600 Subject: [PATCH 14/19] build: bump ndk to r26b --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 15206897f..38df4b67d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,7 +16,7 @@ android { // it here so that binary stripping will work. // TODO: Eventually you might just want to start vendoring the FFMpeg extension so the // NDK use is unified - ndkVersion = "25.2.9519653" + ndkVersion "26.1.10909125" namespace "org.oxycblt.auxio" defaultConfig { From 258dd9205cfe8f3715ef33d8ad1a3796139478fd Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Thu, 22 Aug 2024 10:59:24 -0600 Subject: [PATCH 15/19] build: bump media --- media | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media b/media index 9fc2401b8..3c551372d 160000 --- a/media +++ b/media @@ -1 +1 @@ -Subproject commit 9fc2401b8fdc2b23905402462e775c6db4e1527f +Subproject commit 3c551372d4bca20e6a7519573a5d43bc54e26504 From 1a490eb7b4662a0135d12999576310f6ac88ac70 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Fri, 23 Aug 2024 13:01:15 -0600 Subject: [PATCH 16/19] build: bump ndk to r26d --- app/build.gradle | 2 +- media | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 38df4b67d..8fcf95745 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,7 +16,7 @@ android { // it here so that binary stripping will work. // TODO: Eventually you might just want to start vendoring the FFMpeg extension so the // NDK use is unified - ndkVersion "26.1.10909125" + ndkVersion "26.3.11579264" namespace "org.oxycblt.auxio" defaultConfig { diff --git a/media b/media index 3c551372d..6ecd4f42d 160000 --- a/media +++ b/media @@ -1 +1 @@ -Subproject commit 3c551372d4bca20e6a7519573a5d43bc54e26504 +Subproject commit 6ecd4f42dc574af7a315eeccf16b72e87dbf697a From 3cd09c3cec3f668d5c79297c339c21ff8993f45b Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Fri, 23 Aug 2024 13:28:31 -0600 Subject: [PATCH 17/19] media: bump to fixed build --- media | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media b/media index 6ecd4f42d..34b33175c 160000 --- a/media +++ b/media @@ -1 +1 @@ -Subproject commit 6ecd4f42dc574af7a315eeccf16b72e87dbf697a +Subproject commit 34b33175c00183dc95cdcb8c735033b6785041e1 From a3012abe2347cc2b6609a8b4a835771c997af786 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Fri, 23 Aug 2024 13:41:00 -0600 Subject: [PATCH 18/19] info: update changelogs - Fix typo - Add tasker integration note --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d953459b7..840e6e0a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,15 @@ ## 3.5.3 +#### What's New +- Basic Tasker integration for safely starting Auxio's service + #### What's Improved - Added support for informal singular-spaced tags like `album artist` in file metadata #### What's Fixed -- Fix "Foregroud not allowed" music loading crash from starting too early +- Fix "Foreground not allowed" music loading crash from starting too early - Fixed widget not loading on some devices due to the cover being too large ## 3.5.2 From d91343070aa5888982c56338b9a17d4e9187dcc9 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Fri, 23 Aug 2024 13:43:49 -0600 Subject: [PATCH 19/19] build: bump to 3.5.3 Bump the version to 3.5.3 (49) --- README.md | 4 ++-- app/build.gradle | 4 ++-- .../main/java/org/oxycblt/auxio/music/cache/CacheDatabase.kt | 2 +- fastlane/metadata/android/en-US/changelogs/49.txt | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/49.txt diff --git a/README.md b/README.md index 6a3e6a632..191ca51ec 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 8fcf95745..98ded55d6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,8 +21,8 @@ android { defaultConfig { applicationId namespace - versionName "3.5.2" - versionCode 48 + versionName "3.5.3" + versionCode 49 minSdk 24 targetSdk 34 diff --git a/app/src/main/java/org/oxycblt/auxio/music/cache/CacheDatabase.kt b/app/src/main/java/org/oxycblt/auxio/music/cache/CacheDatabase.kt index 2a7113066..5ea7c8ebf 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/cache/CacheDatabase.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/cache/CacheDatabase.kt @@ -32,7 +32,7 @@ import org.oxycblt.auxio.music.info.Date import org.oxycblt.auxio.music.metadata.correctWhitespace import org.oxycblt.auxio.music.metadata.splitEscaped -@Database(entities = [CachedSong::class], version = 46, exportSchema = false) +@Database(entities = [CachedSong::class], version = 49, exportSchema = false) abstract class CacheDatabase : RoomDatabase() { abstract fun cachedSongsDao(): CachedSongsDao } diff --git a/fastlane/metadata/android/en-US/changelogs/49.txt b/fastlane/metadata/android/en-US/changelogs/49.txt new file mode 100644 index 000000000..62d3f517b --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/49.txt @@ -0,0 +1,3 @@ +Auxio 3.5.0 adds support for android auto alongside various playback and music quality of life improvements. +This release adds basic Tasker integration while fixing a few issues that affected certain devices. +For more information, see https://github.com/OxygenCobalt/Auxio/releases/tag/v3.5.3