/*
* Copyright 2018 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.media2;
import static androidx.media2.MediaItem2.FLAG_PLAYABLE;
import static androidx.media2.MediaMetadata2.METADATA_KEY_DISPLAY_DESCRIPTION;
import static androidx.media2.MediaMetadata2.METADATA_KEY_DISPLAY_ICON;
import static androidx.media2.MediaMetadata2.METADATA_KEY_DISPLAY_ICON_URI;
import static androidx.media2.MediaMetadata2.METADATA_KEY_DISPLAY_SUBTITLE;
import static androidx.media2.MediaMetadata2.METADATA_KEY_DISPLAY_TITLE;
import static androidx.media2.MediaMetadata2.METADATA_KEY_MEDIA_ID;
import static androidx.media2.MediaMetadata2.METADATA_KEY_MEDIA_URI;
import static androidx.media2.MediaMetadata2.METADATA_KEY_TITLE;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.media.MediaBrowserCompat.MediaItem;
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 androidx.annotation.NonNull;
import androidx.media.AudioAttributesCompat;
import androidx.media.MediaBrowserServiceCompat.BrowserRoot;
import androidx.media2.MediaSession2.CommandButton;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
class MediaUtils2 {
static final String TAG = "MediaUtils2";
// Stub BrowserRoot for accepting any connection here.
// See MyBrowserService#onGetRoot() for detail.
static final BrowserRoot sDefaultBrowserRoot =
new BrowserRoot(MediaLibraryService2.SERVICE_INTERFACE, null);
private MediaUtils2() {
}
/**
* Creates a {@link MediaItem} from the {@link MediaItem2}.
*
* @param item2 an item.
* @return The newly created media item.
*/
static MediaItem convertToMediaItem(MediaItem2 item2) {
if (item2 == null) {
return null;
}
MediaDescriptionCompat descCompat;
MediaMetadata2 metadata = item2.getMetadata();
if (metadata == null) {
descCompat = new MediaDescriptionCompat.Builder()
.setMediaId(item2.getMediaId())
.build();
} else {
MediaDescriptionCompat.Builder builder = new MediaDescriptionCompat.Builder()
.setMediaId(item2.getMediaId())
.setSubtitle(metadata.getText(METADATA_KEY_DISPLAY_SUBTITLE))
.setDescription(metadata.getText(METADATA_KEY_DISPLAY_DESCRIPTION))
.setIconBitmap(metadata.getBitmap(METADATA_KEY_DISPLAY_ICON))
.setExtras(metadata.getExtras());
String title = metadata.getString(METADATA_KEY_TITLE);
if (title != null) {
builder.setTitle(title);
} else {
builder.setTitle(metadata.getString(METADATA_KEY_DISPLAY_TITLE));
}
String displayIconUri = metadata.getString(METADATA_KEY_DISPLAY_ICON_URI);
if (displayIconUri != null) {
builder.setIconUri(Uri.parse(displayIconUri));
}
String mediaUri = metadata.getString(METADATA_KEY_MEDIA_URI);
if (mediaUri != null) {
builder.setMediaUri(Uri.parse(mediaUri));
}
descCompat = builder.build();
}
return new MediaItem(descCompat, item2.getFlags());
}
static List<MediaItem> convertToMediaItemList(List<MediaItem2> items) {
if (items == null) {
return null;
}
List<MediaItem> result = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
result.add(convertToMediaItem(items.get(i)));
}
return result;
}
/**
* Creates a {@link MediaItem2} from the {@link MediaItem}.
*
* @param item an item.
* @return The newly created media item.
*/
static MediaItem2 convertToMediaItem2(MediaItem item) {
if (item == null || item.getMediaId() == null) {
return null;
}
MediaMetadata2 metadata2 = convertToMediaMetadata2(item.getDescription());
return new MediaItem2.Builder(item.getFlags())
.setMediaId(item.getMediaId())
.setMetadata(metadata2)
.build();
}
static MediaItem2 convertToMediaItem2(@NonNull QueueItem item) {
if (item == null) {
throw new IllegalArgumentException("item shouldn't be null");
}
// descriptionCompat cannot be null
MediaDescriptionCompat descriptionCompat = item.getDescription();
MediaMetadata2 metadata2 = convertToMediaMetadata2(descriptionCompat);
return new MediaItem2.Builder(FLAG_PLAYABLE).setMetadata(metadata2)
.setUuid(createUuidByQueueIdAndMediaId(item.getQueueId(),
descriptionCompat.getMediaId()))
.build();
}
static UUID createUuidByQueueIdAndMediaId(long queueId, String mediaId) {
return new UUID(queueId, (mediaId == null) ? 0 : mediaId.hashCode());
}
static MediaItem2 convertToMediaItem2(MediaMetadataCompat metadataCompat) {
MediaMetadata2 metadata2 = convertToMediaMetadata2(metadataCompat);
if (metadata2 == null || metadata2.getMediaId() == null) {
return null;
}
return new MediaItem2.Builder(FLAG_PLAYABLE).setMetadata(metadata2).build();
}
static List<MediaItem2> convertToMediaItem2List(Parcelable[] itemParcelableList) {
List<MediaItem2> playlist = new ArrayList<>();
if (itemParcelableList != null) {
for (int i = 0; i < itemParcelableList.length; i++) {
if (!(itemParcelableList[i] instanceof Bundle)) {
continue;
}
MediaItem2 item = MediaItem2.fromBundle((Bundle) itemParcelableList[i]);
if (item != null) {
playlist.add(item);
}
}
}
return playlist;
}
static List<MediaItem2> convertMediaItemListToMediaItem2List(List<MediaItem> items) {
if (items == null) {
return null;
}
List<MediaItem2> result = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
result.add(convertToMediaItem2(items.get(i)));
}
return result;
}
static List<MediaItem2> convertBundleListToMediaItem2List(List<Bundle> itemBundleList) {
if (itemBundleList == null) {
return null;
}
List<MediaItem2> playlist = new ArrayList<>();
for (int i = 0; i < itemBundleList.size(); i++) {
final Bundle itemBundle = itemBundleList.get(i);
if (itemBundle != null) {
playlist.add(MediaItem2.fromBundle(itemBundle));
}
}
return playlist;
}
static List<MediaItem2> convertQueueItemListToMediaItem2List(List<QueueItem> items) {
if (items == null) {
return null;
}
List<MediaItem2> result = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
result.add(convertToMediaItem2(items.get(i)));
}
return result;
}
static QueueItem convertToQueueItem(MediaItem2 item) {
MediaDescriptionCompat description = (item.getMetadata() == null)
? new MediaDescriptionCompat.Builder().setMediaId(item.getMediaId()).build()
: convertToMediaMetadataCompat(item.getMetadata()).getDescription();
return new QueueItem(description, item.getUuid().getMostSignificantBits());
}
static List<QueueItem> convertToQueueItemList(List<MediaItem2> items) {
if (items == null) {
return null;
}
List<QueueItem> result = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
result.add(convertToQueueItem(items.get(i)));
}
return result;
}
/**
* Creates a {@link MediaMetadata2} from the {@link MediaDescriptionCompat}.
*
* @param descCompat A {@link MediaDescriptionCompat} object.
* @return The newly created {@link MediaMetadata2} object.
*/
static MediaMetadata2 convertToMediaMetadata2(MediaDescriptionCompat descCompat) {
if (descCompat == null) {
return null;
}
MediaMetadata2.Builder metadata2Builder = new MediaMetadata2.Builder();
metadata2Builder.putString(METADATA_KEY_MEDIA_ID, descCompat.getMediaId());
CharSequence title = descCompat.getTitle();
if (title != null) {
metadata2Builder.putText(METADATA_KEY_DISPLAY_TITLE, title);
}
CharSequence description = descCompat.getDescription();
if (description != null) {
metadata2Builder.putText(METADATA_KEY_DISPLAY_DESCRIPTION, descCompat.getDescription());
}
CharSequence subtitle = descCompat.getSubtitle();
if (subtitle != null) {
metadata2Builder.putText(METADATA_KEY_DISPLAY_SUBTITLE, subtitle);
}
Bitmap icon = descCompat.getIconBitmap();
if (icon != null) {
metadata2Builder.putBitmap(METADATA_KEY_DISPLAY_ICON, icon);
}
Uri iconUri = descCompat.getIconUri();
if (iconUri != null) {
metadata2Builder.putText(METADATA_KEY_DISPLAY_ICON_URI, iconUri.toString());
}
Bundle bundle = descCompat.getExtras();
if (bundle != null) {
metadata2Builder.setExtras(descCompat.getExtras());
}
Uri mediaUri = descCompat.getMediaUri();
if (mediaUri != null) {
metadata2Builder.putText(METADATA_KEY_MEDIA_URI, mediaUri.toString());
}
return metadata2Builder.build();
}
/**
* Creates a {@link MediaMetadata2} from the {@link MediaMetadataCompat}.
*
* @param metadataCompat A {@link MediaMetadataCompat} object.
* @return The newly created {@link MediaMetadata2} object.
*/
static MediaMetadata2 convertToMediaMetadata2(MediaMetadataCompat metadataCompat) {
if (metadataCompat == null) {
return null;
}
return new MediaMetadata2(metadataCompat.getBundle());
}
static MediaMetadata2 convertToMediaMetadata2(CharSequence queueTitle) {
if (queueTitle == null) {
return null;
}
return new MediaMetadata2.Builder()
.putString(METADATA_KEY_TITLE, queueTitle.toString()).build();
}
/**
* Creates a {@link MediaMetadataCompat} from the {@link MediaMetadata2}.
*
* @param metadata2 A {@link MediaMetadata2} object.
* @return The newly created {@link MediaMetadataCompat} object.
*/
static MediaMetadataCompat convertToMediaMetadataCompat(MediaMetadata2 metadata2) {
if (metadata2 == null) {
return null;
}
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
Bundle bundle = metadata2.toBundle();
for (String key : bundle.keySet()) {
Object value = bundle.get(key);
if (value instanceof CharSequence) {
builder.putText(key, (CharSequence) value);
} else if (value instanceof Rating2) {
builder.putRating(key, convertToRatingCompat((Rating2) value));
} else if (value instanceof Bitmap) {
builder.putBitmap(key, (Bitmap) value);
} else if (value instanceof Long) {
builder.putLong(key, (Long) value);
}
}
return builder.build();
}
static MediaMetadataCompat convertToMediaMetadataCompat(MediaDescriptionCompat description) {
return convertToMediaMetadataCompat(convertToMediaMetadata2(description));
}
/**
* Creates a {@link Rating2} from the {@link RatingCompat}.
*
* @param ratingCompat A {@link RatingCompat} object.
* @return The newly created {@link Rating2} object.
*/
static Rating2 convertToRating2(RatingCompat ratingCompat) {
if (ratingCompat == null) {
return null;
}
if (!ratingCompat.isRated()) {
return Rating2.newUnratedRating(ratingCompat.getRatingStyle());
}
switch (ratingCompat.getRatingStyle()) {
case RatingCompat.RATING_3_STARS:
case RatingCompat.RATING_4_STARS:
case RatingCompat.RATING_5_STARS:
return Rating2.newStarRating(
ratingCompat.getRatingStyle(), ratingCompat.getStarRating());
case RatingCompat.RATING_HEART:
return Rating2.newHeartRating(ratingCompat.hasHeart());
case RatingCompat.RATING_THUMB_UP_DOWN:
return Rating2.newThumbRating(ratingCompat.isThumbUp());
case RatingCompat.RATING_PERCENTAGE:
return Rating2.newPercentageRating(ratingCompat.getPercentRating());
default:
return null;
}
}
/**
* Creates a {@link RatingCompat} from the {@link Rating2}.
*
* @param rating2 A {@link Rating2} object.
* @return The newly created {@link RatingCompat} object.
*/
static RatingCompat convertToRatingCompat(Rating2 rating2) {
if (rating2 == null) {
return null;
}
if (!rating2.isRated()) {
return RatingCompat.newUnratedRating(rating2.getRatingStyle());
}
switch (rating2.getRatingStyle()) {
case Rating2.RATING_3_STARS:
case Rating2.RATING_4_STARS:
case Rating2.RATING_5_STARS:
return RatingCompat.newStarRating(
rating2.getRatingStyle(), rating2.getStarRating());
case Rating2.RATING_HEART:
return RatingCompat.newHeartRating(rating2.hasHeart());
case Rating2.RATING_THUMB_UP_DOWN:
return RatingCompat.newThumbRating(rating2.isThumbUp());
case Rating2.RATING_PERCENTAGE:
return RatingCompat.newPercentageRating(rating2.getPercentRating());
default:
return null;
}
}
static List<Bundle> convertToBundleList(Parcelable[] array) {
if (array == null) {
return null;
}
List<Bundle> bundleList = new ArrayList<>();
for (Parcelable p : array) {
bundleList.add((Bundle) p);
}
return bundleList;
}
static List<Bundle> convertMediaItem2ListToBundleList(List<MediaItem2> playlist) {
if (playlist == null) {
return null;
}
List<Bundle> itemBundleList = new ArrayList<>();
for (int i = 0; i < playlist.size(); i++) {
final MediaItem2 item = playlist.get(i);
if (item != null) {
final Bundle itemBundle = item.toBundle();
if (itemBundle != null) {
itemBundleList.add(itemBundle);
}
}
}
return itemBundleList;
}
static List<Bundle> convertCommandButtonListToBundleList(
List<CommandButton> commandButtonList) {
List<Bundle> commandButtonBundleList = new ArrayList<>();
for (int i = 0; i < commandButtonList.size(); i++) {
Bundle bundle = commandButtonList.get(i).toBundle();
if (bundle != null) {
commandButtonBundleList.add(bundle);
}
}
return commandButtonBundleList;
}
static Parcelable[] convertMediaItem2ListToParcelableArray(List<MediaItem2> playlist) {
if (playlist == null) {
return null;
}
List<Parcelable> parcelableList = new ArrayList<>();
for (int i = 0; i < playlist.size(); i++) {
final MediaItem2 item = playlist.get(i);
if (item != null) {
final Parcelable itemBundle = item.toBundle();
if (itemBundle != null) {
parcelableList.add(itemBundle);
}
}
}
return parcelableList.toArray(new Parcelable[0]);
}
static Parcelable[] convertCommandButtonListToParcelableArray(List<CommandButton> layout) {
if (layout == null) {
return null;
}
List<Bundle> layoutBundles = new ArrayList<>();
for (int i = 0; i < layout.size(); i++) {
Bundle bundle = layout.get(i).toBundle();
if (bundle != null) {
layoutBundles.add(bundle);
}
}
return layoutBundles.toArray(new Parcelable[0]);
}
static List<CommandButton> convertToCommandButtonList(List<Bundle> commandButtonBundleList) {
List<CommandButton> commandButtonList = new ArrayList<>();
for (int i = 0; i < commandButtonBundleList.size(); i++) {
Bundle bundle = commandButtonBundleList.get(i);
if (bundle != null) {
commandButtonList.add(CommandButton.fromBundle(bundle));
}
}
return commandButtonList;
}
static List<CommandButton> convertToCommandButtonList(Parcelable[] list) {
List<CommandButton> layout = new ArrayList<>();
if (layout != null) {
for (int i = 0; i < list.length; i++) {
if (!(list[i] instanceof Bundle)) {
continue;
}
CommandButton button = CommandButton.fromBundle((Bundle) list[i]);
if (button != null) {
layout.add(button);
}
}
}
return layout;
}
static int convertToPlaybackStateCompatState(int playerState, int bufferingState) {
switch (playerState) {
case BaseMediaPlayer.PLAYER_STATE_PLAYING:
switch (bufferingState) {
case BaseMediaPlayer.BUFFERING_STATE_BUFFERING_AND_STARVED:
return PlaybackStateCompat.STATE_BUFFERING;
}
return PlaybackStateCompat.STATE_PLAYING;
case BaseMediaPlayer.PLAYER_STATE_PAUSED:
return PlaybackStateCompat.STATE_PAUSED;
case BaseMediaPlayer.PLAYER_STATE_IDLE:
return PlaybackStateCompat.STATE_NONE;
case BaseMediaPlayer.PLAYER_STATE_ERROR:
return PlaybackStateCompat.STATE_ERROR;
}
// For unknown value
return PlaybackStateCompat.STATE_ERROR;
}
static int convertToPlayerState(int playbackStateCompatState) {
switch (playbackStateCompatState) {
case PlaybackStateCompat.STATE_ERROR:
return BaseMediaPlayer.PLAYER_STATE_ERROR;
case PlaybackStateCompat.STATE_NONE:
return BaseMediaPlayer.PLAYER_STATE_IDLE;
case PlaybackStateCompat.STATE_PAUSED:
case PlaybackStateCompat.STATE_STOPPED:
case PlaybackStateCompat.STATE_BUFFERING: // means paused for buffering.
return BaseMediaPlayer.PLAYER_STATE_PAUSED;
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:
case PlaybackStateCompat.STATE_CONNECTING: // Note: there's no perfect match for this.
return BaseMediaPlayer.PLAYER_STATE_PLAYING;
}
return BaseMediaPlayer.PLAYER_STATE_ERROR;
}
// Note: there's no perfect match for this.
static int toBufferingState(int playbackStateCompatState) {
switch (playbackStateCompatState) {
case PlaybackStateCompat.STATE_BUFFERING:
return BaseMediaPlayer.BUFFERING_STATE_BUFFERING_AND_STARVED;
case PlaybackStateCompat.STATE_PLAYING:
return BaseMediaPlayer.BUFFERING_STATE_BUFFERING_COMPLETE;
default:
return BaseMediaPlayer.BUFFERING_STATE_UNKNOWN;
}
}
static MediaController2.PlaybackInfo toPlaybackInfo2(MediaControllerCompat.PlaybackInfo info) {
return MediaController2.PlaybackInfo.createPlaybackInfo(info.getPlaybackType(),
new AudioAttributesCompat.Builder()
.setLegacyStreamType(info.getAudioStream()).build(),
info.getVolumeControl(), info.getMaxVolume(), info.getCurrentVolume());
}
static boolean isDefaultLibraryRootHint(Bundle bundle) {
return bundle != null && bundle.getBoolean(MediaConstants2.ROOT_EXTRA_DEFAULT, false);
}
static Bundle createBundle(Bundle bundle) {
return (bundle == null) ? new Bundle() : new Bundle(bundle);
}
}