Add artwork to stored preferences

This allows it to be displayed after reboot when we can't reload
it safely over network.

PiperOrigin-RevId: 753122948
This commit is contained in:
tonihei 2025-04-30 05:11:14 -07:00 committed by Copybara-Service
parent 8d87a69351
commit deb466c496
4 changed files with 44 additions and 6 deletions

View File

@ -17,7 +17,9 @@ package androidx.media3.demo.session
import android.os.Bundle
import androidx.annotation.OptIn
import androidx.core.net.toUri
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import androidx.media3.common.util.UnstableApi
import androidx.media3.demo.session.service.R
import androidx.media3.session.CommandButton
@ -189,7 +191,16 @@ open class DemoMediaLibrarySessionCallback(val service: DemoPlaybackService) :
return CoroutineScope(Dispatchers.Unconfined).future {
service.retrieveLastStoredMediaUidAndPosition()?.let {
maybeExpandSingleItemToPlaylist(
mediaItem = MediaItem.Builder().setMediaId(it.mediaId).build(),
mediaItem =
MediaItem.Builder()
.setMediaId(it.mediaId)
.setMediaMetadata(
MediaMetadata.Builder()
.setArtworkUri(it.artworkOriginalUri.toUri())
.setArtworkData(it.artworkData.toByteArray(), MediaMetadata.PICTURE_TYPE_MEDIA)
.build()
)
.build(),
startIndex = 0,
startPositionMs = it.positionMs,
)
@ -229,8 +240,8 @@ open class DemoMediaLibrarySessionCallback(val service: DemoPlaybackService) :
// Try to get the parent and its children.
MediaItemTree.getParentId(mediaId)?.let {
playlist =
MediaItemTree.getChildren(it).map { mediaItem ->
if (mediaItem.mediaId == mediaId) MediaItemTree.expandItem(mediaItem)!! else mediaItem
MediaItemTree.getChildren(it).map { childItem ->
if (childItem.mediaId == mediaId) MediaItemTree.expandItem(mediaItem)!! else childItem
}
indexInPlaylist = MediaItemTree.getIndexInMediaItems(mediaId, playlist)
}

View File

@ -21,6 +21,7 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.os.Build
import androidx.annotation.OptIn
import androidx.core.app.NotificationCompat
@ -40,11 +41,13 @@ import androidx.media3.session.MediaConstants
import androidx.media3.session.MediaLibraryService
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSession.ControllerInfo
import com.google.protobuf.ByteString
import java.io.InputStream
import java.io.OutputStream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.guava.await
import kotlinx.coroutines.launch
open class DemoPlaybackService : MediaLibraryService() {
@ -167,15 +170,30 @@ open class DemoPlaybackService : MediaLibraryService() {
}
}
@OptIn(UnstableApi::class) // BitmapLoader
private fun storeCurrentMediaUidAndPosition() {
val mediaID = mediaLibrarySession.player.currentMediaItem?.mediaId
if (mediaID == null) {
return
}
val artworkUri = mediaLibrarySession.player.currentMediaItem?.mediaMetadata?.artworkUri
val positionMs = mediaLibrarySession.player.currentPosition
CoroutineScope(Dispatchers.IO).launch {
PreferenceDataStore.get(this@DemoPlaybackService).updateData { _ ->
Preferences.newBuilder().setMediaId(mediaID).setPositionMs(positionMs).build()
PreferenceDataStore.get(this@DemoPlaybackService).updateData { preferences ->
val builder = preferences.toBuilder().setMediaId(mediaID).setPositionMs(positionMs)
val artworkUriString = artworkUri?.toString() ?: ""
if (artworkUriString != preferences.artworkOriginalUri) {
builder.setArtworkOriginalUri(artworkUriString)
if (artworkUri == null) {
builder.setArtworkData(ByteString.EMPTY)
} else {
val bitmap = mediaLibrarySession.bitmapLoader.loadBitmap(artworkUri).await()
val outputStream = ByteString.newOutput()
bitmap.compress(Bitmap.CompressFormat.PNG, /* quality= */ 90, outputStream)
builder.setArtworkData(outputStream.toByteString())
}
}
builder.build()
}
}
}

View File

@ -25,4 +25,8 @@ message Preferences {
string media_id = 1;
// The position in the last played item in milliseconds.
int64 position_ms = 2;
// The original artwork URI of the last played item.
string artwork_original_uri = 3;
// The artwork image data of the last played item.
bytes artwork_data = 4;
}

View File

@ -1696,9 +1696,14 @@ public class MediaSession {
* play without a current {@link MediaItem}.
*
* <p>This happens, for example, if <a
* href="https://developer.android.com/guide/topics/media/session/mediasession#resumption">playback
* href="https://developer.android.com/media/media3/session/background-playback#resumption">playback
* resumption</a> is requested from a media button receiver or the System UI notification.
*
* <p>Use {@link MediaMetadata#artworkData} or {@link MediaMetadata#artworkUri} with a content
* URI to set locally available artwork data for the System UI notification after reboot of the
* device. Note that network access may not be available when this method is called during boot
* time.
*
* <p>The method will only be called if the {@link Player} has {@link
* Player#COMMAND_GET_CURRENT_MEDIA_ITEM} and either {@link Player#COMMAND_SET_MEDIA_ITEM} or
* {@link Player#COMMAND_CHANGE_MEDIA_ITEMS} available.