From 88a413b2cba0b2cccaf704db056c0d44316fe3fc Mon Sep 17 00:00:00 2001 From: tianyifeng Date: Tue, 25 Oct 2022 13:21:08 +0000 Subject: [PATCH] Add injection of `BitmapLoader` from `MediaSession`. * Add `BitmapLoader` in `MediaSession.Builder` and `MediaLibrarySession.Builder`. * Pass `BitmapLoader` into the constructor of `MediaSession`, `MediaSessionImpl`, `MediaLibrarySession` and `MediaLibrarySessionImpl`. * Add an interface method `loadBitmapFromMetadata(MediaMetadata)` in `BitmapLoader`. * Remove the reference of `BitmapLoader` in `DefaultMediaNotificationProvider`. PiperOrigin-RevId: 483654596 (cherry picked from commit 3f69df72db7a4aa481dd315f008375148f548805) --- .../session/SimpleBitmapLoaderTest.java | 59 +++++++++++++++++++ .../androidx/media3/session/BitmapLoader.java | 25 ++++++++ .../DefaultMediaNotificationProvider.java | 38 +----------- .../media3/session/MediaLibraryService.java | 46 +++++++++++++-- .../session/MediaLibrarySessionImpl.java | 5 +- .../androidx/media3/session/MediaSession.java | 53 +++++++++++++++-- .../media3/session/MediaSessionImpl.java | 9 ++- .../DefaultMediaNotificationProviderTest.java | 22 ++++++- 8 files changed, 207 insertions(+), 50 deletions(-) diff --git a/libraries/session/src/androidTest/java/androidx/media3/session/SimpleBitmapLoaderTest.java b/libraries/session/src/androidTest/java/androidx/media3/session/SimpleBitmapLoaderTest.java index ff3c92236d..f84cf65124 100644 --- a/libraries/session/src/androidTest/java/androidx/media3/session/SimpleBitmapLoaderTest.java +++ b/libraries/session/src/androidTest/java/androidx/media3/session/SimpleBitmapLoaderTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertThrows; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; +import androidx.media3.common.MediaMetadata; import androidx.media3.test.utils.TestUtil; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -166,6 +167,64 @@ public class SimpleBitmapLoaderTest { /* messagePart= */ "unknown protocol"); } + @Test + public void loadBitmapFromMetadata_decodeFromArtworkData() throws Exception { + byte[] imageData = + TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TEST_IMAGE_PATH); + MockWebServer mockWebServer = new MockWebServer(); + Uri uri = Uri.parse(mockWebServer.url("test_path").toString()); + // Set both artworkData and artworkUri + MediaMetadata metadata = + new MediaMetadata.Builder() + .setArtworkData(imageData, MediaMetadata.PICTURE_TYPE_FRONT_COVER) + .setArtworkUri(uri) + .build(); + SimpleBitmapLoader bitmapLoader = + new SimpleBitmapLoader(MoreExecutors.newDirectExecutorService()); + + Bitmap bitmap = bitmapLoader.loadBitmapFromMetadata(metadata).get(); + + assertThat( + bitmap.sameAs( + BitmapFactory.decodeByteArray(imageData, /* offset= */ 0, imageData.length))) + .isTrue(); + assertThat(mockWebServer.getRequestCount()).isEqualTo(0); + } + + @Test + public void loadBitmapFromMetadata_loadFromArtworkUri() throws Exception { + byte[] imageData = + TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TEST_IMAGE_PATH); + MockWebServer mockWebServer = new MockWebServer(); + Buffer responseBody = new Buffer().write(imageData); + mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(responseBody)); + Uri uri = Uri.parse(mockWebServer.url("test_path").toString()); + // Just set artworkUri + MediaMetadata metadata = new MediaMetadata.Builder().setArtworkUri(uri).build(); + SimpleBitmapLoader bitmapLoader = + new SimpleBitmapLoader(MoreExecutors.newDirectExecutorService()); + + Bitmap bitmap = bitmapLoader.loadBitmapFromMetadata(metadata).get(); + + assertThat( + bitmap.sameAs( + BitmapFactory.decodeByteArray(imageData, /* offset= */ 0, imageData.length))) + .isTrue(); + assertThat(mockWebServer.getRequestCount()).isEqualTo(1); + } + + @Test + public void loadBitmapFromMetadata_returnNull() throws Exception { + // Neither artworkData nor artworkUri is set + MediaMetadata metadata = new MediaMetadata.Builder().build(); + SimpleBitmapLoader bitmapLoader = + new SimpleBitmapLoader(MoreExecutors.newDirectExecutorService()); + + ListenableFuture bitmapFuture = bitmapLoader.loadBitmapFromMetadata(metadata); + + assertThat(bitmapFuture).isNull(); + } + private static void assertException( ThrowingRunnable runnable, Class clazz, String messagePart) { ExecutionException executionException = assertThrows(ExecutionException.class, runnable); diff --git a/libraries/session/src/main/java/androidx/media3/session/BitmapLoader.java b/libraries/session/src/main/java/androidx/media3/session/BitmapLoader.java index 2b88806943..d0142c1df1 100644 --- a/libraries/session/src/main/java/androidx/media3/session/BitmapLoader.java +++ b/libraries/session/src/main/java/androidx/media3/session/BitmapLoader.java @@ -17,6 +17,8 @@ package androidx.media3.session; import android.graphics.Bitmap; import android.net.Uri; +import androidx.annotation.Nullable; +import androidx.media3.common.MediaMetadata; import androidx.media3.common.util.UnstableApi; import com.google.common.util.concurrent.ListenableFuture; @@ -25,6 +27,29 @@ import com.google.common.util.concurrent.ListenableFuture; public interface BitmapLoader { /** Decodes an image from compressed binary data. */ ListenableFuture decodeBitmap(byte[] data); + /** Loads an image from {@code uri}. */ ListenableFuture loadBitmap(Uri uri); + + /** + * Loads an image from {@link MediaMetadata}. Returns null if {@code metadata} doesn't contain + * bitmap information. + * + *

By default, the method will try to decode an image from {@link MediaMetadata#artworkData} if + * it is present. Otherwise, the method will try to load an image from {@link + * MediaMetadata#artworkUri} if it is present. The method will return null if neither {@link + * MediaMetadata#artworkData} nor {@link MediaMetadata#artworkUri} is present. + */ + @Nullable + default ListenableFuture loadBitmapFromMetadata(MediaMetadata metadata) { + @Nullable ListenableFuture future; + if (metadata.artworkData != null) { + future = decodeBitmap(metadata.artworkData); + } else if (metadata.artworkUri != null) { + future = loadBitmap(metadata.artworkUri); + } else { + future = null; + } + return future; + } } diff --git a/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java b/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java index 4397b56e8f..008ba329eb 100644 --- a/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java +++ b/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java @@ -123,7 +123,6 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi private NotificationIdProvider notificationIdProvider; private String channelId; @StringRes private int channelNameResourceId; - private BitmapLoader bitmapLoader; private boolean built; /** @@ -136,7 +135,6 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi notificationIdProvider = session -> DEFAULT_NOTIFICATION_ID; channelId = DEFAULT_CHANNEL_ID; channelNameResourceId = DEFAULT_CHANNEL_NAME_RESOURCE_ID; - bitmapLoader = new SimpleBitmapLoader(); } /** @@ -196,19 +194,6 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi return this; } - /** - * Sets the {@link BitmapLoader} used load artwork. By default, a {@link CacheBitmapLoader} with - * a {@link SimpleBitmapLoader} inside will be used. - * - * @param bitmapLoader The bitmap loader. - * @return This builder. - */ - @CanIgnoreReturnValue - public Builder setBitmapLoader(BitmapLoader bitmapLoader) { - this.bitmapLoader = new CacheBitmapLoader(bitmapLoader); - return this; - } - /** * Builds the {@link DefaultMediaNotificationProvider}. The method can be called at most once. */ @@ -259,7 +244,6 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi private final String channelId; @StringRes private final int channelNameResourceId; private final NotificationManager notificationManager; - private final BitmapLoader bitmapLoader; // Cache the last bitmap load request to avoid reloading the bitmap again, particularly useful // when showing a notification for the same item (e.g. when switching from playing to paused). private final Handler mainHandler; @@ -272,7 +256,6 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi this.notificationIdProvider = builder.notificationIdProvider; this.channelId = builder.channelId; this.channelNameResourceId = builder.channelNameResourceId; - this.bitmapLoader = new CacheBitmapLoader(builder.bitmapLoader); notificationManager = checkStateNotNull( (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)); @@ -312,7 +295,9 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi builder .setContentTitle(getNotificationContentTitle(metadata)) .setContentText(getNotificationContentText(metadata)); - @Nullable ListenableFuture bitmapFuture = loadArtworkBitmap(metadata); + @Nullable + ListenableFuture bitmapFuture = + mediaSession.getBitmapLoader().loadBitmapFromMetadata(metadata); if (bitmapFuture != null) { if (pendingOnBitmapLoadedFutureCallback != null) { pendingOnBitmapLoadedFutureCallback.discardIfPending(); @@ -578,23 +563,6 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi notificationManager, channelId, context.getString(channelNameResourceId)); } - /** - * Requests from the bitmapLoader to load artwork or returns null if the metadata don't include - * artwork. - */ - @Nullable - private ListenableFuture loadArtworkBitmap(MediaMetadata metadata) { - @Nullable ListenableFuture future; - if (metadata.artworkData != null) { - future = bitmapLoader.decodeBitmap(metadata.artworkData); - } else if (metadata.artworkUri != null) { - future = bitmapLoader.loadBitmap(metadata.artworkUri); - } else { - future = null; - } - return future; - } - private static long getPlaybackStartTimeEpochMs(Player player) { // Changing "showWhen" causes notification flicker if SDK_INT < 21. if (Util.SDK_INT >= 21 diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java index 233bc2a5b2..bb6b8ebfa9 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java @@ -24,6 +24,7 @@ import static java.lang.annotation.ElementType.TYPE_USE; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import androidx.annotation.IntDef; @@ -420,6 +421,28 @@ public abstract class MediaLibraryService extends MediaSessionService { return super.setExtras(extras); } + /** + * Sets a {@link BitmapLoader} for the {@link MediaLibrarySession} to decode bitmaps from + * compressed binary data or load bitmaps from {@link Uri}. If not set, a {@link + * CacheBitmapLoader} with a {@link SimpleBitmapLoader} inside will be used. + * + *

The provided instance will likely be called repeatedly with the same request, so it + * would be best if any provided instance does some caching. Simple caching can be added to + * any {@link BitmapLoader} implementation by wrapping it in {@link CacheBitmapLoader} before + * passing it to this method. + * + *

If no instance is set, a {@link CacheBitmapLoader} with a {@link SimpleBitmapLoader} + * inside will be used. + * + * @param bitmapLoader The bitmap loader {@link BitmapLoader}. + * @return The builder to allow chaining. + */ + @UnstableApi + @Override + public Builder setBitmapLoader(BitmapLoader bitmapLoader) { + return super.setBitmapLoader(bitmapLoader); + } + /** * Builds a {@link MediaLibrarySession}. * @@ -429,7 +452,11 @@ public abstract class MediaLibraryService extends MediaSessionService { */ @Override public MediaLibrarySession build() { - return new MediaLibrarySession(context, id, player, sessionActivity, callback, extras); + if (bitmapLoader == null) { + bitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); + } + return new MediaLibrarySession( + context, id, player, sessionActivity, callback, extras, checkNotNull(bitmapLoader)); } } @@ -439,8 +466,9 @@ public abstract class MediaLibraryService extends MediaSessionService { Player player, @Nullable PendingIntent sessionActivity, MediaSession.Callback callback, - Bundle tokenExtras) { - super(context, id, player, sessionActivity, callback, tokenExtras); + Bundle tokenExtras, + BitmapLoader bitmapLoader) { + super(context, id, player, sessionActivity, callback, tokenExtras, bitmapLoader); } @Override @@ -450,9 +478,17 @@ public abstract class MediaLibraryService extends MediaSessionService { Player player, @Nullable PendingIntent sessionActivity, MediaSession.Callback callback, - Bundle tokenExtras) { + Bundle tokenExtras, + BitmapLoader bitmapLoader) { return new MediaLibrarySessionImpl( - this, context, id, player, sessionActivity, (Callback) callback, tokenExtras); + this, + context, + id, + player, + sessionActivity, + (Callback) callback, + tokenExtras, + bitmapLoader); } @Override diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java index e2c966547d..2bed390949 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java @@ -64,8 +64,9 @@ import java.util.concurrent.Future; Player player, @Nullable PendingIntent sessionActivity, MediaLibrarySession.Callback callback, - Bundle tokenExtras) { - super(instance, context, id, player, sessionActivity, callback, tokenExtras); + Bundle tokenExtras, + BitmapLoader bitmapLoader) { + super(instance, context, id, player, sessionActivity, callback, tokenExtras, bitmapLoader); this.instance = instance; this.callback = callback; subscriptions = new ArrayMap<>(); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java index 11ce5e5be1..a2b58d3cfe 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java @@ -61,6 +61,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import java.util.HashMap; import java.util.List; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * A session that allows a media app to expose its transport controls and playback information in a @@ -307,6 +308,28 @@ public class MediaSession { return super.setExtras(extras); } + /** + * Sets a {@link BitmapLoader} for the {@link MediaSession} to decode bitmaps from compressed + * binary data or load bitmaps from {@link Uri}. If not set, a {@link CacheBitmapLoader} with a + * {@link SimpleBitmapLoader} inside will be used. + * + *

The provided instance will likely be called repeatedly with the same request, so it would + * be best if any provided instance does some caching. Simple caching can be added to any {@link + * BitmapLoader} implementation by wrapping it in {@link CacheBitmapLoader} before passing it to + * this method. + * + *

If no instance is set, a {@link CacheBitmapLoader} with a {@link SimpleBitmapLoader} + * inside will be used. + * + * @param bitmapLoader The bitmap loader {@link BitmapLoader}. + * @return The builder to allow chaining. + */ + @UnstableApi + @Override + public Builder setBitmapLoader(BitmapLoader bitmapLoader) { + return super.setBitmapLoader(bitmapLoader); + } + /** * Builds a {@link MediaSession}. * @@ -316,7 +339,11 @@ public class MediaSession { */ @Override public MediaSession build() { - return new MediaSession(context, id, player, sessionActivity, callback, extras); + if (bitmapLoader == null) { + bitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); + } + return new MediaSession( + context, id, player, sessionActivity, callback, extras, checkNotNull(bitmapLoader)); } } @@ -487,14 +514,15 @@ public class MediaSession { Player player, @Nullable PendingIntent sessionActivity, Callback callback, - Bundle tokenExtras) { + Bundle tokenExtras, + BitmapLoader bitmapLoader) { synchronized (STATIC_LOCK) { if (SESSION_ID_TO_SESSION_MAP.containsKey(id)) { throw new IllegalStateException("Session ID must be unique. ID=" + id); } SESSION_ID_TO_SESSION_MAP.put(id, this); } - impl = createImpl(context, id, player, sessionActivity, callback, tokenExtras); + impl = createImpl(context, id, player, sessionActivity, callback, tokenExtras, bitmapLoader); } /* package */ MediaSessionImpl createImpl( @@ -503,8 +531,10 @@ public class MediaSession { Player player, @Nullable PendingIntent sessionActivity, Callback callback, - Bundle tokenExtras) { - return new MediaSessionImpl(this, context, id, player, sessionActivity, callback, tokenExtras); + Bundle tokenExtras, + BitmapLoader bitmapLoader) { + return new MediaSessionImpl( + this, context, id, player, sessionActivity, callback, tokenExtras, bitmapLoader); } /* package */ MediaSessionImpl getImpl() { @@ -741,6 +771,12 @@ public class MediaSession { impl.setSessionExtras(controller, sessionExtras); } + /** Returns the {@link BitmapLoader}. */ + @UnstableApi + public BitmapLoader getBitmapLoader() { + return impl.getBitmapLoader(); + } + /** * Sends a custom command to a specific controller. * @@ -1218,6 +1254,7 @@ public class MediaSession { /* package */ C callback; /* package */ @Nullable PendingIntent sessionActivity; /* package */ Bundle extras; + /* package */ @MonotonicNonNull BitmapLoader bitmapLoader; public BuilderBase(Context context, Player player, C callback) { this.context = checkNotNull(context); @@ -1252,6 +1289,12 @@ public class MediaSession { return (U) this; } + @SuppressWarnings("unchecked") + public U setBitmapLoader(BitmapLoader bitmapLoader) { + this.bitmapLoader = bitmapLoader; + return (U) this; + } + public abstract T build(); } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java index 1bf05a7255..68f8eee074 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -116,6 +116,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; private final PendingIntent mediaButtonIntent; @Nullable private final BroadcastReceiver broadcastReceiver; private final Handler applicationHandler; + private final BitmapLoader bitmapLoader; @Nullable private PlayerListener playerListener; @@ -139,7 +140,8 @@ import org.checkerframework.checker.initialization.qual.Initialized; Player player, @Nullable PendingIntent sessionActivity, MediaSession.Callback callback, - Bundle tokenExtras) { + Bundle tokenExtras, + BitmapLoader bitmapLoader) { this.context = context; this.instance = instance; @@ -152,6 +154,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; applicationHandler = new Handler(player.getApplicationLooper()); this.callback = callback; + this.bitmapLoader = bitmapLoader; playerInfo = PlayerInfo.DEFAULT; onPlayerInfoChangedHandler = new PlayerInfoChangedHandler(player.getApplicationLooper()); @@ -357,6 +360,10 @@ import org.checkerframework.checker.initialization.qual.Initialized; } } + public BitmapLoader getBitmapLoader() { + return bitmapLoader; + } + public void setAvailableCommands( ControllerInfo controller, SessionCommands sessionCommands, Player.Commands playerCommands) { if (sessionStub.getConnectedControllersManager().isConnected(controller)) { diff --git a/libraries/session/src/test/java/androidx/media3/session/DefaultMediaNotificationProviderTest.java b/libraries/session/src/test/java/androidx/media3/session/DefaultMediaNotificationProviderTest.java index 23b15cb7fe..bf23e9c894 100644 --- a/libraries/session/src/test/java/androidx/media3/session/DefaultMediaNotificationProviderTest.java +++ b/libraries/session/src/test/java/androidx/media3/session/DefaultMediaNotificationProviderTest.java @@ -418,10 +418,10 @@ public class DefaultMediaNotificationProviderTest { new DefaultActionFactory(Robolectric.setupService(TestService.class)); BitmapLoader mockBitmapLoader = mock(BitmapLoader.class); SettableFuture bitmapFuture = SettableFuture.create(); - when(mockBitmapLoader.loadBitmap(any())).thenReturn(bitmapFuture); + when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(bitmapFuture); + when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader); DefaultMediaNotificationProvider defaultMediaNotificationProvider = new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext()) - .setBitmapLoader(mockBitmapLoader) .build(); // Ask the notification provider to create a notification twice. Use separate callback instances @@ -456,6 +456,9 @@ public class DefaultMediaNotificationProviderTest { DefaultMediaNotificationProvider defaultMediaNotificationProvider = new DefaultMediaNotificationProvider.Builder(context).build(); MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY); + BitmapLoader mockBitmapLoader = mock(BitmapLoader.class); + when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null); + when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader); DefaultActionFactory defaultActionFactory = new DefaultActionFactory(Robolectric.setupService(TestService.class)); @@ -487,6 +490,9 @@ public class DefaultMediaNotificationProviderTest { .setChannelName(/* channelNameResourceId= */ R.string.media3_controls_play_description) .build(); MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY); + BitmapLoader mockBitmapLoader = mock(BitmapLoader.class); + when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null); + when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader); DefaultActionFactory defaultActionFactory = new DefaultActionFactory(Robolectric.setupService(TestService.class)); @@ -521,6 +527,9 @@ public class DefaultMediaNotificationProviderTest { .setChannelName(/* channelNameResourceId= */ R.string.media3_controls_play_description) .build(); MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY); + BitmapLoader mockBitmapLoader = mock(BitmapLoader.class); + when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null); + when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader); DefaultActionFactory defaultActionFactory = new DefaultActionFactory(Robolectric.setupService(TestService.class)); @@ -542,6 +551,9 @@ public class DefaultMediaNotificationProviderTest { DefaultActionFactory defaultActionFactory = new DefaultActionFactory(Robolectric.setupService(TestService.class)); MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY); + BitmapLoader mockBitmapLoader = mock(BitmapLoader.class); + when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null); + when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader); MediaNotification notification = defaultMediaNotificationProvider.createNotification( @@ -574,6 +586,9 @@ public class DefaultMediaNotificationProviderTest { MediaSession mockMediaSession = createMockMediaSessionForNotification( new MediaMetadata.Builder().setTitle("title").build()); + BitmapLoader mockBitmapLoader = mock(BitmapLoader.class); + when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null); + when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader); MediaNotification notification = defaultMediaNotificationProvider.createNotification( @@ -597,6 +612,9 @@ public class DefaultMediaNotificationProviderTest { MediaSession mockMediaSession = createMockMediaSessionForNotification( new MediaMetadata.Builder().setArtist("artist").build()); + BitmapLoader mockBitmapLoader = mock(BitmapLoader.class); + when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null); + when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader); MediaNotification notification = defaultMediaNotificationProvider.createNotification(