mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 07:29:14 +01:00
address review: offload artwork decode, drop the redundant metadata override, cancel the loader
This commit is contained in:
@@ -5,9 +5,9 @@ import android.graphics.Bitmap
|
|||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.media3.common.MediaMetadata
|
|
||||||
import androidx.media3.common.util.BitmapLoader
|
import androidx.media3.common.util.BitmapLoader
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import androidx.media3.common.util.Util
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import coil3.request.SuccessResult
|
import coil3.request.SuccessResult
|
||||||
@@ -20,6 +20,7 @@ import com.subtracks.data.repo.SourceRepository
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
private const val ARTWORK_SIZE = 512
|
private const val ARTWORK_SIZE = 512
|
||||||
@@ -46,7 +47,11 @@ class CoverArtBitmapLoader(
|
|||||||
) : BitmapLoader {
|
) : BitmapLoader {
|
||||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
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<Bitmap> {
|
override fun loadBitmap(uri: Uri): ListenableFuture<Bitmap> {
|
||||||
val coverArtId =
|
val coverArtId =
|
||||||
@@ -80,19 +85,20 @@ class CoverArtBitmapLoader(
|
|||||||
return future
|
return future
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadBitmapFromMetadata(metadata: MediaMetadata): ListenableFuture<Bitmap> {
|
override fun decodeBitmap(data: ByteArray): ListenableFuture<Bitmap> {
|
||||||
metadata.artworkData?.let { return decodeBitmap(it) }
|
val future = SettableFuture.create<Bitmap>()
|
||||||
metadata.artworkUri?.let { return loadBitmap(it) }
|
scope.launch {
|
||||||
return Futures.immediateFailedFuture(IllegalArgumentException("Artwork has no data or URI"))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun decodeBitmap(data: ByteArray): ListenableFuture<Bitmap> =
|
|
||||||
try {
|
try {
|
||||||
BitmapFactory
|
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
|
||||||
.decodeByteArray(data, 0, data.size)
|
if (bitmap == null) {
|
||||||
?.let { Futures.immediateFuture(it) }
|
future.setException(IllegalArgumentException("Could not decode artwork"))
|
||||||
?: Futures.immediateFailedFuture(IllegalArgumentException("Could not decode artwork"))
|
} else {
|
||||||
|
future.set(bitmap)
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Futures.immediateFailedFuture(e)
|
future.setException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return future
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ class Media3PlayerHandle(
|
|||||||
.setTitle(item.title)
|
.setTitle(item.title)
|
||||||
.setArtist(item.artist)
|
.setArtist(item.artist)
|
||||||
.setAlbumTitle(item.album)
|
.setAlbumTitle(item.album)
|
||||||
.setArtworkUri(item.coverArtId?.let(CoverArtArtwork::uri))
|
.setArtworkUri(item.coverArtId?.takeIf { it.isNotEmpty() }?.let(CoverArtArtwork::uri))
|
||||||
.setIsBrowsable(false)
|
.setIsBrowsable(false)
|
||||||
.setIsPlayable(true)
|
.setIsPlayable(true)
|
||||||
.setExtras(
|
.setExtras(
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import org.koin.core.context.GlobalContext
|
|||||||
|
|
||||||
class PlaybackService : MediaSessionService() {
|
class PlaybackService : MediaSessionService() {
|
||||||
private var session: MediaSession? = null
|
private var session: MediaSession? = null
|
||||||
|
private var artworkLoader: CoverArtBitmapLoader? = null
|
||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
@@ -39,6 +40,13 @@ class PlaybackService : MediaSessionService() {
|
|||||||
.setMediaSourceFactory(
|
.setMediaSourceFactory(
|
||||||
DefaultMediaSourceFactory(this).setDataSourceFactory(OkHttpDataSource.Factory(streamingClient())),
|
DefaultMediaSourceFactory(this).setDataSourceFactory(OkHttpDataSource.Factory(streamingClient())),
|
||||||
).build()
|
).build()
|
||||||
|
val loader =
|
||||||
|
CoverArtBitmapLoader(
|
||||||
|
applicationContext,
|
||||||
|
GlobalContext.get().get<SourceRepository>(),
|
||||||
|
SingletonImageLoader.get(this),
|
||||||
|
)
|
||||||
|
artworkLoader = loader
|
||||||
session =
|
session =
|
||||||
MediaSession
|
MediaSession
|
||||||
.Builder(this, player)
|
.Builder(this, player)
|
||||||
@@ -49,13 +57,8 @@ class PlaybackService : MediaSessionService() {
|
|||||||
Intent(this, MainActivity::class.java),
|
Intent(this, MainActivity::class.java),
|
||||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
|
||||||
),
|
),
|
||||||
).setBitmapLoader(
|
).setBitmapLoader(loader)
|
||||||
CoverArtBitmapLoader(
|
.build()
|
||||||
this,
|
|
||||||
GlobalContext.get().get<SourceRepository>(),
|
|
||||||
SingletonImageLoader.get(this),
|
|
||||||
),
|
|
||||||
).build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? = session
|
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? = session
|
||||||
@@ -85,6 +88,8 @@ class PlaybackService : MediaSessionService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
artworkLoader?.shutdown()
|
||||||
|
artworkLoader = null
|
||||||
session?.run {
|
session?.run {
|
||||||
player.release()
|
player.release()
|
||||||
release()
|
release()
|
||||||
|
|||||||
Reference in New Issue
Block a user