mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
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
This commit is contained in:
parent
026aea7d3d
commit
3f69df72db
8 changed files with 207 additions and 50 deletions
|
|
@ -21,6 +21,7 @@ import static org.junit.Assert.assertThrows;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import androidx.media3.common.MediaMetadata;
|
||||||
import androidx.media3.test.utils.TestUtil;
|
import androidx.media3.test.utils.TestUtil;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
@ -166,6 +167,64 @@ public class SimpleBitmapLoaderTest {
|
||||||
/* messagePart= */ "unknown protocol");
|
/* 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<Bitmap> bitmapFuture = bitmapLoader.loadBitmapFromMetadata(metadata);
|
||||||
|
|
||||||
|
assertThat(bitmapFuture).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
private static void assertException(
|
private static void assertException(
|
||||||
ThrowingRunnable runnable, Class<? extends Exception> clazz, String messagePart) {
|
ThrowingRunnable runnable, Class<? extends Exception> clazz, String messagePart) {
|
||||||
ExecutionException executionException = assertThrows(ExecutionException.class, runnable);
|
ExecutionException executionException = assertThrows(ExecutionException.class, runnable);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ package androidx.media3.session;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.media3.common.MediaMetadata;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
|
|
@ -25,6 +27,29 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||||
public interface BitmapLoader {
|
public interface BitmapLoader {
|
||||||
/** Decodes an image from compressed binary data. */
|
/** Decodes an image from compressed binary data. */
|
||||||
ListenableFuture<Bitmap> decodeBitmap(byte[] data);
|
ListenableFuture<Bitmap> decodeBitmap(byte[] data);
|
||||||
|
|
||||||
/** Loads an image from {@code uri}. */
|
/** Loads an image from {@code uri}. */
|
||||||
ListenableFuture<Bitmap> loadBitmap(Uri uri);
|
ListenableFuture<Bitmap> loadBitmap(Uri uri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an image from {@link MediaMetadata}. Returns null if {@code metadata} doesn't contain
|
||||||
|
* bitmap information.
|
||||||
|
*
|
||||||
|
* <p>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<Bitmap> loadBitmapFromMetadata(MediaMetadata metadata) {
|
||||||
|
@Nullable ListenableFuture<Bitmap> future;
|
||||||
|
if (metadata.artworkData != null) {
|
||||||
|
future = decodeBitmap(metadata.artworkData);
|
||||||
|
} else if (metadata.artworkUri != null) {
|
||||||
|
future = loadBitmap(metadata.artworkUri);
|
||||||
|
} else {
|
||||||
|
future = null;
|
||||||
|
}
|
||||||
|
return future;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,6 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||||
private NotificationIdProvider notificationIdProvider;
|
private NotificationIdProvider notificationIdProvider;
|
||||||
private String channelId;
|
private String channelId;
|
||||||
@StringRes private int channelNameResourceId;
|
@StringRes private int channelNameResourceId;
|
||||||
private BitmapLoader bitmapLoader;
|
|
||||||
private boolean built;
|
private boolean built;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -136,7 +135,6 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||||
notificationIdProvider = session -> DEFAULT_NOTIFICATION_ID;
|
notificationIdProvider = session -> DEFAULT_NOTIFICATION_ID;
|
||||||
channelId = DEFAULT_CHANNEL_ID;
|
channelId = DEFAULT_CHANNEL_ID;
|
||||||
channelNameResourceId = DEFAULT_CHANNEL_NAME_RESOURCE_ID;
|
channelNameResourceId = DEFAULT_CHANNEL_NAME_RESOURCE_ID;
|
||||||
bitmapLoader = new SimpleBitmapLoader();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -196,19 +194,6 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||||
return this;
|
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.
|
* 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;
|
private final String channelId;
|
||||||
@StringRes private final int channelNameResourceId;
|
@StringRes private final int channelNameResourceId;
|
||||||
private final NotificationManager notificationManager;
|
private final NotificationManager notificationManager;
|
||||||
private final BitmapLoader bitmapLoader;
|
|
||||||
// Cache the last bitmap load request to avoid reloading the bitmap again, particularly useful
|
// 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).
|
// when showing a notification for the same item (e.g. when switching from playing to paused).
|
||||||
private final Handler mainHandler;
|
private final Handler mainHandler;
|
||||||
|
|
@ -272,7 +256,6 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||||
this.notificationIdProvider = builder.notificationIdProvider;
|
this.notificationIdProvider = builder.notificationIdProvider;
|
||||||
this.channelId = builder.channelId;
|
this.channelId = builder.channelId;
|
||||||
this.channelNameResourceId = builder.channelNameResourceId;
|
this.channelNameResourceId = builder.channelNameResourceId;
|
||||||
this.bitmapLoader = new CacheBitmapLoader(builder.bitmapLoader);
|
|
||||||
notificationManager =
|
notificationManager =
|
||||||
checkStateNotNull(
|
checkStateNotNull(
|
||||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE));
|
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE));
|
||||||
|
|
@ -312,7 +295,9 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||||
builder
|
builder
|
||||||
.setContentTitle(getNotificationContentTitle(metadata))
|
.setContentTitle(getNotificationContentTitle(metadata))
|
||||||
.setContentText(getNotificationContentText(metadata));
|
.setContentText(getNotificationContentText(metadata));
|
||||||
@Nullable ListenableFuture<Bitmap> bitmapFuture = loadArtworkBitmap(metadata);
|
@Nullable
|
||||||
|
ListenableFuture<Bitmap> bitmapFuture =
|
||||||
|
mediaSession.getBitmapLoader().loadBitmapFromMetadata(metadata);
|
||||||
if (bitmapFuture != null) {
|
if (bitmapFuture != null) {
|
||||||
if (pendingOnBitmapLoadedFutureCallback != null) {
|
if (pendingOnBitmapLoadedFutureCallback != null) {
|
||||||
pendingOnBitmapLoadedFutureCallback.discardIfPending();
|
pendingOnBitmapLoadedFutureCallback.discardIfPending();
|
||||||
|
|
@ -578,23 +563,6 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||||
notificationManager, channelId, context.getString(channelNameResourceId));
|
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<Bitmap> loadArtworkBitmap(MediaMetadata metadata) {
|
|
||||||
@Nullable ListenableFuture<Bitmap> 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) {
|
private static long getPlaybackStartTimeEpochMs(Player player) {
|
||||||
// Changing "showWhen" causes notification flicker if SDK_INT < 21.
|
// Changing "showWhen" causes notification flicker if SDK_INT < 21.
|
||||||
if (Util.SDK_INT >= 21
|
if (Util.SDK_INT >= 21
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import static java.lang.annotation.ElementType.TYPE_USE;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
|
|
@ -420,6 +421,28 @@ public abstract class MediaLibraryService extends MediaSessionService {
|
||||||
return super.setExtras(extras);
|
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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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}.
|
* Builds a {@link MediaLibrarySession}.
|
||||||
*
|
*
|
||||||
|
|
@ -429,7 +452,11 @@ public abstract class MediaLibraryService extends MediaSessionService {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public MediaLibrarySession build() {
|
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,
|
Player player,
|
||||||
@Nullable PendingIntent sessionActivity,
|
@Nullable PendingIntent sessionActivity,
|
||||||
MediaSession.Callback callback,
|
MediaSession.Callback callback,
|
||||||
Bundle tokenExtras) {
|
Bundle tokenExtras,
|
||||||
super(context, id, player, sessionActivity, callback, tokenExtras);
|
BitmapLoader bitmapLoader) {
|
||||||
|
super(context, id, player, sessionActivity, callback, tokenExtras, bitmapLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -450,9 +478,17 @@ public abstract class MediaLibraryService extends MediaSessionService {
|
||||||
Player player,
|
Player player,
|
||||||
@Nullable PendingIntent sessionActivity,
|
@Nullable PendingIntent sessionActivity,
|
||||||
MediaSession.Callback callback,
|
MediaSession.Callback callback,
|
||||||
Bundle tokenExtras) {
|
Bundle tokenExtras,
|
||||||
|
BitmapLoader bitmapLoader) {
|
||||||
return new MediaLibrarySessionImpl(
|
return new MediaLibrarySessionImpl(
|
||||||
this, context, id, player, sessionActivity, (Callback) callback, tokenExtras);
|
this,
|
||||||
|
context,
|
||||||
|
id,
|
||||||
|
player,
|
||||||
|
sessionActivity,
|
||||||
|
(Callback) callback,
|
||||||
|
tokenExtras,
|
||||||
|
bitmapLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,9 @@ import java.util.concurrent.Future;
|
||||||
Player player,
|
Player player,
|
||||||
@Nullable PendingIntent sessionActivity,
|
@Nullable PendingIntent sessionActivity,
|
||||||
MediaLibrarySession.Callback callback,
|
MediaLibrarySession.Callback callback,
|
||||||
Bundle tokenExtras) {
|
Bundle tokenExtras,
|
||||||
super(instance, context, id, player, sessionActivity, callback, tokenExtras);
|
BitmapLoader bitmapLoader) {
|
||||||
|
super(instance, context, id, player, sessionActivity, callback, tokenExtras, bitmapLoader);
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
subscriptions = new ArrayMap<>();
|
subscriptions = new ArrayMap<>();
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
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
|
* 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);
|
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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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}.
|
* Builds a {@link MediaSession}.
|
||||||
*
|
*
|
||||||
|
|
@ -316,7 +339,11 @@ public class MediaSession {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public MediaSession build() {
|
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,
|
Player player,
|
||||||
@Nullable PendingIntent sessionActivity,
|
@Nullable PendingIntent sessionActivity,
|
||||||
Callback callback,
|
Callback callback,
|
||||||
Bundle tokenExtras) {
|
Bundle tokenExtras,
|
||||||
|
BitmapLoader bitmapLoader) {
|
||||||
synchronized (STATIC_LOCK) {
|
synchronized (STATIC_LOCK) {
|
||||||
if (SESSION_ID_TO_SESSION_MAP.containsKey(id)) {
|
if (SESSION_ID_TO_SESSION_MAP.containsKey(id)) {
|
||||||
throw new IllegalStateException("Session ID must be unique. ID=" + id);
|
throw new IllegalStateException("Session ID must be unique. ID=" + id);
|
||||||
}
|
}
|
||||||
SESSION_ID_TO_SESSION_MAP.put(id, this);
|
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(
|
/* package */ MediaSessionImpl createImpl(
|
||||||
|
|
@ -503,8 +531,10 @@ public class MediaSession {
|
||||||
Player player,
|
Player player,
|
||||||
@Nullable PendingIntent sessionActivity,
|
@Nullable PendingIntent sessionActivity,
|
||||||
Callback callback,
|
Callback callback,
|
||||||
Bundle tokenExtras) {
|
Bundle tokenExtras,
|
||||||
return new MediaSessionImpl(this, context, id, player, sessionActivity, callback, tokenExtras);
|
BitmapLoader bitmapLoader) {
|
||||||
|
return new MediaSessionImpl(
|
||||||
|
this, context, id, player, sessionActivity, callback, tokenExtras, bitmapLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ MediaSessionImpl getImpl() {
|
/* package */ MediaSessionImpl getImpl() {
|
||||||
|
|
@ -741,6 +771,12 @@ public class MediaSession {
|
||||||
impl.setSessionExtras(controller, sessionExtras);
|
impl.setSessionExtras(controller, sessionExtras);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the {@link BitmapLoader}. */
|
||||||
|
@UnstableApi
|
||||||
|
public BitmapLoader getBitmapLoader() {
|
||||||
|
return impl.getBitmapLoader();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a custom command to a specific controller.
|
* Sends a custom command to a specific controller.
|
||||||
*
|
*
|
||||||
|
|
@ -1218,6 +1254,7 @@ public class MediaSession {
|
||||||
/* package */ C callback;
|
/* package */ C callback;
|
||||||
/* package */ @Nullable PendingIntent sessionActivity;
|
/* package */ @Nullable PendingIntent sessionActivity;
|
||||||
/* package */ Bundle extras;
|
/* package */ Bundle extras;
|
||||||
|
/* package */ @MonotonicNonNull BitmapLoader bitmapLoader;
|
||||||
|
|
||||||
public BuilderBase(Context context, Player player, C callback) {
|
public BuilderBase(Context context, Player player, C callback) {
|
||||||
this.context = checkNotNull(context);
|
this.context = checkNotNull(context);
|
||||||
|
|
@ -1252,6 +1289,12 @@ public class MediaSession {
|
||||||
return (U) this;
|
return (U) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public U setBitmapLoader(BitmapLoader bitmapLoader) {
|
||||||
|
this.bitmapLoader = bitmapLoader;
|
||||||
|
return (U) this;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract T build();
|
public abstract T build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
||||||
private final PendingIntent mediaButtonIntent;
|
private final PendingIntent mediaButtonIntent;
|
||||||
@Nullable private final BroadcastReceiver broadcastReceiver;
|
@Nullable private final BroadcastReceiver broadcastReceiver;
|
||||||
private final Handler applicationHandler;
|
private final Handler applicationHandler;
|
||||||
|
private final BitmapLoader bitmapLoader;
|
||||||
|
|
||||||
@Nullable private PlayerListener playerListener;
|
@Nullable private PlayerListener playerListener;
|
||||||
|
|
||||||
|
|
@ -139,7 +140,8 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
||||||
Player player,
|
Player player,
|
||||||
@Nullable PendingIntent sessionActivity,
|
@Nullable PendingIntent sessionActivity,
|
||||||
MediaSession.Callback callback,
|
MediaSession.Callback callback,
|
||||||
Bundle tokenExtras) {
|
Bundle tokenExtras,
|
||||||
|
BitmapLoader bitmapLoader) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
|
|
||||||
|
|
@ -152,6 +154,7 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
||||||
|
|
||||||
applicationHandler = new Handler(player.getApplicationLooper());
|
applicationHandler = new Handler(player.getApplicationLooper());
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
this.bitmapLoader = bitmapLoader;
|
||||||
|
|
||||||
playerInfo = PlayerInfo.DEFAULT;
|
playerInfo = PlayerInfo.DEFAULT;
|
||||||
onPlayerInfoChangedHandler = new PlayerInfoChangedHandler(player.getApplicationLooper());
|
onPlayerInfoChangedHandler = new PlayerInfoChangedHandler(player.getApplicationLooper());
|
||||||
|
|
@ -357,6 +360,10 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BitmapLoader getBitmapLoader() {
|
||||||
|
return bitmapLoader;
|
||||||
|
}
|
||||||
|
|
||||||
public void setAvailableCommands(
|
public void setAvailableCommands(
|
||||||
ControllerInfo controller, SessionCommands sessionCommands, Player.Commands playerCommands) {
|
ControllerInfo controller, SessionCommands sessionCommands, Player.Commands playerCommands) {
|
||||||
if (sessionStub.getConnectedControllersManager().isConnected(controller)) {
|
if (sessionStub.getConnectedControllersManager().isConnected(controller)) {
|
||||||
|
|
|
||||||
|
|
@ -418,10 +418,10 @@ public class DefaultMediaNotificationProviderTest {
|
||||||
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
||||||
BitmapLoader mockBitmapLoader = mock(BitmapLoader.class);
|
BitmapLoader mockBitmapLoader = mock(BitmapLoader.class);
|
||||||
SettableFuture<Bitmap> bitmapFuture = SettableFuture.create();
|
SettableFuture<Bitmap> bitmapFuture = SettableFuture.create();
|
||||||
when(mockBitmapLoader.loadBitmap(any())).thenReturn(bitmapFuture);
|
when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(bitmapFuture);
|
||||||
|
when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader);
|
||||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||||
.setBitmapLoader(mockBitmapLoader)
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Ask the notification provider to create a notification twice. Use separate callback instances
|
// Ask the notification provider to create a notification twice. Use separate callback instances
|
||||||
|
|
@ -456,6 +456,9 @@ public class DefaultMediaNotificationProviderTest {
|
||||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
new DefaultMediaNotificationProvider.Builder(context).build();
|
new DefaultMediaNotificationProvider.Builder(context).build();
|
||||||
MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY);
|
MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY);
|
||||||
|
BitmapLoader mockBitmapLoader = mock(BitmapLoader.class);
|
||||||
|
when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null);
|
||||||
|
when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader);
|
||||||
DefaultActionFactory defaultActionFactory =
|
DefaultActionFactory defaultActionFactory =
|
||||||
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
||||||
|
|
||||||
|
|
@ -487,6 +490,9 @@ public class DefaultMediaNotificationProviderTest {
|
||||||
.setChannelName(/* channelNameResourceId= */ R.string.media3_controls_play_description)
|
.setChannelName(/* channelNameResourceId= */ R.string.media3_controls_play_description)
|
||||||
.build();
|
.build();
|
||||||
MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY);
|
MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY);
|
||||||
|
BitmapLoader mockBitmapLoader = mock(BitmapLoader.class);
|
||||||
|
when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null);
|
||||||
|
when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader);
|
||||||
DefaultActionFactory defaultActionFactory =
|
DefaultActionFactory defaultActionFactory =
|
||||||
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
||||||
|
|
||||||
|
|
@ -521,6 +527,9 @@ public class DefaultMediaNotificationProviderTest {
|
||||||
.setChannelName(/* channelNameResourceId= */ R.string.media3_controls_play_description)
|
.setChannelName(/* channelNameResourceId= */ R.string.media3_controls_play_description)
|
||||||
.build();
|
.build();
|
||||||
MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY);
|
MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY);
|
||||||
|
BitmapLoader mockBitmapLoader = mock(BitmapLoader.class);
|
||||||
|
when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null);
|
||||||
|
when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader);
|
||||||
DefaultActionFactory defaultActionFactory =
|
DefaultActionFactory defaultActionFactory =
|
||||||
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
||||||
|
|
||||||
|
|
@ -542,6 +551,9 @@ public class DefaultMediaNotificationProviderTest {
|
||||||
DefaultActionFactory defaultActionFactory =
|
DefaultActionFactory defaultActionFactory =
|
||||||
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
||||||
MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY);
|
MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY);
|
||||||
|
BitmapLoader mockBitmapLoader = mock(BitmapLoader.class);
|
||||||
|
when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null);
|
||||||
|
when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader);
|
||||||
|
|
||||||
MediaNotification notification =
|
MediaNotification notification =
|
||||||
defaultMediaNotificationProvider.createNotification(
|
defaultMediaNotificationProvider.createNotification(
|
||||||
|
|
@ -574,6 +586,9 @@ public class DefaultMediaNotificationProviderTest {
|
||||||
MediaSession mockMediaSession =
|
MediaSession mockMediaSession =
|
||||||
createMockMediaSessionForNotification(
|
createMockMediaSessionForNotification(
|
||||||
new MediaMetadata.Builder().setTitle("title").build());
|
new MediaMetadata.Builder().setTitle("title").build());
|
||||||
|
BitmapLoader mockBitmapLoader = mock(BitmapLoader.class);
|
||||||
|
when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null);
|
||||||
|
when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader);
|
||||||
|
|
||||||
MediaNotification notification =
|
MediaNotification notification =
|
||||||
defaultMediaNotificationProvider.createNotification(
|
defaultMediaNotificationProvider.createNotification(
|
||||||
|
|
@ -597,6 +612,9 @@ public class DefaultMediaNotificationProviderTest {
|
||||||
MediaSession mockMediaSession =
|
MediaSession mockMediaSession =
|
||||||
createMockMediaSessionForNotification(
|
createMockMediaSessionForNotification(
|
||||||
new MediaMetadata.Builder().setArtist("artist").build());
|
new MediaMetadata.Builder().setArtist("artist").build());
|
||||||
|
BitmapLoader mockBitmapLoader = mock(BitmapLoader.class);
|
||||||
|
when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null);
|
||||||
|
when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader);
|
||||||
|
|
||||||
MediaNotification notification =
|
MediaNotification notification =
|
||||||
defaultMediaNotificationProvider.createNotification(
|
defaultMediaNotificationProvider.createNotification(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue