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.
This commit is contained in:
parent
0a3382cafd
commit
9087ad5e45
5 changed files with 16 additions and 102 deletions
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<Bitmap> {
|
|
||||||
throw NotImplementedError()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadBitmap(uri: Uri): ListenableFuture<Bitmap> {
|
|
||||||
throw NotImplementedError()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun supportsMimeType(mimeType: String): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadBitmapFromMetadata(metadata: MediaMetadata): ListenableFuture<Bitmap>? {
|
|
||||||
val deviceLibrary = musicRepository.deviceLibrary ?: return null
|
|
||||||
val future = SettableFuture.create<Bitmap>()
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -74,7 +74,7 @@ fun Song.toMediaItem(context: Context, parent: MusicParent?): MediaItem {
|
||||||
.setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC)
|
.setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC)
|
||||||
.setIsPlayable(true)
|
.setIsPlayable(true)
|
||||||
.setIsBrowsable(false)
|
.setIsBrowsable(false)
|
||||||
.setArtworkUri(album.cover.single.mediaStoreCoverUri)
|
.setArtworkUri(cover.mediaStoreCoverUri)
|
||||||
.setExtras(
|
.setExtras(
|
||||||
Bundle().apply {
|
Bundle().apply {
|
||||||
putString("uid", mediaSessionUID.toString())
|
putString("uid", mediaSessionUID.toString())
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.oxycblt.auxio.playback.service
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.media.audiofx.AudioEffect
|
import android.media.audiofx.AudioEffect
|
||||||
|
import android.os.Bundle
|
||||||
import androidx.media3.common.AudioAttributes
|
import androidx.media3.common.AudioAttributes
|
||||||
import androidx.media3.common.C
|
import androidx.media3.common.C
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
|
@ -42,6 +43,7 @@ import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.yield
|
import kotlinx.coroutines.yield
|
||||||
|
import org.oxycblt.auxio.image.ImageSettings
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
import org.oxycblt.auxio.music.MusicRepository
|
import org.oxycblt.auxio.music.MusicRepository
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
|
@ -69,13 +71,15 @@ class ExoPlaybackStateHolder(
|
||||||
private val persistenceRepository: PersistenceRepository,
|
private val persistenceRepository: PersistenceRepository,
|
||||||
private val playbackSettings: PlaybackSettings,
|
private val playbackSettings: PlaybackSettings,
|
||||||
private val commandFactory: PlaybackCommand.Factory,
|
private val commandFactory: PlaybackCommand.Factory,
|
||||||
|
private val replayGainProcessor: ReplayGainAudioProcessor,
|
||||||
private val musicRepository: MusicRepository,
|
private val musicRepository: MusicRepository,
|
||||||
private val replayGainProcessor: ReplayGainAudioProcessor
|
private val imageSettings: ImageSettings
|
||||||
) :
|
) :
|
||||||
PlaybackStateHolder,
|
PlaybackStateHolder,
|
||||||
Player.Listener,
|
Player.Listener,
|
||||||
MusicRepository.UpdateListener,
|
MusicRepository.UpdateListener,
|
||||||
PlaybackSettings.Listener {
|
PlaybackSettings.Listener,
|
||||||
|
ImageSettings.Listener {
|
||||||
private val saveJob = Job()
|
private val saveJob = Job()
|
||||||
private val saveScope = CoroutineScope(Dispatchers.IO + saveJob)
|
private val saveScope = CoroutineScope(Dispatchers.IO + saveJob)
|
||||||
private val restoreScope = CoroutineScope(Dispatchers.IO + saveJob)
|
private val restoreScope = CoroutineScope(Dispatchers.IO + saveJob)
|
||||||
|
@ -86,6 +90,7 @@ class ExoPlaybackStateHolder(
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun attach() {
|
fun attach() {
|
||||||
|
imageSettings.registerListener(this)
|
||||||
player.addListener(this)
|
player.addListener(this)
|
||||||
replayGainProcessor.attach()
|
replayGainProcessor.attach()
|
||||||
playbackManager.registerStateHolder(this)
|
playbackManager.registerStateHolder(this)
|
||||||
|
@ -99,6 +104,7 @@ class ExoPlaybackStateHolder(
|
||||||
playbackManager.unregisterStateHolder(this)
|
playbackManager.unregisterStateHolder(this)
|
||||||
musicRepository.removeUpdateListener(this)
|
musicRepository.removeUpdateListener(this)
|
||||||
replayGainProcessor.release()
|
replayGainProcessor.release()
|
||||||
|
imageSettings.unregisterListener(this)
|
||||||
player.release()
|
player.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,9 +522,10 @@ class ExoPlaybackStateHolder(
|
||||||
private val persistenceRepository: PersistenceRepository,
|
private val persistenceRepository: PersistenceRepository,
|
||||||
private val playbackSettings: PlaybackSettings,
|
private val playbackSettings: PlaybackSettings,
|
||||||
private val commandFactory: PlaybackCommand.Factory,
|
private val commandFactory: PlaybackCommand.Factory,
|
||||||
private val musicRepository: MusicRepository,
|
|
||||||
private val mediaSourceFactory: MediaSource.Factory,
|
private val mediaSourceFactory: MediaSource.Factory,
|
||||||
private val replayGainProcessor: ReplayGainAudioProcessor
|
private val replayGainProcessor: ReplayGainAudioProcessor,
|
||||||
|
private val musicRepository: MusicRepository,
|
||||||
|
private val imageSettings: ImageSettings,
|
||||||
) {
|
) {
|
||||||
fun create(): ExoPlaybackStateHolder {
|
fun create(): ExoPlaybackStateHolder {
|
||||||
// Since Auxio is a music player, only specify an audio renderer to save
|
// Since Auxio is a music player, only specify an audio renderer to save
|
||||||
|
@ -556,8 +563,9 @@ class ExoPlaybackStateHolder(
|
||||||
persistenceRepository,
|
persistenceRepository,
|
||||||
playbackSettings,
|
playbackSettings,
|
||||||
commandFactory,
|
commandFactory,
|
||||||
|
replayGainProcessor,
|
||||||
musicRepository,
|
musicRepository,
|
||||||
replayGainProcessor)
|
imageSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,6 @@ import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.auxio.ForegroundListener
|
import org.oxycblt.auxio.ForegroundListener
|
||||||
import org.oxycblt.auxio.IntegerTable
|
import org.oxycblt.auxio.IntegerTable
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.image.service.MediaSessionBitmapLoader
|
|
||||||
import org.oxycblt.auxio.music.service.MediaItemBrowser
|
import org.oxycblt.auxio.music.service.MediaItemBrowser
|
||||||
import org.oxycblt.auxio.playback.state.DeferredPlayback
|
import org.oxycblt.auxio.playback.state.DeferredPlayback
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||||
|
@ -61,7 +60,6 @@ constructor(
|
||||||
private val playbackManager: PlaybackStateManager,
|
private val playbackManager: PlaybackStateManager,
|
||||||
private val actionHandler: PlaybackActionHandler,
|
private val actionHandler: PlaybackActionHandler,
|
||||||
private val mediaItemBrowser: MediaItemBrowser,
|
private val mediaItemBrowser: MediaItemBrowser,
|
||||||
private val bitmapLoader: MediaSessionBitmapLoader,
|
|
||||||
exoHolderFactory: ExoPlaybackStateHolder.Factory
|
exoHolderFactory: ExoPlaybackStateHolder.Factory
|
||||||
) :
|
) :
|
||||||
MediaLibrarySession.Callback,
|
MediaLibrarySession.Callback,
|
||||||
|
@ -143,9 +141,7 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSession(service: MediaLibraryService) =
|
private fun createSession(service: MediaLibraryService) =
|
||||||
MediaLibrarySession.Builder(service, exoHolder.mediaSessionPlayer, this)
|
MediaLibrarySession.Builder(service, exoHolder.mediaSessionPlayer, this).build()
|
||||||
.setBitmapLoader(bitmapLoader)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun onConnect(
|
override fun onConnect(
|
||||||
session: MediaSession,
|
session: MediaSession,
|
||||||
|
|
|
@ -12,7 +12,7 @@ buildscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
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 "androidx.navigation.safeargs.kotlin" version "$navigation_version" apply false
|
||||||
id "org.jetbrains.kotlin.android" version "$kotlin_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
|
id "com.google.devtools.ksp" version '1.9.23-1.0.20' apply false
|
||||||
|
|
Loading…
Reference in a new issue