| /* |
| * 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.media; |
| |
| import static androidx.media.AudioAttributesCompat.CONTENT_TYPE_UNKNOWN; |
| import static androidx.media.AudioAttributesCompat.USAGE_UNKNOWN; |
| import static androidx.media.MediaMetadata2.METADATA_KEY_DISPLAY_DESCRIPTION; |
| import static androidx.media.MediaMetadata2.METADATA_KEY_DISPLAY_ICON; |
| import static androidx.media.MediaMetadata2.METADATA_KEY_DISPLAY_ICON_URI; |
| import static androidx.media.MediaMetadata2.METADATA_KEY_DISPLAY_SUBTITLE; |
| import static androidx.media.MediaMetadata2.METADATA_KEY_DISPLAY_TITLE; |
| import static androidx.media.MediaMetadata2.METADATA_KEY_EXTRAS; |
| import static androidx.media.MediaMetadata2.METADATA_KEY_MEDIA_ID; |
| import static androidx.media.MediaMetadata2.METADATA_KEY_MEDIA_URI; |
| import static androidx.media.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.PlaybackStateCompat; |
| |
| import androidx.media.MediaSession2.CommandButton; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| class MediaUtils2 { |
| static final String AUDIO_ATTRIBUTES_USAGE = "androidx.media.audio_attrs.USAGE"; |
| static final String AUDIO_ATTRIBUTES_CONTENT_TYPE = "androidx.media.audio_attrs.CONTENT_TYPE"; |
| static final String AUDIO_ATTRIBUTES_FLAGS = "androidx.media.audio_attrs.FLAGS"; |
| |
| private MediaUtils2() { |
| } |
| |
| /** |
| * Creates a {@link MediaItem} from the {@link MediaItem2}. |
| * |
| * @param item2 an item. |
| * @return The newly created media item. |
| */ |
| static MediaItem createMediaItem(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()); |
| } |
| |
| /** |
| * Creates a {@link MediaItem2} from the {@link MediaItem}. |
| * |
| * @param item an item. |
| * @return The newly created media item. |
| */ |
| static MediaItem2 createMediaItem2(MediaItem item) { |
| if (item == null || item.getMediaId() == null) { |
| return null; |
| } |
| |
| MediaMetadata2 metadata2 = createMediaMetadata2(item.getDescription()); |
| return new MediaItem2.Builder(item.getFlags()) |
| .setMediaId(item.getMediaId()) |
| .setMetadata(metadata2) |
| .build(); |
| } |
| |
| /** |
| * Creates a {@link MediaMetadata2} from the {@link MediaDescriptionCompat}. |
| * |
| * @param descCompat A {@link MediaDescriptionCompat} object. |
| * @return The newly created {@link MediaMetadata2} object. |
| */ |
| static MediaMetadata2 createMediaMetadata2(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. |
| */ |
| MediaMetadata2 createMediaMetadata2(MediaMetadataCompat metadataCompat) { |
| if (metadataCompat == null) { |
| return null; |
| } |
| return new MediaMetadata2(metadataCompat.getBundle()); |
| } |
| |
| /** |
| * Creates a {@link MediaMetadataCompat} from the {@link MediaMetadata2}. |
| * |
| * @param metadata2 A {@link MediaMetadata2} object. |
| * @return The newly created {@link MediaMetadataCompat} object. |
| */ |
| MediaMetadataCompat createMediaMetadataCompat(MediaMetadata2 metadata2) { |
| if (metadata2 == null) { |
| return null; |
| } |
| |
| MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(); |
| |
| List<String> skippedKeys = new ArrayList<>(); |
| 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, createRatingCompat((Rating2) value)); |
| } else if (value instanceof Bitmap) { |
| builder.putBitmap(key, (Bitmap) value); |
| } else if (value instanceof Long) { |
| builder.putLong(key, (Long) value); |
| } else { |
| // There is no 'float' or 'bundle' type in MediaMetadataCompat. |
| skippedKeys.add(key); |
| } |
| } |
| |
| MediaMetadataCompat result = builder.build(); |
| for (String key : skippedKeys) { |
| Object value = bundle.get(key); |
| if (value instanceof Float) { |
| // Compatibility for MediaMetadata2.Builder.putFloat() |
| result.getBundle().putFloat(key, (Float) value); |
| } else if (METADATA_KEY_EXTRAS.equals(value)) { |
| // Compatibility for MediaMetadata2.Builder.setExtras() |
| result.getBundle().putBundle(key, (Bundle) value); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Creates a {@link Rating2} from the {@link RatingCompat}. |
| * |
| * @param ratingCompat A {@link RatingCompat} object. |
| * @return The newly created {@link Rating2} object. |
| */ |
| Rating2 createRating2(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. |
| */ |
| RatingCompat createRatingCompat(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 Parcelable[] toMediaItem2ParcelableArray(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 List<MediaItem2> fromMediaItem2ParcelableArray(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 Parcelable[] toCommandButtonParcelableArray(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> fromCommandButtonParcelableArray(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 Bundle toAudioAttributesBundle(AudioAttributesCompat attrs) { |
| if (attrs == null) { |
| return null; |
| } |
| Bundle bundle = new Bundle(); |
| bundle.putInt(AUDIO_ATTRIBUTES_USAGE, attrs.getUsage()); |
| bundle.putInt(AUDIO_ATTRIBUTES_CONTENT_TYPE, attrs.getContentType()); |
| bundle.putInt(AUDIO_ATTRIBUTES_FLAGS, attrs.getFlags()); |
| return bundle; |
| } |
| |
| static AudioAttributesCompat fromAudioAttributesBundle(Bundle bundle) { |
| if (bundle == null) { |
| return null; |
| } |
| return new AudioAttributesCompat.Builder() |
| .setUsage(bundle.getInt(AUDIO_ATTRIBUTES_USAGE, USAGE_UNKNOWN)) |
| .setContentType(bundle.getInt(AUDIO_ATTRIBUTES_CONTENT_TYPE, CONTENT_TYPE_UNKNOWN)) |
| .setFlags(bundle.getInt(AUDIO_ATTRIBUTES_FLAGS, 0)) |
| .build(); |
| } |
| |
| static List<Bundle> toBundleList(Parcelable[] array) { |
| if (array == null) { |
| return null; |
| } |
| List<Bundle> bundleList = new ArrayList<>(); |
| for (Parcelable p : array) { |
| bundleList.add((Bundle) p); |
| } |
| return bundleList; |
| } |
| |
| static int createPlaybackStateCompatState(int playerState, int bufferingState) { |
| switch (playerState) { |
| case MediaPlayerBase.PLAYER_STATE_PLAYING: |
| switch (bufferingState) { |
| case MediaPlayerBase.BUFFERING_STATE_BUFFERING_AND_STARVED: |
| return PlaybackStateCompat.STATE_BUFFERING; |
| } |
| return PlaybackStateCompat.STATE_PLAYING; |
| case MediaPlayerBase.PLAYER_STATE_PAUSED: |
| return PlaybackStateCompat.STATE_PAUSED; |
| case MediaPlayerBase.PLAYER_STATE_IDLE: |
| return PlaybackStateCompat.STATE_NONE; |
| case MediaPlayerBase.PLAYER_STATE_ERROR: |
| return PlaybackStateCompat.STATE_ERROR; |
| } |
| // For unknown value |
| return PlaybackStateCompat.STATE_ERROR; |
| } |
| |
| static int toPlayerState(int playbackStateCompatState) { |
| switch (playbackStateCompatState) { |
| case PlaybackStateCompat.STATE_ERROR: |
| return MediaPlayerBase.PLAYER_STATE_ERROR; |
| case PlaybackStateCompat.STATE_NONE: |
| return MediaPlayerBase.PLAYER_STATE_IDLE; |
| case PlaybackStateCompat.STATE_PAUSED: |
| case PlaybackStateCompat.STATE_STOPPED: |
| case PlaybackStateCompat.STATE_BUFFERING: // means paused for buffering. |
| return MediaPlayerBase.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 MediaPlayerBase.PLAYER_STATE_PLAYING; |
| } |
| return MediaPlayerBase.PLAYER_STATE_ERROR; |
| } |
| |
| static boolean isDefaultLibraryRootHint(Bundle bundle) { |
| return bundle != null && bundle.getBoolean(MediaConstants2.ROOT_EXTRA_DEFAULT, false); |
| } |
| } |