From 72b7019578f3051e1bec826cf0ac401a86d818dc Mon Sep 17 00:00:00 2001 From: ibaker Date: Wed, 1 Nov 2023 08:02:33 -0700 Subject: [PATCH] Split media1/media3 conversion methods out of `MediaUtils` Android Studio removed some nested imports, but I think the extra qualification at the usage site is actually mostly helpful, so I'm leaving it as-is. PiperOrigin-RevId: 578518880 --- .../media3/session/LegacyConversions.java | 1366 +++++++++++++++++ .../session/MediaBrowserImplLegacy.java | 15 +- .../session/MediaControllerImplBase.java | 6 +- .../session/MediaControllerImplLegacy.java | 81 +- .../MediaLibraryServiceLegacyStub.java | 22 +- .../session/MediaSessionLegacyStub.java | 28 +- .../androidx/media3/session/MediaUtils.java | 1329 +--------------- .../media3/session/PlayerWrapper.java | 5 +- .../media3/session/QueueTimeline.java | 2 +- .../media3/session/LegacyConversionsTest.java | 1062 +++++++++++++ ...lerCompatCallbackWithMediaSessionTest.java | 14 +- ...tateCompatActionsWithMediaSessionTest.java | 10 +- .../session/MediaControllerListenerTest.java | 2 +- ...aSessionCompatCallbackAggregationTest.java | 17 +- ...aControllerWithMediaSessionCompatTest.java | 20 +- ...CallbackWithMediaControllerCompatTest.java | 6 +- ...CompatCallbackWithMediaControllerTest.java | 4 +- .../session/MediaSessionServiceTest.java | 4 +- .../media3/session/MediaUtilsTest.java | 1016 ------------ .../media3/session/MediaTestUtils.java | 5 +- 20 files changed, 2564 insertions(+), 2450 deletions(-) create mode 100644 libraries/session/src/main/java/androidx/media3/session/LegacyConversions.java create mode 100644 libraries/test_session_current/src/androidTest/java/androidx/media3/session/LegacyConversionsTest.java diff --git a/libraries/session/src/main/java/androidx/media3/session/LegacyConversions.java b/libraries/session/src/main/java/androidx/media3/session/LegacyConversions.java new file mode 100644 index 0000000000..e7646445ed --- /dev/null +++ b/libraries/session/src/main/java/androidx/media3/session/LegacyConversions.java @@ -0,0 +1,1366 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.session; + +import static android.support.v4.media.session.MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS; +import static androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS; +import static androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME; +import static androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS; +import static androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS; +import static androidx.media3.common.Player.COMMAND_GET_AUDIO_ATTRIBUTES; +import static androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM; +import static androidx.media3.common.Player.COMMAND_GET_DEVICE_VOLUME; +import static androidx.media3.common.Player.COMMAND_GET_METADATA; +import static androidx.media3.common.Player.COMMAND_GET_TIMELINE; +import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE; +import static androidx.media3.common.Player.COMMAND_PREPARE; +import static androidx.media3.common.Player.COMMAND_RELEASE; +import static androidx.media3.common.Player.COMMAND_SEEK_BACK; +import static androidx.media3.common.Player.COMMAND_SEEK_FORWARD; +import static androidx.media3.common.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM; +import static androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION; +import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT; +import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM; +import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS; +import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM; +import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME; +import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS; +import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM; +import static androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE; +import static androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE; +import static androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH; +import static androidx.media3.common.Player.COMMAND_STOP; +import static androidx.media3.common.util.Assertions.checkNotNull; +import static androidx.media3.common.util.Util.constrainValue; +import static androidx.media3.session.MediaConstants.EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY; +import static java.lang.Math.max; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Bitmap; +import android.media.AudioManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.SystemClock; +import android.support.v4.media.MediaBrowserCompat; +import android.support.v4.media.MediaDescriptionCompat; +import android.support.v4.media.MediaMetadataCompat; +import android.support.v4.media.RatingCompat; +import android.support.v4.media.session.MediaControllerCompat; +import android.support.v4.media.session.MediaSessionCompat.QueueItem; +import android.support.v4.media.session.PlaybackStateCompat; +import android.support.v4.media.session.PlaybackStateCompat.CustomAction; +import android.text.TextUtils; +import androidx.annotation.Nullable; +import androidx.media.AudioAttributesCompat; +import androidx.media.MediaBrowserServiceCompat.BrowserRoot; +import androidx.media.VolumeProviderCompat; +import androidx.media3.common.AdPlaybackState; +import androidx.media3.common.AudioAttributes; +import androidx.media3.common.C; +import androidx.media3.common.DeviceInfo; +import androidx.media3.common.HeartRating; +import androidx.media3.common.MediaItem; +import androidx.media3.common.MediaMetadata; +import androidx.media3.common.PercentageRating; +import androidx.media3.common.PlaybackException; +import androidx.media3.common.PlaybackParameters; +import androidx.media3.common.Player; +import androidx.media3.common.Rating; +import androidx.media3.common.StarRating; +import androidx.media3.common.ThumbRating; +import androidx.media3.common.Timeline; +import androidx.media3.common.Timeline.Period; +import androidx.media3.common.Timeline.Window; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.Util; +import androidx.media3.session.MediaLibraryService.LibraryParams; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeoutException; + +/** Util methods for converting between {@code androidx.media3} and {@code androidx.media} types. */ +/* package*/ class LegacyConversions { + + private static final String TAG = "LegacyConversions"; + + // Stub BrowserRoot for accepting any connection here. + public static final BrowserRoot defaultBrowserRoot = + new BrowserRoot(MediaLibraryService.SERVICE_INTERFACE, null); + + public static final ImmutableSet KNOWN_METADATA_COMPAT_KEYS = + ImmutableSet.of( + MediaMetadataCompat.METADATA_KEY_TITLE, + MediaMetadataCompat.METADATA_KEY_ARTIST, + MediaMetadataCompat.METADATA_KEY_DURATION, + MediaMetadataCompat.METADATA_KEY_ALBUM, + MediaMetadataCompat.METADATA_KEY_AUTHOR, + MediaMetadataCompat.METADATA_KEY_WRITER, + MediaMetadataCompat.METADATA_KEY_COMPOSER, + MediaMetadataCompat.METADATA_KEY_COMPILATION, + MediaMetadataCompat.METADATA_KEY_DATE, + MediaMetadataCompat.METADATA_KEY_YEAR, + MediaMetadataCompat.METADATA_KEY_GENRE, + MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, + MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, + MediaMetadataCompat.METADATA_KEY_DISC_NUMBER, + MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, + MediaMetadataCompat.METADATA_KEY_ART, + MediaMetadataCompat.METADATA_KEY_ART_URI, + MediaMetadataCompat.METADATA_KEY_ALBUM_ART, + MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, + MediaMetadataCompat.METADATA_KEY_USER_RATING, + MediaMetadataCompat.METADATA_KEY_RATING, + MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, + MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, + MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, + MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, + MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, + MediaMetadataCompat.METADATA_KEY_MEDIA_ID, + MediaMetadataCompat.METADATA_KEY_MEDIA_URI, + MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE, + MediaMetadataCompat.METADATA_KEY_ADVERTISEMENT, + MediaMetadataCompat.METADATA_KEY_DOWNLOAD_STATUS, + MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT); + + /** Converts {@link PlaybackStateCompat} to {@link PlaybackException}. */ + @Nullable + public static PlaybackException convertToPlaybackException( + @Nullable PlaybackStateCompat playbackStateCompat) { + if (playbackStateCompat == null + || playbackStateCompat.getState() != PlaybackStateCompat.STATE_ERROR) { + return null; + } + StringBuilder stringBuilder = new StringBuilder(); + if (!TextUtils.isEmpty(playbackStateCompat.getErrorMessage())) { + stringBuilder.append(playbackStateCompat.getErrorMessage().toString()).append(", "); + } + stringBuilder.append("code=").append(playbackStateCompat.getErrorCode()); + String errorMessage = stringBuilder.toString(); + return new PlaybackException( + errorMessage, /* cause= */ null, PlaybackException.ERROR_CODE_REMOTE_ERROR); + } + + public static MediaBrowserCompat.MediaItem convertToBrowserItem( + MediaItem item, @Nullable Bitmap artworkBitmap) { + MediaDescriptionCompat description = convertToMediaDescriptionCompat(item, artworkBitmap); + MediaMetadata metadata = item.mediaMetadata; + int flags = 0; + if (metadata.isBrowsable != null && metadata.isBrowsable) { + flags |= MediaBrowserCompat.MediaItem.FLAG_BROWSABLE; + } + if (metadata.isPlayable != null && metadata.isPlayable) { + flags |= MediaBrowserCompat.MediaItem.FLAG_PLAYABLE; + } + return new MediaBrowserCompat.MediaItem(description, flags); + } + + /** Converts a {@link MediaBrowserCompat.MediaItem} to a {@link MediaItem}. */ + public static MediaItem convertToMediaItem(MediaBrowserCompat.MediaItem item) { + return convertToMediaItem(item.getDescription(), item.isBrowsable(), item.isPlayable()); + } + + /** Converts a {@link QueueItem} to a {@link MediaItem}. */ + public static MediaItem convertToMediaItem(QueueItem item) { + return convertToMediaItem(item.getDescription()); + } + + /** Converts a {@link QueueItem} to a {@link MediaItem}. */ + public static MediaItem convertToMediaItem(MediaDescriptionCompat description) { + checkNotNull(description); + return convertToMediaItem(description, /* browsable= */ false, /* playable= */ true); + } + + /** Converts a {@link MediaMetadataCompat} to a {@link MediaItem}. */ + public static MediaItem convertToMediaItem( + MediaMetadataCompat metadataCompat, @RatingCompat.Style int ratingType) { + return convertToMediaItem( + metadataCompat.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID), + metadataCompat, + ratingType); + } + + /** Converts a {@code mediaId} and {@link MediaMetadataCompat} to a {@link MediaItem}. */ + public static MediaItem convertToMediaItem( + @Nullable String mediaId, + MediaMetadataCompat metadataCompat, + @RatingCompat.Style int ratingType) { + MediaItem.Builder builder = new MediaItem.Builder(); + if (mediaId != null) { + builder.setMediaId(mediaId); + } + @Nullable + String mediaUriString = metadataCompat.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI); + if (mediaUriString != null) { + builder.setRequestMetadata( + new MediaItem.RequestMetadata.Builder().setMediaUri(Uri.parse(mediaUriString)).build()); + } + builder.setMediaMetadata(convertToMediaMetadata(metadataCompat, ratingType)); + return builder.build(); + } + + private static MediaItem convertToMediaItem( + MediaDescriptionCompat descriptionCompat, boolean browsable, boolean playable) { + @Nullable String mediaId = descriptionCompat.getMediaId(); + return new MediaItem.Builder() + .setMediaId(mediaId == null ? MediaItem.DEFAULT_MEDIA_ID : mediaId) + .setRequestMetadata( + new MediaItem.RequestMetadata.Builder() + .setMediaUri(descriptionCompat.getMediaUri()) + .build()) + .setMediaMetadata( + convertToMediaMetadata( + descriptionCompat, RatingCompat.RATING_NONE, browsable, playable)) + .build(); + } + + /** Converts a list of {@link MediaBrowserCompat.MediaItem} to a list of {@link MediaItem}. */ + public static ImmutableList convertBrowserItemListToMediaItemList( + List items) { + ImmutableList.Builder builder = new ImmutableList.Builder<>(); + for (int i = 0; i < items.size(); i++) { + builder.add(convertToMediaItem(items.get(i))); + } + return builder.build(); + } + + /** Converts a {@link Timeline} to a list of {@link MediaItem MediaItems}. */ + public static List convertToMediaItemList(Timeline timeline) { + List mediaItems = new ArrayList<>(); + Window window = new Window(); + for (int i = 0; i < timeline.getWindowCount(); i++) { + mediaItems.add(timeline.getWindow(i, window).mediaItem); + } + return mediaItems; + } + + /** + * Converts a {@link MediaItem} to a {@link QueueItem}. The index of the item in the playlist + * would be used as the queue ID to match the behavior of {@link MediaController}. + */ + public static QueueItem convertToQueueItem( + MediaItem item, int mediaItemIndex, @Nullable Bitmap artworkBitmap) { + MediaDescriptionCompat description = convertToMediaDescriptionCompat(item, artworkBitmap); + long id = convertToQueueItemId(mediaItemIndex); + return new QueueItem(description, id); + } + + /** Converts the index of a {@link MediaItem} in a playlist into id of {@link QueueItem}. */ + public static long convertToQueueItemId(int mediaItemIndex) { + if (mediaItemIndex == C.INDEX_UNSET) { + return QueueItem.UNKNOWN_ID; + } + return mediaItemIndex; + } + + public static Window convertToWindow(MediaItem mediaItem, int periodIndex) { + Window window = new Window(); + window.set( + /* uid= */ 0, + mediaItem, + /* manifest= */ null, + /* presentationStartTimeMs= */ 0, + /* windowStartTimeMs= */ 0, + /* elapsedRealtimeEpochOffsetMs= */ 0, + /* isSeekable= */ true, + /* isDynamic= */ false, + /* liveConfiguration= */ null, + /* defaultPositionUs= */ 0, + /* durationUs= */ C.TIME_UNSET, + /* firstPeriodIndex= */ periodIndex, + /* lastPeriodIndex= */ periodIndex, + /* positionInFirstPeriodUs= */ 0); + return window; + } + + public static Period convertToPeriod(int windowIndex) { + Period period = new Period(); + period.set( + /* id= */ null, + /* uid= */ null, + windowIndex, + /* durationUs= */ C.TIME_UNSET, + /* positionInWindowUs= */ 0, + /* adPlaybackState= */ AdPlaybackState.NONE, + /* isPlaceholder= */ true); + return period; + } + + /** Converts a {@link MediaItem} to a {@link MediaDescriptionCompat} */ + @SuppressWarnings("deprecation") // Converting deprecated fields. + public static MediaDescriptionCompat convertToMediaDescriptionCompat( + MediaItem item, @Nullable Bitmap artworkBitmap) { + MediaDescriptionCompat.Builder builder = + new MediaDescriptionCompat.Builder() + .setMediaId(item.mediaId.equals(MediaItem.DEFAULT_MEDIA_ID) ? null : item.mediaId); + MediaMetadata metadata = item.mediaMetadata; + if (artworkBitmap != null) { + builder.setIconBitmap(artworkBitmap); + } + @Nullable Bundle extras = metadata.extras; + boolean hasFolderType = + metadata.folderType != null && metadata.folderType != MediaMetadata.FOLDER_TYPE_NONE; + boolean hasMediaType = metadata.mediaType != null; + if (hasFolderType || hasMediaType) { + if (extras == null) { + extras = new Bundle(); + } else { + extras = new Bundle(extras); + } + if (hasFolderType) { + extras.putLong( + MediaDescriptionCompat.EXTRA_BT_FOLDER_TYPE, + convertToExtraBtFolderType(checkNotNull(metadata.folderType))); + } + if (hasMediaType) { + extras.putLong( + MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT, checkNotNull(metadata.mediaType)); + } + } + return builder + .setTitle(metadata.title) + // The BT AVRPC service expects the subtitle of the media description to be the artist + // (see https://github.com/androidx/media/issues/148). + .setSubtitle(metadata.artist != null ? metadata.artist : metadata.subtitle) + .setDescription(metadata.description) + .setIconUri(metadata.artworkUri) + .setMediaUri(item.requestMetadata.mediaUri) + .setExtras(extras) + .build(); + } + + /** Creates {@link MediaMetadata} from the {@link CharSequence queue title}. */ + public static MediaMetadata convertToMediaMetadata(@Nullable CharSequence queueTitle) { + if (queueTitle == null) { + return MediaMetadata.EMPTY; + } + return new MediaMetadata.Builder().setTitle(queueTitle).build(); + } + + public static MediaMetadata convertToMediaMetadata( + @Nullable MediaDescriptionCompat descriptionCompat, @RatingCompat.Style int ratingType) { + return convertToMediaMetadata( + descriptionCompat, ratingType, /* browsable= */ false, /* playable= */ true); + } + + @SuppressWarnings("deprecation") // Populating deprecated fields. + private static MediaMetadata convertToMediaMetadata( + @Nullable MediaDescriptionCompat descriptionCompat, + @RatingCompat.Style int ratingType, + boolean browsable, + boolean playable) { + if (descriptionCompat == null) { + return MediaMetadata.EMPTY; + } + + MediaMetadata.Builder builder = new MediaMetadata.Builder(); + + builder + .setTitle(descriptionCompat.getTitle()) + .setSubtitle(descriptionCompat.getSubtitle()) + .setDescription(descriptionCompat.getDescription()) + .setArtworkUri(descriptionCompat.getIconUri()) + .setUserRating(convertToRating(RatingCompat.newUnratedRating(ratingType))); + + @Nullable Bitmap iconBitmap = descriptionCompat.getIconBitmap(); + if (iconBitmap != null) { + @Nullable byte[] artworkData = null; + try { + artworkData = convertToByteArray(iconBitmap); + } catch (IOException e) { + Log.w(TAG, "Failed to convert iconBitmap to artworkData", e); + } + builder.setArtworkData(artworkData, MediaMetadata.PICTURE_TYPE_FRONT_COVER); + } + + @Nullable Bundle compatExtras = descriptionCompat.getExtras(); + @Nullable Bundle extras = compatExtras == null ? null : new Bundle(compatExtras); + + if (extras != null && extras.containsKey(MediaDescriptionCompat.EXTRA_BT_FOLDER_TYPE)) { + builder.setFolderType( + convertToFolderType(extras.getLong(MediaDescriptionCompat.EXTRA_BT_FOLDER_TYPE))); + extras.remove(MediaDescriptionCompat.EXTRA_BT_FOLDER_TYPE); + } + builder.setIsBrowsable(browsable); + + if (extras != null && extras.containsKey(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT)) { + builder.setMediaType((int) extras.getLong(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT)); + extras.remove(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT); + } + if (extras != null && !extras.isEmpty()) { + builder.setExtras(extras); + } + + builder.setIsPlayable(playable); + + return builder.build(); + } + + /** Creates {@link MediaMetadata} from the {@link MediaMetadataCompat} and rating type. */ + @SuppressWarnings("deprecation") // Populating deprecated fields. + public static MediaMetadata convertToMediaMetadata( + @Nullable MediaMetadataCompat metadataCompat, @RatingCompat.Style int ratingType) { + if (metadataCompat == null) { + return MediaMetadata.EMPTY; + } + + MediaMetadata.Builder builder = new MediaMetadata.Builder(); + + builder + .setTitle( + getFirstText( + metadataCompat, + MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, + MediaMetadataCompat.METADATA_KEY_TITLE)) + .setSubtitle(metadataCompat.getText(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE)) + .setDescription( + metadataCompat.getText(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION)) + .setArtist(metadataCompat.getText(MediaMetadataCompat.METADATA_KEY_ARTIST)) + .setAlbumTitle(metadataCompat.getText(MediaMetadataCompat.METADATA_KEY_ALBUM)) + .setAlbumArtist(metadataCompat.getText(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST)) + .setOverallRating( + convertToRating(metadataCompat.getRating(MediaMetadataCompat.METADATA_KEY_RATING))); + + @Nullable + Rating userRating = + convertToRating(metadataCompat.getRating(MediaMetadataCompat.METADATA_KEY_USER_RATING)); + if (userRating != null) { + builder.setUserRating(userRating); + } else { + builder.setUserRating(convertToRating(RatingCompat.newUnratedRating(ratingType))); + } + + if (metadataCompat.containsKey(MediaMetadataCompat.METADATA_KEY_YEAR)) { + long year = metadataCompat.getLong(MediaMetadataCompat.METADATA_KEY_YEAR); + builder.setRecordingYear((int) year); + } + + @Nullable + String artworkUriString = + getFirstString( + metadataCompat, + MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, + MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI); + if (artworkUriString != null) { + builder.setArtworkUri(Uri.parse(artworkUriString)); + } + + @Nullable + Bitmap artworkBitmap = + getFirstBitmap( + metadataCompat, + MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, + MediaMetadataCompat.METADATA_KEY_ALBUM_ART); + if (artworkBitmap != null) { + try { + byte[] artworkData = convertToByteArray(artworkBitmap); + builder.setArtworkData(artworkData, MediaMetadata.PICTURE_TYPE_FRONT_COVER); + } catch (IOException e) { + Log.w(TAG, "Failed to convert artworkBitmap to artworkData", e); + } + } + + boolean isBrowsable = + metadataCompat.containsKey(MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE); + builder.setIsBrowsable(isBrowsable); + if (isBrowsable) { + builder.setFolderType( + convertToFolderType( + metadataCompat.getLong(MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE))); + } + + if (metadataCompat.containsKey(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT)) { + builder.setMediaType( + (int) metadataCompat.getLong(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT)); + } + + builder.setIsPlayable(true); + + Bundle extras = metadataCompat.getBundle(); + for (String key : KNOWN_METADATA_COMPAT_KEYS) { + extras.remove(key); + } + if (!extras.isEmpty()) { + builder.setExtras(extras); + } + + return builder.build(); + } + + @Nullable + private static Bitmap getFirstBitmap(MediaMetadataCompat mediaMetadataCompat, String... keys) { + for (String key : keys) { + if (mediaMetadataCompat.containsKey(key)) { + return mediaMetadataCompat.getBitmap(key); + } + } + return null; + } + + @Nullable + private static String getFirstString(MediaMetadataCompat mediaMetadataCompat, String... keys) { + for (String key : keys) { + if (mediaMetadataCompat.containsKey(key)) { + return mediaMetadataCompat.getString(key); + } + } + return null; + } + + @Nullable + private static CharSequence getFirstText( + MediaMetadataCompat mediaMetadataCompat, String... keys) { + for (String key : keys) { + if (mediaMetadataCompat.containsKey(key)) { + return mediaMetadataCompat.getText(key); + } + } + return null; + } + + /** + * Converts a {@link MediaMetadata} to a {@link MediaMetadataCompat}. + * + * @param metadata The {@link MediaMetadata} instance to convert. + * @param mediaId The corresponding media ID. + * @param mediaUri The corresponding media URI, or null if unknown. + * @param durationMs The duration of the media, in milliseconds or {@link C#TIME_UNSET}, if no + * duration should be included. + * @return An instance of the legacy {@link MediaMetadataCompat}. + */ + @SuppressWarnings("deprecation") // Converting deprecated fields. + public static MediaMetadataCompat convertToMediaMetadataCompat( + MediaMetadata metadata, + String mediaId, + @Nullable Uri mediaUri, + long durationMs, + @Nullable Bitmap artworkBitmap) { + MediaMetadataCompat.Builder builder = + new MediaMetadataCompat.Builder() + .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, mediaId); + + if (metadata.title != null) { + builder.putText(MediaMetadataCompat.METADATA_KEY_TITLE, metadata.title); + builder.putText(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, metadata.title); + } + + if (metadata.subtitle != null) { + builder.putText(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, metadata.subtitle); + } + + if (metadata.description != null) { + builder.putText(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, metadata.description); + } + + if (metadata.artist != null) { + builder.putText(MediaMetadataCompat.METADATA_KEY_ARTIST, metadata.artist); + } + + if (metadata.albumTitle != null) { + builder.putText(MediaMetadataCompat.METADATA_KEY_ALBUM, metadata.albumTitle); + } + + if (metadata.albumArtist != null) { + builder.putText(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, metadata.albumArtist); + } + + if (metadata.recordingYear != null) { + builder.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, metadata.recordingYear); + } + + if (mediaUri != null) { + builder.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI, mediaUri.toString()); + } + + if (metadata.artworkUri != null) { + builder.putString( + MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, metadata.artworkUri.toString()); + builder.putString( + MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, metadata.artworkUri.toString()); + } + + if (artworkBitmap != null) { + builder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, artworkBitmap); + builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, artworkBitmap); + } + + if (metadata.folderType != null && metadata.folderType != MediaMetadata.FOLDER_TYPE_NONE) { + builder.putLong( + MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE, + convertToExtraBtFolderType(metadata.folderType)); + } + + if (durationMs != C.TIME_UNSET) { + builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, durationMs); + } + + @Nullable RatingCompat userRatingCompat = convertToRatingCompat(metadata.userRating); + if (userRatingCompat != null) { + builder.putRating(MediaMetadataCompat.METADATA_KEY_USER_RATING, userRatingCompat); + } + + @Nullable RatingCompat overallRatingCompat = convertToRatingCompat(metadata.overallRating); + if (overallRatingCompat != null) { + builder.putRating(MediaMetadataCompat.METADATA_KEY_RATING, overallRatingCompat); + } + + if (metadata.mediaType != null) { + builder.putLong(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT, metadata.mediaType); + } + + return builder.build(); + } + + @SuppressWarnings("deprecation") // Converting to deprecated constants. + @MediaMetadata.FolderType + private static int convertToFolderType(long extraBtFolderType) { + if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_MIXED) { + return MediaMetadata.FOLDER_TYPE_MIXED; + } else if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_TITLES) { + return MediaMetadata.FOLDER_TYPE_TITLES; + } else if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_ALBUMS) { + return MediaMetadata.FOLDER_TYPE_ALBUMS; + } else if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_ARTISTS) { + return MediaMetadata.FOLDER_TYPE_ARTISTS; + } else if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_GENRES) { + return MediaMetadata.FOLDER_TYPE_GENRES; + } else if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_PLAYLISTS) { + return MediaMetadata.FOLDER_TYPE_PLAYLISTS; + } else if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_YEARS) { + return MediaMetadata.FOLDER_TYPE_YEARS; + } else { + return MediaMetadata.FOLDER_TYPE_MIXED; + } + } + + @SuppressWarnings("deprecation") // Converting from deprecated constants. + private static long convertToExtraBtFolderType(@MediaMetadata.FolderType int folderType) { + switch (folderType) { + case MediaMetadata.FOLDER_TYPE_MIXED: + return MediaDescriptionCompat.BT_FOLDER_TYPE_MIXED; + case MediaMetadata.FOLDER_TYPE_TITLES: + return MediaDescriptionCompat.BT_FOLDER_TYPE_TITLES; + case MediaMetadata.FOLDER_TYPE_ALBUMS: + return MediaDescriptionCompat.BT_FOLDER_TYPE_ALBUMS; + case MediaMetadata.FOLDER_TYPE_ARTISTS: + return MediaDescriptionCompat.BT_FOLDER_TYPE_ARTISTS; + case MediaMetadata.FOLDER_TYPE_GENRES: + return MediaDescriptionCompat.BT_FOLDER_TYPE_GENRES; + case MediaMetadata.FOLDER_TYPE_PLAYLISTS: + return MediaDescriptionCompat.BT_FOLDER_TYPE_PLAYLISTS; + case MediaMetadata.FOLDER_TYPE_YEARS: + return MediaDescriptionCompat.BT_FOLDER_TYPE_YEARS; + case MediaMetadata.FOLDER_TYPE_NONE: + default: + throw new IllegalArgumentException("Unrecognized FolderType: " + folderType); + } + } + + /** + * Creates a {@link Rating} from the {@link RatingCompat}. + * + * @param ratingCompat A {@link RatingCompat} object. + * @return The newly created {@link Rating} object. + */ + @Nullable + public static Rating convertToRating(@Nullable RatingCompat ratingCompat) { + if (ratingCompat == null) { + return null; + } + switch (ratingCompat.getRatingStyle()) { + case RatingCompat.RATING_3_STARS: + return ratingCompat.isRated() + ? new StarRating(3, ratingCompat.getStarRating()) + : new StarRating(3); + case RatingCompat.RATING_4_STARS: + return ratingCompat.isRated() + ? new StarRating(4, ratingCompat.getStarRating()) + : new StarRating(4); + case RatingCompat.RATING_5_STARS: + return ratingCompat.isRated() + ? new StarRating(5, ratingCompat.getStarRating()) + : new StarRating(5); + case RatingCompat.RATING_HEART: + return ratingCompat.isRated() + ? new HeartRating(ratingCompat.hasHeart()) + : new HeartRating(); + case RatingCompat.RATING_THUMB_UP_DOWN: + return ratingCompat.isRated() + ? new ThumbRating(ratingCompat.isThumbUp()) + : new ThumbRating(); + case RatingCompat.RATING_PERCENTAGE: + return ratingCompat.isRated() + ? new PercentageRating(ratingCompat.getPercentRating()) + : new PercentageRating(); + case RatingCompat.RATING_NONE: + default: + return null; + } + } + + /** + * Creates a {@link RatingCompat} from the {@link Rating}. + * + * @param rating A {@link Rating} object. + * @return The newly created {@link RatingCompat} object. + */ + @SuppressLint("WrongConstant") // for @StarStyle + @Nullable + public static RatingCompat convertToRatingCompat(@Nullable Rating rating) { + if (rating == null) { + return null; + } + int ratingCompatStyle = getRatingCompatStyle(rating); + if (!rating.isRated()) { + return RatingCompat.newUnratedRating(ratingCompatStyle); + } + + switch (ratingCompatStyle) { + case RatingCompat.RATING_3_STARS: + case RatingCompat.RATING_4_STARS: + case RatingCompat.RATING_5_STARS: + return RatingCompat.newStarRating(ratingCompatStyle, ((StarRating) rating).getStarRating()); + case RatingCompat.RATING_HEART: + return RatingCompat.newHeartRating(((HeartRating) rating).isHeart()); + case RatingCompat.RATING_THUMB_UP_DOWN: + return RatingCompat.newThumbRating(((ThumbRating) rating).isThumbsUp()); + case RatingCompat.RATING_PERCENTAGE: + return RatingCompat.newPercentageRating(((PercentageRating) rating).getPercent()); + case RatingCompat.RATING_NONE: + default: + return null; + } + } + + /** Converts {@link Player}' states to state of {@link PlaybackStateCompat}. */ + @PlaybackStateCompat.State + public static int convertToPlaybackStateCompatState(Player player, boolean playIfSuppressed) { + if (player.getPlayerError() != null) { + return PlaybackStateCompat.STATE_ERROR; + } + @Player.State int playbackState = player.getPlaybackState(); + boolean shouldShowPlayButton = Util.shouldShowPlayButton(player, playIfSuppressed); + switch (playbackState) { + case Player.STATE_IDLE: + return PlaybackStateCompat.STATE_NONE; + case Player.STATE_READY: + return shouldShowPlayButton + ? PlaybackStateCompat.STATE_PAUSED + : PlaybackStateCompat.STATE_PLAYING; + case Player.STATE_ENDED: + return PlaybackStateCompat.STATE_STOPPED; + case Player.STATE_BUFFERING: + return shouldShowPlayButton + ? PlaybackStateCompat.STATE_PAUSED + : PlaybackStateCompat.STATE_BUFFERING; + default: + throw new IllegalArgumentException("Unrecognized State: " + playbackState); + } + } + + /** Converts a {@link PlaybackStateCompat} to {@link PlaybackParameters}. */ + public static PlaybackParameters convertToPlaybackParameters( + @Nullable PlaybackStateCompat playbackStateCompat) { + return playbackStateCompat == null + ? PlaybackParameters.DEFAULT + : new PlaybackParameters(playbackStateCompat.getPlaybackSpeed()); + } + + /** Converts a {@link PlaybackStateCompat} to {@link Player}'s play when ready. */ + public static boolean convertToPlayWhenReady(@Nullable PlaybackStateCompat playbackState) { + if (playbackState == null) { + return false; + } + switch (playbackState.getState()) { + case PlaybackStateCompat.STATE_BUFFERING: + case PlaybackStateCompat.STATE_FAST_FORWARDING: + case PlaybackStateCompat.STATE_PLAYING: + case PlaybackStateCompat.STATE_REWINDING: + case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT: + case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS: + case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM: + return true; + case PlaybackStateCompat.STATE_CONNECTING: + case PlaybackStateCompat.STATE_ERROR: + case PlaybackStateCompat.STATE_NONE: + case PlaybackStateCompat.STATE_PAUSED: + case PlaybackStateCompat.STATE_STOPPED: + return false; + } + return false; + } + + /** Converts a {@link PlaybackStateCompat} to {@link Player.State} */ + public static @Player.State int convertToPlaybackState( + @Nullable PlaybackStateCompat playbackStateCompat, + @Nullable MediaMetadataCompat currentMediaMetadata, + long timeDiffMs) { + if (playbackStateCompat == null) { + return Player.STATE_IDLE; + } + switch (playbackStateCompat.getState()) { + case PlaybackStateCompat.STATE_CONNECTING: + case PlaybackStateCompat.STATE_ERROR: + case PlaybackStateCompat.STATE_NONE: + case PlaybackStateCompat.STATE_STOPPED: + return Player.STATE_IDLE; + case PlaybackStateCompat.STATE_BUFFERING: + case PlaybackStateCompat.STATE_FAST_FORWARDING: + case PlaybackStateCompat.STATE_REWINDING: + case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT: + case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS: + case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM: + return Player.STATE_BUFFERING; + case PlaybackStateCompat.STATE_PLAYING: + return Player.STATE_READY; + case PlaybackStateCompat.STATE_PAUSED: + long duration = convertToDurationMs(currentMediaMetadata); + if (duration == C.TIME_UNSET) { + return Player.STATE_READY; + } + long currentPosition = + convertToCurrentPositionMs(playbackStateCompat, currentMediaMetadata, timeDiffMs); + return (currentPosition < duration) ? Player.STATE_READY : Player.STATE_ENDED; + default: + throw new IllegalStateException( + "Unrecognized PlaybackStateCompat: " + playbackStateCompat.getState()); + } + } + + /** Converts a {@link PlaybackStateCompat} to isPlaying, defined by {@link Player#isPlaying()}. */ + public static boolean convertToIsPlaying(@Nullable PlaybackStateCompat playbackStateCompat) { + if (playbackStateCompat == null) { + return false; + } + return playbackStateCompat.getState() == PlaybackStateCompat.STATE_PLAYING; + } + + /** Converts a {@link PlaybackStateCompat} to isPlaying, defined by {@link Player#isPlaying()}. */ + public static boolean convertToIsPlayingAd(@Nullable MediaMetadataCompat metadataCompat) { + if (metadataCompat == null) { + return false; + } + return metadataCompat.getLong(MediaMetadataCompat.METADATA_KEY_ADVERTISEMENT) != 0; + } + + /** Gets the current position. {@code 0} will be returned if unknown. */ + public static long convertToCurrentPositionMs( + @Nullable PlaybackStateCompat playbackStateCompat, + @Nullable MediaMetadataCompat metadataCompat, + long timeDiffMs) { + if (playbackStateCompat == null) { + return 0; + } + long positionMs = + playbackStateCompat.getState() == PlaybackStateCompat.STATE_PLAYING + ? getCurrentPosition(playbackStateCompat, timeDiffMs) + : playbackStateCompat.getPosition(); + long durationMs = convertToDurationMs(metadataCompat); + return durationMs == C.TIME_UNSET + ? max(0, positionMs) + : constrainValue(positionMs, /* min= */ 0, durationMs); + } + + @SuppressWarnings("nullness:argument") // PlaybackStateCompat#getCurrentPosition can take null. + private static long getCurrentPosition(PlaybackStateCompat playbackStateCompat, long timeDiffMs) { + return playbackStateCompat.getCurrentPosition(timeDiffMs == C.TIME_UNSET ? null : timeDiffMs); + } + + /** Gets the duration. {@link C#TIME_UNSET} will be returned if unknown. */ + public static long convertToDurationMs(@Nullable MediaMetadataCompat metadataCompat) { + if (metadataCompat == null + || !metadataCompat.containsKey(MediaMetadataCompat.METADATA_KEY_DURATION)) { + return C.TIME_UNSET; + } + long legacyDurationMs = metadataCompat.getLong(MediaMetadataCompat.METADATA_KEY_DURATION); + return legacyDurationMs <= 0 ? C.TIME_UNSET : legacyDurationMs; + } + + /** Gets the buffered position. {@code 0} will be returned if unknown. */ + public static long convertToBufferedPositionMs( + @Nullable PlaybackStateCompat playbackStateCompat, + @Nullable MediaMetadataCompat metadataCompat, + long timeDiffMs) { + long legacyBufferedPositionMs = + (playbackStateCompat == null) ? 0 : playbackStateCompat.getBufferedPosition(); + long currentPositionMs = + convertToCurrentPositionMs(playbackStateCompat, metadataCompat, timeDiffMs); + long durationMs = convertToDurationMs(metadataCompat); + return (durationMs == C.TIME_UNSET) + ? max(currentPositionMs, legacyBufferedPositionMs) + : constrainValue(legacyBufferedPositionMs, currentPositionMs, durationMs); + } + + /** Gets the total buffered duration. {@code 0} will be returned if unknown. */ + public static long convertToTotalBufferedDurationMs( + @Nullable PlaybackStateCompat playbackStateCompat, + @Nullable MediaMetadataCompat metadataCompat, + long timeDiffMs) { + long bufferedPositionMs = + convertToBufferedPositionMs(playbackStateCompat, metadataCompat, timeDiffMs); + long currentPositionMs = + convertToCurrentPositionMs(playbackStateCompat, metadataCompat, timeDiffMs); + return bufferedPositionMs - currentPositionMs; + } + + /** Gets the buffered percentage. {@code 0} will be returned if unknown. */ + public static int convertToBufferedPercentage( + @Nullable PlaybackStateCompat playbackStateCompat, + @Nullable MediaMetadataCompat mediaMetadataCompat, + long timeDiffMs) { + long bufferedPositionMs = + convertToBufferedPositionMs(playbackStateCompat, mediaMetadataCompat, timeDiffMs); + long durationMs = convertToDurationMs(mediaMetadataCompat); + return MediaUtils.calculateBufferedPercentage(bufferedPositionMs, durationMs); + } + + public static @RatingCompat.Style int getRatingCompatStyle(@Nullable Rating rating) { + if (rating instanceof HeartRating) { + return RatingCompat.RATING_HEART; + } else if (rating instanceof ThumbRating) { + return RatingCompat.RATING_THUMB_UP_DOWN; + } else if (rating instanceof StarRating) { + switch (((StarRating) rating).getMaxStars()) { + case 3: + return RatingCompat.RATING_3_STARS; + case 4: + return RatingCompat.RATING_4_STARS; + case 5: + return RatingCompat.RATING_5_STARS; + } + } else if (rating instanceof PercentageRating) { + return RatingCompat.RATING_PERCENTAGE; + } + return RatingCompat.RATING_NONE; + } + + /** Converts {@link PlaybackStateCompat.RepeatMode} to {@link Player.RepeatMode}. */ + public static @Player.RepeatMode int convertToRepeatMode( + @PlaybackStateCompat.RepeatMode int playbackStateCompatRepeatMode) { + switch (playbackStateCompatRepeatMode) { + case PlaybackStateCompat.REPEAT_MODE_INVALID: + case PlaybackStateCompat.REPEAT_MODE_NONE: + return Player.REPEAT_MODE_OFF; + case PlaybackStateCompat.REPEAT_MODE_ONE: + return Player.REPEAT_MODE_ONE; + case PlaybackStateCompat.REPEAT_MODE_ALL: + case PlaybackStateCompat.REPEAT_MODE_GROUP: + return Player.REPEAT_MODE_ALL; + default: + Log.w( + TAG, + "Unrecognized PlaybackStateCompat.RepeatMode: " + + playbackStateCompatRepeatMode + + " was converted to `Player.REPEAT_MODE_OFF`"); + return Player.REPEAT_MODE_OFF; + } + } + + /** Converts {@link Player.RepeatMode} to {@link PlaybackStateCompat.RepeatMode} */ + @PlaybackStateCompat.RepeatMode + public static int convertToPlaybackStateCompatRepeatMode(@Player.RepeatMode int repeatMode) { + switch (repeatMode) { + case Player.REPEAT_MODE_OFF: + return PlaybackStateCompat.REPEAT_MODE_NONE; + case Player.REPEAT_MODE_ONE: + return PlaybackStateCompat.REPEAT_MODE_ONE; + case Player.REPEAT_MODE_ALL: + return PlaybackStateCompat.REPEAT_MODE_ALL; + default: + Log.w( + TAG, + "Unrecognized RepeatMode: " + + repeatMode + + " was converted to `PlaybackStateCompat.REPEAT_MODE_NONE`"); + return PlaybackStateCompat.REPEAT_MODE_NONE; + } + } + + /** Converts {@link PlaybackStateCompat.ShuffleMode} to shuffle mode enabled. */ + public static boolean convertToShuffleModeEnabled( + @PlaybackStateCompat.ShuffleMode int playbackStateCompatShuffleMode) { + switch (playbackStateCompatShuffleMode) { + case PlaybackStateCompat.SHUFFLE_MODE_INVALID: + case PlaybackStateCompat.SHUFFLE_MODE_NONE: + return false; + case PlaybackStateCompat.SHUFFLE_MODE_ALL: + case PlaybackStateCompat.SHUFFLE_MODE_GROUP: + return true; + default: + throw new IllegalArgumentException( + "Unrecognized ShuffleMode: " + playbackStateCompatShuffleMode); + } + } + + /** Converts shuffle mode enabled to {@link PlaybackStateCompat.ShuffleMode} */ + @PlaybackStateCompat.ShuffleMode + public static int convertToPlaybackStateCompatShuffleMode(boolean shuffleModeEnabled) { + return shuffleModeEnabled + ? PlaybackStateCompat.SHUFFLE_MODE_ALL + : PlaybackStateCompat.SHUFFLE_MODE_NONE; + } + + /** Converts the rootHints, option, and extra to the {@link LibraryParams}. */ + @Nullable + public static LibraryParams convertToLibraryParams( + Context context, @Nullable Bundle legacyBundle) { + if (legacyBundle == null) { + return null; + } + try { + legacyBundle.setClassLoader(context.getClassLoader()); + int supportedChildrenFlags = + legacyBundle.getInt( + BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ -1); + if (supportedChildrenFlags >= 0) { + legacyBundle.remove(BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS); + legacyBundle.putBoolean( + EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, + supportedChildrenFlags == MediaBrowserCompat.MediaItem.FLAG_BROWSABLE); + } + return new LibraryParams.Builder() + .setExtras(legacyBundle) + .setRecent(legacyBundle.getBoolean(BrowserRoot.EXTRA_RECENT)) + .setOffline(legacyBundle.getBoolean(BrowserRoot.EXTRA_OFFLINE)) + .setSuggested(legacyBundle.getBoolean(BrowserRoot.EXTRA_SUGGESTED)) + .build(); + } catch (Exception e) { + // Failure when unpacking the legacy bundle. + return new LibraryParams.Builder().setExtras(legacyBundle).build(); + } + } + + /** Converts {@link LibraryParams} to the root hints. */ + @Nullable + public static Bundle convertToRootHints(@Nullable LibraryParams params) { + if (params == null) { + return null; + } + Bundle rootHints = new Bundle(params.extras); + if (params.extras.containsKey(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)) { + boolean browsableChildrenSupported = + params.extras.getBoolean( + EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, /* defaultValue= */ false); + rootHints.remove(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY); + rootHints.putInt( + BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, + browsableChildrenSupported + ? MediaBrowserCompat.MediaItem.FLAG_BROWSABLE + : MediaBrowserCompat.MediaItem.FLAG_BROWSABLE + | MediaBrowserCompat.MediaItem.FLAG_PLAYABLE); + } + rootHints.putBoolean(BrowserRoot.EXTRA_RECENT, params.isRecent); + rootHints.putBoolean(BrowserRoot.EXTRA_OFFLINE, params.isOffline); + rootHints.putBoolean(BrowserRoot.EXTRA_SUGGESTED, params.isSuggested); + return rootHints; + } + + /** + * Converts {@link PlaybackStateCompat}, {@link + * MediaControllerCompat.PlaybackInfo#getVolumeControl() volume control type}, {@link + * MediaControllerCompat#getFlags() session flags} and {@link MediaControllerCompat#isSessionReady + * whether the session is ready} to {@link Player.Commands}. + * + * @param playbackStateCompat The {@link PlaybackStateCompat}. + * @param volumeControlType The {@link MediaControllerCompat.PlaybackInfo#getVolumeControl() + * volume control type}. + * @param sessionFlags The session flags. + * @param isSessionReady Whether the session compat is ready. + * @return The converted player commands. + */ + @SuppressWarnings("deprecation") // Backwards compatibility with old volume commands + public static Player.Commands convertToPlayerCommands( + @Nullable PlaybackStateCompat playbackStateCompat, + int volumeControlType, + long sessionFlags, + boolean isSessionReady) { + Player.Commands.Builder playerCommandsBuilder = new Player.Commands.Builder(); + long actions = playbackStateCompat == null ? 0 : playbackStateCompat.getActions(); + if ((hasAction(actions, PlaybackStateCompat.ACTION_PLAY) + && hasAction(actions, PlaybackStateCompat.ACTION_PAUSE)) + || hasAction(actions, PlaybackStateCompat.ACTION_PLAY_PAUSE)) { + playerCommandsBuilder.add(COMMAND_PLAY_PAUSE); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_PREPARE)) { + playerCommandsBuilder.add(COMMAND_PREPARE); + } + if ((hasAction(actions, PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID) + && hasAction(actions, PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID)) + || (hasAction(actions, PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH) + && hasAction(actions, PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH)) + || (hasAction(actions, PlaybackStateCompat.ACTION_PREPARE_FROM_URI) + && hasAction(actions, PlaybackStateCompat.ACTION_PLAY_FROM_URI))) { + // Require both PREPARE and PLAY actions as we have no logic to handle having just one action. + playerCommandsBuilder.addAll(COMMAND_SET_MEDIA_ITEM, COMMAND_PREPARE); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_REWIND)) { + playerCommandsBuilder.add(COMMAND_SEEK_BACK); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_FAST_FORWARD)) { + playerCommandsBuilder.add(COMMAND_SEEK_FORWARD); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_SEEK_TO)) { + playerCommandsBuilder.addAll( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_DEFAULT_POSITION); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_SKIP_TO_NEXT)) { + playerCommandsBuilder.addAll(COMMAND_SEEK_TO_NEXT, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)) { + playerCommandsBuilder.addAll(COMMAND_SEEK_TO_PREVIOUS, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_SET_PLAYBACK_SPEED)) { + playerCommandsBuilder.add(COMMAND_SET_SPEED_AND_PITCH); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_STOP)) { + playerCommandsBuilder.add(COMMAND_STOP); + } + if (volumeControlType == VolumeProviderCompat.VOLUME_CONTROL_RELATIVE) { + playerCommandsBuilder.addAll( + COMMAND_ADJUST_DEVICE_VOLUME, COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS); + } else if (volumeControlType == VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE) { + playerCommandsBuilder.addAll( + COMMAND_ADJUST_DEVICE_VOLUME, + COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS, + COMMAND_SET_DEVICE_VOLUME, + COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS); + } + playerCommandsBuilder.addAll( + COMMAND_GET_DEVICE_VOLUME, + COMMAND_GET_TIMELINE, + COMMAND_GET_METADATA, + COMMAND_GET_CURRENT_MEDIA_ITEM, + COMMAND_GET_AUDIO_ATTRIBUTES, + COMMAND_RELEASE); + if ((sessionFlags & FLAG_HANDLES_QUEUE_COMMANDS) != 0) { + playerCommandsBuilder.add(COMMAND_CHANGE_MEDIA_ITEMS); + if (hasAction(actions, PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM)) { + playerCommandsBuilder.add(Player.COMMAND_SEEK_TO_MEDIA_ITEM); + } + } + if (isSessionReady) { + if (hasAction(actions, PlaybackStateCompat.ACTION_SET_REPEAT_MODE)) { + playerCommandsBuilder.add(COMMAND_SET_REPEAT_MODE); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE)) { + playerCommandsBuilder.add(COMMAND_SET_SHUFFLE_MODE); + } + } + return playerCommandsBuilder.build(); + } + + /** + * Checks if the set of actions contains the specified action. + * + * @param actions A bit set of actions. + * @param action The action to check. + * @return Whether the action is contained in the set. + */ + private static boolean hasAction(long actions, @PlaybackStateCompat.Actions long action) { + return (actions & action) != 0; + } + + /** + * Converts {@link PlaybackStateCompat} to {@link SessionCommands}. + * + *

This ignores {@link PlaybackStateCompat#getActions() actions} in the {@link + * PlaybackStateCompat} to workaround media apps' issues that they don't set playback state + * correctly. + * + * @param state playback state + * @param isSessionReady Whether the session compat is ready. + * @return the converted session commands + */ + public static SessionCommands convertToSessionCommands( + @Nullable PlaybackStateCompat state, boolean isSessionReady) { + SessionCommands.Builder sessionCommandsBuilder = new SessionCommands.Builder(); + sessionCommandsBuilder.addAllSessionCommands(); + if (!isSessionReady) { + // Disables rating function when session isn't ready because of the + // MediaController#setRating(RatingCompat, Bundle) and MediaController#getRatingType(). + sessionCommandsBuilder.remove(SessionCommand.COMMAND_CODE_SESSION_SET_RATING); + } + + if (state != null && state.getCustomActions() != null) { + for (CustomAction customAction : state.getCustomActions()) { + String action = customAction.getAction(); + @Nullable Bundle extras = customAction.getExtras(); + sessionCommandsBuilder.add( + new SessionCommand(action, extras == null ? Bundle.EMPTY : extras)); + } + } + return sessionCommandsBuilder.build(); + } + + /** + * Converts {@link CustomAction} in the {@link PlaybackStateCompat} to the custom layout which is + * the list of the {@link CommandButton}. + * + * @param state playback state + * @return custom layout. Always non-null. + */ + public static ImmutableList convertToCustomLayout( + @Nullable PlaybackStateCompat state) { + if (state == null) { + return ImmutableList.of(); + } + ImmutableList.Builder layout = new ImmutableList.Builder<>(); + for (CustomAction customAction : state.getCustomActions()) { + String action = customAction.getAction(); + @Nullable Bundle extras = customAction.getExtras(); + CommandButton button = + new CommandButton.Builder() + .setSessionCommand(new SessionCommand(action, extras == null ? Bundle.EMPTY : extras)) + .setDisplayName(customAction.getName()) + .setEnabled(true) + .setIconResId(customAction.getIcon()) + .build(); + layout.add(button); + } + return layout.build(); + } + + /** Converts {@link AudioAttributesCompat} into {@link AudioAttributes}. */ + /* + * @AudioAttributesCompat.AttributeUsage and @C.AudioUsage both use the same constant values, + * defined by AudioAttributes in the platform. + */ + @SuppressLint("WrongConstant") + public static AudioAttributes convertToAudioAttributes( + @Nullable AudioAttributesCompat audioAttributesCompat) { + if (audioAttributesCompat == null) { + return AudioAttributes.DEFAULT; + } + return new AudioAttributes.Builder() + .setContentType(audioAttributesCompat.getContentType()) + .setFlags(audioAttributesCompat.getFlags()) + .setUsage(audioAttributesCompat.getUsage()) + .build(); + } + + /** Converts {@link MediaControllerCompat.PlaybackInfo} to {@link AudioAttributes}. */ + public static AudioAttributes convertToAudioAttributes( + @Nullable MediaControllerCompat.PlaybackInfo playbackInfoCompat) { + if (playbackInfoCompat == null) { + return AudioAttributes.DEFAULT; + } + return convertToAudioAttributes(playbackInfoCompat.getAudioAttributes()); + } + + /** Converts {@link AudioAttributes} into {@link AudioAttributesCompat}. */ + public static AudioAttributesCompat convertToAudioAttributesCompat( + AudioAttributes audioAttributes) { + return new AudioAttributesCompat.Builder() + .setContentType(audioAttributes.contentType) + .setFlags(audioAttributes.flags) + .setUsage(audioAttributes.usage) + .build(); + } + + /** + * Gets the legacy stream type from {@link AudioAttributes}. + * + * @param audioAttributes audio attributes + * @return int legacy stream type from {@link AudioManager} + */ + public static int getLegacyStreamType(AudioAttributes audioAttributes) { + int legacyStreamType = convertToAudioAttributesCompat(audioAttributes).getLegacyStreamType(); + if (legacyStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { + // Usually, AudioAttributesCompat#getLegacyStreamType() does not return + // USE_DEFAULT_STREAM_TYPE unless the developer sets it with + // AudioAttributesCompat.Builder#setLegacyStreamType(). + // But for safety, let's convert USE_DEFAULT_STREAM_TYPE to STREAM_MUSIC here. + return AudioManager.STREAM_MUSIC; + } + return legacyStreamType; + } + + public static T getFutureResult(Future future, long timeoutMs) + throws ExecutionException, TimeoutException { + long initialTimeMs = SystemClock.elapsedRealtime(); + long remainingTimeMs = timeoutMs; + boolean interrupted = false; + try { + while (true) { + try { + return future.get(remainingTimeMs, MILLISECONDS); + } catch (InterruptedException e) { + interrupted = true; + long elapsedTimeMs = SystemClock.elapsedRealtime() - initialTimeMs; + if (elapsedTimeMs >= timeoutMs) { + throw new TimeoutException(); + } + remainingTimeMs = timeoutMs - elapsedTimeMs; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** Converts {@link MediaControllerCompat.PlaybackInfo} to {@link DeviceInfo}. */ + public static DeviceInfo convertToDeviceInfo( + @Nullable MediaControllerCompat.PlaybackInfo playbackInfoCompat, + @Nullable String routingControllerId) { + if (playbackInfoCompat == null) { + return DeviceInfo.UNKNOWN; + } + return new DeviceInfo.Builder( + playbackInfoCompat.getPlaybackType() + == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE + ? DeviceInfo.PLAYBACK_TYPE_REMOTE + : DeviceInfo.PLAYBACK_TYPE_LOCAL) + .setMaxVolume(playbackInfoCompat.getMaxVolume()) + .setRoutingControllerId(routingControllerId) + .build(); + } + + /** Converts {@link MediaControllerCompat.PlaybackInfo} to device volume. */ + public static int convertToDeviceVolume( + @Nullable MediaControllerCompat.PlaybackInfo playbackInfoCompat) { + if (playbackInfoCompat == null) { + return 0; + } + return playbackInfoCompat.getCurrentVolume(); + } + + /** Converts {@link MediaControllerCompat.PlaybackInfo} to device muted. */ + public static boolean convertToIsDeviceMuted( + @Nullable MediaControllerCompat.PlaybackInfo playbackInfoCompat) { + if (playbackInfoCompat == null) { + return false; + } + return playbackInfoCompat.getCurrentVolume() == 0; + } + + private static byte[] convertToByteArray(Bitmap bitmap) throws IOException { + try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { + bitmap.compress(Bitmap.CompressFormat.PNG, /* ignored */ 0, stream); + return stream.toByteArray(); + } + } + + private LegacyConversions() {} +} diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaBrowserImplLegacy.java b/libraries/session/src/main/java/androidx/media3/session/MediaBrowserImplLegacy.java index 1c3243a7d1..68301d2d4f 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaBrowserImplLegacy.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaBrowserImplLegacy.java @@ -100,7 +100,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; // Already connected with the given extras. result.set(LibraryResult.ofItem(createRootMediaItem(browserCompat), null)); } else { - Bundle rootHints = MediaUtils.convertToRootHints(params); + Bundle rootHints = LegacyConversions.convertToRootHints(params); MediaBrowserCompat newBrowser = new MediaBrowserCompat( getContext(), @@ -193,7 +193,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; public void onItemLoaded(MediaBrowserCompat.MediaItem item) { if (item != null) { result.set( - LibraryResult.ofItem(MediaUtils.convertToMediaItem(item), /* params= */ null)); + LibraryResult.ofItem( + LegacyConversions.convertToMediaItem(item), /* params= */ null)); } else { result.set(LibraryResult.ofError(RESULT_ERROR_BAD_VALUE)); } @@ -278,7 +279,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; String query, Bundle extrasSent, List items) { future.set( LibraryResult.ofItemList( - MediaUtils.convertBrowserItemListToMediaItemList(items), /* params= */ null)); + LegacyConversions.convertBrowserItemListToMediaItemList(items), + /* params= */ null)); } @Override @@ -342,7 +344,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; result.set( LibraryResult.ofItem( createRootMediaItem(browserCompat), - MediaUtils.convertToLibraryParams(context, browserCompat.getExtras()))); + LegacyConversions.convertToLibraryParams(context, browserCompat.getExtras()))); } } @@ -414,7 +416,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; } LibraryParams params = - MediaUtils.convertToLibraryParams( + LegacyConversions.convertToLibraryParams( context, browserCompat.getNotifyChildrenChangedOptions()); getInstance() .notifyBrowserListener( @@ -486,7 +488,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; // - New API: Extra from MediaLibraryService to MediaBrowser future.set( LibraryResult.ofItemList( - MediaUtils.convertBrowserItemListToMediaItemList(children), /* params= */ null)); + LegacyConversions.convertBrowserItemListToMediaItemList(children), + /* params= */ null)); } } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java index 2ba418400b..febd4e485d 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -314,7 +314,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; ListenableFuture future = dispatchRemoteSessionTask(iSession, task, /* addToPendingMaskingOperations= */ true); try { - MediaUtils.getFutureResult(future, /* timeoutMs= */ 3_000); + LegacyConversions.getFutureResult(future, /* timeoutMs= */ 3_000); } catch (ExecutionException e) { // Never happens because future.setException will not be called. throw new IllegalStateException(e); @@ -1937,8 +1937,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; List windows = new ArrayList<>(); List periods = new ArrayList<>(); for (int i = 0; i < mediaItems.size(); i++) { - windows.add(MediaUtils.convertToWindow(mediaItems.get(i), i)); - periods.add(MediaUtils.convertToPeriod(i)); + windows.add(LegacyConversions.convertToWindow(mediaItems.get(i), i)); + periods.add(LegacyConversions.convertToPeriod(i)); } Timeline newTimeline = createMaskingTimeline(windows, periods); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java index ae62eb1ae0..f0c8203818 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java @@ -503,14 +503,18 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; String currentMediaItemMediaId = legacyPlayerInfo.mediaMetadataCompat.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID); if (mediaId.equals(currentMediaItemMediaId)) { - controllerCompat.getTransportControls().setRating(MediaUtils.convertToRatingCompat(rating)); + controllerCompat + .getTransportControls() + .setRating(LegacyConversions.convertToRatingCompat(rating)); } return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS)); } @Override public ListenableFuture setRating(Rating rating) { - controllerCompat.getTransportControls().setRating(MediaUtils.convertToRatingCompat(rating)); + controllerCompat + .getTransportControls() + .setRating(LegacyConversions.convertToRatingCompat(rating)); return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS)); } @@ -936,7 +940,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; controllerCompat .getTransportControls() - .setRepeatMode(MediaUtils.convertToPlaybackStateCompatRepeatMode(repeatMode)); + .setRepeatMode(LegacyConversions.convertToPlaybackStateCompatRepeatMode(repeatMode)); } @Override @@ -962,7 +966,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; controllerCompat .getTransportControls() - .setShuffleMode(MediaUtils.convertToPlaybackStateCompatShuffleMode(shuffleModeEnabled)); + .setShuffleMode( + LegacyConversions.convertToPlaybackStateCompatShuffleMode(shuffleModeEnabled)); } @Override @@ -1469,7 +1474,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; } } controllerCompat.addQueueItem( - MediaUtils.convertToMediaDescriptionCompat(mediaItems.get(i), bitmap), + LegacyConversions.convertToMediaDescriptionCompat(mediaItems.get(i), bitmap), /* index= */ startIndex + i); } } @@ -1584,7 +1589,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; if (!MediaUtils.areEqualError( oldLegacyPlayerInfo.playbackStateCompat, newLegacyPlayerInfo.playbackStateCompat)) { PlaybackException error = - MediaUtils.convertToPlaybackException(newLegacyPlayerInfo.playbackStateCompat); + LegacyConversions.convertToPlaybackException(newLegacyPlayerInfo.playbackStateCompat); listeners.queueEvent( Player.EVENT_PLAYER_ERROR, (listener) -> listener.onPlayerErrorChanged(error)); if (error != null) { @@ -1899,18 +1904,20 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; long oldActiveQueueId = getActiveQueueId(oldLegacyPlayerInfo.playbackStateCompat); long newActiveQueueId = getActiveQueueId(newLegacyPlayerInfo.playbackStateCompat); boolean isCurrentActiveQueueIdChanged = (oldActiveQueueId != newActiveQueueId) || initialUpdate; - long durationMs = MediaUtils.convertToDurationMs(newLegacyPlayerInfo.mediaMetadataCompat); + long durationMs = + LegacyConversions.convertToDurationMs(newLegacyPlayerInfo.mediaMetadataCompat); if (isMetadataCompatChanged || isCurrentActiveQueueIdChanged || isQueueChanged) { currentMediaItemIndex = findQueueItemIndex(newLegacyPlayerInfo.queue, newActiveQueueId); boolean hasMediaMetadataCompat = newLegacyPlayerInfo.mediaMetadataCompat != null; if (hasMediaMetadataCompat && isMetadataCompatChanged) { mediaMetadata = - MediaUtils.convertToMediaMetadata(newLegacyPlayerInfo.mediaMetadataCompat, ratingType); + LegacyConversions.convertToMediaMetadata( + newLegacyPlayerInfo.mediaMetadataCompat, ratingType); } else if (!hasMediaMetadataCompat && isCurrentActiveQueueIdChanged) { mediaMetadata = (currentMediaItemIndex == C.INDEX_UNSET) ? MediaMetadata.EMPTY - : MediaUtils.convertToMediaMetadata( + : LegacyConversions.convertToMediaMetadata( newLegacyPlayerInfo.queue.get(currentMediaItemIndex).getDescription(), ratingType); } else { @@ -1924,7 +1931,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; + " the active queue id and current Timeline should have currently playing" + " MediaItem."); MediaItem fakeMediaItem = - MediaUtils.convertToMediaItem(newLegacyPlayerInfo.mediaMetadataCompat, ratingType); + LegacyConversions.convertToMediaItem( + newLegacyPlayerInfo.mediaMetadataCompat, ratingType); currentTimeline = currentTimeline.copyWithFakeMediaItem(fakeMediaItem, durationMs); currentMediaItemIndex = currentTimeline.getWindowCount() - 1; } else { @@ -1938,7 +1946,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; currentTimeline = currentTimeline.copyWithClearedFakeMediaItem(); if (hasMediaMetadataCompat) { MediaItem mediaItem = - MediaUtils.convertToMediaItem( + LegacyConversions.convertToMediaItem( checkNotNull(currentTimeline.getMediaItemAt(currentMediaItemIndex)).mediaId, newLegacyPlayerInfo.mediaMetadataCompat, ratingType); @@ -1960,14 +1968,16 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; playlistMetadata = oldLegacyPlayerInfo.queueTitle == newLegacyPlayerInfo.queueTitle ? oldControllerInfo.playerInfo.playlistMetadata - : MediaUtils.convertToMediaMetadata(newLegacyPlayerInfo.queueTitle); - repeatMode = MediaUtils.convertToRepeatMode(newLegacyPlayerInfo.repeatMode); - shuffleModeEnabled = MediaUtils.convertToShuffleModeEnabled(newLegacyPlayerInfo.shuffleMode); + : LegacyConversions.convertToMediaMetadata(newLegacyPlayerInfo.queueTitle); + repeatMode = LegacyConversions.convertToRepeatMode(newLegacyPlayerInfo.repeatMode); + shuffleModeEnabled = + LegacyConversions.convertToShuffleModeEnabled(newLegacyPlayerInfo.shuffleMode); if (oldLegacyPlayerInfo.playbackStateCompat != newLegacyPlayerInfo.playbackStateCompat) { availableSessionCommands = - MediaUtils.convertToSessionCommands( + LegacyConversions.convertToSessionCommands( newLegacyPlayerInfo.playbackStateCompat, isSessionReady); - customLayout = MediaUtils.convertToCustomLayout(newLegacyPlayerInfo.playbackStateCompat); + customLayout = + LegacyConversions.convertToCustomLayout(newLegacyPlayerInfo.playbackStateCompat); } else { availableSessionCommands = oldControllerInfo.availableSessionCommands; customLayout = oldControllerInfo.customLayout; @@ -1980,53 +1990,58 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; ? newLegacyPlayerInfo.playbackInfoCompat.getVolumeControl() : VolumeProviderCompat.VOLUME_CONTROL_FIXED; availablePlayerCommands = - MediaUtils.convertToPlayerCommands( + LegacyConversions.convertToPlayerCommands( newLegacyPlayerInfo.playbackStateCompat, volumeControlType, sessionFlags, isSessionReady); PlaybackException playerError = - MediaUtils.convertToPlaybackException(newLegacyPlayerInfo.playbackStateCompat); + LegacyConversions.convertToPlaybackException(newLegacyPlayerInfo.playbackStateCompat); long currentPositionMs = - MediaUtils.convertToCurrentPositionMs( + LegacyConversions.convertToCurrentPositionMs( newLegacyPlayerInfo.playbackStateCompat, newLegacyPlayerInfo.mediaMetadataCompat, timeDiffMs); long bufferedPositionMs = - MediaUtils.convertToBufferedPositionMs( + LegacyConversions.convertToBufferedPositionMs( newLegacyPlayerInfo.playbackStateCompat, newLegacyPlayerInfo.mediaMetadataCompat, timeDiffMs); int bufferedPercentage = - MediaUtils.convertToBufferedPercentage( + LegacyConversions.convertToBufferedPercentage( newLegacyPlayerInfo.playbackStateCompat, newLegacyPlayerInfo.mediaMetadataCompat, timeDiffMs); long totalBufferedDurationMs = - MediaUtils.convertToTotalBufferedDurationMs( + LegacyConversions.convertToTotalBufferedDurationMs( newLegacyPlayerInfo.playbackStateCompat, newLegacyPlayerInfo.mediaMetadataCompat, timeDiffMs); - boolean isPlayingAd = MediaUtils.convertToIsPlayingAd(newLegacyPlayerInfo.mediaMetadataCompat); + boolean isPlayingAd = + LegacyConversions.convertToIsPlayingAd(newLegacyPlayerInfo.mediaMetadataCompat); PlaybackParameters playbackParameters = - MediaUtils.convertToPlaybackParameters(newLegacyPlayerInfo.playbackStateCompat); + LegacyConversions.convertToPlaybackParameters(newLegacyPlayerInfo.playbackStateCompat); AudioAttributes audioAttributes = - MediaUtils.convertToAudioAttributes(newLegacyPlayerInfo.playbackInfoCompat); + LegacyConversions.convertToAudioAttributes(newLegacyPlayerInfo.playbackInfoCompat); boolean playWhenReady = - MediaUtils.convertToPlayWhenReady(newLegacyPlayerInfo.playbackStateCompat); + LegacyConversions.convertToPlayWhenReady(newLegacyPlayerInfo.playbackStateCompat); @Player.State int playbackState = - MediaUtils.convertToPlaybackState( + LegacyConversions.convertToPlaybackState( newLegacyPlayerInfo.playbackStateCompat, newLegacyPlayerInfo.mediaMetadataCompat, timeDiffMs); - boolean isPlaying = MediaUtils.convertToIsPlaying(newLegacyPlayerInfo.playbackStateCompat); + boolean isPlaying = + LegacyConversions.convertToIsPlaying(newLegacyPlayerInfo.playbackStateCompat); DeviceInfo deviceInfo = - MediaUtils.convertToDeviceInfo(newLegacyPlayerInfo.playbackInfoCompat, routingControllerId); - int deviceVolume = MediaUtils.convertToDeviceVolume(newLegacyPlayerInfo.playbackInfoCompat); - boolean deviceMuted = MediaUtils.convertToIsDeviceMuted(newLegacyPlayerInfo.playbackInfoCompat); + LegacyConversions.convertToDeviceInfo( + newLegacyPlayerInfo.playbackInfoCompat, routingControllerId); + int deviceVolume = + LegacyConversions.convertToDeviceVolume(newLegacyPlayerInfo.playbackInfoCompat); + boolean deviceMuted = + LegacyConversions.convertToIsDeviceMuted(newLegacyPlayerInfo.playbackInfoCompat); long seekBackIncrementMs = oldControllerInfo.playerInfo.seekBackIncrementMs; long seekForwardIncrementMs = oldControllerInfo.playerInfo.seekForwardIncrementMs; @@ -2100,12 +2115,12 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; } else if (oldCurrentMediaItem.equals(newControllerInfo.playerInfo.getCurrentMediaItem())) { // Current item is the same. long oldCurrentPosition = - MediaUtils.convertToCurrentPositionMs( + LegacyConversions.convertToCurrentPositionMs( oldLegacyPlayerInfo.playbackStateCompat, oldLegacyPlayerInfo.mediaMetadataCompat, timeDiffMs); long newCurrentPosition = - MediaUtils.convertToCurrentPositionMs( + LegacyConversions.convertToCurrentPositionMs( newLegacyPlayerInfo.playbackStateCompat, newLegacyPlayerInfo.mediaMetadataCompat, timeDiffMs); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryServiceLegacyStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryServiceLegacyStub.java index 5bb8e0124a..77e26df420 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryServiceLegacyStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryServiceLegacyStub.java @@ -98,7 +98,7 @@ import java.util.concurrent.atomic.AtomicReference; } @Nullable LibraryParams params = - MediaUtils.convertToLibraryParams(librarySessionImpl.getContext(), rootHints); + LegacyConversions.convertToLibraryParams(librarySessionImpl.getContext(), rootHints); AtomicReference>> futureReference = new AtomicReference<>(); ConditionVariable haveFuture = new ConditionVariable(); @@ -118,7 +118,9 @@ import java.util.concurrent.atomic.AtomicReference; if (result != null && result.resultCode == RESULT_SUCCESS && result.value != null) { @Nullable Bundle extras = - result.params != null ? MediaUtils.convertToRootHints(result.params) : new Bundle(); + result.params != null + ? LegacyConversions.convertToRootHints(result.params) + : new Bundle(); boolean isSearchSessionCommandAvailable = getConnectedControllersManager() .isSessionCommandAvailable(controller, SessionCommand.COMMAND_CODE_LIBRARY_SEARCH); @@ -157,7 +159,7 @@ import java.util.concurrent.atomic.AtomicReference; } @Nullable LibraryParams params = - MediaUtils.convertToLibraryParams(librarySessionImpl.getContext(), option); + LegacyConversions.convertToLibraryParams(librarySessionImpl.getContext(), option); ignoreFuture(librarySessionImpl.onSubscribeOnHandler(controller, id, params)); }); } @@ -224,7 +226,8 @@ import java.util.concurrent.atomic.AtomicReference; // Requesting the list of children through pagination. @Nullable LibraryParams params = - MediaUtils.convertToLibraryParams(librarySessionImpl.getContext(), options); + LegacyConversions.convertToLibraryParams( + librarySessionImpl.getContext(), options); ListenableFuture>> future = librarySessionImpl.onGetChildrenOnHandler( controller, parentId, page, pageSize, params); @@ -315,7 +318,7 @@ import java.util.concurrent.atomic.AtomicReference; cb.registerSearchRequest(controller, query, extras, result); @Nullable LibraryParams params = - MediaUtils.convertToLibraryParams(librarySessionImpl.getContext(), extras); + LegacyConversions.convertToLibraryParams(librarySessionImpl.getContext(), extras); ignoreFuture(librarySessionImpl.onSearchOnHandler(controller, query, params)); // Actual search result will be sent by notifySearchResultChanged(). }); @@ -483,7 +486,7 @@ import java.util.concurrent.atomic.AtomicReference; Log.d(TAG, "Failed to get bitmap", e); } } - outputMediaItems.add(MediaUtils.convertToBrowserItem(mediaItems.get(i), bitmap)); + outputMediaItems.add(LegacyConversions.convertToBrowserItem(mediaItems.get(i), bitmap)); } outputFuture.set(outputMediaItems); } @@ -510,7 +513,8 @@ import java.util.concurrent.atomic.AtomicReference; MediaItem mediaItem = result.value; MediaMetadata metadata = mediaItem.mediaMetadata; if (metadata.artworkData == null) { - outputFuture.set(MediaUtils.convertToBrowserItem(mediaItem, /* artworkBitmap= */ null)); + outputFuture.set( + LegacyConversions.convertToBrowserItem(mediaItem, /* artworkBitmap= */ null)); return outputFuture; } @@ -531,7 +535,7 @@ import java.util.concurrent.atomic.AtomicReference; } catch (CancellationException | ExecutionException e) { Log.d(TAG, "failed to get bitmap", e); } - outputFuture.set(MediaUtils.convertToBrowserItem(mediaItem, bitmap)); + outputFuture.set(LegacyConversions.convertToBrowserItem(mediaItem, bitmap)); }, MoreExecutors.directExecutor()); return outputFuture; @@ -634,7 +638,7 @@ import java.util.concurrent.atomic.AtomicReference; } @Nullable LibraryParams libraryParams = - MediaUtils.convertToLibraryParams( + LegacyConversions.convertToLibraryParams( librarySessionImpl.getContext(), request.extras); ListenableFuture>> future = librarySessionImpl.onGetSearchResultOnHandler( diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java index cc0b17b340..8907c0f18f 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java @@ -504,7 +504,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; @Override public void onSetRating(RatingCompat ratingCompat, @Nullable Bundle unusedExtras) { - @Nullable Rating rating = MediaUtils.convertToRating(ratingCompat); + @Nullable Rating rating = LegacyConversions.convertToRating(ratingCompat); if (rating == null) { Log.w(TAG, "Ignoring invalid RatingCompat " + ratingCompat); return; @@ -535,7 +535,8 @@ import org.checkerframework.checker.initialization.qual.Initialized; controller -> sessionImpl .getPlayerWrapper() - .setRepeatMode(MediaUtils.convertToRepeatMode(playbackStateCompatRepeatMode)), + .setRepeatMode( + LegacyConversions.convertToRepeatMode(playbackStateCompatRepeatMode)), sessionCompat.getCurrentControllerInfo()); } @@ -546,7 +547,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; controller -> sessionImpl .getPlayerWrapper() - .setShuffleModeEnabled(MediaUtils.convertToShuffleModeEnabled(shuffleMode)), + .setShuffleModeEnabled(LegacyConversions.convertToShuffleModeEnabled(shuffleMode)), sessionCompat.getCurrentControllerInfo()); } @@ -861,7 +862,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; Log.w(TAG, "onAddQueueItem(): Media ID shouldn't be empty"); return; } - MediaItem mediaItem = MediaUtils.convertToMediaItem(description); + MediaItem mediaItem = LegacyConversions.convertToMediaItem(description); ListenableFuture> mediaItemsFuture = sessionImpl.onAddMediaItemsOnHandler(controller, ImmutableList.of(mediaItem)); Futures.addCallback( @@ -1143,7 +1144,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; sessionCompat.setRatingType(RatingCompat.RATING_NONE); } else { sessionCompat.setRatingType( - MediaUtils.getRatingCompatStyle(mediaItem.mediaMetadata.userRating)); + LegacyConversions.getRatingCompatStyle(mediaItem.mediaMetadata.userRating)); } updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper()); } @@ -1167,7 +1168,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; setQueue(sessionCompat, /* queue= */ null); return; } - List mediaItemList = MediaUtils.convertToMediaItemList(timeline); + List mediaItemList = LegacyConversions.convertToMediaItemList(timeline); List<@NullableType ListenableFuture> bitmapFutures = new ArrayList<>(); final AtomicInteger resultCount = new AtomicInteger(0); Runnable handleBitmapFuturesTask = @@ -1209,7 +1210,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; Log.d(TAG, "Failed to get bitmap", e); } } - queueItemList.add(MediaUtils.convertToQueueItem(mediaItems.get(i), i, bitmap)); + queueItemList.add(LegacyConversions.convertToQueueItem(mediaItems.get(i), i, bitmap)); } if (Util.SDK_INT < 21) { @@ -1245,12 +1246,13 @@ import org.checkerframework.checker.initialization.qual.Initialized; public void onShuffleModeEnabledChanged(int seq, boolean shuffleModeEnabled) throws RemoteException { sessionCompat.setShuffleMode( - MediaUtils.convertToPlaybackStateCompatShuffleMode(shuffleModeEnabled)); + LegacyConversions.convertToPlaybackStateCompatShuffleMode(shuffleModeEnabled)); } @Override public void onRepeatModeChanged(int seq, @RepeatMode int repeatMode) throws RemoteException { - sessionCompat.setRepeatMode(MediaUtils.convertToPlaybackStateCompatRepeatMode(repeatMode)); + sessionCompat.setRepeatMode( + LegacyConversions.convertToPlaybackStateCompatRepeatMode(repeatMode)); } @Override @@ -1258,7 +1260,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; @DeviceInfo.PlaybackType int playbackType = sessionImpl.getPlayerWrapper().getDeviceInfo().playbackType; if (playbackType == DeviceInfo.PLAYBACK_TYPE_LOCAL) { - int legacyStreamType = MediaUtils.getLegacyStreamType(audioAttributes); + int legacyStreamType = LegacyConversions.getLegacyStreamType(audioAttributes); sessionCompat.setPlaybackToLocal(legacyStreamType); } } @@ -1269,7 +1271,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; volumeProviderCompat = player.createVolumeProviderCompat(); if (volumeProviderCompat == null) { int streamType = - MediaUtils.getLegacyStreamType(player.getAudioAttributesWithCommandCheck()); + LegacyConversions.getLegacyStreamType(player.getAudioAttributesWithCommandCheck()); sessionCompat.setPlaybackToLocal(streamType); } else { sessionCompat.setPlaybackToRemote(volumeProviderCompat); @@ -1340,7 +1342,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; } setMetadata( sessionCompat, - MediaUtils.convertToMediaMetadataCompat( + LegacyConversions.convertToMediaMetadataCompat( newMediaMetadata, newMediaId, newMediaUri, @@ -1365,7 +1367,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; } setMetadata( sessionCompat, - MediaUtils.convertToMediaMetadataCompat( + LegacyConversions.convertToMediaMetadataCompat( newMediaMetadata, newMediaId, newMediaUri, newDurationMs, artworkBitmap)); } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java b/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java index 95949f0701..5a3a224ef2 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java @@ -15,146 +15,40 @@ */ package androidx.media3.session; -import static android.support.v4.media.session.MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS; -import static androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS; -import static androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME; -import static androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS; import static androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS; -import static androidx.media3.common.Player.COMMAND_GET_AUDIO_ATTRIBUTES; -import static androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM; -import static androidx.media3.common.Player.COMMAND_GET_DEVICE_VOLUME; -import static androidx.media3.common.Player.COMMAND_GET_METADATA; -import static androidx.media3.common.Player.COMMAND_GET_TIMELINE; -import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE; -import static androidx.media3.common.Player.COMMAND_PREPARE; -import static androidx.media3.common.Player.COMMAND_RELEASE; -import static androidx.media3.common.Player.COMMAND_SEEK_BACK; -import static androidx.media3.common.Player.COMMAND_SEEK_FORWARD; -import static androidx.media3.common.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM; -import static androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION; -import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT; -import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM; -import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS; -import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM; -import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME; -import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS; -import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM; -import static androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE; -import static androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE; -import static androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH; -import static androidx.media3.common.Player.COMMAND_STOP; -import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Util.castNonNull; -import static androidx.media3.common.util.Util.constrainValue; -import static androidx.media3.session.MediaConstants.EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY; -import static java.lang.Math.max; import static java.lang.Math.min; -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Bitmap; -import android.media.AudioManager; -import android.net.Uri; -import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; -import android.support.v4.media.MediaBrowserCompat; -import android.support.v4.media.MediaDescriptionCompat; -import android.support.v4.media.MediaMetadataCompat; -import android.support.v4.media.RatingCompat; -import android.support.v4.media.session.MediaControllerCompat; -import android.support.v4.media.session.MediaSessionCompat.QueueItem; import android.support.v4.media.session.PlaybackStateCompat; -import android.support.v4.media.session.PlaybackStateCompat.CustomAction; import android.text.TextUtils; import android.util.Pair; import androidx.annotation.Nullable; -import androidx.media.AudioAttributesCompat; import androidx.media.MediaBrowserServiceCompat.BrowserRoot; -import androidx.media.VolumeProviderCompat; -import androidx.media3.common.AdPlaybackState; -import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; -import androidx.media3.common.DeviceInfo; -import androidx.media3.common.HeartRating; -import androidx.media3.common.MediaItem; -import androidx.media3.common.MediaMetadata; -import androidx.media3.common.PercentageRating; -import androidx.media3.common.PlaybackException; -import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; import androidx.media3.common.Player.Command; import androidx.media3.common.Player.Commands; -import androidx.media3.common.Rating; -import androidx.media3.common.StarRating; -import androidx.media3.common.ThumbRating; -import androidx.media3.common.Timeline; -import androidx.media3.common.Timeline.Period; -import androidx.media3.common.Timeline.Window; -import androidx.media3.common.util.Log; import androidx.media3.common.util.NullableType; import androidx.media3.common.util.Util; -import androidx.media3.session.MediaLibraryService.LibraryParams; import androidx.media3.session.PlayerInfo.BundlingExclusions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeoutException; /* package */ final class MediaUtils { - public static final String TAG = "MediaUtils"; public static final int TRANSACTION_SIZE_LIMIT_IN_BYTES = 256 * 1024; // 256KB /** Constant to identify whether two calculated positions are considered as same */ public static final long POSITION_DIFF_TOLERANCE_MS = 100; + private static final String TAG = "MediaUtils"; // Stub BrowserRoot for accepting any connection here. public static final BrowserRoot defaultBrowserRoot = new BrowserRoot(MediaLibraryService.SERVICE_INTERFACE, null); - public static final ImmutableSet KNOWN_METADATA_COMPAT_KEYS = - ImmutableSet.of( - MediaMetadataCompat.METADATA_KEY_TITLE, - MediaMetadataCompat.METADATA_KEY_ARTIST, - MediaMetadataCompat.METADATA_KEY_DURATION, - MediaMetadataCompat.METADATA_KEY_ALBUM, - MediaMetadataCompat.METADATA_KEY_AUTHOR, - MediaMetadataCompat.METADATA_KEY_WRITER, - MediaMetadataCompat.METADATA_KEY_COMPOSER, - MediaMetadataCompat.METADATA_KEY_COMPILATION, - MediaMetadataCompat.METADATA_KEY_DATE, - MediaMetadataCompat.METADATA_KEY_YEAR, - MediaMetadataCompat.METADATA_KEY_GENRE, - MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, - MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, - MediaMetadataCompat.METADATA_KEY_DISC_NUMBER, - MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, - MediaMetadataCompat.METADATA_KEY_ART, - MediaMetadataCompat.METADATA_KEY_ART_URI, - MediaMetadataCompat.METADATA_KEY_ALBUM_ART, - MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, - MediaMetadataCompat.METADATA_KEY_USER_RATING, - MediaMetadataCompat.METADATA_KEY_RATING, - MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, - MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, - MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, - MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, - MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, - MediaMetadataCompat.METADATA_KEY_MEDIA_ID, - MediaMetadataCompat.METADATA_KEY_MEDIA_URI, - MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE, - MediaMetadataCompat.METADATA_KEY_ADVERTISEMENT, - MediaMetadataCompat.METADATA_KEY_DOWNLOAD_STATUS, - MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT); - /** Returns whether two {@link PlaybackStateCompat} have equal error. */ public static boolean areEqualError( @Nullable PlaybackStateCompat a, @Nullable PlaybackStateCompat b) { @@ -167,169 +61,6 @@ import java.util.concurrent.TimeoutException; return aHasError == bHasError; } - /** Converts {@link PlaybackStateCompat} to {@link PlaybackException}. */ - @Nullable - public static PlaybackException convertToPlaybackException( - @Nullable PlaybackStateCompat playbackStateCompat) { - if (playbackStateCompat == null - || playbackStateCompat.getState() != PlaybackStateCompat.STATE_ERROR) { - return null; - } - StringBuilder stringBuilder = new StringBuilder(); - if (!TextUtils.isEmpty(playbackStateCompat.getErrorMessage())) { - stringBuilder.append(playbackStateCompat.getErrorMessage().toString()).append(", "); - } - stringBuilder.append("code=").append(playbackStateCompat.getErrorCode()); - String errorMessage = stringBuilder.toString(); - return new PlaybackException( - errorMessage, /* cause= */ null, PlaybackException.ERROR_CODE_REMOTE_ERROR); - } - - public static MediaBrowserCompat.MediaItem convertToBrowserItem( - MediaItem item, @Nullable Bitmap artworkBitmap) { - MediaDescriptionCompat description = convertToMediaDescriptionCompat(item, artworkBitmap); - MediaMetadata metadata = item.mediaMetadata; - int flags = 0; - if (metadata.isBrowsable != null && metadata.isBrowsable) { - flags |= MediaBrowserCompat.MediaItem.FLAG_BROWSABLE; - } - if (metadata.isPlayable != null && metadata.isPlayable) { - flags |= MediaBrowserCompat.MediaItem.FLAG_PLAYABLE; - } - return new MediaBrowserCompat.MediaItem(description, flags); - } - - /** Converts a {@link MediaBrowserCompat.MediaItem} to a {@link MediaItem}. */ - public static MediaItem convertToMediaItem(MediaBrowserCompat.MediaItem item) { - return convertToMediaItem(item.getDescription(), item.isBrowsable(), item.isPlayable()); - } - - /** Converts a {@link QueueItem} to a {@link MediaItem}. */ - public static MediaItem convertToMediaItem(QueueItem item) { - return convertToMediaItem(item.getDescription()); - } - - /** Converts a {@link QueueItem} to a {@link MediaItem}. */ - public static MediaItem convertToMediaItem(MediaDescriptionCompat description) { - checkNotNull(description); - return convertToMediaItem(description, /* browsable= */ false, /* playable= */ true); - } - - /** Converts a {@link MediaMetadataCompat} to a {@link MediaItem}. */ - public static MediaItem convertToMediaItem( - MediaMetadataCompat metadataCompat, @RatingCompat.Style int ratingType) { - return convertToMediaItem( - metadataCompat.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID), - metadataCompat, - ratingType); - } - - /** Converts a {@code mediaId} and {@link MediaMetadataCompat} to a {@link MediaItem}. */ - public static MediaItem convertToMediaItem( - @Nullable String mediaId, - MediaMetadataCompat metadataCompat, - @RatingCompat.Style int ratingType) { - MediaItem.Builder builder = new MediaItem.Builder(); - if (mediaId != null) { - builder.setMediaId(mediaId); - } - @Nullable - String mediaUriString = metadataCompat.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI); - if (mediaUriString != null) { - builder.setRequestMetadata( - new MediaItem.RequestMetadata.Builder().setMediaUri(Uri.parse(mediaUriString)).build()); - } - builder.setMediaMetadata(convertToMediaMetadata(metadataCompat, ratingType)); - return builder.build(); - } - - private static MediaItem convertToMediaItem( - MediaDescriptionCompat descriptionCompat, boolean browsable, boolean playable) { - @Nullable String mediaId = descriptionCompat.getMediaId(); - return new MediaItem.Builder() - .setMediaId(mediaId == null ? MediaItem.DEFAULT_MEDIA_ID : mediaId) - .setRequestMetadata( - new MediaItem.RequestMetadata.Builder() - .setMediaUri(descriptionCompat.getMediaUri()) - .build()) - .setMediaMetadata( - convertToMediaMetadata( - descriptionCompat, RatingCompat.RATING_NONE, browsable, playable)) - .build(); - } - - /** Converts a list of {@link MediaBrowserCompat.MediaItem} to a list of {@link MediaItem}. */ - public static ImmutableList convertBrowserItemListToMediaItemList( - List items) { - ImmutableList.Builder builder = new ImmutableList.Builder<>(); - for (int i = 0; i < items.size(); i++) { - builder.add(convertToMediaItem(items.get(i))); - } - return builder.build(); - } - - /** Converts a {@link Timeline} to a list of {@link MediaItem MediaItems}. */ - public static List convertToMediaItemList(Timeline timeline) { - List mediaItems = new ArrayList<>(); - Timeline.Window window = new Timeline.Window(); - for (int i = 0; i < timeline.getWindowCount(); i++) { - mediaItems.add(timeline.getWindow(i, window).mediaItem); - } - return mediaItems; - } - - /** - * Converts a {@link MediaItem} to a {@link QueueItem}. The index of the item in the playlist - * would be used as the queue ID to match the behavior of {@link MediaController}. - */ - public static QueueItem convertToQueueItem( - MediaItem item, int mediaItemIndex, @Nullable Bitmap artworkBitmap) { - MediaDescriptionCompat description = convertToMediaDescriptionCompat(item, artworkBitmap); - long id = convertToQueueItemId(mediaItemIndex); - return new QueueItem(description, id); - } - - /** Converts the index of a {@link MediaItem} in a playlist into id of {@link QueueItem}. */ - public static long convertToQueueItemId(int mediaItemIndex) { - if (mediaItemIndex == C.INDEX_UNSET) { - return QueueItem.UNKNOWN_ID; - } - return mediaItemIndex; - } - - public static Window convertToWindow(MediaItem mediaItem, int periodIndex) { - Window window = new Window(); - window.set( - /* uid= */ 0, - mediaItem, - /* manifest= */ null, - /* presentationStartTimeMs= */ 0, - /* windowStartTimeMs= */ 0, - /* elapsedRealtimeEpochOffsetMs= */ 0, - /* isSeekable= */ true, - /* isDynamic= */ false, - /* liveConfiguration= */ null, - /* defaultPositionUs= */ 0, - /* durationUs= */ C.TIME_UNSET, - /* firstPeriodIndex= */ periodIndex, - /* lastPeriodIndex= */ periodIndex, - /* positionInFirstPeriodUs= */ 0); - return window; - } - - public static Period convertToPeriod(int windowIndex) { - Period period = new Period(); - period.set( - /* id= */ null, - /* uid= */ null, - windowIndex, - /* durationUs= */ C.TIME_UNSET, - /* positionInWindowUs= */ 0, - /* adPlaybackState= */ AdPlaybackState.NONE, - /* isPlaceholder= */ true); - return period; - } - /** * Returns a list which consists of first {@code N} items of the given list with the same order. * {@code N} is determined as the maximum number of items whose total parcelled size is less than @@ -356,776 +87,6 @@ import java.util.concurrent.TimeoutException; return result; } - /** Converts a {@link MediaItem} to a {@link MediaDescriptionCompat} */ - @SuppressWarnings("deprecation") // Converting deprecated fields. - public static MediaDescriptionCompat convertToMediaDescriptionCompat( - MediaItem item, @Nullable Bitmap artworkBitmap) { - MediaDescriptionCompat.Builder builder = - new MediaDescriptionCompat.Builder() - .setMediaId(item.mediaId.equals(MediaItem.DEFAULT_MEDIA_ID) ? null : item.mediaId); - MediaMetadata metadata = item.mediaMetadata; - if (artworkBitmap != null) { - builder.setIconBitmap(artworkBitmap); - } - @Nullable Bundle extras = metadata.extras; - boolean hasFolderType = - metadata.folderType != null && metadata.folderType != MediaMetadata.FOLDER_TYPE_NONE; - boolean hasMediaType = metadata.mediaType != null; - if (hasFolderType || hasMediaType) { - if (extras == null) { - extras = new Bundle(); - } else { - extras = new Bundle(extras); - } - if (hasFolderType) { - extras.putLong( - MediaDescriptionCompat.EXTRA_BT_FOLDER_TYPE, - convertToExtraBtFolderType(checkNotNull(metadata.folderType))); - } - if (hasMediaType) { - extras.putLong( - MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT, checkNotNull(metadata.mediaType)); - } - } - return builder - .setTitle(metadata.title) - // The BT AVRPC service expects the subtitle of the media description to be the artist - // (see https://github.com/androidx/media/issues/148). - .setSubtitle(metadata.artist != null ? metadata.artist : metadata.subtitle) - .setDescription(metadata.description) - .setIconUri(metadata.artworkUri) - .setMediaUri(item.requestMetadata.mediaUri) - .setExtras(extras) - .build(); - } - - /** Creates {@link MediaMetadata} from the {@link CharSequence queue title}. */ - public static MediaMetadata convertToMediaMetadata(@Nullable CharSequence queueTitle) { - if (queueTitle == null) { - return MediaMetadata.EMPTY; - } - return new MediaMetadata.Builder().setTitle(queueTitle).build(); - } - - public static MediaMetadata convertToMediaMetadata( - @Nullable MediaDescriptionCompat descriptionCompat, @RatingCompat.Style int ratingType) { - return convertToMediaMetadata( - descriptionCompat, ratingType, /* browsable= */ false, /* playable= */ true); - } - - @SuppressWarnings("deprecation") // Populating deprecated fields. - private static MediaMetadata convertToMediaMetadata( - @Nullable MediaDescriptionCompat descriptionCompat, - @RatingCompat.Style int ratingType, - boolean browsable, - boolean playable) { - if (descriptionCompat == null) { - return MediaMetadata.EMPTY; - } - - MediaMetadata.Builder builder = new MediaMetadata.Builder(); - - builder - .setTitle(descriptionCompat.getTitle()) - .setSubtitle(descriptionCompat.getSubtitle()) - .setDescription(descriptionCompat.getDescription()) - .setArtworkUri(descriptionCompat.getIconUri()) - .setUserRating(convertToRating(RatingCompat.newUnratedRating(ratingType))); - - @Nullable Bitmap iconBitmap = descriptionCompat.getIconBitmap(); - if (iconBitmap != null) { - @Nullable byte[] artworkData = null; - try { - artworkData = convertToByteArray(iconBitmap); - } catch (IOException e) { - Log.w(TAG, "Failed to convert iconBitmap to artworkData", e); - } - builder.setArtworkData(artworkData, MediaMetadata.PICTURE_TYPE_FRONT_COVER); - } - - @Nullable Bundle compatExtras = descriptionCompat.getExtras(); - @Nullable Bundle extras = compatExtras == null ? null : new Bundle(compatExtras); - - if (extras != null && extras.containsKey(MediaDescriptionCompat.EXTRA_BT_FOLDER_TYPE)) { - builder.setFolderType( - convertToFolderType(extras.getLong(MediaDescriptionCompat.EXTRA_BT_FOLDER_TYPE))); - extras.remove(MediaDescriptionCompat.EXTRA_BT_FOLDER_TYPE); - } - builder.setIsBrowsable(browsable); - - if (extras != null && extras.containsKey(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT)) { - builder.setMediaType((int) extras.getLong(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT)); - extras.remove(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT); - } - if (extras != null && !extras.isEmpty()) { - builder.setExtras(extras); - } - - builder.setIsPlayable(playable); - - return builder.build(); - } - - /** Creates {@link MediaMetadata} from the {@link MediaMetadataCompat} and rating type. */ - @SuppressWarnings("deprecation") // Populating deprecated fields. - public static MediaMetadata convertToMediaMetadata( - @Nullable MediaMetadataCompat metadataCompat, @RatingCompat.Style int ratingType) { - if (metadataCompat == null) { - return MediaMetadata.EMPTY; - } - - MediaMetadata.Builder builder = new MediaMetadata.Builder(); - - builder - .setTitle( - getFirstText( - metadataCompat, - MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, - MediaMetadataCompat.METADATA_KEY_TITLE)) - .setSubtitle(metadataCompat.getText(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE)) - .setDescription( - metadataCompat.getText(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION)) - .setArtist(metadataCompat.getText(MediaMetadataCompat.METADATA_KEY_ARTIST)) - .setAlbumTitle(metadataCompat.getText(MediaMetadataCompat.METADATA_KEY_ALBUM)) - .setAlbumArtist(metadataCompat.getText(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST)) - .setOverallRating( - convertToRating(metadataCompat.getRating(MediaMetadataCompat.METADATA_KEY_RATING))); - - @Nullable - Rating userRating = - convertToRating(metadataCompat.getRating(MediaMetadataCompat.METADATA_KEY_USER_RATING)); - if (userRating != null) { - builder.setUserRating(userRating); - } else { - builder.setUserRating(convertToRating(RatingCompat.newUnratedRating(ratingType))); - } - - if (metadataCompat.containsKey(MediaMetadataCompat.METADATA_KEY_YEAR)) { - long year = metadataCompat.getLong(MediaMetadataCompat.METADATA_KEY_YEAR); - builder.setRecordingYear((int) year); - } - - @Nullable - String artworkUriString = - getFirstString( - metadataCompat, - MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, - MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI); - if (artworkUriString != null) { - builder.setArtworkUri(Uri.parse(artworkUriString)); - } - - @Nullable - Bitmap artworkBitmap = - getFirstBitmap( - metadataCompat, - MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, - MediaMetadataCompat.METADATA_KEY_ALBUM_ART); - if (artworkBitmap != null) { - try { - byte[] artworkData = convertToByteArray(artworkBitmap); - builder.setArtworkData(artworkData, MediaMetadata.PICTURE_TYPE_FRONT_COVER); - } catch (IOException e) { - Log.w(TAG, "Failed to convert artworkBitmap to artworkData", e); - } - } - - boolean isBrowsable = - metadataCompat.containsKey(MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE); - builder.setIsBrowsable(isBrowsable); - if (isBrowsable) { - builder.setFolderType( - convertToFolderType( - metadataCompat.getLong(MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE))); - } - - if (metadataCompat.containsKey(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT)) { - builder.setMediaType( - (int) metadataCompat.getLong(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT)); - } - - builder.setIsPlayable(true); - - Bundle extras = metadataCompat.getBundle(); - for (String key : KNOWN_METADATA_COMPAT_KEYS) { - extras.remove(key); - } - if (!extras.isEmpty()) { - builder.setExtras(extras); - } - - return builder.build(); - } - - @Nullable - private static Bitmap getFirstBitmap(MediaMetadataCompat mediaMetadataCompat, String... keys) { - for (String key : keys) { - if (mediaMetadataCompat.containsKey(key)) { - return mediaMetadataCompat.getBitmap(key); - } - } - return null; - } - - @Nullable - private static String getFirstString(MediaMetadataCompat mediaMetadataCompat, String... keys) { - for (String key : keys) { - if (mediaMetadataCompat.containsKey(key)) { - return mediaMetadataCompat.getString(key); - } - } - return null; - } - - @Nullable - private static CharSequence getFirstText( - MediaMetadataCompat mediaMetadataCompat, String... keys) { - for (String key : keys) { - if (mediaMetadataCompat.containsKey(key)) { - return mediaMetadataCompat.getText(key); - } - } - return null; - } - - /** - * Converts a {@link MediaMetadata} to a {@link MediaMetadataCompat}. - * - * @param metadata The {@link MediaMetadata} instance to convert. - * @param mediaId The corresponding media ID. - * @param mediaUri The corresponding media URI, or null if unknown. - * @param durationMs The duration of the media, in milliseconds or {@link C#TIME_UNSET}, if no - * duration should be included. - * @return An instance of the legacy {@link MediaMetadataCompat}. - */ - @SuppressWarnings("deprecation") // Converting deprecated fields. - public static MediaMetadataCompat convertToMediaMetadataCompat( - MediaMetadata metadata, - String mediaId, - @Nullable Uri mediaUri, - long durationMs, - @Nullable Bitmap artworkBitmap) { - MediaMetadataCompat.Builder builder = - new MediaMetadataCompat.Builder() - .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, mediaId); - - if (metadata.title != null) { - builder.putText(MediaMetadataCompat.METADATA_KEY_TITLE, metadata.title); - builder.putText(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, metadata.title); - } - - if (metadata.subtitle != null) { - builder.putText(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, metadata.subtitle); - } - - if (metadata.description != null) { - builder.putText(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, metadata.description); - } - - if (metadata.artist != null) { - builder.putText(MediaMetadataCompat.METADATA_KEY_ARTIST, metadata.artist); - } - - if (metadata.albumTitle != null) { - builder.putText(MediaMetadataCompat.METADATA_KEY_ALBUM, metadata.albumTitle); - } - - if (metadata.albumArtist != null) { - builder.putText(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, metadata.albumArtist); - } - - if (metadata.recordingYear != null) { - builder.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, metadata.recordingYear); - } - - if (mediaUri != null) { - builder.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI, mediaUri.toString()); - } - - if (metadata.artworkUri != null) { - builder.putString( - MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, metadata.artworkUri.toString()); - builder.putString( - MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, metadata.artworkUri.toString()); - } - - if (artworkBitmap != null) { - builder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, artworkBitmap); - builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, artworkBitmap); - } - - if (metadata.folderType != null && metadata.folderType != MediaMetadata.FOLDER_TYPE_NONE) { - builder.putLong( - MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE, - convertToExtraBtFolderType(metadata.folderType)); - } - - if (durationMs != C.TIME_UNSET) { - builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, durationMs); - } - - @Nullable RatingCompat userRatingCompat = convertToRatingCompat(metadata.userRating); - if (userRatingCompat != null) { - builder.putRating(MediaMetadataCompat.METADATA_KEY_USER_RATING, userRatingCompat); - } - - @Nullable RatingCompat overallRatingCompat = convertToRatingCompat(metadata.overallRating); - if (overallRatingCompat != null) { - builder.putRating(MediaMetadataCompat.METADATA_KEY_RATING, overallRatingCompat); - } - - if (metadata.mediaType != null) { - builder.putLong(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT, metadata.mediaType); - } - - return builder.build(); - } - - @SuppressWarnings("deprecation") // Converting to deprecated constants. - @MediaMetadata.FolderType - private static int convertToFolderType(long extraBtFolderType) { - if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_MIXED) { - return MediaMetadata.FOLDER_TYPE_MIXED; - } else if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_TITLES) { - return MediaMetadata.FOLDER_TYPE_TITLES; - } else if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_ALBUMS) { - return MediaMetadata.FOLDER_TYPE_ALBUMS; - } else if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_ARTISTS) { - return MediaMetadata.FOLDER_TYPE_ARTISTS; - } else if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_GENRES) { - return MediaMetadata.FOLDER_TYPE_GENRES; - } else if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_PLAYLISTS) { - return MediaMetadata.FOLDER_TYPE_PLAYLISTS; - } else if (extraBtFolderType == MediaDescriptionCompat.BT_FOLDER_TYPE_YEARS) { - return MediaMetadata.FOLDER_TYPE_YEARS; - } else { - return MediaMetadata.FOLDER_TYPE_MIXED; - } - } - - @SuppressWarnings("deprecation") // Converting from deprecated constants. - private static long convertToExtraBtFolderType(@MediaMetadata.FolderType int folderType) { - switch (folderType) { - case MediaMetadata.FOLDER_TYPE_MIXED: - return MediaDescriptionCompat.BT_FOLDER_TYPE_MIXED; - case MediaMetadata.FOLDER_TYPE_TITLES: - return MediaDescriptionCompat.BT_FOLDER_TYPE_TITLES; - case MediaMetadata.FOLDER_TYPE_ALBUMS: - return MediaDescriptionCompat.BT_FOLDER_TYPE_ALBUMS; - case MediaMetadata.FOLDER_TYPE_ARTISTS: - return MediaDescriptionCompat.BT_FOLDER_TYPE_ARTISTS; - case MediaMetadata.FOLDER_TYPE_GENRES: - return MediaDescriptionCompat.BT_FOLDER_TYPE_GENRES; - case MediaMetadata.FOLDER_TYPE_PLAYLISTS: - return MediaDescriptionCompat.BT_FOLDER_TYPE_PLAYLISTS; - case MediaMetadata.FOLDER_TYPE_YEARS: - return MediaDescriptionCompat.BT_FOLDER_TYPE_YEARS; - case MediaMetadata.FOLDER_TYPE_NONE: - default: - throw new IllegalArgumentException("Unrecognized FolderType: " + folderType); - } - } - - /** - * Creates a {@link Rating} from the {@link RatingCompat}. - * - * @param ratingCompat A {@link RatingCompat} object. - * @return The newly created {@link Rating} object. - */ - @Nullable - public static Rating convertToRating(@Nullable RatingCompat ratingCompat) { - if (ratingCompat == null) { - return null; - } - switch (ratingCompat.getRatingStyle()) { - case RatingCompat.RATING_3_STARS: - return ratingCompat.isRated() - ? new StarRating(3, ratingCompat.getStarRating()) - : new StarRating(3); - case RatingCompat.RATING_4_STARS: - return ratingCompat.isRated() - ? new StarRating(4, ratingCompat.getStarRating()) - : new StarRating(4); - case RatingCompat.RATING_5_STARS: - return ratingCompat.isRated() - ? new StarRating(5, ratingCompat.getStarRating()) - : new StarRating(5); - case RatingCompat.RATING_HEART: - return ratingCompat.isRated() - ? new HeartRating(ratingCompat.hasHeart()) - : new HeartRating(); - case RatingCompat.RATING_THUMB_UP_DOWN: - return ratingCompat.isRated() - ? new ThumbRating(ratingCompat.isThumbUp()) - : new ThumbRating(); - case RatingCompat.RATING_PERCENTAGE: - return ratingCompat.isRated() - ? new PercentageRating(ratingCompat.getPercentRating()) - : new PercentageRating(); - case RatingCompat.RATING_NONE: - default: - return null; - } - } - - /** - * Creates a {@link RatingCompat} from the {@link Rating}. - * - * @param rating A {@link Rating} object. - * @return The newly created {@link RatingCompat} object. - */ - @SuppressLint("WrongConstant") // for @StarStyle - @Nullable - public static RatingCompat convertToRatingCompat(@Nullable Rating rating) { - if (rating == null) { - return null; - } - int ratingCompatStyle = getRatingCompatStyle(rating); - if (!rating.isRated()) { - return RatingCompat.newUnratedRating(ratingCompatStyle); - } - - switch (ratingCompatStyle) { - case RatingCompat.RATING_3_STARS: - case RatingCompat.RATING_4_STARS: - case RatingCompat.RATING_5_STARS: - return RatingCompat.newStarRating(ratingCompatStyle, ((StarRating) rating).getStarRating()); - case RatingCompat.RATING_HEART: - return RatingCompat.newHeartRating(((HeartRating) rating).isHeart()); - case RatingCompat.RATING_THUMB_UP_DOWN: - return RatingCompat.newThumbRating(((ThumbRating) rating).isThumbsUp()); - case RatingCompat.RATING_PERCENTAGE: - return RatingCompat.newPercentageRating(((PercentageRating) rating).getPercent()); - case RatingCompat.RATING_NONE: - default: - return null; - } - } - - /** Converts {@link Player}' states to state of {@link PlaybackStateCompat}. */ - @PlaybackStateCompat.State - public static int convertToPlaybackStateCompatState(Player player, boolean playIfSuppressed) { - if (player.getPlayerError() != null) { - return PlaybackStateCompat.STATE_ERROR; - } - @Player.State int playbackState = player.getPlaybackState(); - boolean shouldShowPlayButton = Util.shouldShowPlayButton(player, playIfSuppressed); - switch (playbackState) { - case Player.STATE_IDLE: - return PlaybackStateCompat.STATE_NONE; - case Player.STATE_READY: - return shouldShowPlayButton - ? PlaybackStateCompat.STATE_PAUSED - : PlaybackStateCompat.STATE_PLAYING; - case Player.STATE_ENDED: - return PlaybackStateCompat.STATE_STOPPED; - case Player.STATE_BUFFERING: - return shouldShowPlayButton - ? PlaybackStateCompat.STATE_PAUSED - : PlaybackStateCompat.STATE_BUFFERING; - default: - throw new IllegalArgumentException("Unrecognized State: " + playbackState); - } - } - - /** Converts a {@link PlaybackStateCompat} to {@link PlaybackParameters}. */ - public static PlaybackParameters convertToPlaybackParameters( - @Nullable PlaybackStateCompat playbackStateCompat) { - return playbackStateCompat == null - ? PlaybackParameters.DEFAULT - : new PlaybackParameters(playbackStateCompat.getPlaybackSpeed()); - } - - /** Converts a {@link PlaybackStateCompat} to {@link Player}'s play when ready. */ - public static boolean convertToPlayWhenReady(@Nullable PlaybackStateCompat playbackState) { - if (playbackState == null) { - return false; - } - switch (playbackState.getState()) { - case PlaybackStateCompat.STATE_BUFFERING: - case PlaybackStateCompat.STATE_FAST_FORWARDING: - case PlaybackStateCompat.STATE_PLAYING: - case PlaybackStateCompat.STATE_REWINDING: - case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT: - case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS: - case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM: - return true; - case PlaybackStateCompat.STATE_CONNECTING: - case PlaybackStateCompat.STATE_ERROR: - case PlaybackStateCompat.STATE_NONE: - case PlaybackStateCompat.STATE_PAUSED: - case PlaybackStateCompat.STATE_STOPPED: - return false; - } - return false; - } - - /** Converts a {@link PlaybackStateCompat} to {@link Player.State} */ - public static @Player.State int convertToPlaybackState( - @Nullable PlaybackStateCompat playbackStateCompat, - @Nullable MediaMetadataCompat currentMediaMetadata, - long timeDiffMs) { - if (playbackStateCompat == null) { - return Player.STATE_IDLE; - } - switch (playbackStateCompat.getState()) { - case PlaybackStateCompat.STATE_CONNECTING: - case PlaybackStateCompat.STATE_ERROR: - case PlaybackStateCompat.STATE_NONE: - case PlaybackStateCompat.STATE_STOPPED: - return Player.STATE_IDLE; - case PlaybackStateCompat.STATE_BUFFERING: - case PlaybackStateCompat.STATE_FAST_FORWARDING: - case PlaybackStateCompat.STATE_REWINDING: - case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT: - case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS: - case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM: - return Player.STATE_BUFFERING; - case PlaybackStateCompat.STATE_PLAYING: - return Player.STATE_READY; - case PlaybackStateCompat.STATE_PAUSED: - long duration = convertToDurationMs(currentMediaMetadata); - if (duration == C.TIME_UNSET) { - return Player.STATE_READY; - } - long currentPosition = - convertToCurrentPositionMs(playbackStateCompat, currentMediaMetadata, timeDiffMs); - return (currentPosition < duration) ? Player.STATE_READY : Player.STATE_ENDED; - default: - throw new IllegalStateException( - "Unrecognized PlaybackStateCompat: " + playbackStateCompat.getState()); - } - } - - /** Converts a {@link PlaybackStateCompat} to isPlaying, defined by {@link Player#isPlaying()}. */ - public static boolean convertToIsPlaying(@Nullable PlaybackStateCompat playbackStateCompat) { - if (playbackStateCompat == null) { - return false; - } - return playbackStateCompat.getState() == PlaybackStateCompat.STATE_PLAYING; - } - - /** Converts a {@link PlaybackStateCompat} to isPlaying, defined by {@link Player#isPlaying()}. */ - public static boolean convertToIsPlayingAd(@Nullable MediaMetadataCompat metadataCompat) { - if (metadataCompat == null) { - return false; - } - return metadataCompat.getLong(MediaMetadataCompat.METADATA_KEY_ADVERTISEMENT) != 0; - } - - /** Gets the current position. {@code 0} will be returned if unknown. */ - public static long convertToCurrentPositionMs( - @Nullable PlaybackStateCompat playbackStateCompat, - @Nullable MediaMetadataCompat metadataCompat, - long timeDiffMs) { - if (playbackStateCompat == null) { - return 0; - } - long positionMs = - playbackStateCompat.getState() == PlaybackStateCompat.STATE_PLAYING - ? getCurrentPosition(playbackStateCompat, timeDiffMs) - : playbackStateCompat.getPosition(); - long durationMs = convertToDurationMs(metadataCompat); - return durationMs == C.TIME_UNSET - ? max(0, positionMs) - : constrainValue(positionMs, /* min= */ 0, durationMs); - } - - @SuppressWarnings("nullness:argument") // PlaybackStateCompat#getCurrentPosition can take null. - private static long getCurrentPosition(PlaybackStateCompat playbackStateCompat, long timeDiffMs) { - return playbackStateCompat.getCurrentPosition(timeDiffMs == C.TIME_UNSET ? null : timeDiffMs); - } - - /** Gets the duration. {@link C#TIME_UNSET} will be returned if unknown. */ - public static long convertToDurationMs(@Nullable MediaMetadataCompat metadataCompat) { - if (metadataCompat == null - || !metadataCompat.containsKey(MediaMetadataCompat.METADATA_KEY_DURATION)) { - return C.TIME_UNSET; - } - long legacyDurationMs = metadataCompat.getLong(MediaMetadataCompat.METADATA_KEY_DURATION); - return legacyDurationMs <= 0 ? C.TIME_UNSET : legacyDurationMs; - } - - /** Gets the buffered position. {@code 0} will be returned if unknown. */ - public static long convertToBufferedPositionMs( - @Nullable PlaybackStateCompat playbackStateCompat, - @Nullable MediaMetadataCompat metadataCompat, - long timeDiffMs) { - long legacyBufferedPositionMs = - (playbackStateCompat == null) ? 0 : playbackStateCompat.getBufferedPosition(); - long currentPositionMs = - convertToCurrentPositionMs(playbackStateCompat, metadataCompat, timeDiffMs); - long durationMs = convertToDurationMs(metadataCompat); - return (durationMs == C.TIME_UNSET) - ? max(currentPositionMs, legacyBufferedPositionMs) - : constrainValue(legacyBufferedPositionMs, currentPositionMs, durationMs); - } - - /** Gets the total buffered duration. {@code 0} will be returned if unknown. */ - public static long convertToTotalBufferedDurationMs( - @Nullable PlaybackStateCompat playbackStateCompat, - @Nullable MediaMetadataCompat metadataCompat, - long timeDiffMs) { - long bufferedPositionMs = - convertToBufferedPositionMs(playbackStateCompat, metadataCompat, timeDiffMs); - long currentPositionMs = - convertToCurrentPositionMs(playbackStateCompat, metadataCompat, timeDiffMs); - return bufferedPositionMs - currentPositionMs; - } - - /** Gets the buffered percentage. {@code 0} will be returned if unknown. */ - public static int convertToBufferedPercentage( - @Nullable PlaybackStateCompat playbackStateCompat, - @Nullable MediaMetadataCompat mediaMetadataCompat, - long timeDiffMs) { - long bufferedPositionMs = - MediaUtils.convertToBufferedPositionMs( - playbackStateCompat, mediaMetadataCompat, timeDiffMs); - long durationMs = MediaUtils.convertToDurationMs(mediaMetadataCompat); - return calculateBufferedPercentage(bufferedPositionMs, durationMs); - } - - public static @RatingCompat.Style int getRatingCompatStyle(@Nullable Rating rating) { - if (rating instanceof HeartRating) { - return RatingCompat.RATING_HEART; - } else if (rating instanceof ThumbRating) { - return RatingCompat.RATING_THUMB_UP_DOWN; - } else if (rating instanceof StarRating) { - switch (((StarRating) rating).getMaxStars()) { - case 3: - return RatingCompat.RATING_3_STARS; - case 4: - return RatingCompat.RATING_4_STARS; - case 5: - return RatingCompat.RATING_5_STARS; - } - } else if (rating instanceof PercentageRating) { - return RatingCompat.RATING_PERCENTAGE; - } - return RatingCompat.RATING_NONE; - } - - /** Converts {@link PlaybackStateCompat.RepeatMode} to {@link Player.RepeatMode}. */ - public static @Player.RepeatMode int convertToRepeatMode( - @PlaybackStateCompat.RepeatMode int playbackStateCompatRepeatMode) { - switch (playbackStateCompatRepeatMode) { - case PlaybackStateCompat.REPEAT_MODE_INVALID: - case PlaybackStateCompat.REPEAT_MODE_NONE: - return Player.REPEAT_MODE_OFF; - case PlaybackStateCompat.REPEAT_MODE_ONE: - return Player.REPEAT_MODE_ONE; - case PlaybackStateCompat.REPEAT_MODE_ALL: - case PlaybackStateCompat.REPEAT_MODE_GROUP: - return Player.REPEAT_MODE_ALL; - default: - Log.w( - TAG, - "Unrecognized PlaybackStateCompat.RepeatMode: " - + playbackStateCompatRepeatMode - + " was converted to `Player.REPEAT_MODE_OFF`"); - return Player.REPEAT_MODE_OFF; - } - } - - /** Converts {@link Player.RepeatMode} to {@link PlaybackStateCompat.RepeatMode} */ - @PlaybackStateCompat.RepeatMode - public static int convertToPlaybackStateCompatRepeatMode(@Player.RepeatMode int repeatMode) { - switch (repeatMode) { - case Player.REPEAT_MODE_OFF: - return PlaybackStateCompat.REPEAT_MODE_NONE; - case Player.REPEAT_MODE_ONE: - return PlaybackStateCompat.REPEAT_MODE_ONE; - case Player.REPEAT_MODE_ALL: - return PlaybackStateCompat.REPEAT_MODE_ALL; - default: - Log.w( - TAG, - "Unrecognized RepeatMode: " - + repeatMode - + " was converted to `PlaybackStateCompat.REPEAT_MODE_NONE`"); - return PlaybackStateCompat.REPEAT_MODE_NONE; - } - } - - /** Converts {@link PlaybackStateCompat.ShuffleMode} to shuffle mode enabled. */ - public static boolean convertToShuffleModeEnabled( - @PlaybackStateCompat.ShuffleMode int playbackStateCompatShuffleMode) { - switch (playbackStateCompatShuffleMode) { - case PlaybackStateCompat.SHUFFLE_MODE_INVALID: - case PlaybackStateCompat.SHUFFLE_MODE_NONE: - return false; - case PlaybackStateCompat.SHUFFLE_MODE_ALL: - case PlaybackStateCompat.SHUFFLE_MODE_GROUP: - return true; - default: - throw new IllegalArgumentException( - "Unrecognized ShuffleMode: " + playbackStateCompatShuffleMode); - } - } - - /** Converts shuffle mode enabled to {@link PlaybackStateCompat.ShuffleMode} */ - @PlaybackStateCompat.ShuffleMode - public static int convertToPlaybackStateCompatShuffleMode(boolean shuffleModeEnabled) { - return shuffleModeEnabled - ? PlaybackStateCompat.SHUFFLE_MODE_ALL - : PlaybackStateCompat.SHUFFLE_MODE_NONE; - } - - /** Converts the rootHints, option, and extra to the {@link LibraryParams}. */ - @Nullable - public static LibraryParams convertToLibraryParams( - Context context, @Nullable Bundle legacyBundle) { - if (legacyBundle == null) { - return null; - } - try { - legacyBundle.setClassLoader(context.getClassLoader()); - int supportedChildrenFlags = - legacyBundle.getInt( - BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ -1); - if (supportedChildrenFlags >= 0) { - legacyBundle.remove(BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS); - legacyBundle.putBoolean( - EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, - supportedChildrenFlags == MediaBrowserCompat.MediaItem.FLAG_BROWSABLE); - } - return new LibraryParams.Builder() - .setExtras(legacyBundle) - .setRecent(legacyBundle.getBoolean(BrowserRoot.EXTRA_RECENT)) - .setOffline(legacyBundle.getBoolean(BrowserRoot.EXTRA_OFFLINE)) - .setSuggested(legacyBundle.getBoolean(BrowserRoot.EXTRA_SUGGESTED)) - .build(); - } catch (Exception e) { - // Failure when unpacking the legacy bundle. - return new LibraryParams.Builder().setExtras(legacyBundle).build(); - } - } - - /** Converts {@link LibraryParams} to the root hints. */ - @Nullable - public static Bundle convertToRootHints(@Nullable LibraryParams params) { - if (params == null) { - return null; - } - Bundle rootHints = new Bundle(params.extras); - if (params.extras.containsKey(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)) { - boolean browsableChildrenSupported = - params.extras.getBoolean( - EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, /* defaultValue= */ false); - rootHints.remove(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY); - rootHints.putInt( - BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, - browsableChildrenSupported - ? MediaBrowserCompat.MediaItem.FLAG_BROWSABLE - : MediaBrowserCompat.MediaItem.FLAG_BROWSABLE - | MediaBrowserCompat.MediaItem.FLAG_PLAYABLE); - } - rootHints.putBoolean(BrowserRoot.EXTRA_RECENT, params.isRecent); - rootHints.putBoolean(BrowserRoot.EXTRA_OFFLINE, params.isOffline); - rootHints.putBoolean(BrowserRoot.EXTRA_SUGGESTED, params.isSuggested); - return rootHints; - } - /** Returns a new list that only contains non-null elements of the original list. */ public static List removeNullElements(List<@NullableType T> list) { List newList = new ArrayList<>(); @@ -1137,287 +98,6 @@ import java.util.concurrent.TimeoutException; return newList; } - /** - * Converts {@link PlaybackStateCompat}, {@link - * MediaControllerCompat.PlaybackInfo#getVolumeControl() volume control type}, {@link - * MediaControllerCompat#getFlags() session flags} and {@link MediaControllerCompat#isSessionReady - * whether the session is ready} to {@link Player.Commands}. - * - * @param playbackStateCompat The {@link PlaybackStateCompat}. - * @param volumeControlType The {@link MediaControllerCompat.PlaybackInfo#getVolumeControl() - * volume control type}. - * @param sessionFlags The session flags. - * @param isSessionReady Whether the session compat is ready. - * @return The converted player commands. - */ - @SuppressWarnings("deprecation") // Backwards compatibility with old volume commands - public static Player.Commands convertToPlayerCommands( - @Nullable PlaybackStateCompat playbackStateCompat, - int volumeControlType, - long sessionFlags, - boolean isSessionReady) { - Commands.Builder playerCommandsBuilder = new Commands.Builder(); - long actions = playbackStateCompat == null ? 0 : playbackStateCompat.getActions(); - if ((hasAction(actions, PlaybackStateCompat.ACTION_PLAY) - && hasAction(actions, PlaybackStateCompat.ACTION_PAUSE)) - || hasAction(actions, PlaybackStateCompat.ACTION_PLAY_PAUSE)) { - playerCommandsBuilder.add(COMMAND_PLAY_PAUSE); - } - if (hasAction(actions, PlaybackStateCompat.ACTION_PREPARE)) { - playerCommandsBuilder.add(COMMAND_PREPARE); - } - if ((hasAction(actions, PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID) - && hasAction(actions, PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID)) - || (hasAction(actions, PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH) - && hasAction(actions, PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH)) - || (hasAction(actions, PlaybackStateCompat.ACTION_PREPARE_FROM_URI) - && hasAction(actions, PlaybackStateCompat.ACTION_PLAY_FROM_URI))) { - // Require both PREPARE and PLAY actions as we have no logic to handle having just one action. - playerCommandsBuilder.addAll(COMMAND_SET_MEDIA_ITEM, COMMAND_PREPARE); - } - if (hasAction(actions, PlaybackStateCompat.ACTION_REWIND)) { - playerCommandsBuilder.add(COMMAND_SEEK_BACK); - } - if (hasAction(actions, PlaybackStateCompat.ACTION_FAST_FORWARD)) { - playerCommandsBuilder.add(COMMAND_SEEK_FORWARD); - } - if (hasAction(actions, PlaybackStateCompat.ACTION_SEEK_TO)) { - playerCommandsBuilder.addAll( - COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_DEFAULT_POSITION); - } - if (hasAction(actions, PlaybackStateCompat.ACTION_SKIP_TO_NEXT)) { - playerCommandsBuilder.addAll(COMMAND_SEEK_TO_NEXT, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); - } - if (hasAction(actions, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)) { - playerCommandsBuilder.addAll(COMMAND_SEEK_TO_PREVIOUS, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); - } - if (hasAction(actions, PlaybackStateCompat.ACTION_SET_PLAYBACK_SPEED)) { - playerCommandsBuilder.add(COMMAND_SET_SPEED_AND_PITCH); - } - if (hasAction(actions, PlaybackStateCompat.ACTION_STOP)) { - playerCommandsBuilder.add(COMMAND_STOP); - } - if (volumeControlType == VolumeProviderCompat.VOLUME_CONTROL_RELATIVE) { - playerCommandsBuilder.addAll( - COMMAND_ADJUST_DEVICE_VOLUME, COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS); - } else if (volumeControlType == VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE) { - playerCommandsBuilder.addAll( - COMMAND_ADJUST_DEVICE_VOLUME, - COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS, - COMMAND_SET_DEVICE_VOLUME, - COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS); - } - playerCommandsBuilder.addAll( - COMMAND_GET_DEVICE_VOLUME, - COMMAND_GET_TIMELINE, - COMMAND_GET_METADATA, - COMMAND_GET_CURRENT_MEDIA_ITEM, - COMMAND_GET_AUDIO_ATTRIBUTES, - COMMAND_RELEASE); - if ((sessionFlags & FLAG_HANDLES_QUEUE_COMMANDS) != 0) { - playerCommandsBuilder.add(COMMAND_CHANGE_MEDIA_ITEMS); - if (hasAction(actions, PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM)) { - playerCommandsBuilder.add(Player.COMMAND_SEEK_TO_MEDIA_ITEM); - } - } - if (isSessionReady) { - if (hasAction(actions, PlaybackStateCompat.ACTION_SET_REPEAT_MODE)) { - playerCommandsBuilder.add(COMMAND_SET_REPEAT_MODE); - } - if (hasAction(actions, PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE)) { - playerCommandsBuilder.add(COMMAND_SET_SHUFFLE_MODE); - } - } - return playerCommandsBuilder.build(); - } - - /** - * Checks if the set of actions contains the specified action. - * - * @param actions A bit set of actions. - * @param action The action to check. - * @return Whether the action is contained in the set. - */ - private static boolean hasAction(long actions, @PlaybackStateCompat.Actions long action) { - return (actions & action) != 0; - } - - /** - * Converts {@link PlaybackStateCompat} to {@link SessionCommands}. - * - *

This ignores {@link PlaybackStateCompat#getActions() actions} in the {@link - * PlaybackStateCompat} to workaround media apps' issues that they don't set playback state - * correctly. - * - * @param state playback state - * @param isSessionReady Whether the session compat is ready. - * @return the converted session commands - */ - public static SessionCommands convertToSessionCommands( - @Nullable PlaybackStateCompat state, boolean isSessionReady) { - SessionCommands.Builder sessionCommandsBuilder = new SessionCommands.Builder(); - sessionCommandsBuilder.addAllSessionCommands(); - if (!isSessionReady) { - // Disables rating function when session isn't ready because of the - // MediaController#setRating(RatingCompat, Bundle) and MediaController#getRatingType(). - sessionCommandsBuilder.remove(SessionCommand.COMMAND_CODE_SESSION_SET_RATING); - } - - if (state != null && state.getCustomActions() != null) { - for (CustomAction customAction : state.getCustomActions()) { - String action = customAction.getAction(); - @Nullable Bundle extras = customAction.getExtras(); - sessionCommandsBuilder.add( - new SessionCommand(action, extras == null ? Bundle.EMPTY : extras)); - } - } - return sessionCommandsBuilder.build(); - } - - /** - * Converts {@link CustomAction} in the {@link PlaybackStateCompat} to the custom layout which is - * the list of the {@link CommandButton}. - * - * @param state playback state - * @return custom layout. Always non-null. - */ - public static ImmutableList convertToCustomLayout( - @Nullable PlaybackStateCompat state) { - if (state == null) { - return ImmutableList.of(); - } - ImmutableList.Builder layout = new ImmutableList.Builder<>(); - for (CustomAction customAction : state.getCustomActions()) { - String action = customAction.getAction(); - @Nullable Bundle extras = customAction.getExtras(); - CommandButton button = - new CommandButton.Builder() - .setSessionCommand(new SessionCommand(action, extras == null ? Bundle.EMPTY : extras)) - .setDisplayName(customAction.getName()) - .setEnabled(true) - .setIconResId(customAction.getIcon()) - .build(); - layout.add(button); - } - return layout.build(); - } - - /** Converts {@link AudioAttributesCompat} into {@link AudioAttributes}. */ - /* - * @AudioAttributesCompat.AttributeUsage and @C.AudioUsage both use the same constant values, - * defined by AudioAttributes in the platform. - */ - @SuppressLint("WrongConstant") - public static AudioAttributes convertToAudioAttributes( - @Nullable AudioAttributesCompat audioAttributesCompat) { - if (audioAttributesCompat == null) { - return AudioAttributes.DEFAULT; - } - return new AudioAttributes.Builder() - .setContentType(audioAttributesCompat.getContentType()) - .setFlags(audioAttributesCompat.getFlags()) - .setUsage(audioAttributesCompat.getUsage()) - .build(); - } - - /** Converts {@link MediaControllerCompat.PlaybackInfo} to {@link AudioAttributes}. */ - public static AudioAttributes convertToAudioAttributes( - @Nullable MediaControllerCompat.PlaybackInfo playbackInfoCompat) { - if (playbackInfoCompat == null) { - return AudioAttributes.DEFAULT; - } - return MediaUtils.convertToAudioAttributes(playbackInfoCompat.getAudioAttributes()); - } - - /** Converts {@link AudioAttributes} into {@link AudioAttributesCompat}. */ - public static AudioAttributesCompat convertToAudioAttributesCompat( - AudioAttributes audioAttributes) { - return new AudioAttributesCompat.Builder() - .setContentType(audioAttributes.contentType) - .setFlags(audioAttributes.flags) - .setUsage(audioAttributes.usage) - .build(); - } - - /** - * Gets the legacy stream type from {@link AudioAttributes}. - * - * @param audioAttributes audio attributes - * @return int legacy stream type from {@link AudioManager} - */ - public static int getLegacyStreamType(AudioAttributes audioAttributes) { - int legacyStreamType = - MediaUtils.convertToAudioAttributesCompat(audioAttributes).getLegacyStreamType(); - if (legacyStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { - // Usually, AudioAttributesCompat#getLegacyStreamType() does not return - // USE_DEFAULT_STREAM_TYPE unless the developer sets it with - // AudioAttributesCompat.Builder#setLegacyStreamType(). - // But for safety, let's convert USE_DEFAULT_STREAM_TYPE to STREAM_MUSIC here. - return AudioManager.STREAM_MUSIC; - } - return legacyStreamType; - } - - public static T getFutureResult(Future future, long timeoutMs) - throws ExecutionException, TimeoutException { - long initialTimeMs = SystemClock.elapsedRealtime(); - long remainingTimeMs = timeoutMs; - boolean interrupted = false; - try { - while (true) { - try { - return future.get(remainingTimeMs, MILLISECONDS); - } catch (InterruptedException e) { - interrupted = true; - long elapsedTimeMs = SystemClock.elapsedRealtime() - initialTimeMs; - if (elapsedTimeMs >= timeoutMs) { - throw new TimeoutException(); - } - remainingTimeMs = timeoutMs - elapsedTimeMs; - } - } - } finally { - if (interrupted) { - Thread.currentThread().interrupt(); - } - } - } - - /** Converts {@link MediaControllerCompat.PlaybackInfo} to {@link DeviceInfo}. */ - public static DeviceInfo convertToDeviceInfo( - @Nullable MediaControllerCompat.PlaybackInfo playbackInfoCompat, - @Nullable String routingControllerId) { - if (playbackInfoCompat == null) { - return DeviceInfo.UNKNOWN; - } - return new DeviceInfo.Builder( - playbackInfoCompat.getPlaybackType() - == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE - ? DeviceInfo.PLAYBACK_TYPE_REMOTE - : DeviceInfo.PLAYBACK_TYPE_LOCAL) - .setMaxVolume(playbackInfoCompat.getMaxVolume()) - .setRoutingControllerId(routingControllerId) - .build(); - } - - /** Converts {@link MediaControllerCompat.PlaybackInfo} to device volume. */ - public static int convertToDeviceVolume( - @Nullable MediaControllerCompat.PlaybackInfo playbackInfoCompat) { - if (playbackInfoCompat == null) { - return 0; - } - return playbackInfoCompat.getCurrentVolume(); - } - - /** Converts {@link MediaControllerCompat.PlaybackInfo} to device muted. */ - public static boolean convertToIsDeviceMuted( - @Nullable MediaControllerCompat.PlaybackInfo playbackInfoCompat) { - if (playbackInfoCompat == null) { - return false; - } - return playbackInfoCompat.getCurrentVolume() == 0; - } - public static Commands createPlayerCommandsWith(@Command int command) { return new Commands.Builder().add(command).build(); } @@ -1591,12 +271,5 @@ import java.util.concurrent.TimeoutException; return estimatedPositionMs; } - private static byte[] convertToByteArray(Bitmap bitmap) throws IOException { - try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { - bitmap.compress(Bitmap.CompressFormat.PNG, /* ignored */ 0, stream); - return stream.toByteArray(); - } - } - private MediaUtils() {} } diff --git a/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java b/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java index 0b901a26c7..07478bc845 100644 --- a/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java +++ b/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java @@ -998,7 +998,8 @@ import java.util.List; .build(); } @Nullable PlaybackException playerError = getPlayerError(); - int state = MediaUtils.convertToPlaybackStateCompatState(/* player= */ this, playIfSuppressed); + int state = + LegacyConversions.convertToPlaybackStateCompatState(/* player= */ this, playIfSuppressed); // Always advertise ACTION_SET_RATING. long actions = PlaybackStateCompat.ACTION_SET_RATING; Commands availableCommands = intersect(availablePlayerCommands, getAvailableCommands()); @@ -1007,7 +1008,7 @@ import java.util.List; } long queueItemId = isCommandAvailable(COMMAND_GET_TIMELINE) - ? MediaUtils.convertToQueueItemId(getCurrentMediaItemIndex()) + ? LegacyConversions.convertToQueueItemId(getCurrentMediaItemIndex()) : MediaSessionCompat.QueueItem.UNKNOWN_ID; float playbackSpeed = getPlaybackParameters().speed; float sessionPlaybackSpeed = isPlaying() ? playbackSpeed : 0f; diff --git a/libraries/session/src/main/java/androidx/media3/session/QueueTimeline.java b/libraries/session/src/main/java/androidx/media3/session/QueueTimeline.java index ac28d16705..b0913b9ddd 100644 --- a/libraries/session/src/main/java/androidx/media3/session/QueueTimeline.java +++ b/libraries/session/src/main/java/androidx/media3/session/QueueTimeline.java @@ -61,7 +61,7 @@ import java.util.List; ImmutableList.Builder queuedMediaItemsBuilder = new ImmutableList.Builder<>(); for (int i = 0; i < queue.size(); i++) { QueueItem queueItem = queue.get(i); - MediaItem mediaItem = MediaUtils.convertToMediaItem(queueItem); + MediaItem mediaItem = LegacyConversions.convertToMediaItem(queueItem); queuedMediaItemsBuilder.add( new QueuedMediaItem(mediaItem, queueItem.getQueueId(), /* durationMs= */ C.TIME_UNSET)); } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/LegacyConversionsTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/LegacyConversionsTest.java new file mode 100644 index 0000000000..3e13b522a8 --- /dev/null +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/LegacyConversionsTest.java @@ -0,0 +1,1062 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.session; + +import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_BROWSABLE; +import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_PLAYABLE; +import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_DURATION; +import static androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS; +import static androidx.media3.session.MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT; +import static androidx.media3.session.MediaConstants.EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY; +import static androidx.media3.test.session.common.TestUtils.getCommandsAsList; +import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.SECONDS; + +import android.content.Context; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.service.media.MediaBrowserService; +import android.support.v4.media.MediaBrowserCompat; +import android.support.v4.media.MediaDescriptionCompat; +import android.support.v4.media.MediaMetadataCompat; +import android.support.v4.media.RatingCompat; +import android.support.v4.media.session.MediaControllerCompat; +import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.media.session.PlaybackStateCompat; +import androidx.annotation.Nullable; +import androidx.media.AudioAttributesCompat; +import androidx.media.VolumeProviderCompat; +import androidx.media.utils.MediaConstants; +import androidx.media3.common.AudioAttributes; +import androidx.media3.common.C; +import androidx.media3.common.HeartRating; +import androidx.media3.common.MediaItem; +import androidx.media3.common.MediaMetadata; +import androidx.media3.common.PercentageRating; +import androidx.media3.common.Player; +import androidx.media3.common.Rating; +import androidx.media3.common.StarRating; +import androidx.media3.common.ThumbRating; +import androidx.media3.common.util.BitmapLoader; +import androidx.media3.datasource.DataSourceBitmapLoader; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SdkSuppress; +import androidx.test.filters.SmallTest; +import com.google.common.util.concurrent.ListenableFuture; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for {@link LegacyConversions}. */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class LegacyConversionsTest { + + private Context context; + private BitmapLoader bitmapLoader; + + @Before + public void setUp() { + context = ApplicationProvider.getApplicationContext(); + bitmapLoader = new CacheBitmapLoader(new DataSourceBitmapLoader(context)); + } + + @Test + public void convertToMediaItem_browserItemToMediaItem() { + String mediaId = "testId"; + String title = "testTitle"; + MediaDescriptionCompat descriptionCompat = + new MediaDescriptionCompat.Builder().setMediaId(mediaId).setTitle(title).build(); + MediaBrowserCompat.MediaItem browserItem = + new MediaBrowserCompat.MediaItem(descriptionCompat, /* flags= */ 0); + + MediaItem mediaItem = LegacyConversions.convertToMediaItem(browserItem); + assertThat(mediaItem.mediaId).isEqualTo(mediaId); + assertThat(mediaItem.mediaMetadata.title.toString()).isEqualTo(title); + } + + @Test + public void convertToMediaItem_queueItemToMediaItem() { + String mediaId = "testMediaId"; + String title = "testTitle"; + MediaDescriptionCompat descriptionCompat = + new MediaDescriptionCompat.Builder().setMediaId(mediaId).setTitle(title).build(); + MediaSessionCompat.QueueItem queueItem = + new MediaSessionCompat.QueueItem(descriptionCompat, /* id= */ 1); + MediaItem mediaItem = LegacyConversions.convertToMediaItem(queueItem); + assertThat(mediaItem.mediaId).isEqualTo(mediaId); + assertThat(mediaItem.mediaMetadata.title.toString()).isEqualTo(title); + } + + @Test + public void convertBrowserItemListToMediaItemList() { + int size = 3; + List browserItems = MediaTestUtils.createBrowserItems(size); + List mediaItems = + LegacyConversions.convertBrowserItemListToMediaItemList(browserItems); + assertThat(mediaItems).hasSize(size); + for (int i = 0; i < size; ++i) { + assertThat(mediaItems.get(i).mediaId).isEqualTo(browserItems.get(i).getMediaId()); + } + } + + @Test + public void convertToQueueItem_withArtworkData() throws Exception { + MediaItem mediaItem = MediaTestUtils.createMediaItemWithArtworkData("testId"); + MediaMetadata mediaMetadata = mediaItem.mediaMetadata; + ListenableFuture bitmapFuture = bitmapLoader.decodeBitmap(mediaMetadata.artworkData); + @Nullable Bitmap bitmap = bitmapFuture.get(10, SECONDS); + + MediaSessionCompat.QueueItem queueItem = + LegacyConversions.convertToQueueItem( + mediaItem, + /** mediaItemIndex= */ + 100, + bitmap); + + assertThat(queueItem.getQueueId()).isEqualTo(100); + assertThat(queueItem.getDescription().getIconBitmap()).isNotNull(); + } + + @Test + public void convertToMediaDescriptionCompat_setsExpectedValues() { + String mediaId = "testId"; + String title = "testTitle"; + String description = "testDesc"; + MediaMetadata metadata = + new MediaMetadata.Builder() + .setTitle(title) + .setDescription(description) + .setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC) + .build(); + MediaItem mediaItem = + new MediaItem.Builder().setMediaId(mediaId).setMediaMetadata(metadata).build(); + MediaDescriptionCompat descriptionCompat = + LegacyConversions.convertToMediaDescriptionCompat(mediaItem, /* artworkBitmap= */ null); + + assertThat(descriptionCompat.getMediaId()).isEqualTo(mediaId); + assertThat(descriptionCompat.getTitle().toString()).isEqualTo(title); + assertThat(descriptionCompat.getDescription().toString()).isEqualTo(description); + assertThat(descriptionCompat.getExtras().getLong(EXTRAS_KEY_MEDIA_TYPE_COMPAT)) + .isEqualTo(MediaMetadata.MEDIA_TYPE_MUSIC); + } + + @Test + public void convertToQueueItemId() { + assertThat(LegacyConversions.convertToQueueItemId(C.INDEX_UNSET)) + .isEqualTo(MediaSessionCompat.QueueItem.UNKNOWN_ID); + assertThat(LegacyConversions.convertToQueueItemId(100)).isEqualTo(100); + } + + @Test + public void convertToMediaMetadata_withoutTitle() { + assertThat(LegacyConversions.convertToMediaMetadata((CharSequence) null)) + .isEqualTo(MediaMetadata.EMPTY); + } + + @Test + public void convertToMediaMetadata_withTitle() { + String title = "title"; + assertThat(LegacyConversions.convertToMediaMetadata(title).title.toString()).isEqualTo(title); + } + + @Test + public void convertToMediaMetadata_withCustomKey() { + MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(); + builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title"); + builder.putLong(EXTRAS_KEY_MEDIA_TYPE_COMPAT, (long) MediaMetadata.MEDIA_TYPE_MUSIC); + builder.putString("custom_key", "value"); + MediaMetadataCompat testMediaMetadataCompat = builder.build(); + + MediaMetadata mediaMetadata = + LegacyConversions.convertToMediaMetadata(testMediaMetadataCompat, RatingCompat.RATING_NONE); + + assertThat(mediaMetadata.title.toString()).isEqualTo("title"); + assertThat(mediaMetadata.mediaType).isEqualTo(MediaMetadata.MEDIA_TYPE_MUSIC); + assertThat(mediaMetadata.extras).isNotNull(); + assertThat(mediaMetadata.extras.getString("custom_key")).isEqualTo("value"); + assertThat(mediaMetadata.extras.containsKey(MediaMetadataCompat.METADATA_KEY_TITLE)).isFalse(); + assertThat(mediaMetadata.extras.containsKey(EXTRAS_KEY_MEDIA_TYPE_COMPAT)).isFalse(); + } + + @Test + public void convertToMediaMetadata_roundTripViaMediaMetadataCompat_returnsEqualMediaItemMetadata() + throws Exception { + MediaItem testMediaItem = MediaTestUtils.createMediaItemWithArtworkData("testZZZ"); + MediaMetadata testMediaMetadata = testMediaItem.mediaMetadata; + @Nullable Bitmap testArtworkBitmap = null; + @Nullable + ListenableFuture bitmapFuture = bitmapLoader.loadBitmapFromMetadata(testMediaMetadata); + if (bitmapFuture != null) { + testArtworkBitmap = bitmapFuture.get(10, SECONDS); + } + MediaMetadataCompat testMediaMetadataCompat = + LegacyConversions.convertToMediaMetadataCompat( + testMediaMetadata, + "mediaId", + Uri.parse("http://example.com"), + /* durationMs= */ 100L, + testArtworkBitmap); + + MediaMetadata mediaMetadata = + LegacyConversions.convertToMediaMetadata(testMediaMetadataCompat, RatingCompat.RATING_NONE); + + assertThat(mediaMetadata).isEqualTo(testMediaMetadata); + assertThat(mediaMetadata.artworkData).isNotNull(); + } + + @Test + public void + convertToMediaMetadata_roundTripViaMediaDescriptionCompat_returnsEqualMediaItemMetadata() + throws Exception { + MediaItem testMediaItem = MediaTestUtils.createMediaItemWithArtworkData("testZZZ"); + MediaMetadata testMediaMetadata = testMediaItem.mediaMetadata; + @Nullable Bitmap testArtworkBitmap = null; + @Nullable + ListenableFuture bitmapFuture = bitmapLoader.loadBitmapFromMetadata(testMediaMetadata); + if (bitmapFuture != null) { + testArtworkBitmap = bitmapFuture.get(10, SECONDS); + } + MediaDescriptionCompat mediaDescriptionCompat = + LegacyConversions.convertToMediaDescriptionCompat(testMediaItem, testArtworkBitmap); + + MediaMetadata mediaMetadata = + LegacyConversions.convertToMediaMetadata(mediaDescriptionCompat, RatingCompat.RATING_NONE); + + assertThat(mediaMetadata).isEqualTo(testMediaMetadata); + assertThat(mediaMetadata.artworkData).isNotNull(); + } + + @Test + public void convertToMediaMetadataCompat_withMediaType_setsMediaType() { + MediaItem mediaItem = + new MediaItem.Builder() + .setMediaMetadata( + new MediaMetadata.Builder().setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC).build()) + .build(); + + MediaMetadataCompat mediaMetadataCompat = + LegacyConversions.convertToMediaMetadataCompat( + mediaItem.mediaMetadata, + "mediaId", + Uri.parse("http://www.example.com"), + /* durationMs= */ C.TIME_UNSET, + /* artworkBitmap= */ null); + + assertThat(mediaMetadataCompat.getLong(EXTRAS_KEY_MEDIA_TYPE_COMPAT)) + .isEqualTo(MediaMetadata.MEDIA_TYPE_MUSIC); + } + + @Test + public void convertBetweenRatingAndRatingCompat() { + assertRatingEquals( + LegacyConversions.convertToRating(null), LegacyConversions.convertToRatingCompat(null)); + assertRatingEquals( + LegacyConversions.convertToRating(RatingCompat.newUnratedRating(RatingCompat.RATING_NONE)), + LegacyConversions.convertToRatingCompat(null)); + assertRatingEquals( + LegacyConversions.convertToRating(RatingCompat.newUnratedRating(RatingCompat.RATING_HEART)), + LegacyConversions.convertToRatingCompat(new HeartRating())); + assertRatingEquals( + LegacyConversions.convertToRating(RatingCompat.newHeartRating(true)), + LegacyConversions.convertToRatingCompat(new HeartRating(true))); + assertRatingEquals( + LegacyConversions.convertToRating(RatingCompat.newThumbRating(false)), + LegacyConversions.convertToRatingCompat(new ThumbRating(false))); + assertRatingEquals( + LegacyConversions.convertToRating(RatingCompat.newThumbRating(false)), + LegacyConversions.convertToRatingCompat(new ThumbRating(false))); + assertRatingEquals( + LegacyConversions.convertToRating( + RatingCompat.newStarRating(RatingCompat.RATING_3_STARS, 1f)), + LegacyConversions.convertToRatingCompat(new StarRating(3, 1f))); + assertRatingEquals( + LegacyConversions.convertToRating( + RatingCompat.newStarRating(RatingCompat.RATING_4_STARS, 0f)), + LegacyConversions.convertToRatingCompat(new StarRating(4, 0f))); + assertRatingEquals( + LegacyConversions.convertToRating( + RatingCompat.newStarRating(RatingCompat.RATING_5_STARS, 5f)), + LegacyConversions.convertToRatingCompat(new StarRating(5, 5f))); + assertRatingEquals( + LegacyConversions.convertToRating(RatingCompat.newPercentageRating(80f)), + LegacyConversions.convertToRatingCompat(new PercentageRating(80f))); + } + + void assertRatingEquals(Rating rating, RatingCompat ratingCompat) { + if (rating == null && ratingCompat == null) { + return; + } + assertThat(rating.isRated()).isEqualTo(ratingCompat.isRated()); + if (rating instanceof HeartRating) { + assertThat(ratingCompat.getRatingStyle()).isEqualTo(RatingCompat.RATING_HEART); + assertThat(((HeartRating) rating).isHeart()).isEqualTo(ratingCompat.hasHeart()); + } else if (rating instanceof ThumbRating) { + assertThat(ratingCompat.getRatingStyle()).isEqualTo(RatingCompat.RATING_THUMB_UP_DOWN); + assertThat(((ThumbRating) rating).isThumbsUp()).isEqualTo(ratingCompat.isThumbUp()); + } else if (rating instanceof StarRating) { + StarRating starRating = (StarRating) rating; + switch (starRating.getMaxStars()) { + case 3: + assertThat(ratingCompat.getRatingStyle()).isEqualTo(RatingCompat.RATING_3_STARS); + break; + case 4: + assertThat(ratingCompat.getRatingStyle()).isEqualTo(RatingCompat.RATING_4_STARS); + break; + case 5: + assertThat(ratingCompat.getRatingStyle()).isEqualTo(RatingCompat.RATING_5_STARS); + break; + default: // fall out + } + assertThat(starRating.getStarRating()).isEqualTo(ratingCompat.getStarRating()); + } else if (rating instanceof PercentageRating) { + assertThat(ratingCompat.getRatingStyle()).isEqualTo(RatingCompat.RATING_PERCENTAGE); + assertThat(((PercentageRating) rating).getPercent()) + .isEqualTo(ratingCompat.getPercentRating()); + } + } + + @Test + public void convertToLibraryParams() { + assertThat(LegacyConversions.convertToLibraryParams(context, null)).isNull(); + Bundle rootHints = new Bundle(); + rootHints.putString("key", "value"); + rootHints.putInt( + MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, FLAG_BROWSABLE); + rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_OFFLINE, true); + rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); + rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_SUGGESTED, true); + + MediaLibraryService.LibraryParams params = + LegacyConversions.convertToLibraryParams(context, rootHints); + + assertThat(params.extras.getString("key")).isEqualTo("value"); + assertThat(params.extras.getBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isTrue(); + assertThat(params.extras.containsKey(BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS)) + .isFalse(); + assertThat(params.isOffline).isTrue(); + assertThat(params.isRecent).isTrue(); + assertThat(params.isSuggested).isTrue(); + } + + @Test + public void convertToLibraryParams_rootHintsBrowsableNoFlagSet_browsableOnlyFalse() { + Bundle rootHints = new Bundle(); + rootHints.putInt(MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, 0); + + MediaLibraryService.LibraryParams params = + LegacyConversions.convertToLibraryParams(context, rootHints); + + assertThat(params.extras.getBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isFalse(); + } + + @Test + public void convertToLibraryParams_rootHintsPlayableFlagSet_browsableOnlyFalse() { + Bundle rootHints = new Bundle(); + rootHints.putInt( + MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, + FLAG_PLAYABLE | FLAG_BROWSABLE); + + MediaLibraryService.LibraryParams params = + LegacyConversions.convertToLibraryParams(context, rootHints); + + assertThat(params.extras.getBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isFalse(); + } + + @Test + public void convertToLibraryParams_rootHintsBrowsableAbsentKey_browsableOnlyFalse() { + MediaLibraryService.LibraryParams params = + LegacyConversions.convertToLibraryParams(context, /* legacyBundle= */ Bundle.EMPTY); + + assertThat(params.extras.getBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isFalse(); + } + + @Test + public void convertToRootHints() { + assertThat(LegacyConversions.convertToRootHints(null)).isNull(); + Bundle extras = new Bundle(); + extras.putString("key", "value"); + extras.putBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, true); + MediaLibraryService.LibraryParams param = + new MediaLibraryService.LibraryParams.Builder() + .setOffline(true) + .setRecent(true) + .setSuggested(true) + .setExtras(extras) + .build(); + + Bundle rootHints = LegacyConversions.convertToRootHints(param); + + assertThat( + rootHints.getInt( + BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ 0)) + .isEqualTo(FLAG_BROWSABLE); + assertThat(rootHints.getString("key")).isEqualTo("value"); + assertThat(rootHints.get(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isNull(); + assertThat(rootHints.getBoolean(MediaBrowserService.BrowserRoot.EXTRA_OFFLINE)).isTrue(); + assertThat(rootHints.getBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT)).isTrue(); + assertThat(rootHints.getBoolean(MediaBrowserService.BrowserRoot.EXTRA_SUGGESTED)).isTrue(); + } + + @Test + public void convertToRootHints_browsableOnlyFalse_correctLegacyBrowsableFlags() { + Bundle extras = new Bundle(); + extras.putBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, false); + MediaLibraryService.LibraryParams param = + new MediaLibraryService.LibraryParams.Builder().setExtras(extras).build(); + + Bundle rootHints = LegacyConversions.convertToRootHints(param); + + assertThat( + rootHints.getInt( + BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ -1)) + .isEqualTo(FLAG_BROWSABLE | FLAG_PLAYABLE); + assertThat(rootHints.get(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isNull(); + } + + @Test + public void convertToRootHints_browsableAbsentKey_noLegacyKeyAdded() { + MediaLibraryService.LibraryParams param = + new MediaLibraryService.LibraryParams.Builder().build(); + + Bundle rootHints = LegacyConversions.convertToRootHints(param); + + assertThat(rootHints.get(BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS)).isNull(); + } + + @Test + public void convertToSessionCommands_withCustomAction_containsCustomAction() { + PlaybackStateCompat playbackState = + new PlaybackStateCompat.Builder() + .addCustomAction("action", "name", /* icon= */ 100) + .build(); + SessionCommands sessionCommands = + LegacyConversions.convertToSessionCommands(playbackState, /* isSessionReady= */ true); + assertThat(sessionCommands.contains(new SessionCommand("action", /* extras= */ Bundle.EMPTY))) + .isTrue(); + } + + @SdkSuppress(minSdkVersion = 21) + @Test + public void convertToSessionCommands_whenSessionIsNotReadyOnSdk21_disallowsRating() { + SessionCommands sessionCommands = + LegacyConversions.convertToSessionCommands(/* state= */ null, /* isSessionReady= */ false); + assertThat(sessionCommands.contains(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)).isFalse(); + } + + @Test + public void convertToPlayerCommands_withNoActions_onlyDefaultCommandsAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(/* capabilities= */ 0).build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsExactly( + Player.COMMAND_GET_TIMELINE, + Player.COMMAND_GET_CURRENT_MEDIA_ITEM, + Player.COMMAND_GET_DEVICE_VOLUME, + Player.COMMAND_GET_METADATA, + Player.COMMAND_GET_AUDIO_ATTRIBUTES, + Player.COMMAND_RELEASE); + } + + @Test + public void convertToPlayerCommands_withJustPlayAction_playPauseCommandNotAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PLAY).build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).doesNotContain(Player.COMMAND_PLAY_PAUSE); + } + + @Test + public void convertToPlayerCommands_withJustPauseAction_playPauseCommandNotAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PAUSE).build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).doesNotContain(Player.COMMAND_PLAY_PAUSE); + } + + @Test + public void convertToPlayerCommands_withPlayAndPauseAction_playPauseCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE) + .build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_PLAY_PAUSE); + } + + @Test + public void convertToPlayerCommands_withPlayPauseAction_playPauseCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE).build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_PLAY_PAUSE); + } + + @Test + public void convertToPlayerCommands_withPrepareAction_prepareCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PREPARE).build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_PREPARE); + } + + @Test + public void convertToPlayerCommands_withRewindAction_seekBackCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_REWIND).build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_SEEK_BACK); + } + + @Test + public void convertToPlayerCommands_withFastForwardAction_seekForwardCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_FAST_FORWARD) + .build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_SEEK_FORWARD); + } + + @Test + public void convertToPlayerCommands_withSeekToAction_seekInCurrentMediaItemCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_SEEK_TO).build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .contains(Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + } + + @Test + public void convertToPlayerCommands_withSkipToNextAction_seekToNextCommandsAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_SKIP_TO_NEXT) + .build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast(Player.COMMAND_SEEK_TO_NEXT, Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + } + + @Test + public void convertToPlayerCommands_withSkipToPreviousAction_seekToPreviousCommandsAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) + .build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast( + Player.COMMAND_SEEK_TO_PREVIOUS, Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + } + + @Test + public void + convertToPlayerCommands_withPlayFromActionsWithoutPrepareFromAction_setMediaItemCommandNotAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID + | PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH + | PlaybackStateCompat.ACTION_PLAY_FROM_URI) + .build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsNoneOf(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); + } + + @Test + public void + convertToPlayerCommands_withPrepareFromActionsWithoutPlayFromAction_setMediaItemCommandNotAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID + | PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH + | PlaybackStateCompat.ACTION_PREPARE_FROM_URI) + .build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsNoneOf(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); + } + + @Test + public void + convertToPlayerCommands_withPlayFromAndPrepareFromMediaId_setMediaItemPrepareAndPlayAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID + | PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID) + .build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); + } + + @Test + public void + convertToPlayerCommands_withPlayFromAndPrepareFromSearch_setMediaItemPrepareAndPlayAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH + | PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH) + .build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); + } + + @Test + public void + convertToPlayerCommands_withPlayFromAndPrepareFromUri_setMediaItemPrepareAndPlayAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_PLAY_FROM_URI + | PlaybackStateCompat.ACTION_PREPARE_FROM_URI) + .build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); + } + + @Test + public void convertToPlayerCommands_withSetPlaybackSpeedAction_setSpeedCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_SET_PLAYBACK_SPEED) + .build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_SET_SPEED_AND_PITCH); + } + + @Test + public void convertToPlayerCommands_withStopAction_stopCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_STOP).build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_STOP); + } + + @Test + public void convertToPlayerCommands_withRelativeVolumeControl_adjustVolumeCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(/* capabilities= */ 0).build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_ADJUST_DEVICE_VOLUME); + assertThat(getCommandsAsList(playerCommands)).doesNotContain(Player.COMMAND_SET_DEVICE_VOLUME); + } + + @Test + public void convertToPlayerCommands_withAbsoluteVolumeControl_adjustVolumeCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(/* capabilities= */ 0).build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast(Player.COMMAND_ADJUST_DEVICE_VOLUME, Player.COMMAND_SET_DEVICE_VOLUME); + } + + @Test + public void + convertToPlayerCommands_withShuffleRepeatActionsAndSessionReady_shuffleAndRepeatCommandsAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_SET_REPEAT_MODE + | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE) + .build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast(Player.COMMAND_SET_REPEAT_MODE, Player.COMMAND_SET_SHUFFLE_MODE); + } + + @Test + public void + convertToPlayerCommands_withShuffleRepeatActionsAndSessionNotReady_shuffleAndRepeatCommandsNotAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_SET_REPEAT_MODE + | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE) + .build(); + + Player.Commands playerCommands = + LegacyConversions.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ false); + + assertThat(getCommandsAsList(playerCommands)) + .containsNoneOf(Player.COMMAND_SET_REPEAT_MODE, Player.COMMAND_SET_SHUFFLE_MODE); + } + + @Test + public void convertToCustomLayout() { + assertThat(LegacyConversions.convertToCustomLayout(null)).isEmpty(); + + String extraKey = "key"; + String extraValue = "value"; + String actionStr = "action"; + String displayName = "display_name"; + int iconRes = 21; + + Bundle extras = new Bundle(); + extras.putString(extraKey, extraValue); + + PlaybackStateCompat.CustomAction action = + new PlaybackStateCompat.CustomAction.Builder(actionStr, displayName, iconRes) + .setExtras(extras) + .build(); + + PlaybackStateCompat state = + new PlaybackStateCompat.Builder() + .setState( + PlaybackStateCompat.STATE_NONE, + /* position= */ 0, + /* playbackSpeed= */ 1, + /* updateTime= */ 100) + .addCustomAction(action) + .build(); + + List buttons = LegacyConversions.convertToCustomLayout(state); + assertThat(buttons).hasSize(1); + CommandButton button = buttons.get(0); + assertThat(button.displayName.toString()).isEqualTo(displayName); + assertThat(button.isEnabled).isTrue(); + assertThat(button.iconResId).isEqualTo(iconRes); + assertThat(button.sessionCommand.customAction).isEqualTo(actionStr); + assertThat(button.sessionCommand.customExtras.getString(extraKey)).isEqualTo(extraValue); + } + + @Test + public void convertToAudioAttributes() { + assertThat(LegacyConversions.convertToAudioAttributes((AudioAttributesCompat) null)) + .isSameInstanceAs(AudioAttributes.DEFAULT); + assertThat( + LegacyConversions.convertToAudioAttributes((MediaControllerCompat.PlaybackInfo) null)) + .isSameInstanceAs(AudioAttributes.DEFAULT); + + int contentType = AudioAttributesCompat.CONTENT_TYPE_MUSIC; + int flags = AudioAttributesCompat.FLAG_AUDIBILITY_ENFORCED; + int usage = AudioAttributesCompat.USAGE_MEDIA; + AudioAttributesCompat aaCompat = + new AudioAttributesCompat.Builder() + .setContentType(contentType) + .setFlags(flags) + .setUsage(usage) + .build(); + AudioAttributes aa = + new AudioAttributes.Builder() + .setContentType(contentType) + .setFlags(flags) + .setUsage(usage) + .build(); + assertThat(LegacyConversions.convertToAudioAttributes(aaCompat)).isEqualTo(aa); + assertThat(LegacyConversions.convertToAudioAttributesCompat(aa)).isEqualTo(aaCompat); + } + + @Test + public void convertToCurrentPosition_byDefault_returnsZero() { + long currentPositionMs = + LegacyConversions.convertToCurrentPositionMs( + /* playbackStateCompat= */ null, + /* metadataCompat= */ null, + /* timeDiffMs= */ C.TIME_UNSET); + assertThat(currentPositionMs).isEqualTo(0); + } + + @Test + public void convertToCurrentPositionMs_withNegativePosition_adjustsToZero() { + long testPositionMs = -100L; + PlaybackStateCompat state = + new PlaybackStateCompat.Builder() + .setState(PlaybackStateCompat.STATE_PAUSED, testPositionMs, /* playbackSpeed= */ 1.0f) + .build(); + long currentPositionMs = + LegacyConversions.convertToCurrentPositionMs( + state, /* metadataCompat= */ null, /* timeDiffMs= */ C.TIME_UNSET); + assertThat(currentPositionMs).isEqualTo(0); + } + + @Test + public void convertToCurrentPositionMs_withGreaterThanDuration_adjustsToDuration() { + long testDurationMs = 100L; + long testPositionMs = 200L; + MediaMetadataCompat metadata = + new MediaMetadataCompat.Builder().putLong(METADATA_KEY_DURATION, testDurationMs).build(); + PlaybackStateCompat state = + new PlaybackStateCompat.Builder() + .setState(PlaybackStateCompat.STATE_PAUSED, testPositionMs, /* playbackSpeed= */ 1.0f) + .build(); + long currentPositionMs = + LegacyConversions.convertToCurrentPositionMs( + state, metadata, /* timeDiffMs= */ C.TIME_UNSET); + assertThat(currentPositionMs).isEqualTo(testDurationMs); + } + + @Test + public void convertToDurationMs() { + long testDurationMs = 100L; + MediaMetadataCompat metadata = + new MediaMetadataCompat.Builder().putLong(METADATA_KEY_DURATION, testDurationMs).build(); + long currentPositionMs = LegacyConversions.convertToDurationMs(metadata); + assertThat(currentPositionMs).isEqualTo(testDurationMs); + } + + @Test + public void convertToDurationMs_withNegativeDuration_returnsTimeUnset() { + long testDurationMs = -100L; + MediaMetadataCompat metadata = + new MediaMetadataCompat.Builder().putLong(METADATA_KEY_DURATION, testDurationMs).build(); + long currentPositionMs = LegacyConversions.convertToDurationMs(metadata); + assertThat(currentPositionMs).isEqualTo(C.TIME_UNSET); + } + + @Test + public void convertToBufferedPositionMs() { + long testPositionMs = 300L; + long testBufferedPositionMs = 331L; + PlaybackStateCompat state = + new PlaybackStateCompat.Builder() + .setState(PlaybackStateCompat.STATE_PAUSED, testPositionMs, /* playbackSpeed= */ 1.0f) + .setBufferedPosition(testBufferedPositionMs) + .build(); + + long bufferedPositionMs = + LegacyConversions.convertToBufferedPositionMs( + state, /* metadataCompat= */ null, /* timeDiffMs= */ C.TIME_UNSET); + assertThat(bufferedPositionMs).isEqualTo(testBufferedPositionMs); + } + + @Test + public void convertToBufferedPositionMs_withLessThanPosition_adjustsToPosition() { + long testPositionMs = 300L; + long testBufferedPositionMs = 100L; + PlaybackStateCompat state = + new PlaybackStateCompat.Builder() + .setState(PlaybackStateCompat.STATE_PAUSED, testPositionMs, /* playbackSpeed= */ 1.0f) + .setBufferedPosition(testBufferedPositionMs) + .build(); + long bufferedPositionMs = + LegacyConversions.convertToBufferedPositionMs( + state, /* metadataCompat= */ null, /* timeDiffMs= */ C.TIME_UNSET); + assertThat(bufferedPositionMs).isEqualTo(testPositionMs); + } + + @Test + public void convertToBufferedPositionMs_withLessThanPositionAndWithTimeDiff_adjustsToPosition() { + long testPositionMs = 200L; + long testBufferedPositionMs = 100L; + long testTimeDiffMs = 100; + float testPlaybackSpeed = 1.0f; + long expectedPositionMs = testPositionMs + (long) (testPlaybackSpeed * testTimeDiffMs); + PlaybackStateCompat state = + new PlaybackStateCompat.Builder() + .setState(PlaybackStateCompat.STATE_PLAYING, testPositionMs, testPlaybackSpeed) + .setBufferedPosition(testBufferedPositionMs) + .build(); + long bufferedPositionMs = + LegacyConversions.convertToBufferedPositionMs( + state, /* metadataCompat= */ null, testTimeDiffMs); + assertThat(bufferedPositionMs).isEqualTo(expectedPositionMs); + } + + @Test + public void convertToBufferedPositionMs_withGreaterThanDuration_adjustsToDuration() { + long testDurationMs = 100L; + long testBufferedPositionMs = 200L; + MediaMetadataCompat metadata = + new MediaMetadataCompat.Builder().putLong(METADATA_KEY_DURATION, testDurationMs).build(); + PlaybackStateCompat state = + new PlaybackStateCompat.Builder().setBufferedPosition(testBufferedPositionMs).build(); + long bufferedPositionMs = + LegacyConversions.convertToBufferedPositionMs( + state, metadata, /* timeDiffMs= */ C.TIME_UNSET); + assertThat(bufferedPositionMs).isEqualTo(testDurationMs); + } + + @Test + public void convertToTotalBufferedDurationMs() { + long testCurrentPositionMs = 224L; + long testBufferedPositionMs = 331L; + long testTotalBufferedDurationMs = testBufferedPositionMs - testCurrentPositionMs; + PlaybackStateCompat state = + new PlaybackStateCompat.Builder() + .setState( + PlaybackStateCompat.STATE_PAUSED, testCurrentPositionMs, /* playbackSpeed= */ 1.0f) + .setBufferedPosition(testBufferedPositionMs) + .build(); + + long totalBufferedDurationMs = + LegacyConversions.convertToTotalBufferedDurationMs( + state, /* metadataCompat= */ null, /* timeDiffMs= */ C.INDEX_UNSET); + assertThat(totalBufferedDurationMs).isEqualTo(testTotalBufferedDurationMs); + } +} diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java index 54d1c40825..e24402f7aa 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java @@ -156,7 +156,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); assertThat( - MediaUtils.convertToPlaybackState( + LegacyConversions.convertToPlaybackState( controller.getPlaybackState(), controller.getMetadata(), /* timeDiffMs= */ C.TIME_UNSET)) @@ -398,7 +398,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { assertThat(metadataRef.get().getLong(METADATA_KEY_DURATION)).isEqualTo(testDurationMs); @PlaybackStateCompat.State int playbackStateFromControllerCompat = - MediaUtils.convertToPlaybackState( + LegacyConversions.convertToPlaybackState( playbackStateRef.get(), metadataRef.get(), /* timeDiffMs= */ C.TIME_UNSET); assertThat(playbackStateFromControllerCompat).isEqualTo(testState); assertThat(metadataRef.get().getRating(METADATA_KEY_USER_RATING).hasHeart()).isTrue(); @@ -463,7 +463,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { int legacyStream = AudioManager.STREAM_RING; AudioAttributesCompat attrsCompat = new AudioAttributesCompat.Builder().setLegacyStreamType(legacyStream).build(); - AudioAttributes attrs = MediaUtils.convertToAudioAttributes(attrsCompat); + AudioAttributes attrs = LegacyConversions.convertToAudioAttributes(attrsCompat); CountDownLatch playbackInfoNotified = new CountDownLatch(1); MediaControllerCompat.Callback callback = new MediaControllerCompat.Callback() { @@ -509,7 +509,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { int legacyStream = AudioManager.STREAM_RING; AudioAttributesCompat attrsCompat = new AudioAttributesCompat.Builder().setLegacyStreamType(legacyStream).build(); - AudioAttributes attrs = MediaUtils.convertToAudioAttributes(attrsCompat); + AudioAttributes attrs = LegacyConversions.convertToAudioAttributes(attrsCompat); CountDownLatch playbackInfoNotified = new CountDownLatch(1); MediaControllerCompat.Callback callback = new MediaControllerCompat.Callback() { @@ -967,7 +967,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { new MediaControllerCompat.Callback() { @Override public void onPlaybackStateChanged(PlaybackStateCompat state) { - reportedCustomLayouts.add(MediaUtils.convertToCustomLayout(state)); + reportedCustomLayouts.add(LegacyConversions.convertToCustomLayout(state)); latch1.countDown(); } }; @@ -1129,9 +1129,9 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { assertThat(playbackStateRef.get().getPosition()).isEqualTo(testPosition); assertThat(controllerCompat.getPlaybackState().getPosition()).isEqualTo(testPosition); assertThat(playbackStateRef.get().getActiveQueueItemId()) - .isEqualTo(MediaUtils.convertToQueueItemId(testItemIndex)); + .isEqualTo(LegacyConversions.convertToQueueItemId(testItemIndex)); assertThat(controllerCompat.getPlaybackState().getActiveQueueItemId()) - .isEqualTo(MediaUtils.convertToQueueItemId(testItemIndex)); + .isEqualTo(LegacyConversions.convertToQueueItemId(testItemIndex)); assertThat(callbackOrder) .containsExactly("onMetadataChanged", "onPlaybackStateChanged") .inOrder(); diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest.java index 7823b9fd11..3a392943a2 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest.java @@ -1477,7 +1477,7 @@ public class MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest connectMediaNotificationController(mediaSession); MediaControllerCompat controllerCompat = createMediaControllerCompat(mediaSession); - assertThat(MediaUtils.convertToCustomLayout(controllerCompat.getPlaybackState())) + assertThat(LegacyConversions.convertToCustomLayout(controllerCompat.getPlaybackState())) .containsExactly(customLayout.get(0).copyWithIsEnabled(true)); mediaSession.release(); releasePlayer(player); @@ -1520,14 +1520,14 @@ public class MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest connectMediaNotificationController(mediaSession); MediaControllerCompat controllerCompat = createMediaControllerCompat(mediaSession); ImmutableList initialCustomLayout = - MediaUtils.convertToCustomLayout(controllerCompat.getPlaybackState()); + LegacyConversions.convertToCustomLayout(controllerCompat.getPlaybackState()); AtomicReference> reportedCustomLayout = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); controllerCompat.registerCallback( new MediaControllerCompat.Callback() { @Override public void onPlaybackStateChanged(PlaybackStateCompat state) { - reportedCustomLayout.set(MediaUtils.convertToCustomLayout(state)); + reportedCustomLayout.set(LegacyConversions.convertToCustomLayout(state)); latch.countDown(); } }, @@ -1581,14 +1581,14 @@ public class MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest connectMediaNotificationController(mediaSession); MediaControllerCompat controllerCompat = createMediaControllerCompat(mediaSession); ImmutableList initialCustomLayout = - MediaUtils.convertToCustomLayout(controllerCompat.getPlaybackState()); + LegacyConversions.convertToCustomLayout(controllerCompat.getPlaybackState()); AtomicReference> reportedCustomLayout = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); controllerCompat.registerCallback( new MediaControllerCompat.Callback() { @Override public void onPlaybackStateChanged(PlaybackStateCompat state) { - reportedCustomLayout.set(MediaUtils.convertToCustomLayout(state)); + reportedCustomLayout.set(LegacyConversions.convertToCustomLayout(state)); latch.countDown(); } }, diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java index aed15b0651..9433249f4e 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java @@ -322,7 +322,7 @@ public class MediaControllerListenerTest { Timeline testTimeline = MediaTestUtils.createTimeline(/* windowCount= */ 3); MediaMetadata testPlaylistMetadata = new MediaMetadata.Builder().setTitle("title").build(); AudioAttributes testAudioAttributes = - MediaUtils.convertToAudioAttributes( + LegacyConversions.convertToAudioAttributes( new AudioAttributesCompat.Builder() .setLegacyStreamType(AudioManager.STREAM_RING) .build()); diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerMediaSessionCompatCallbackAggregationTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerMediaSessionCompatCallbackAggregationTest.java index baa3becabe..9de288bdae 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerMediaSessionCompatCallbackAggregationTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerMediaSessionCompatCallbackAggregationTest.java @@ -102,7 +102,7 @@ public class MediaControllerMediaSessionCompatCallbackAggregationTest { MediaMetadataCompat testMediaMetadataCompat = createMediaMetadataCompat(); @RatingCompat.Style int testRatingType = RatingCompat.RATING_HEART; MediaMetadata testMediaMetadata = - MediaUtils.convertToMediaMetadata(testMediaMetadataCompat, testRatingType); + LegacyConversions.convertToMediaMetadata(testMediaMetadataCompat, testRatingType); MediaItem testCurrentMediaItem = new MediaItem.Builder() .setMediaId(testMediaItems.get(testMediaItemIndex).mediaId) @@ -234,7 +234,7 @@ public class MediaControllerMediaSessionCompatCallbackAggregationTest { MediaMetadataCompat testMediaMetadataCompat = createMediaMetadataCompat(); @RatingCompat.Style int testRatingType = RatingCompat.RATING_HEART; MediaMetadata testMediaMetadata = - MediaUtils.convertToMediaMetadata(testMediaMetadataCompat, testRatingType); + LegacyConversions.convertToMediaMetadata(testMediaMetadataCompat, testRatingType); Events testEvents = new Events( new FlagSet.Builder() @@ -245,7 +245,8 @@ public class MediaControllerMediaSessionCompatCallbackAggregationTest { EVENT_TIMELINE_CHANGED) .build()); int testMediaItemIndex = testSize; // Index of fake item. - testMediaItems.add(MediaUtils.convertToMediaItem(testMediaMetadataCompat, testRatingType)); + testMediaItems.add( + LegacyConversions.convertToMediaItem(testMediaMetadataCompat, testRatingType)); MediaController controller = controllerTestRule.createController(session.getSessionToken()); CountDownLatch latch = new CountDownLatch(5); @@ -427,7 +428,7 @@ public class MediaControllerMediaSessionCompatCallbackAggregationTest { MediaMetadataCompat testMediaMetadataCompat = createMediaMetadataCompat(); @RatingCompat.Style int testRatingType = RatingCompat.RATING_HEART; MediaMetadata testMediaMetadata = - MediaUtils.convertToMediaMetadata(testMediaMetadataCompat, testRatingType); + LegacyConversions.convertToMediaMetadata(testMediaMetadataCompat, testRatingType); Events testEvents = new Events( new FlagSet.Builder() @@ -439,7 +440,8 @@ public class MediaControllerMediaSessionCompatCallbackAggregationTest { .build()); int testMediaItemIndex = 0; List testMediaItems = new ArrayList<>(); - testMediaItems.add(MediaUtils.convertToMediaItem(testMediaMetadataCompat, testRatingType)); + testMediaItems.add( + LegacyConversions.convertToMediaItem(testMediaMetadataCompat, testRatingType)); MediaController controller = controllerTestRule.createController(session.getSessionToken()); CountDownLatch latch = new CountDownLatch(5); @@ -547,8 +549,9 @@ public class MediaControllerMediaSessionCompatCallbackAggregationTest { MediaItem item = mediaItems.get(i); @Nullable Bitmap bitmap = bitmapLoader.decodeBitmap(item.mediaMetadata.artworkData).get(10, SECONDS); - MediaDescriptionCompat description = MediaUtils.convertToMediaDescriptionCompat(item, bitmap); - long id = MediaUtils.convertToQueueItemId(i); + MediaDescriptionCompat description = + LegacyConversions.convertToMediaDescriptionCompat(item, bitmap); + long id = LegacyConversions.convertToQueueItemId(i); list.add(new MediaSessionCompat.QueueItem(description, id)); } return list; diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerWithMediaSessionCompatTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerWithMediaSessionCompatTest.java index a9d31c33c1..c91be5d5b2 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerWithMediaSessionCompatTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerWithMediaSessionCompatTest.java @@ -451,7 +451,7 @@ public class MediaControllerWithMediaSessionCompatTest { Timeline testTimeline = MediaTestUtils.createTimeline(/* windowCount= */ 2); List testQueue = MediaTestUtils.convertToQueueItemsWithoutBitmap( - MediaUtils.convertToMediaItemList(testTimeline)); + LegacyConversions.convertToMediaItemList(testTimeline)); session.setQueue(testQueue); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); @@ -465,7 +465,7 @@ public class MediaControllerWithMediaSessionCompatTest { Timeline timeline = MediaTestUtils.createTimeline(/* windowCount= */ 2); List queue = MediaTestUtils.convertToQueueItemsWithoutBitmap( - MediaUtils.convertToMediaItemList(timeline)); + LegacyConversions.convertToMediaItemList(timeline)); session.setQueue(queue); CountDownLatch latch = new CountDownLatch(1); @@ -515,7 +515,7 @@ public class MediaControllerWithMediaSessionCompatTest { ImmutableList.copyOf(Iterables.concat(mediaItems, mediaItems))); List testQueue = MediaTestUtils.convertToQueueItemsWithoutBitmap( - MediaUtils.convertToMediaItemList(testTimeline)); + LegacyConversions.convertToMediaItemList(testTimeline)); session.setQueue(testQueue); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); @@ -823,7 +823,7 @@ public class MediaControllerWithMediaSessionCompatTest { List testQueue = MediaTestUtils.convertToQueueItemsWithoutBitmap(testList); MediaItem testRemoveMediaItem = MediaTestUtils.createMediaItem("removed"); MediaMetadataCompat testMetadataCompat = - MediaUtils.convertToMediaMetadataCompat( + LegacyConversions.convertToMediaMetadataCompat( testRemoveMediaItem.mediaMetadata, "mediaId", Uri.parse("http://example.com"), @@ -845,7 +845,7 @@ public class MediaControllerWithMediaSessionCompatTest { List testQueue = MediaTestUtils.convertToQueueItemsWithoutBitmap(testList); MediaItem testRemoveMediaItem = MediaTestUtils.createMediaItem("removed"); MediaMetadataCompat testMetadataCompat = - MediaUtils.convertToMediaMetadataCompat( + LegacyConversions.convertToMediaMetadataCompat( testRemoveMediaItem.mediaMetadata, "mediaId", Uri.parse("http://example.com"), @@ -884,7 +884,7 @@ public class MediaControllerWithMediaSessionCompatTest { List testQueue = MediaTestUtils.convertToQueueItemsWithoutBitmap(testList); MediaItem testRemoveMediaItem = MediaTestUtils.createMediaItem("removed"); MediaMetadataCompat testMetadataCompat = - MediaUtils.convertToMediaMetadataCompat( + LegacyConversions.convertToMediaMetadataCompat( testRemoveMediaItem.mediaMetadata, "mediaId", Uri.parse("http://example.com"), @@ -906,7 +906,7 @@ public class MediaControllerWithMediaSessionCompatTest { MediaItem testMediaItem = MediaTestUtils.createMediaItem("test"); MediaMetadata testMediaMetadata = testMediaItem.mediaMetadata; MediaMetadataCompat testMediaMetadataCompat = - MediaUtils.convertToMediaMetadataCompat( + LegacyConversions.convertToMediaMetadataCompat( testMediaMetadata, "mediaId", Uri.parse("http://example.com"), @@ -928,7 +928,7 @@ public class MediaControllerWithMediaSessionCompatTest { MediaMetadata testMediaMetadata = testMediaItem.mediaMetadata; @Nullable Bitmap artworkBitmap = getBitmapFromMetadata(testMediaMetadata); MediaMetadataCompat testMediaMetadataCompat = - MediaUtils.convertToMediaMetadataCompat( + LegacyConversions.convertToMediaMetadataCompat( testMediaMetadata, "mediaId", Uri.parse("http://example.com"), @@ -1275,7 +1275,7 @@ public class MediaControllerWithMediaSessionCompatTest { List testPlaylist = MediaTestUtils.createMediaItems(/* size= */ 1); MediaItem firstMediaItemInPlaylist = testPlaylist.get(0); MediaMetadataCompat metadata = - MediaUtils.convertToMediaMetadataCompat( + LegacyConversions.convertToMediaMetadataCompat( firstMediaItemInPlaylist.mediaMetadata, "mediaId", Uri.parse("http://example.com"), @@ -1325,7 +1325,7 @@ public class MediaControllerWithMediaSessionCompatTest { List testPlaylist = MediaTestUtils.createMediaItems(1); MediaItem firstMediaItemInPlaylist = testPlaylist.get(0); MediaMetadataCompat metadata = - MediaUtils.convertToMediaMetadataCompat( + LegacyConversions.convertToMediaMetadataCompat( firstMediaItemInPlaylist.mediaMetadata, "mediaId", Uri.parse("http://example.com"), diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackWithMediaControllerCompatTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackWithMediaControllerCompatTest.java index b5689477d6..c0ebab372a 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackWithMediaControllerCompatTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackWithMediaControllerCompatTest.java @@ -1147,7 +1147,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest { () -> { // Set stream of the session. AudioAttributes attrs = - MediaUtils.convertToAudioAttributes( + LegacyConversions.convertToAudioAttributes( new AudioAttributesCompat.Builder().setLegacyStreamType(stream).build()); player.audioAttributes = attrs; player.notifyAudioAttributesChanged(attrs); @@ -1195,7 +1195,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest { () -> { // Set stream of the session. AudioAttributes attrs = - MediaUtils.convertToAudioAttributes( + LegacyConversions.convertToAudioAttributes( new AudioAttributesCompat.Builder().setLegacyStreamType(stream).build()); player.audioAttributes = attrs; player.notifyAudioAttributesChanged(attrs); @@ -1764,7 +1764,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest { String mediaIdOut, Rating ratingOut) { assertThat(mediaIdOut).isEqualTo(mediaId); - assertThat(ratingOut).isEqualTo(MediaUtils.convertToRating(rating)); + assertThat(ratingOut).isEqualTo(LegacyConversions.convertToRating(rating)); latch.countDown(); return Futures.immediateFuture(new SessionResult(RESULT_SUCCESS)); } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCompatCallbackWithMediaControllerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCompatCallbackWithMediaControllerTest.java index f47432b63d..25dac2cf40 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCompatCallbackWithMediaControllerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCompatCallbackWithMediaControllerTest.java @@ -717,7 +717,7 @@ public class MediaSessionCompatCallbackWithMediaControllerTest { controller.setRating(mediaId, rating); assertThat(sessionCallback.await(TIMEOUT_MS)).isTrue(); assertThat(sessionCallback.onSetRatingCalled).isTrue(); - assertThat(MediaUtils.convertToRating(sessionCallback.rating)).isEqualTo(rating); + assertThat(LegacyConversions.convertToRating(sessionCallback.rating)).isEqualTo(rating); } @Test @@ -730,7 +730,7 @@ public class MediaSessionCompatCallbackWithMediaControllerTest { controller.setRating(rating); assertThat(sessionCallback.await(TIMEOUT_MS)).isTrue(); assertThat(sessionCallback.onSetRatingCalled).isTrue(); - assertThat(MediaUtils.convertToRating(sessionCallback.rating)).isEqualTo(rating); + assertThat(LegacyConversions.convertToRating(sessionCallback.rating)).isEqualTo(rating); } @Test diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionServiceTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionServiceTest.java index b328c60f16..7d9f16c21e 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionServiceTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionServiceTest.java @@ -300,7 +300,7 @@ public class MediaSessionServiceTest { new MediaControllerCompat( ApplicationProvider.getApplicationContext(), mediaSession.getSessionCompat()); ImmutableList initialCustomLayoutInControllerCompat = - MediaUtils.convertToCustomLayout(mediaControllerCompat.getPlaybackState()); + LegacyConversions.convertToCustomLayout(mediaControllerCompat.getPlaybackState()); // Start the service by creating a remote controller. RemoteMediaController remoteController = @@ -321,7 +321,7 @@ public class MediaSessionServiceTest { .isEqualTo(PlaybackStateCompat.ACTION_SET_RATING); assertThat(remoteController.getCustomLayout()).containsExactly(button1, button2).inOrder(); assertThat(initialCustomLayoutInControllerCompat).isEmpty(); - assertThat(MediaUtils.convertToCustomLayout(mediaControllerCompat.getPlaybackState())) + assertThat(LegacyConversions.convertToCustomLayout(mediaControllerCompat.getPlaybackState())) .containsExactly(button1.copyWithIsEnabled(true), button3.copyWithIsEnabled(true)) .inOrder(); mediaSession.release(); diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaUtilsTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaUtilsTest.java index 3574329fbb..2cbe5d7e16 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaUtilsTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaUtilsTest.java @@ -15,48 +15,18 @@ */ package androidx.media3.session; -import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_BROWSABLE; -import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_PLAYABLE; -import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_DURATION; -import static androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS; import static androidx.media3.common.MimeTypes.AUDIO_AAC; import static androidx.media3.common.MimeTypes.VIDEO_H264; import static androidx.media3.common.MimeTypes.VIDEO_H265; -import static androidx.media3.session.MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT; -import static androidx.media3.session.MediaConstants.EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY; -import static androidx.media3.test.session.common.TestUtils.getCommandsAsList; import static com.google.common.truth.Truth.assertThat; -import static java.util.concurrent.TimeUnit.SECONDS; import android.content.Context; -import android.graphics.Bitmap; -import android.net.Uri; import android.os.Bundle; import android.os.Parcel; -import android.service.media.MediaBrowserService; -import android.support.v4.media.MediaBrowserCompat; -import android.support.v4.media.MediaDescriptionCompat; -import android.support.v4.media.MediaMetadataCompat; -import android.support.v4.media.RatingCompat; -import android.support.v4.media.session.MediaControllerCompat; -import android.support.v4.media.session.MediaSessionCompat; -import android.support.v4.media.session.PlaybackStateCompat; import android.util.Pair; -import androidx.annotation.Nullable; -import androidx.media.AudioAttributesCompat; -import androidx.media.VolumeProviderCompat; -import androidx.media.utils.MediaConstants; -import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; import androidx.media3.common.Format; -import androidx.media3.common.HeartRating; -import androidx.media3.common.MediaItem; -import androidx.media3.common.MediaMetadata; -import androidx.media3.common.PercentageRating; import androidx.media3.common.Player; -import androidx.media3.common.Rating; -import androidx.media3.common.StarRating; -import androidx.media3.common.ThumbRating; import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroup; import androidx.media3.common.Tracks; @@ -65,10 +35,8 @@ import androidx.media3.datasource.DataSourceBitmapLoader; import androidx.media3.session.PlayerInfo.BundlingExclusions; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SdkSuppress; import androidx.test.filters.SmallTest; import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ListenableFuture; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -90,92 +58,6 @@ public final class MediaUtilsTest { bitmapLoader = new CacheBitmapLoader(new DataSourceBitmapLoader(context)); } - @Test - public void convertToMediaItem_browserItemToMediaItem() { - String mediaId = "testId"; - String title = "testTitle"; - MediaDescriptionCompat descriptionCompat = - new MediaDescriptionCompat.Builder().setMediaId(mediaId).setTitle(title).build(); - MediaBrowserCompat.MediaItem browserItem = - new MediaBrowserCompat.MediaItem(descriptionCompat, /* flags= */ 0); - - MediaItem mediaItem = MediaUtils.convertToMediaItem(browserItem); - assertThat(mediaItem.mediaId).isEqualTo(mediaId); - assertThat(mediaItem.mediaMetadata.title.toString()).isEqualTo(title); - } - - @Test - public void convertToMediaItem_queueItemToMediaItem() { - String mediaId = "testMediaId"; - String title = "testTitle"; - MediaDescriptionCompat descriptionCompat = - new MediaDescriptionCompat.Builder().setMediaId(mediaId).setTitle(title).build(); - MediaSessionCompat.QueueItem queueItem = - new MediaSessionCompat.QueueItem(descriptionCompat, /* id= */ 1); - MediaItem mediaItem = MediaUtils.convertToMediaItem(queueItem); - assertThat(mediaItem.mediaId).isEqualTo(mediaId); - assertThat(mediaItem.mediaMetadata.title.toString()).isEqualTo(title); - } - - @Test - public void convertBrowserItemListToMediaItemList() { - int size = 3; - List browserItems = MediaTestUtils.createBrowserItems(size); - List mediaItems = MediaUtils.convertBrowserItemListToMediaItemList(browserItems); - assertThat(mediaItems).hasSize(size); - for (int i = 0; i < size; ++i) { - assertThat(mediaItems.get(i).mediaId).isEqualTo(browserItems.get(i).getMediaId()); - } - } - - @Test - public void convertToQueueItem_withArtworkData() throws Exception { - MediaItem mediaItem = MediaTestUtils.createMediaItemWithArtworkData("testId"); - MediaMetadata mediaMetadata = mediaItem.mediaMetadata; - ListenableFuture bitmapFuture = bitmapLoader.decodeBitmap(mediaMetadata.artworkData); - @Nullable Bitmap bitmap = bitmapFuture.get(10, SECONDS); - - MediaSessionCompat.QueueItem queueItem = - MediaUtils.convertToQueueItem( - mediaItem, - /** mediaItemIndex= */ - 100, - bitmap); - - assertThat(queueItem.getQueueId()).isEqualTo(100); - assertThat(queueItem.getDescription().getIconBitmap()).isNotNull(); - } - - @Test - public void convertToMediaDescriptionCompat_setsExpectedValues() { - String mediaId = "testId"; - String title = "testTitle"; - String description = "testDesc"; - MediaMetadata metadata = - new MediaMetadata.Builder() - .setTitle(title) - .setDescription(description) - .setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC) - .build(); - MediaItem mediaItem = - new MediaItem.Builder().setMediaId(mediaId).setMediaMetadata(metadata).build(); - MediaDescriptionCompat descriptionCompat = - MediaUtils.convertToMediaDescriptionCompat(mediaItem, /* artworkBitmap= */ null); - - assertThat(descriptionCompat.getMediaId()).isEqualTo(mediaId); - assertThat(descriptionCompat.getTitle().toString()).isEqualTo(title); - assertThat(descriptionCompat.getDescription().toString()).isEqualTo(description); - assertThat(descriptionCompat.getExtras().getLong(EXTRAS_KEY_MEDIA_TYPE_COMPAT)) - .isEqualTo(MediaMetadata.MEDIA_TYPE_MUSIC); - } - - @Test - public void convertToQueueItemId() { - assertThat(MediaUtils.convertToQueueItemId(C.INDEX_UNSET)) - .isEqualTo(MediaSessionCompat.QueueItem.UNKNOWN_ID); - assertThat(MediaUtils.convertToQueueItemId(100)).isEqualTo(100); - } - @Test public void truncateListBySize() { List bundleList = new ArrayList<>(); @@ -194,904 +76,6 @@ public final class MediaUtilsTest { } } - @Test - public void convertToMediaMetadata_withoutTitle() { - assertThat(MediaUtils.convertToMediaMetadata((CharSequence) null)) - .isEqualTo(MediaMetadata.EMPTY); - } - - @Test - public void convertToMediaMetadata_withTitle() { - String title = "title"; - assertThat(MediaUtils.convertToMediaMetadata(title).title.toString()).isEqualTo(title); - } - - @Test - public void convertToMediaMetadata_withCustomKey() { - MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(); - builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title"); - builder.putLong(EXTRAS_KEY_MEDIA_TYPE_COMPAT, (long) MediaMetadata.MEDIA_TYPE_MUSIC); - builder.putString("custom_key", "value"); - MediaMetadataCompat testMediaMetadataCompat = builder.build(); - - MediaMetadata mediaMetadata = - MediaUtils.convertToMediaMetadata(testMediaMetadataCompat, RatingCompat.RATING_NONE); - - assertThat(mediaMetadata.title.toString()).isEqualTo("title"); - assertThat(mediaMetadata.mediaType).isEqualTo(MediaMetadata.MEDIA_TYPE_MUSIC); - assertThat(mediaMetadata.extras).isNotNull(); - assertThat(mediaMetadata.extras.getString("custom_key")).isEqualTo("value"); - assertThat(mediaMetadata.extras.containsKey(MediaMetadataCompat.METADATA_KEY_TITLE)).isFalse(); - assertThat(mediaMetadata.extras.containsKey(EXTRAS_KEY_MEDIA_TYPE_COMPAT)).isFalse(); - } - - @Test - public void convertToMediaMetadata_roundTripViaMediaMetadataCompat_returnsEqualMediaItemMetadata() - throws Exception { - MediaItem testMediaItem = MediaTestUtils.createMediaItemWithArtworkData("testZZZ"); - MediaMetadata testMediaMetadata = testMediaItem.mediaMetadata; - @Nullable Bitmap testArtworkBitmap = null; - @Nullable - ListenableFuture bitmapFuture = bitmapLoader.loadBitmapFromMetadata(testMediaMetadata); - if (bitmapFuture != null) { - testArtworkBitmap = bitmapFuture.get(10, SECONDS); - } - MediaMetadataCompat testMediaMetadataCompat = - MediaUtils.convertToMediaMetadataCompat( - testMediaMetadata, - "mediaId", - Uri.parse("http://example.com"), - /* durationMs= */ 100L, - testArtworkBitmap); - - MediaMetadata mediaMetadata = - MediaUtils.convertToMediaMetadata(testMediaMetadataCompat, RatingCompat.RATING_NONE); - - assertThat(mediaMetadata).isEqualTo(testMediaMetadata); - assertThat(mediaMetadata.artworkData).isNotNull(); - } - - @Test - public void - convertToMediaMetadata_roundTripViaMediaDescriptionCompat_returnsEqualMediaItemMetadata() - throws Exception { - MediaItem testMediaItem = MediaTestUtils.createMediaItemWithArtworkData("testZZZ"); - MediaMetadata testMediaMetadata = testMediaItem.mediaMetadata; - @Nullable Bitmap testArtworkBitmap = null; - @Nullable - ListenableFuture bitmapFuture = bitmapLoader.loadBitmapFromMetadata(testMediaMetadata); - if (bitmapFuture != null) { - testArtworkBitmap = bitmapFuture.get(10, SECONDS); - } - MediaDescriptionCompat mediaDescriptionCompat = - MediaUtils.convertToMediaDescriptionCompat(testMediaItem, testArtworkBitmap); - - MediaMetadata mediaMetadata = - MediaUtils.convertToMediaMetadata(mediaDescriptionCompat, RatingCompat.RATING_NONE); - - assertThat(mediaMetadata).isEqualTo(testMediaMetadata); - assertThat(mediaMetadata.artworkData).isNotNull(); - } - - @Test - public void convertToMediaMetadataCompat_withMediaType_setsMediaType() { - MediaItem mediaItem = - new MediaItem.Builder() - .setMediaMetadata( - new MediaMetadata.Builder().setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC).build()) - .build(); - - MediaMetadataCompat mediaMetadataCompat = - MediaUtils.convertToMediaMetadataCompat( - mediaItem.mediaMetadata, - "mediaId", - Uri.parse("http://www.example.com"), - /* durationMs= */ C.TIME_UNSET, - /* artworkBitmap= */ null); - - assertThat(mediaMetadataCompat.getLong(EXTRAS_KEY_MEDIA_TYPE_COMPAT)) - .isEqualTo(MediaMetadata.MEDIA_TYPE_MUSIC); - } - - @Test - public void convertBetweenRatingAndRatingCompat() { - assertRatingEquals(MediaUtils.convertToRating(null), MediaUtils.convertToRatingCompat(null)); - assertRatingEquals( - MediaUtils.convertToRating(RatingCompat.newUnratedRating(RatingCompat.RATING_NONE)), - MediaUtils.convertToRatingCompat(null)); - assertRatingEquals( - MediaUtils.convertToRating(RatingCompat.newUnratedRating(RatingCompat.RATING_HEART)), - MediaUtils.convertToRatingCompat(new HeartRating())); - assertRatingEquals( - MediaUtils.convertToRating(RatingCompat.newHeartRating(true)), - MediaUtils.convertToRatingCompat(new HeartRating(true))); - assertRatingEquals( - MediaUtils.convertToRating(RatingCompat.newThumbRating(false)), - MediaUtils.convertToRatingCompat(new ThumbRating(false))); - assertRatingEquals( - MediaUtils.convertToRating(RatingCompat.newThumbRating(false)), - MediaUtils.convertToRatingCompat(new ThumbRating(false))); - assertRatingEquals( - MediaUtils.convertToRating(RatingCompat.newStarRating(RatingCompat.RATING_3_STARS, 1f)), - MediaUtils.convertToRatingCompat(new StarRating(3, 1f))); - assertRatingEquals( - MediaUtils.convertToRating(RatingCompat.newStarRating(RatingCompat.RATING_4_STARS, 0f)), - MediaUtils.convertToRatingCompat(new StarRating(4, 0f))); - assertRatingEquals( - MediaUtils.convertToRating(RatingCompat.newStarRating(RatingCompat.RATING_5_STARS, 5f)), - MediaUtils.convertToRatingCompat(new StarRating(5, 5f))); - assertRatingEquals( - MediaUtils.convertToRating(RatingCompat.newPercentageRating(80f)), - MediaUtils.convertToRatingCompat(new PercentageRating(80f))); - } - - void assertRatingEquals(Rating rating, RatingCompat ratingCompat) { - if (rating == null && ratingCompat == null) { - return; - } - assertThat(rating.isRated()).isEqualTo(ratingCompat.isRated()); - if (rating instanceof HeartRating) { - assertThat(ratingCompat.getRatingStyle()).isEqualTo(RatingCompat.RATING_HEART); - assertThat(((HeartRating) rating).isHeart()).isEqualTo(ratingCompat.hasHeart()); - } else if (rating instanceof ThumbRating) { - assertThat(ratingCompat.getRatingStyle()).isEqualTo(RatingCompat.RATING_THUMB_UP_DOWN); - assertThat(((ThumbRating) rating).isThumbsUp()).isEqualTo(ratingCompat.isThumbUp()); - } else if (rating instanceof StarRating) { - StarRating starRating = (StarRating) rating; - switch (starRating.getMaxStars()) { - case 3: - assertThat(ratingCompat.getRatingStyle()).isEqualTo(RatingCompat.RATING_3_STARS); - break; - case 4: - assertThat(ratingCompat.getRatingStyle()).isEqualTo(RatingCompat.RATING_4_STARS); - break; - case 5: - assertThat(ratingCompat.getRatingStyle()).isEqualTo(RatingCompat.RATING_5_STARS); - break; - default: // fall out - } - assertThat(starRating.getStarRating()).isEqualTo(ratingCompat.getStarRating()); - } else if (rating instanceof PercentageRating) { - assertThat(ratingCompat.getRatingStyle()).isEqualTo(RatingCompat.RATING_PERCENTAGE); - assertThat(((PercentageRating) rating).getPercent()) - .isEqualTo(ratingCompat.getPercentRating()); - } - } - - @Test - public void convertToLibraryParams() { - assertThat(MediaUtils.convertToLibraryParams(context, null)).isNull(); - Bundle rootHints = new Bundle(); - rootHints.putString("key", "value"); - rootHints.putInt( - MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, FLAG_BROWSABLE); - rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_OFFLINE, true); - rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); - rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_SUGGESTED, true); - - MediaLibraryService.LibraryParams params = - MediaUtils.convertToLibraryParams(context, rootHints); - - assertThat(params.extras.getString("key")).isEqualTo("value"); - assertThat(params.extras.getBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isTrue(); - assertThat(params.extras.containsKey(BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS)) - .isFalse(); - assertThat(params.isOffline).isTrue(); - assertThat(params.isRecent).isTrue(); - assertThat(params.isSuggested).isTrue(); - } - - @Test - public void convertToLibraryParams_rootHintsBrowsableNoFlagSet_browsableOnlyFalse() { - Bundle rootHints = new Bundle(); - rootHints.putInt(MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, 0); - - MediaLibraryService.LibraryParams params = - MediaUtils.convertToLibraryParams(context, rootHints); - - assertThat(params.extras.getBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isFalse(); - } - - @Test - public void convertToLibraryParams_rootHintsPlayableFlagSet_browsableOnlyFalse() { - Bundle rootHints = new Bundle(); - rootHints.putInt( - MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, - FLAG_PLAYABLE | FLAG_BROWSABLE); - - MediaLibraryService.LibraryParams params = - MediaUtils.convertToLibraryParams(context, rootHints); - - assertThat(params.extras.getBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isFalse(); - } - - @Test - public void convertToLibraryParams_rootHintsBrowsableAbsentKey_browsableOnlyFalse() { - MediaLibraryService.LibraryParams params = - MediaUtils.convertToLibraryParams(context, /* legacyBundle= */ Bundle.EMPTY); - - assertThat(params.extras.getBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isFalse(); - } - - @Test - public void convertToRootHints() { - assertThat(MediaUtils.convertToRootHints(null)).isNull(); - Bundle extras = new Bundle(); - extras.putString("key", "value"); - extras.putBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, true); - MediaLibraryService.LibraryParams param = - new MediaLibraryService.LibraryParams.Builder() - .setOffline(true) - .setRecent(true) - .setSuggested(true) - .setExtras(extras) - .build(); - - Bundle rootHints = MediaUtils.convertToRootHints(param); - - assertThat( - rootHints.getInt( - BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ 0)) - .isEqualTo(FLAG_BROWSABLE); - assertThat(rootHints.getString("key")).isEqualTo("value"); - assertThat(rootHints.get(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isNull(); - assertThat(rootHints.getBoolean(MediaBrowserService.BrowserRoot.EXTRA_OFFLINE)).isTrue(); - assertThat(rootHints.getBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT)).isTrue(); - assertThat(rootHints.getBoolean(MediaBrowserService.BrowserRoot.EXTRA_SUGGESTED)).isTrue(); - } - - @Test - public void convertToRootHints_browsableOnlyFalse_correctLegacyBrowsableFlags() { - Bundle extras = new Bundle(); - extras.putBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, false); - MediaLibraryService.LibraryParams param = - new MediaLibraryService.LibraryParams.Builder().setExtras(extras).build(); - - Bundle rootHints = MediaUtils.convertToRootHints(param); - - assertThat( - rootHints.getInt( - BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ -1)) - .isEqualTo(FLAG_BROWSABLE | FLAG_PLAYABLE); - assertThat(rootHints.get(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isNull(); - } - - @Test - public void convertToRootHints_browsableAbsentKey_noLegacyKeyAdded() { - MediaLibraryService.LibraryParams param = - new MediaLibraryService.LibraryParams.Builder().build(); - - Bundle rootHints = MediaUtils.convertToRootHints(param); - - assertThat(rootHints.get(BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS)).isNull(); - } - - @Test - public void removeNullElements() { - List strings = new ArrayList<>(); - strings.add("str1"); - strings.add(null); - strings.add("str2"); - strings.add(null); - assertThat(MediaUtils.removeNullElements(strings)).containsExactly("str1", "str2"); - } - - @Test - public void convertToSessionCommands_withCustomAction_containsCustomAction() { - PlaybackStateCompat playbackState = - new PlaybackStateCompat.Builder() - .addCustomAction("action", "name", /* icon= */ 100) - .build(); - SessionCommands sessionCommands = - MediaUtils.convertToSessionCommands(playbackState, /* isSessionReady= */ true); - assertThat(sessionCommands.contains(new SessionCommand("action", /* extras= */ Bundle.EMPTY))) - .isTrue(); - } - - @SdkSuppress(minSdkVersion = 21) - @Test - public void convertToSessionCommands_whenSessionIsNotReadyOnSdk21_disallowsRating() { - SessionCommands sessionCommands = - MediaUtils.convertToSessionCommands(/* state= */ null, /* isSessionReady= */ false); - assertThat(sessionCommands.contains(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)).isFalse(); - } - - @Test - public void convertToPlayerCommands_withNoActions_onlyDefaultCommandsAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder().setActions(/* capabilities= */ 0).build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)) - .containsExactly( - Player.COMMAND_GET_TIMELINE, - Player.COMMAND_GET_CURRENT_MEDIA_ITEM, - Player.COMMAND_GET_DEVICE_VOLUME, - Player.COMMAND_GET_METADATA, - Player.COMMAND_GET_AUDIO_ATTRIBUTES, - Player.COMMAND_RELEASE); - } - - @Test - public void convertToPlayerCommands_withJustPlayAction_playPauseCommandNotAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PLAY).build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)).doesNotContain(Player.COMMAND_PLAY_PAUSE); - } - - @Test - public void convertToPlayerCommands_withJustPauseAction_playPauseCommandNotAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PAUSE).build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)).doesNotContain(Player.COMMAND_PLAY_PAUSE); - } - - @Test - public void convertToPlayerCommands_withPlayAndPauseAction_playPauseCommandAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder() - .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE) - .build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_PLAY_PAUSE); - } - - @Test - public void convertToPlayerCommands_withPlayPauseAction_playPauseCommandAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE).build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_PLAY_PAUSE); - } - - @Test - public void convertToPlayerCommands_withPrepareAction_prepareCommandAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PREPARE).build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_PREPARE); - } - - @Test - public void convertToPlayerCommands_withRewindAction_seekBackCommandAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_REWIND).build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_SEEK_BACK); - } - - @Test - public void convertToPlayerCommands_withFastForwardAction_seekForwardCommandAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder() - .setActions(PlaybackStateCompat.ACTION_FAST_FORWARD) - .build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_SEEK_FORWARD); - } - - @Test - public void convertToPlayerCommands_withSeekToAction_seekInCurrentMediaItemCommandAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_SEEK_TO).build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)) - .contains(Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); - } - - @Test - public void convertToPlayerCommands_withSkipToNextAction_seekToNextCommandsAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder() - .setActions(PlaybackStateCompat.ACTION_SKIP_TO_NEXT) - .build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)) - .containsAtLeast(Player.COMMAND_SEEK_TO_NEXT, Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); - } - - @Test - public void convertToPlayerCommands_withSkipToPreviousAction_seekToPreviousCommandsAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder() - .setActions(PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) - .build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)) - .containsAtLeast( - Player.COMMAND_SEEK_TO_PREVIOUS, Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); - } - - @Test - public void - convertToPlayerCommands_withPlayFromActionsWithoutPrepareFromAction_setMediaItemCommandNotAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder() - .setActions( - PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID - | PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH - | PlaybackStateCompat.ACTION_PLAY_FROM_URI) - .build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)) - .containsNoneOf(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); - } - - @Test - public void - convertToPlayerCommands_withPrepareFromActionsWithoutPlayFromAction_setMediaItemCommandNotAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder() - .setActions( - PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID - | PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH - | PlaybackStateCompat.ACTION_PREPARE_FROM_URI) - .build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)) - .containsNoneOf(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); - } - - @Test - public void - convertToPlayerCommands_withPlayFromAndPrepareFromMediaId_setMediaItemPrepareAndPlayAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder() - .setActions( - PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID - | PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID) - .build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)) - .containsAtLeast(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); - } - - @Test - public void - convertToPlayerCommands_withPlayFromAndPrepareFromSearch_setMediaItemPrepareAndPlayAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder() - .setActions( - PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH - | PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH) - .build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)) - .containsAtLeast(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); - } - - @Test - public void - convertToPlayerCommands_withPlayFromAndPrepareFromUri_setMediaItemPrepareAndPlayAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder() - .setActions( - PlaybackStateCompat.ACTION_PLAY_FROM_URI - | PlaybackStateCompat.ACTION_PREPARE_FROM_URI) - .build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)) - .containsAtLeast(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); - } - - @Test - public void convertToPlayerCommands_withSetPlaybackSpeedAction_setSpeedCommandAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder() - .setActions(PlaybackStateCompat.ACTION_SET_PLAYBACK_SPEED) - .build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_SET_SPEED_AND_PITCH); - } - - @Test - public void convertToPlayerCommands_withStopAction_stopCommandAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_STOP).build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_STOP); - } - - @Test - public void convertToPlayerCommands_withRelativeVolumeControl_adjustVolumeCommandAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder().setActions(/* capabilities= */ 0).build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_ADJUST_DEVICE_VOLUME); - assertThat(getCommandsAsList(playerCommands)).doesNotContain(Player.COMMAND_SET_DEVICE_VOLUME); - } - - @Test - public void convertToPlayerCommands_withAbsoluteVolumeControl_adjustVolumeCommandAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder().setActions(/* capabilities= */ 0).build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)) - .containsAtLeast(Player.COMMAND_ADJUST_DEVICE_VOLUME, Player.COMMAND_SET_DEVICE_VOLUME); - } - - @Test - public void - convertToPlayerCommands_withShuffleRepeatActionsAndSessionReady_shuffleAndRepeatCommandsAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder() - .setActions( - PlaybackStateCompat.ACTION_SET_REPEAT_MODE - | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE) - .build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ true); - - assertThat(getCommandsAsList(playerCommands)) - .containsAtLeast(Player.COMMAND_SET_REPEAT_MODE, Player.COMMAND_SET_SHUFFLE_MODE); - } - - @Test - public void - convertToPlayerCommands_withShuffleRepeatActionsAndSessionNotReady_shuffleAndRepeatCommandsNotAvailable() { - PlaybackStateCompat playbackStateCompat = - new PlaybackStateCompat.Builder() - .setActions( - PlaybackStateCompat.ACTION_SET_REPEAT_MODE - | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE) - .build(); - - Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands( - playbackStateCompat, - /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, - /* sessionFlags= */ 0, - /* isSessionReady= */ false); - - assertThat(getCommandsAsList(playerCommands)) - .containsNoneOf(Player.COMMAND_SET_REPEAT_MODE, Player.COMMAND_SET_SHUFFLE_MODE); - } - - @Test - public void convertToCustomLayout() { - assertThat(MediaUtils.convertToCustomLayout(null)).isEmpty(); - - String extraKey = "key"; - String extraValue = "value"; - String actionStr = "action"; - String displayName = "display_name"; - int iconRes = 21; - - Bundle extras = new Bundle(); - extras.putString(extraKey, extraValue); - - PlaybackStateCompat.CustomAction action = - new PlaybackStateCompat.CustomAction.Builder(actionStr, displayName, iconRes) - .setExtras(extras) - .build(); - - PlaybackStateCompat state = - new PlaybackStateCompat.Builder() - .setState( - PlaybackStateCompat.STATE_NONE, - /* position= */ 0, - /* playbackSpeed= */ 1, - /* updateTime= */ 100) - .addCustomAction(action) - .build(); - - List buttons = MediaUtils.convertToCustomLayout(state); - assertThat(buttons).hasSize(1); - CommandButton button = buttons.get(0); - assertThat(button.displayName.toString()).isEqualTo(displayName); - assertThat(button.isEnabled).isTrue(); - assertThat(button.iconResId).isEqualTo(iconRes); - assertThat(button.sessionCommand.customAction).isEqualTo(actionStr); - assertThat(button.sessionCommand.customExtras.getString(extraKey)).isEqualTo(extraValue); - } - - @Test - public void convertToAudioAttributes() { - assertThat(MediaUtils.convertToAudioAttributes((AudioAttributesCompat) null)) - .isSameInstanceAs(AudioAttributes.DEFAULT); - assertThat(MediaUtils.convertToAudioAttributes((MediaControllerCompat.PlaybackInfo) null)) - .isSameInstanceAs(AudioAttributes.DEFAULT); - - int contentType = AudioAttributesCompat.CONTENT_TYPE_MUSIC; - int flags = AudioAttributesCompat.FLAG_AUDIBILITY_ENFORCED; - int usage = AudioAttributesCompat.USAGE_MEDIA; - AudioAttributesCompat aaCompat = - new AudioAttributesCompat.Builder() - .setContentType(contentType) - .setFlags(flags) - .setUsage(usage) - .build(); - AudioAttributes aa = - new AudioAttributes.Builder() - .setContentType(contentType) - .setFlags(flags) - .setUsage(usage) - .build(); - assertThat(MediaUtils.convertToAudioAttributes(aaCompat)).isEqualTo(aa); - assertThat(MediaUtils.convertToAudioAttributesCompat(aa)).isEqualTo(aaCompat); - } - - @Test - public void convertToCurrentPosition_byDefault_returnsZero() { - long currentPositionMs = - MediaUtils.convertToCurrentPositionMs( - /* playbackStateCompat= */ null, - /* metadataCompat= */ null, - /* timeDiffMs= */ C.TIME_UNSET); - assertThat(currentPositionMs).isEqualTo(0); - } - - @Test - public void convertToCurrentPositionMs_withNegativePosition_adjustsToZero() { - long testPositionMs = -100L; - PlaybackStateCompat state = - new PlaybackStateCompat.Builder() - .setState(PlaybackStateCompat.STATE_PAUSED, testPositionMs, /* playbackSpeed= */ 1.0f) - .build(); - long currentPositionMs = - MediaUtils.convertToCurrentPositionMs( - state, /* metadataCompat= */ null, /* timeDiffMs= */ C.TIME_UNSET); - assertThat(currentPositionMs).isEqualTo(0); - } - - @Test - public void convertToCurrentPositionMs_withGreaterThanDuration_adjustsToDuration() { - long testDurationMs = 100L; - long testPositionMs = 200L; - MediaMetadataCompat metadata = - new MediaMetadataCompat.Builder().putLong(METADATA_KEY_DURATION, testDurationMs).build(); - PlaybackStateCompat state = - new PlaybackStateCompat.Builder() - .setState(PlaybackStateCompat.STATE_PAUSED, testPositionMs, /* playbackSpeed= */ 1.0f) - .build(); - long currentPositionMs = - MediaUtils.convertToCurrentPositionMs(state, metadata, /* timeDiffMs= */ C.TIME_UNSET); - assertThat(currentPositionMs).isEqualTo(testDurationMs); - } - - @Test - public void convertToDurationMs() { - long testDurationMs = 100L; - MediaMetadataCompat metadata = - new MediaMetadataCompat.Builder().putLong(METADATA_KEY_DURATION, testDurationMs).build(); - long currentPositionMs = MediaUtils.convertToDurationMs(metadata); - assertThat(currentPositionMs).isEqualTo(testDurationMs); - } - - @Test - public void convertToDurationMs_withNegativeDuration_returnsTimeUnset() { - long testDurationMs = -100L; - MediaMetadataCompat metadata = - new MediaMetadataCompat.Builder().putLong(METADATA_KEY_DURATION, testDurationMs).build(); - long currentPositionMs = MediaUtils.convertToDurationMs(metadata); - assertThat(currentPositionMs).isEqualTo(C.TIME_UNSET); - } - - @Test - public void convertToBufferedPositionMs() { - long testPositionMs = 300L; - long testBufferedPositionMs = 331L; - PlaybackStateCompat state = - new PlaybackStateCompat.Builder() - .setState(PlaybackStateCompat.STATE_PAUSED, testPositionMs, /* playbackSpeed= */ 1.0f) - .setBufferedPosition(testBufferedPositionMs) - .build(); - - long bufferedPositionMs = - MediaUtils.convertToBufferedPositionMs( - state, /* metadataCompat= */ null, /* timeDiffMs= */ C.TIME_UNSET); - assertThat(bufferedPositionMs).isEqualTo(testBufferedPositionMs); - } - - @Test - public void convertToBufferedPositionMs_withLessThanPosition_adjustsToPosition() { - long testPositionMs = 300L; - long testBufferedPositionMs = 100L; - PlaybackStateCompat state = - new PlaybackStateCompat.Builder() - .setState(PlaybackStateCompat.STATE_PAUSED, testPositionMs, /* playbackSpeed= */ 1.0f) - .setBufferedPosition(testBufferedPositionMs) - .build(); - long bufferedPositionMs = - MediaUtils.convertToBufferedPositionMs( - state, /* metadataCompat= */ null, /* timeDiffMs= */ C.TIME_UNSET); - assertThat(bufferedPositionMs).isEqualTo(testPositionMs); - } - - @Test - public void convertToBufferedPositionMs_withLessThanPositionAndWithTimeDiff_adjustsToPosition() { - long testPositionMs = 200L; - long testBufferedPositionMs = 100L; - long testTimeDiffMs = 100; - float testPlaybackSpeed = 1.0f; - long expectedPositionMs = testPositionMs + (long) (testPlaybackSpeed * testTimeDiffMs); - PlaybackStateCompat state = - new PlaybackStateCompat.Builder() - .setState(PlaybackStateCompat.STATE_PLAYING, testPositionMs, testPlaybackSpeed) - .setBufferedPosition(testBufferedPositionMs) - .build(); - long bufferedPositionMs = - MediaUtils.convertToBufferedPositionMs(state, /* metadataCompat= */ null, testTimeDiffMs); - assertThat(bufferedPositionMs).isEqualTo(expectedPositionMs); - } - - @Test - public void convertToBufferedPositionMs_withGreaterThanDuration_adjustsToDuration() { - long testDurationMs = 100L; - long testBufferedPositionMs = 200L; - MediaMetadataCompat metadata = - new MediaMetadataCompat.Builder().putLong(METADATA_KEY_DURATION, testDurationMs).build(); - PlaybackStateCompat state = - new PlaybackStateCompat.Builder().setBufferedPosition(testBufferedPositionMs).build(); - long bufferedPositionMs = - MediaUtils.convertToBufferedPositionMs(state, metadata, /* timeDiffMs= */ C.TIME_UNSET); - assertThat(bufferedPositionMs).isEqualTo(testDurationMs); - } - - @Test - public void convertToTotalBufferedDurationMs() { - long testCurrentPositionMs = 224L; - long testBufferedPositionMs = 331L; - long testTotalBufferedDurationMs = testBufferedPositionMs - testCurrentPositionMs; - PlaybackStateCompat state = - new PlaybackStateCompat.Builder() - .setState( - PlaybackStateCompat.STATE_PAUSED, testCurrentPositionMs, /* playbackSpeed= */ 1.0f) - .setBufferedPosition(testBufferedPositionMs) - .build(); - - long totalBufferedDurationMs = - MediaUtils.convertToTotalBufferedDurationMs( - state, /* metadataCompat= */ null, /* timeDiffMs= */ C.INDEX_UNSET); - assertThat(totalBufferedDurationMs).isEqualTo(testTotalBufferedDurationMs); - } - @Test public void mergePlayerInfo_timelineAndTracksExcluded_correctMerge() { Timeline timeline = diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaTestUtils.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaTestUtils.java index 4619695176..3eaa366f9c 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaTestUtils.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaTestUtils.java @@ -241,8 +241,9 @@ public final class MediaTestUtils { List list = new ArrayList<>(); for (int i = 0; i < mediaItems.size(); i++) { MediaItem item = mediaItems.get(i); - MediaDescriptionCompat description = MediaUtils.convertToMediaDescriptionCompat(item, null); - long id = MediaUtils.convertToQueueItemId(i); + MediaDescriptionCompat description = + LegacyConversions.convertToMediaDescriptionCompat(item, null); + long id = LegacyConversions.convertToQueueItemId(i); list.add(new MediaSessionCompat.QueueItem(description, id)); } return list;