Load bitmaps for MediaBrowserCompat
.
* Transforms the `ListenableFuture<LibraryResult<MediaItem>>` and `ListenableFuture<LibraryResult<List<MediaItem>>>` to `ListenableFuture<MediaBrowserCompat.MediaItem>` and `ListenableFuture<List<MediaBrowserCompat.MediaItem>>`, and the result will be sent out when `ListenableFuture` the `MediaBrowserCompat.MediaItem` (or the list of it) is fulfilled. * Add `artworkData` to the tests in `MediaBrowserCompatWithMediaLibraryServiceTest`. PiperOrigin-RevId: 489205547
This commit is contained in:
parent
6e73fc545d
commit
4ce171a3cf
@ -26,6 +26,7 @@ import static androidx.media3.session.LibraryResult.RESULT_SUCCESS;
|
|||||||
import static androidx.media3.session.MediaUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES;
|
import static androidx.media3.session.MediaUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.os.BadParcelableException;
|
import android.os.BadParcelableException;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
@ -37,6 +38,7 @@ import androidx.core.util.ObjectsCompat;
|
|||||||
import androidx.media.MediaBrowserServiceCompat;
|
import androidx.media.MediaBrowserServiceCompat;
|
||||||
import androidx.media.MediaSessionManager.RemoteUserInfo;
|
import androidx.media.MediaSessionManager.RemoteUserInfo;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.MediaMetadata;
|
||||||
import androidx.media3.common.util.ConditionVariable;
|
import androidx.media3.common.util.ConditionVariable;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
@ -44,14 +46,19 @@ import androidx.media3.session.MediaLibraryService.LibraryParams;
|
|||||||
import androidx.media3.session.MediaSession.ControllerCb;
|
import androidx.media3.session.MediaSession.ControllerCb;
|
||||||
import androidx.media3.session.MediaSession.ControllerInfo;
|
import androidx.media3.session.MediaSession.ControllerInfo;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.util.concurrent.AsyncFunction;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
import com.google.common.util.concurrent.SettableFuture;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of {@link MediaBrowserServiceCompat} for interoperability between {@link
|
* Implementation of {@link MediaBrowserServiceCompat} for interoperability between {@link
|
||||||
@ -218,7 +225,11 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> future =
|
ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> future =
|
||||||
librarySessionImpl.onGetChildrenOnHandler(
|
librarySessionImpl.onGetChildrenOnHandler(
|
||||||
controller, parentId, page, pageSize, params);
|
controller, parentId, page, pageSize, params);
|
||||||
sendLibraryResultWithMediaItemsWhenReady(result, future);
|
ListenableFuture<@NullableType List<MediaBrowserCompat.MediaItem>>
|
||||||
|
browserItemsFuture =
|
||||||
|
Util.transformFutureAsync(
|
||||||
|
future, createMediaItemsToBrowserItemsAsyncFunction());
|
||||||
|
sendLibraryResultWithMediaItemsWhenReady(result, browserItemsFuture);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Cannot distinguish onLoadChildren() why it's called either by
|
// Cannot distinguish onLoadChildren() why it's called either by
|
||||||
@ -236,7 +247,9 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
/* page= */ 0,
|
/* page= */ 0,
|
||||||
/* pageSize= */ Integer.MAX_VALUE,
|
/* pageSize= */ Integer.MAX_VALUE,
|
||||||
/* params= */ null);
|
/* params= */ null);
|
||||||
sendLibraryResultWithMediaItemsWhenReady(result, future);
|
ListenableFuture<@NullableType List<MediaBrowserCompat.MediaItem>> browserItemsFuture =
|
||||||
|
Util.transformFutureAsync(future, createMediaItemsToBrowserItemsAsyncFunction());
|
||||||
|
sendLibraryResultWithMediaItemsWhenReady(result, browserItemsFuture);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +277,9 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
}
|
}
|
||||||
ListenableFuture<LibraryResult<MediaItem>> future =
|
ListenableFuture<LibraryResult<MediaItem>> future =
|
||||||
librarySessionImpl.onGetItemOnHandler(controller, itemId);
|
librarySessionImpl.onGetItemOnHandler(controller, itemId);
|
||||||
sendLibraryResultWithMediaItemWhenReady(result, future);
|
ListenableFuture<MediaBrowserCompat.@NullableType MediaItem> browserItemFuture =
|
||||||
|
Util.transformFutureAsync(future, createMediaItemToBrowserItemAsyncFunction());
|
||||||
|
sendLibraryResultWithMediaItemWhenReady(result, browserItemFuture);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,17 +377,12 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
|
|
||||||
private static void sendLibraryResultWithMediaItemWhenReady(
|
private static void sendLibraryResultWithMediaItemWhenReady(
|
||||||
Result<MediaBrowserCompat.MediaItem> result,
|
Result<MediaBrowserCompat.MediaItem> result,
|
||||||
ListenableFuture<LibraryResult<MediaItem>> future) {
|
ListenableFuture<MediaBrowserCompat.@NullableType MediaItem> future) {
|
||||||
future.addListener(
|
future.addListener(
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
LibraryResult<MediaItem> libraryResult =
|
MediaBrowserCompat.MediaItem mediaItem = future.get();
|
||||||
checkNotNull(future.get(), "LibraryResult must not be null");
|
result.sendResult(mediaItem);
|
||||||
if (libraryResult.resultCode != RESULT_SUCCESS || libraryResult.value == null) {
|
|
||||||
result.sendResult(/* result= */ null);
|
|
||||||
} else {
|
|
||||||
result.sendResult(MediaUtils.convertToBrowserItem(libraryResult.value));
|
|
||||||
}
|
|
||||||
} catch (CancellationException | ExecutionException | InterruptedException unused) {
|
} catch (CancellationException | ExecutionException | InterruptedException unused) {
|
||||||
result.sendError(/* extras= */ null);
|
result.sendError(/* extras= */ null);
|
||||||
}
|
}
|
||||||
@ -382,20 +392,15 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
|
|
||||||
private static void sendLibraryResultWithMediaItemsWhenReady(
|
private static void sendLibraryResultWithMediaItemsWhenReady(
|
||||||
Result<List<MediaBrowserCompat.MediaItem>> result,
|
Result<List<MediaBrowserCompat.MediaItem>> result,
|
||||||
ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> future) {
|
ListenableFuture<@NullableType List<MediaBrowserCompat.MediaItem>> future) {
|
||||||
future.addListener(
|
future.addListener(
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
LibraryResult<ImmutableList<MediaItem>> libraryResult =
|
List<MediaBrowserCompat.MediaItem> mediaItems = future.get();
|
||||||
checkNotNull(future.get(), "LibraryResult must not be null");
|
|
||||||
if (libraryResult.resultCode != RESULT_SUCCESS || libraryResult.value == null) {
|
|
||||||
result.sendResult(/* result= */ null);
|
|
||||||
} else {
|
|
||||||
result.sendResult(
|
result.sendResult(
|
||||||
MediaUtils.truncateListBySize(
|
(mediaItems == null)
|
||||||
MediaUtils.convertToBrowserItemList(libraryResult.value),
|
? null
|
||||||
TRANSACTION_SIZE_LIMIT_IN_BYTES));
|
: MediaUtils.truncateListBySize(mediaItems, TRANSACTION_SIZE_LIMIT_IN_BYTES));
|
||||||
}
|
|
||||||
} catch (CancellationException | ExecutionException | InterruptedException unused) {
|
} catch (CancellationException | ExecutionException | InterruptedException unused) {
|
||||||
result.sendError(/* extras= */ null);
|
result.sendError(/* extras= */ null);
|
||||||
}
|
}
|
||||||
@ -403,6 +408,130 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
MoreExecutors.directExecutor());
|
MoreExecutors.directExecutor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AsyncFunction<
|
||||||
|
LibraryResult<ImmutableList<MediaItem>>, @NullableType List<MediaBrowserCompat.MediaItem>>
|
||||||
|
createMediaItemsToBrowserItemsAsyncFunction() {
|
||||||
|
return result -> {
|
||||||
|
checkNotNull(result, "LibraryResult must not be null");
|
||||||
|
SettableFuture<@NullableType List<MediaBrowserCompat.MediaItem>> outputFuture =
|
||||||
|
SettableFuture.create();
|
||||||
|
if (result.resultCode != RESULT_SUCCESS || result.value == null) {
|
||||||
|
outputFuture.set(null);
|
||||||
|
return outputFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableList<MediaItem> mediaItems = result.value;
|
||||||
|
if (mediaItems.isEmpty()) {
|
||||||
|
outputFuture.set(new ArrayList<>());
|
||||||
|
return outputFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<@NullableType ListenableFuture<Bitmap>> bitmapFutures = new ArrayList<>();
|
||||||
|
outputFuture.addListener(
|
||||||
|
() -> {
|
||||||
|
if (outputFuture.isCancelled()) {
|
||||||
|
cancelAllFutures(bitmapFutures);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MoreExecutors.directExecutor());
|
||||||
|
|
||||||
|
final AtomicInteger resultCount = new AtomicInteger(0);
|
||||||
|
Runnable handleBitmapFuturesTask =
|
||||||
|
() -> {
|
||||||
|
int completedBitmapFutureCount = resultCount.incrementAndGet();
|
||||||
|
if (completedBitmapFutureCount == mediaItems.size()) {
|
||||||
|
handleBitmapFuturesAllCompletedAndSetOutputFuture(
|
||||||
|
bitmapFutures, mediaItems, outputFuture);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < mediaItems.size(); i++) {
|
||||||
|
MediaItem mediaItem = mediaItems.get(i);
|
||||||
|
MediaMetadata metadata = mediaItem.mediaMetadata;
|
||||||
|
if (metadata.artworkData == null) {
|
||||||
|
bitmapFutures.add(null);
|
||||||
|
handleBitmapFuturesTask.run();
|
||||||
|
} else {
|
||||||
|
ListenableFuture<Bitmap> bitmapFuture =
|
||||||
|
librarySessionImpl.getBitmapLoader().decodeBitmap(metadata.artworkData);
|
||||||
|
bitmapFutures.add(bitmapFuture);
|
||||||
|
bitmapFuture.addListener(handleBitmapFuturesTask, MoreExecutors.directExecutor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputFuture;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleBitmapFuturesAllCompletedAndSetOutputFuture(
|
||||||
|
List<@NullableType ListenableFuture<Bitmap>> bitmapFutures,
|
||||||
|
List<MediaItem> mediaItems,
|
||||||
|
SettableFuture<@NullableType List<MediaBrowserCompat.MediaItem>> outputFuture) {
|
||||||
|
List<MediaBrowserCompat.MediaItem> outputMediaItems = new ArrayList<>();
|
||||||
|
for (int i = 0; i < bitmapFutures.size(); i++) {
|
||||||
|
@Nullable ListenableFuture<Bitmap> future = bitmapFutures.get(i);
|
||||||
|
@Nullable Bitmap bitmap = null;
|
||||||
|
if (future != null) {
|
||||||
|
try {
|
||||||
|
bitmap = Futures.getDone(future);
|
||||||
|
} catch (CancellationException | ExecutionException e) {
|
||||||
|
Log.d(TAG, "Failed to get bitmap");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputMediaItems.add(MediaUtils.convertToBrowserItem(mediaItems.get(i), bitmap));
|
||||||
|
}
|
||||||
|
outputFuture.set(outputMediaItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> void cancelAllFutures(List<@NullableType ListenableFuture<T>> futures) {
|
||||||
|
for (int i = 0; i < futures.size(); i++) {
|
||||||
|
if (futures.get(i) != null) {
|
||||||
|
futures.get(i).cancel(/* mayInterruptIfRunning= */ false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AsyncFunction<LibraryResult<MediaItem>, MediaBrowserCompat.@NullableType MediaItem>
|
||||||
|
createMediaItemToBrowserItemAsyncFunction() {
|
||||||
|
return result -> {
|
||||||
|
checkNotNull(result, "LibraryResult must not be null");
|
||||||
|
SettableFuture<MediaBrowserCompat.@NullableType MediaItem> outputFuture =
|
||||||
|
SettableFuture.create();
|
||||||
|
if (result.resultCode != RESULT_SUCCESS || result.value == null) {
|
||||||
|
outputFuture.set(null);
|
||||||
|
return outputFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaItem mediaItem = result.value;
|
||||||
|
MediaMetadata metadata = mediaItem.mediaMetadata;
|
||||||
|
if (metadata.artworkData == null) {
|
||||||
|
outputFuture.set(MediaUtils.convertToBrowserItem(mediaItem, /* artworkBitmap= */ null));
|
||||||
|
return outputFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListenableFuture<Bitmap> bitmapFuture =
|
||||||
|
librarySessionImpl.getBitmapLoader().decodeBitmap(metadata.artworkData);
|
||||||
|
outputFuture.addListener(
|
||||||
|
() -> {
|
||||||
|
if (outputFuture.isCancelled()) {
|
||||||
|
bitmapFuture.cancel(/* mayInterruptIfRunning= */ false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MoreExecutors.directExecutor());
|
||||||
|
bitmapFuture.addListener(
|
||||||
|
() -> {
|
||||||
|
@Nullable Bitmap bitmap = null;
|
||||||
|
try {
|
||||||
|
bitmap = Futures.getDone(bitmapFuture);
|
||||||
|
} catch (CancellationException | ExecutionException e) {
|
||||||
|
Log.d(TAG, "failed to get bitmap");
|
||||||
|
}
|
||||||
|
outputFuture.set(MediaUtils.convertToBrowserItem(mediaItem, bitmap));
|
||||||
|
},
|
||||||
|
MoreExecutors.directExecutor());
|
||||||
|
return outputFuture;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static <T> void ignoreFuture(Future<T> unused) {
|
private static <T> void ignoreFuture(Future<T> unused) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
@ -504,7 +633,9 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> future =
|
ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> future =
|
||||||
librarySessionImpl.onGetSearchResultOnHandler(
|
librarySessionImpl.onGetSearchResultOnHandler(
|
||||||
request.controller, request.query, page, pageSize, libraryParams);
|
request.controller, request.query, page, pageSize, libraryParams);
|
||||||
sendLibraryResultWithMediaItemsWhenReady(request.result, future);
|
ListenableFuture<@NullableType List<MediaBrowserCompat.MediaItem>> mediaItemsFuture =
|
||||||
|
Util.transformFutureAsync(future, createMediaItemsToBrowserItemsAsyncFunction());
|
||||||
|
sendLibraryResultWithMediaItemsWhenReady(request.result, mediaItemsFuture);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -136,9 +136,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
errorMessage, /* cause= */ null, PlaybackException.ERROR_CODE_REMOTE_ERROR);
|
errorMessage, /* cause= */ null, PlaybackException.ERROR_CODE_REMOTE_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Converts a {@link MediaItem} to a {@link MediaBrowserCompat.MediaItem}. */
|
public static MediaBrowserCompat.MediaItem convertToBrowserItem(
|
||||||
public static MediaBrowserCompat.MediaItem convertToBrowserItem(MediaItem item) {
|
MediaItem item, @Nullable Bitmap artworkBitmap) {
|
||||||
MediaDescriptionCompat description = convertToMediaDescriptionCompat(item);
|
MediaDescriptionCompat description = convertToMediaDescriptionCompat(item, artworkBitmap);
|
||||||
MediaMetadata metadata = item.mediaMetadata;
|
MediaMetadata metadata = item.mediaMetadata;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
if (metadata.folderType != null && metadata.folderType != MediaMetadata.FOLDER_TYPE_NONE) {
|
if (metadata.folderType != null && metadata.folderType != MediaMetadata.FOLDER_TYPE_NONE) {
|
||||||
@ -150,15 +150,6 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
return new MediaBrowserCompat.MediaItem(description, flags);
|
return new MediaBrowserCompat.MediaItem(description, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Converts a list of {@link MediaItem} to a list of {@link MediaBrowserCompat.MediaItem}. */
|
|
||||||
public static List<MediaBrowserCompat.MediaItem> convertToBrowserItemList(List<MediaItem> items) {
|
|
||||||
List<MediaBrowserCompat.MediaItem> result = new ArrayList<>();
|
|
||||||
for (int i = 0; i < items.size(); i++) {
|
|
||||||
result.add(convertToBrowserItem(items.get(i)));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Converts a {@link MediaBrowserCompat.MediaItem} to a {@link MediaItem}. */
|
/** Converts a {@link MediaBrowserCompat.MediaItem} to a {@link MediaItem}. */
|
||||||
public static MediaItem convertToMediaItem(MediaBrowserCompat.MediaItem item) {
|
public static MediaItem convertToMediaItem(MediaBrowserCompat.MediaItem item) {
|
||||||
return convertToMediaItem(item.getDescription(), item.isBrowsable(), item.isPlayable());
|
return convertToMediaItem(item.getDescription(), item.isBrowsable(), item.isPlayable());
|
||||||
@ -320,16 +311,32 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Converts a {@link MediaItem} to a {@link MediaDescriptionCompat}. */
|
/**
|
||||||
|
* Converts a {@link MediaItem} to a {@link MediaDescriptionCompat}.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #convertToMediaDescriptionCompat(MediaItem, Bitmap)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public static MediaDescriptionCompat convertToMediaDescriptionCompat(MediaItem item) {
|
public static MediaDescriptionCompat convertToMediaDescriptionCompat(MediaItem item) {
|
||||||
|
MediaMetadata metadata = item.mediaMetadata;
|
||||||
|
@Nullable Bitmap artworkBitmap = null;
|
||||||
|
if (metadata.artworkData != null) {
|
||||||
|
artworkBitmap =
|
||||||
|
BitmapFactory.decodeByteArray(metadata.artworkData, 0, metadata.artworkData.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return convertToMediaDescriptionCompat(item, artworkBitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts a {@link MediaItem} to a {@link MediaDescriptionCompat} */
|
||||||
|
public static MediaDescriptionCompat convertToMediaDescriptionCompat(
|
||||||
|
MediaItem item, @Nullable Bitmap artworkBitmap) {
|
||||||
MediaDescriptionCompat.Builder builder =
|
MediaDescriptionCompat.Builder builder =
|
||||||
new MediaDescriptionCompat.Builder()
|
new MediaDescriptionCompat.Builder()
|
||||||
.setMediaId(item.mediaId.equals(MediaItem.DEFAULT_MEDIA_ID) ? null : item.mediaId);
|
.setMediaId(item.mediaId.equals(MediaItem.DEFAULT_MEDIA_ID) ? null : item.mediaId);
|
||||||
MediaMetadata metadata = item.mediaMetadata;
|
MediaMetadata metadata = item.mediaMetadata;
|
||||||
if (metadata.artworkData != null) {
|
if (artworkBitmap != null) {
|
||||||
Bitmap artwork =
|
builder.setIconBitmap(artworkBitmap);
|
||||||
BitmapFactory.decodeByteArray(metadata.artworkData, 0, metadata.artworkData.length);
|
|
||||||
builder.setIconBitmap(artwork);
|
|
||||||
}
|
}
|
||||||
@Nullable Bundle extras = metadata.extras;
|
@Nullable Bundle extras = metadata.extras;
|
||||||
if (metadata.folderType != null && metadata.folderType != MediaMetadata.FOLDER_TYPE_NONE) {
|
if (metadata.folderType != null && metadata.folderType != MediaMetadata.FOLDER_TYPE_NONE) {
|
||||||
|
@ -129,6 +129,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
|||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
assertThat(itemRef.get().getMediaId()).isEqualTo(mediaId);
|
assertThat(itemRef.get().getMediaId()).isEqualTo(mediaId);
|
||||||
assertThat(itemRef.get().isBrowsable()).isTrue();
|
assertThat(itemRef.get().isBrowsable()).isTrue();
|
||||||
|
assertThat(itemRef.get().getDescription().getIconBitmap()).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -151,6 +152,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
|||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
assertThat(itemRef.get().getMediaId()).isEqualTo(mediaId);
|
assertThat(itemRef.get().getMediaId()).isEqualTo(mediaId);
|
||||||
assertThat(itemRef.get().isPlayable()).isTrue();
|
assertThat(itemRef.get().isPlayable()).isTrue();
|
||||||
|
assertThat(itemRef.get().getDescription().getIconBitmap()).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -181,6 +183,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
|||||||
BundleSubject.assertThat(description.getExtras())
|
BundleSubject.assertThat(description.getExtras())
|
||||||
.string(METADATA_EXTRA_KEY)
|
.string(METADATA_EXTRA_KEY)
|
||||||
.isEqualTo(METADATA_EXTRA_VALUE);
|
.isEqualTo(METADATA_EXTRA_VALUE);
|
||||||
|
assertThat(description.getIconBitmap()).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -245,6 +248,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
|||||||
EXTRAS_KEY_COMPLETION_STATUS,
|
EXTRAS_KEY_COMPLETION_STATUS,
|
||||||
/* defaultValue= */ EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED + 1))
|
/* defaultValue= */ EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED + 1))
|
||||||
.isEqualTo(EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
|
.isEqualTo(EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
|
||||||
|
assertThat(mediaItem.getDescription().getIconBitmap()).isNotNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,6 +315,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
|||||||
int relativeIndex = originalIndex - fromIndex;
|
int relativeIndex = originalIndex - fromIndex;
|
||||||
assertThat(children.get(relativeIndex).getMediaId())
|
assertThat(children.get(relativeIndex).getMediaId())
|
||||||
.isEqualTo(GET_CHILDREN_RESULT.get(originalIndex));
|
.isEqualTo(GET_CHILDREN_RESULT.get(originalIndex));
|
||||||
|
assertThat(children.get(relativeIndex).getDescription().getIconBitmap()).isNotNull();
|
||||||
}
|
}
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ import android.support.v4.media.RatingCompat;
|
|||||||
import android.support.v4.media.session.MediaControllerCompat;
|
import android.support.v4.media.session.MediaControllerCompat;
|
||||||
import android.support.v4.media.session.MediaSessionCompat;
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
import android.support.v4.media.session.PlaybackStateCompat;
|
import android.support.v4.media.session.PlaybackStateCompat;
|
||||||
import android.text.TextUtils;
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media.AudioAttributesCompat;
|
import androidx.media.AudioAttributesCompat;
|
||||||
import androidx.media3.common.AudioAttributes;
|
import androidx.media3.common.AudioAttributes;
|
||||||
@ -71,23 +70,6 @@ public final class MediaUtilsTest {
|
|||||||
bitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader());
|
bitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void convertToBrowserItem() {
|
|
||||||
String mediaId = "testId";
|
|
||||||
CharSequence trackTitle = "testTitle";
|
|
||||||
MediaItem mediaItem =
|
|
||||||
new MediaItem.Builder()
|
|
||||||
.setMediaId(mediaId)
|
|
||||||
.setMediaMetadata(new MediaMetadata.Builder().setTitle(trackTitle).build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
MediaBrowserCompat.MediaItem browserItem = MediaUtils.convertToBrowserItem(mediaItem);
|
|
||||||
|
|
||||||
assertThat(browserItem.getDescription()).isNotNull();
|
|
||||||
assertThat(browserItem.getDescription().getMediaId()).isEqualTo(mediaId);
|
|
||||||
assertThat(TextUtils.equals(browserItem.getDescription().getTitle(), trackTitle)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertToMediaItem_browserItemToMediaItem() {
|
public void convertToMediaItem_browserItemToMediaItem() {
|
||||||
String mediaId = "testId";
|
String mediaId = "testId";
|
||||||
@ -115,18 +97,6 @@ public final class MediaUtilsTest {
|
|||||||
assertThat(mediaItem.mediaMetadata.title.toString()).isEqualTo(title);
|
assertThat(mediaItem.mediaMetadata.title.toString()).isEqualTo(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void convertToBrowserItemList() {
|
|
||||||
int size = 3;
|
|
||||||
List<MediaItem> mediaItems = MediaTestUtils.createMediaItems(size);
|
|
||||||
List<MediaBrowserCompat.MediaItem> browserItems =
|
|
||||||
MediaUtils.convertToBrowserItemList(mediaItems);
|
|
||||||
assertThat(browserItems).hasSize(size);
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
assertThat(browserItems.get(i).getMediaId()).isEqualTo(mediaItems.get(i).mediaId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertBrowserItemListToMediaItemList() {
|
public void convertBrowserItemListToMediaItemList() {
|
||||||
int size = 3;
|
int size = 3;
|
||||||
|
@ -51,6 +51,7 @@ import static androidx.media3.test.session.common.MediaBrowserConstants.SUBSCRIB
|
|||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.SUBSCRIBE_ID_NOTIFY_CHILDREN_CHANGED_TO_ONE;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.SUBSCRIBE_ID_NOTIFY_CHILDREN_CHANGED_TO_ONE;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.SUBSCRIBE_ID_NOTIFY_CHILDREN_CHANGED_TO_ONE_WITH_NON_SUBSCRIBED_ID;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.SUBSCRIBE_ID_NOTIFY_CHILDREN_CHANGED_TO_ONE_WITH_NON_SUBSCRIBED_ID;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
@ -72,6 +73,7 @@ import androidx.media3.test.session.common.TestUtils;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
@ -92,6 +94,8 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
|||||||
public static final String CONNECTION_HINTS_KEY_REMOVE_COMMAND_CODE_LIBRARY_SEARCH =
|
public static final String CONNECTION_HINTS_KEY_REMOVE_COMMAND_CODE_LIBRARY_SEARCH =
|
||||||
"CONNECTION_HINTS_KEY_REMOVE_SEARCH_SESSION_COMMAND";
|
"CONNECTION_HINTS_KEY_REMOVE_SEARCH_SESSION_COMMAND";
|
||||||
|
|
||||||
|
private static final String TEST_IMAGE_PATH = "media/png/non-motion-photo-shortened.png";
|
||||||
|
|
||||||
public static final MediaItem ROOT_ITEM =
|
public static final MediaItem ROOT_ITEM =
|
||||||
new MediaItem.Builder()
|
new MediaItem.Builder()
|
||||||
.setMediaId(ROOT_ID)
|
.setMediaId(ROOT_ID)
|
||||||
@ -115,6 +119,8 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private static LibraryParams expectedParams;
|
private static LibraryParams expectedParams;
|
||||||
|
|
||||||
|
@Nullable private static byte[] testArtworkData;
|
||||||
|
|
||||||
MediaLibrarySession session;
|
MediaLibrarySession session;
|
||||||
TestHandler handler;
|
TestHandler handler;
|
||||||
HandlerThread handlerThread;
|
HandlerThread handlerThread;
|
||||||
@ -238,7 +244,8 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
|||||||
LibraryResult.ofItem(createBrowsableMediaItem(mediaId), /* params= */ null));
|
LibraryResult.ofItem(createBrowsableMediaItem(mediaId), /* params= */ null));
|
||||||
case MEDIA_ID_GET_PLAYABLE_ITEM:
|
case MEDIA_ID_GET_PLAYABLE_ITEM:
|
||||||
return Futures.immediateFuture(
|
return Futures.immediateFuture(
|
||||||
LibraryResult.ofItem(createPlayableMediaItem(mediaId), /* params= */ null));
|
LibraryResult.ofItem(
|
||||||
|
createPlayableMediaItemWithArtworkData(mediaId), /* params= */ null));
|
||||||
case MEDIA_ID_GET_ITEM_WITH_METADATA:
|
case MEDIA_ID_GET_ITEM_WITH_METADATA:
|
||||||
return Futures.immediateFuture(
|
return Futures.immediateFuture(
|
||||||
LibraryResult.ofItem(createMediaItemWithMetadata(mediaId), /* params= */ null));
|
LibraryResult.ofItem(createMediaItemWithMetadata(mediaId), /* params= */ null));
|
||||||
@ -445,20 +452,32 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
|||||||
// Create a list of MediaItem from the list of media IDs.
|
// Create a list of MediaItem from the list of media IDs.
|
||||||
List<MediaItem> result = new ArrayList<>();
|
List<MediaItem> result = new ArrayList<>();
|
||||||
for (int i = 0; i < paginatedMediaIdList.size(); i++) {
|
for (int i = 0; i < paginatedMediaIdList.size(); i++) {
|
||||||
result.add(createPlayableMediaItem(paginatedMediaIdList.get(i)));
|
result.add(createPlayableMediaItemWithArtworkData(paginatedMediaIdList.get(i)));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MediaItem createBrowsableMediaItem(String mediaId) {
|
private MediaItem createBrowsableMediaItem(String mediaId) {
|
||||||
MediaMetadata mediaMetadata =
|
MediaMetadata mediaMetadata =
|
||||||
new MediaMetadata.Builder()
|
new MediaMetadata.Builder()
|
||||||
.setFolderType(MediaMetadata.FOLDER_TYPE_MIXED)
|
.setFolderType(MediaMetadata.FOLDER_TYPE_MIXED)
|
||||||
.setIsPlayable(false)
|
.setIsPlayable(false)
|
||||||
|
.setArtworkData(getArtworkData(), MediaMetadata.PICTURE_TYPE_FRONT_COVER)
|
||||||
.build();
|
.build();
|
||||||
return new MediaItem.Builder().setMediaId(mediaId).setMediaMetadata(mediaMetadata).build();
|
return new MediaItem.Builder().setMediaId(mediaId).setMediaMetadata(mediaMetadata).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MediaItem createPlayableMediaItemWithArtworkData(String mediaId) {
|
||||||
|
MediaItem mediaItem = createPlayableMediaItem(mediaId);
|
||||||
|
MediaMetadata mediaMetadataWithArtwork =
|
||||||
|
mediaItem
|
||||||
|
.mediaMetadata
|
||||||
|
.buildUpon()
|
||||||
|
.setArtworkData(getArtworkData(), MediaMetadata.PICTURE_TYPE_FRONT_COVER)
|
||||||
|
.build();
|
||||||
|
return mediaItem.buildUpon().setMediaMetadata(mediaMetadataWithArtwork).build();
|
||||||
|
}
|
||||||
|
|
||||||
private static MediaItem createPlayableMediaItem(String mediaId) {
|
private static MediaItem createPlayableMediaItem(String mediaId) {
|
||||||
Bundle extras = new Bundle();
|
Bundle extras = new Bundle();
|
||||||
extras.putInt(EXTRAS_KEY_COMPLETION_STATUS, EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
|
extras.putInt(EXTRAS_KEY_COMPLETION_STATUS, EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
|
||||||
@ -471,15 +490,32 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
|||||||
return new MediaItem.Builder().setMediaId(mediaId).setMediaMetadata(mediaMetadata).build();
|
return new MediaItem.Builder().setMediaId(mediaId).setMediaMetadata(mediaMetadata).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MediaItem createMediaItemWithMetadata(String mediaId) {
|
private MediaItem createMediaItemWithMetadata(String mediaId) {
|
||||||
MediaMetadata mediaMetadata = MediaTestUtils.createMediaMetadata();
|
MediaMetadata mediaMetadataWithArtwork =
|
||||||
|
MediaTestUtils.createMediaMetadata()
|
||||||
|
.buildUpon()
|
||||||
|
.setArtworkData(getArtworkData(), MediaMetadata.PICTURE_TYPE_FRONT_COVER)
|
||||||
|
.build();
|
||||||
return new MediaItem.Builder()
|
return new MediaItem.Builder()
|
||||||
.setMediaId(mediaId)
|
.setMediaId(mediaId)
|
||||||
.setRequestMetadata(
|
.setRequestMetadata(
|
||||||
new MediaItem.RequestMetadata.Builder()
|
new MediaItem.RequestMetadata.Builder()
|
||||||
.setMediaUri(CommonConstants.METADATA_MEDIA_URI)
|
.setMediaUri(CommonConstants.METADATA_MEDIA_URI)
|
||||||
.build())
|
.build())
|
||||||
.setMediaMetadata(mediaMetadata)
|
.setMediaMetadata(mediaMetadataWithArtwork)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] getArtworkData() {
|
||||||
|
if (testArtworkData != null) {
|
||||||
|
return testArtworkData;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
testArtworkData =
|
||||||
|
TestUtils.getByteArrayForScaledBitmap(getApplicationContext(), TEST_IMAGE_PATH);
|
||||||
|
} catch (IOException e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
return testArtworkData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user