From 66db61899c70512990cc20291fac8ef45ac7dd20 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Fri, 17 May 2024 13:38:12 -0600 Subject: [PATCH] playback: remove custom bitmap loading Media3 simply will not tolerate me doing this. I am basically stuck at the mercy of the Android OS now, until I can have my own unified source of truth with cover loading. --- .../image/service/MediaSessionBitmapLoader.kt | 90 ------------------- .../music/service/MediaItemTranslation.kt | 2 +- .../service/ExoPlaybackStateHolder.kt | 18 ++-- .../service/MediaSessionServiceFragment.kt | 6 +- build.gradle | 2 +- 5 files changed, 16 insertions(+), 102 deletions(-) delete mode 100644 app/src/main/java/org/oxycblt/auxio/image/service/MediaSessionBitmapLoader.kt diff --git a/app/src/main/java/org/oxycblt/auxio/image/service/MediaSessionBitmapLoader.kt b/app/src/main/java/org/oxycblt/auxio/image/service/MediaSessionBitmapLoader.kt deleted file mode 100644 index 3d51677e6..000000000 --- a/app/src/main/java/org/oxycblt/auxio/image/service/MediaSessionBitmapLoader.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2024 Auxio Project - * MediaSessionBitmapLoader.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.image.service - -import android.content.Context -import android.graphics.Bitmap -import android.net.Uri -import androidx.media3.common.MediaMetadata -import androidx.media3.common.util.BitmapLoader -import coil.ImageLoader -import coil.memory.MemoryCache -import coil.request.Options -import com.google.common.util.concurrent.ListenableFuture -import com.google.common.util.concurrent.SettableFuture -import dagger.hilt.android.qualifiers.ApplicationContext -import javax.inject.Inject -import org.oxycblt.auxio.image.BitmapProvider -import org.oxycblt.auxio.image.extractor.CoverKeyer -import org.oxycblt.auxio.music.MusicRepository -import org.oxycblt.auxio.music.service.MediaSessionUID - -class MediaSessionBitmapLoader -@Inject -constructor( - @ApplicationContext private val context: Context, - private val musicRepository: MusicRepository, - private val bitmapProvider: BitmapProvider, - private val keyer: CoverKeyer, - private val imageLoader: ImageLoader, -) : BitmapLoader { - override fun decodeBitmap(data: ByteArray): ListenableFuture { - throw NotImplementedError() - } - - override fun loadBitmap(uri: Uri): ListenableFuture { - throw NotImplementedError() - } - - override fun supportsMimeType(mimeType: String): Boolean { - return true - } - - override fun loadBitmapFromMetadata(metadata: MediaMetadata): ListenableFuture? { - val deviceLibrary = musicRepository.deviceLibrary ?: return null - val future = SettableFuture.create() - val song = - when (val uid = - metadata.extras?.getString("uid")?.let { MediaSessionUID.fromString(it) }) { - is MediaSessionUID.Single -> deviceLibrary.findSong(uid.uid) - is MediaSessionUID.Joined -> deviceLibrary.findSong(uid.childUid) - else -> return null - } - ?: return null - // Even launching a coroutine to obtained cached covers is enough to make the notification - // go without covers. - val key = keyer.key(listOf(song.cover), Options(context)) - if (imageLoader.memoryCache?.get(MemoryCache.Key(key)) != null) { - future.set(imageLoader.memoryCache?.get(MemoryCache.Key(key))?.bitmap) - return future - } - bitmapProvider.load( - song, - object : BitmapProvider.Target { - override fun onCompleted(bitmap: Bitmap?) { - if (bitmap == null) { - future.setException(IllegalStateException("Bitmap is null")) - } else { - future.set(bitmap) - } - } - }) - return future - } -} diff --git a/app/src/main/java/org/oxycblt/auxio/music/service/MediaItemTranslation.kt b/app/src/main/java/org/oxycblt/auxio/music/service/MediaItemTranslation.kt index 2e92157b2..bde25c1b5 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/service/MediaItemTranslation.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/service/MediaItemTranslation.kt @@ -74,7 +74,7 @@ fun Song.toMediaItem(context: Context, parent: MusicParent?): MediaItem { .setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC) .setIsPlayable(true) .setIsBrowsable(false) - .setArtworkUri(album.cover.single.mediaStoreCoverUri) + .setArtworkUri(cover.mediaStoreCoverUri) .setExtras( Bundle().apply { putString("uid", mediaSessionUID.toString()) 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 070432bc8..e8dca68be 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 @@ -21,6 +21,7 @@ package org.oxycblt.auxio.playback.service import android.content.Context import android.content.Intent import android.media.audiofx.AudioEffect +import android.os.Bundle import androidx.media3.common.AudioAttributes import androidx.media3.common.C import androidx.media3.common.MediaItem @@ -42,6 +43,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.coroutines.yield +import org.oxycblt.auxio.image.ImageSettings import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicRepository import org.oxycblt.auxio.music.Song @@ -69,13 +71,15 @@ class ExoPlaybackStateHolder( private val persistenceRepository: PersistenceRepository, private val playbackSettings: PlaybackSettings, private val commandFactory: PlaybackCommand.Factory, + private val replayGainProcessor: ReplayGainAudioProcessor, private val musicRepository: MusicRepository, - private val replayGainProcessor: ReplayGainAudioProcessor + private val imageSettings: ImageSettings ) : PlaybackStateHolder, Player.Listener, MusicRepository.UpdateListener, - PlaybackSettings.Listener { + PlaybackSettings.Listener, + ImageSettings.Listener { private val saveJob = Job() private val saveScope = CoroutineScope(Dispatchers.IO + saveJob) private val restoreScope = CoroutineScope(Dispatchers.IO + saveJob) @@ -86,6 +90,7 @@ class ExoPlaybackStateHolder( private set fun attach() { + imageSettings.registerListener(this) player.addListener(this) replayGainProcessor.attach() playbackManager.registerStateHolder(this) @@ -99,6 +104,7 @@ class ExoPlaybackStateHolder( playbackManager.unregisterStateHolder(this) musicRepository.removeUpdateListener(this) replayGainProcessor.release() + imageSettings.unregisterListener(this) player.release() } @@ -516,9 +522,10 @@ class ExoPlaybackStateHolder( private val persistenceRepository: PersistenceRepository, private val playbackSettings: PlaybackSettings, private val commandFactory: PlaybackCommand.Factory, - private val musicRepository: MusicRepository, private val mediaSourceFactory: MediaSource.Factory, - private val replayGainProcessor: ReplayGainAudioProcessor + private val replayGainProcessor: ReplayGainAudioProcessor, + private val musicRepository: MusicRepository, + private val imageSettings: ImageSettings, ) { fun create(): ExoPlaybackStateHolder { // Since Auxio is a music player, only specify an audio renderer to save @@ -556,8 +563,9 @@ class ExoPlaybackStateHolder( persistenceRepository, playbackSettings, commandFactory, + replayGainProcessor, musicRepository, - replayGainProcessor) + imageSettings) } } 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 a626cd4b6..8152b1053 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 @@ -48,7 +48,6 @@ import org.oxycblt.auxio.BuildConfig import org.oxycblt.auxio.ForegroundListener import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.R -import org.oxycblt.auxio.image.service.MediaSessionBitmapLoader import org.oxycblt.auxio.music.service.MediaItemBrowser import org.oxycblt.auxio.playback.state.DeferredPlayback import org.oxycblt.auxio.playback.state.PlaybackStateManager @@ -61,7 +60,6 @@ constructor( private val playbackManager: PlaybackStateManager, private val actionHandler: PlaybackActionHandler, private val mediaItemBrowser: MediaItemBrowser, - private val bitmapLoader: MediaSessionBitmapLoader, exoHolderFactory: ExoPlaybackStateHolder.Factory ) : MediaLibrarySession.Callback, @@ -143,9 +141,7 @@ constructor( } private fun createSession(service: MediaLibraryService) = - MediaLibrarySession.Builder(service, exoHolder.mediaSessionPlayer, this) - .setBitmapLoader(bitmapLoader) - .build() + MediaLibrarySession.Builder(service, exoHolder.mediaSessionPlayer, this).build() override fun onConnect( session: MediaSession, diff --git a/build.gradle b/build.gradle index 2b2c7a9c5..a81768d25 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { } plugins { - id "com.android.application" version '8.3.2' apply false + id "com.android.application" version '8.4.0' apply false id "androidx.navigation.safeargs.kotlin" version "$navigation_version" apply false id "org.jetbrains.kotlin.android" version "$kotlin_version" apply false id "com.google.devtools.ksp" version '1.9.23-1.0.20' apply false