diff --git a/app/src/main/kotlin/com/subtracks/playback/CoverArtBitmapLoader.kt b/app/src/main/kotlin/com/subtracks/playback/CoverArtBitmapLoader.kt index 50353495..d079a3c1 100644 --- a/app/src/main/kotlin/com/subtracks/playback/CoverArtBitmapLoader.kt +++ b/app/src/main/kotlin/com/subtracks/playback/CoverArtBitmapLoader.kt @@ -5,9 +5,9 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri import androidx.annotation.OptIn -import androidx.media3.common.MediaMetadata import androidx.media3.common.util.BitmapLoader import androidx.media3.common.util.UnstableApi +import androidx.media3.common.util.Util import coil3.ImageLoader import coil3.request.ImageRequest import coil3.request.SuccessResult @@ -20,6 +20,7 @@ import com.subtracks.data.repo.SourceRepository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.launch private const val ARTWORK_SIZE = 512 @@ -46,7 +47,11 @@ class CoverArtBitmapLoader( ) : BitmapLoader { private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - override fun supportsMimeType(mimeType: String): Boolean = mimeType.startsWith("image/") + override fun supportsMimeType(mimeType: String): Boolean = Util.isBitmapFactorySupportedMimeType(mimeType) + + fun shutdown() { + scope.cancel() + } override fun loadBitmap(uri: Uri): ListenableFuture { val coverArtId = @@ -80,19 +85,20 @@ class CoverArtBitmapLoader( return future } - override fun loadBitmapFromMetadata(metadata: MediaMetadata): ListenableFuture { - metadata.artworkData?.let { return decodeBitmap(it) } - metadata.artworkUri?.let { return loadBitmap(it) } - return Futures.immediateFailedFuture(IllegalArgumentException("Artwork has no data or URI")) - } - - override fun decodeBitmap(data: ByteArray): ListenableFuture = - try { - BitmapFactory - .decodeByteArray(data, 0, data.size) - ?.let { Futures.immediateFuture(it) } - ?: Futures.immediateFailedFuture(IllegalArgumentException("Could not decode artwork")) - } catch (e: Exception) { - Futures.immediateFailedFuture(e) + override fun decodeBitmap(data: ByteArray): ListenableFuture { + val future = SettableFuture.create() + scope.launch { + try { + val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size) + if (bitmap == null) { + future.setException(IllegalArgumentException("Could not decode artwork")) + } else { + future.set(bitmap) + } + } catch (e: Exception) { + future.setException(e) + } } + return future + } } diff --git a/app/src/main/kotlin/com/subtracks/playback/Media3PlayerHandle.kt b/app/src/main/kotlin/com/subtracks/playback/Media3PlayerHandle.kt index cdb995e8..6c5d3cc6 100644 --- a/app/src/main/kotlin/com/subtracks/playback/Media3PlayerHandle.kt +++ b/app/src/main/kotlin/com/subtracks/playback/Media3PlayerHandle.kt @@ -132,7 +132,7 @@ class Media3PlayerHandle( .setTitle(item.title) .setArtist(item.artist) .setAlbumTitle(item.album) - .setArtworkUri(item.coverArtId?.let(CoverArtArtwork::uri)) + .setArtworkUri(item.coverArtId?.takeIf { it.isNotEmpty() }?.let(CoverArtArtwork::uri)) .setIsBrowsable(false) .setIsPlayable(true) .setExtras( diff --git a/app/src/main/kotlin/com/subtracks/playback/PlaybackService.kt b/app/src/main/kotlin/com/subtracks/playback/PlaybackService.kt index bc565196..1a80e403 100644 --- a/app/src/main/kotlin/com/subtracks/playback/PlaybackService.kt +++ b/app/src/main/kotlin/com/subtracks/playback/PlaybackService.kt @@ -21,6 +21,7 @@ import org.koin.core.context.GlobalContext class PlaybackService : MediaSessionService() { private var session: MediaSession? = null + private var artworkLoader: CoverArtBitmapLoader? = null @OptIn(UnstableApi::class) override fun onCreate() { @@ -39,6 +40,13 @@ class PlaybackService : MediaSessionService() { .setMediaSourceFactory( DefaultMediaSourceFactory(this).setDataSourceFactory(OkHttpDataSource.Factory(streamingClient())), ).build() + val loader = + CoverArtBitmapLoader( + applicationContext, + GlobalContext.get().get(), + SingletonImageLoader.get(this), + ) + artworkLoader = loader session = MediaSession .Builder(this, player) @@ -49,13 +57,8 @@ class PlaybackService : MediaSessionService() { Intent(this, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, ), - ).setBitmapLoader( - CoverArtBitmapLoader( - this, - GlobalContext.get().get(), - SingletonImageLoader.get(this), - ), - ).build() + ).setBitmapLoader(loader) + .build() } override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? = session @@ -85,6 +88,8 @@ class PlaybackService : MediaSessionService() { } override fun onDestroy() { + artworkLoader?.shutdown() + artworkLoader = null session?.run { player.release() release()