1 /* 2 * Copyright 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.media.testmediaapp; 18 19 import androidx.annotation.Nullable; 20 21 import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_PLAYABLE; 22 import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_DURATION; 23 import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_MEDIA_ID; 24 25 import static com.android.car.media.common.MediaConstants.CONTENT_STYLE_BROWSABLE_HINT; 26 import static com.android.car.media.common.MediaConstants.CONTENT_STYLE_GRID_ITEM_HINT_VALUE; 27 import static com.android.car.media.common.MediaConstants.CONTENT_STYLE_LIST_ITEM_HINT_VALUE; 28 import static com.android.car.media.common.MediaConstants.CONTENT_STYLE_PLAYABLE_HINT; 29 30 import android.os.Bundle; 31 import android.support.v4.media.MediaBrowserCompat.MediaItem; 32 import android.support.v4.media.MediaBrowserCompat.MediaItem.Flags; 33 import android.support.v4.media.MediaDescriptionCompat; 34 import android.support.v4.media.MediaMetadataCompat; 35 import android.support.v4.media.session.MediaSessionCompat; 36 import android.support.v4.media.session.MediaSessionCompat.QueueItem; 37 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.List; 41 42 /** Our internal representation of media items. */ 43 public class TmaMediaItem { 44 45 private static final String CUSTOM_ACTION_PREFIX = "com.android.car.media.testmediaapp."; 46 47 /** The name of each entry is the value used in the json file. */ 48 public enum ContentStyle { 49 NONE (0), 50 LIST (CONTENT_STYLE_LIST_ITEM_HINT_VALUE), 51 GRID (CONTENT_STYLE_GRID_ITEM_HINT_VALUE); 52 final int mBundleValue; ContentStyle(int value)53 ContentStyle(int value) { 54 mBundleValue = value; 55 } 56 } 57 58 public enum TmaCustomAction { 59 HEART_PLUS_PLUS(CUSTOM_ACTION_PREFIX + "heart_plus_plus", R.string.heart_plus_plus, 60 R.drawable.ic_heart_plus_plus), 61 HEART_LESS_LESS(CUSTOM_ACTION_PREFIX + "heart_less_less", R.string.heart_less_less, 62 R.drawable.ic_heart_less_less); 63 64 final String mId; 65 final int mNameId; 66 final int mIcon; 67 TmaCustomAction(String id, int name, int icon)68 TmaCustomAction(String id, int name, int icon) { 69 mId = id; 70 mNameId = name; 71 mIcon = icon; 72 } 73 74 } 75 76 private final @MediaItem.Flags int mFlags; 77 private final MediaMetadataCompat mMediaMetadata; 78 private final ContentStyle mPlayableStyle; 79 private final ContentStyle mBrowsableStyle; 80 81 /** Read only list. */ 82 final List<TmaMediaItem> mChildren; 83 /** Read only list. */ 84 private final List<TmaMediaItem> mPlayableChildren; 85 /** Read only list. */ 86 final List<TmaCustomAction> mCustomActions; 87 /** Read only list. Events triggered when starting the playback. */ 88 final List<TmaMediaEvent> mMediaEvents; 89 /** References another json file where to get extra children from. */ 90 final String mInclude; 91 92 private @Nullable TmaMediaItem mParent; 93 int mHearts; 94 95 TmaMediaItem(@lags int flags, ContentStyle playableStyle, ContentStyle browsableStyle, MediaMetadataCompat metadata, List<TmaCustomAction> customActions, List<TmaMediaEvent> mediaEvents, List<TmaMediaItem> children, String include)96 public TmaMediaItem(@Flags int flags, ContentStyle playableStyle, ContentStyle browsableStyle, 97 MediaMetadataCompat metadata, List<TmaCustomAction> customActions, 98 List<TmaMediaEvent> mediaEvents, 99 List<TmaMediaItem> children, String include) { 100 mFlags = flags; 101 mPlayableStyle = playableStyle; 102 mBrowsableStyle = browsableStyle; 103 mMediaMetadata = metadata; 104 mCustomActions = Collections.unmodifiableList(customActions); 105 mChildren = Collections.unmodifiableList(children); 106 mMediaEvents = Collections.unmodifiableList(mediaEvents); 107 mInclude = include; 108 List<TmaMediaItem> playableChildren = new ArrayList<>(children.size()); 109 for (TmaMediaItem child: mChildren) { 110 child.setParent(this); 111 if ((child.mFlags & FLAG_PLAYABLE) != 0) { 112 playableChildren.add(child); 113 } 114 } 115 mPlayableChildren = Collections.unmodifiableList(playableChildren); 116 } 117 setParent(@ullable TmaMediaItem parent)118 private void setParent(@Nullable TmaMediaItem parent) { 119 mParent = parent; 120 } 121 122 @Nullable getParent()123 TmaMediaItem getParent() { 124 return mParent; 125 } 126 getPlayableByIndex(long index)127 TmaMediaItem getPlayableByIndex(long index) { 128 return mPlayableChildren.get((int)index); 129 } 130 131 @Nullable getPrevious()132 TmaMediaItem getPrevious() { 133 if (mParent == null) return null; 134 List<TmaMediaItem> queueItems = mParent.mPlayableChildren; 135 int myIndex = queueItems.indexOf(this); 136 return (myIndex > 0) ? queueItems.get(myIndex - 1) : null; 137 } 138 139 @Nullable getNext()140 TmaMediaItem getNext() { 141 if (mParent == null) return null; 142 List<TmaMediaItem> queueItems = mParent.mPlayableChildren; 143 int myIndex = queueItems.indexOf(this); 144 return (myIndex < queueItems.size() - 1) ? queueItems.get(myIndex + 1) : null; 145 } 146 getMediaId()147 String getMediaId() { 148 return mMediaMetadata.getString(METADATA_KEY_MEDIA_ID); 149 } 150 151 /** Returns -1 if the duration key is unspecified or <= 0. */ getDuration()152 long getDuration() { 153 long result = mMediaMetadata.getLong(METADATA_KEY_DURATION); 154 if (result <= 0) return -1; 155 return result; 156 } 157 append(List<TmaMediaItem> children)158 TmaMediaItem append(List<TmaMediaItem> children) { 159 List<TmaMediaItem> allChildren = new ArrayList<>(mChildren.size() + children.size()); 160 allChildren.addAll(mChildren); 161 allChildren.addAll(children); 162 return new TmaMediaItem(mFlags, mPlayableStyle, mBrowsableStyle, mMediaMetadata, 163 mCustomActions, mMediaEvents, allChildren, null); 164 } 165 updateSessionMetadata(MediaSessionCompat session)166 void updateSessionMetadata(MediaSessionCompat session) { 167 session.setMetadata(mMediaMetadata); 168 } 169 toMediaItem()170 MediaItem toMediaItem() { 171 return new MediaItem(buildDescription(), mFlags); 172 } 173 buildQueue()174 List<QueueItem> buildQueue() { 175 int count = mPlayableChildren.size(); 176 List<QueueItem> queue = new ArrayList<>(count); 177 for (int i = 0 ; i < count; i++) { 178 TmaMediaItem child = mPlayableChildren.get(i); 179 queue.add(new QueueItem(child.buildDescription(), i)); 180 } 181 return queue; 182 } 183 184 /** Returns the id of the item in the queue. */ getQueueId()185 long getQueueId() { 186 if (mParent != null) { 187 int index = mParent.mPlayableChildren.indexOf(this); 188 if (index >= 0) return index; 189 } 190 return MediaSessionCompat.QueueItem.UNKNOWN_ID; 191 } 192 buildDescription()193 private MediaDescriptionCompat buildDescription() { 194 195 // Use the default media description but add our extras. 196 MediaDescriptionCompat metadataDescription = mMediaMetadata.getDescription(); 197 198 MediaDescriptionCompat.Builder bob = new MediaDescriptionCompat.Builder(); 199 bob.setMediaId(metadataDescription.getMediaId()); 200 bob.setTitle(metadataDescription.getTitle()); 201 bob.setSubtitle(metadataDescription.getSubtitle()); 202 bob.setDescription(metadataDescription.getDescription()); 203 bob.setIconBitmap(metadataDescription.getIconBitmap()); 204 bob.setIconUri(metadataDescription.getIconUri()); 205 bob.setMediaUri(metadataDescription.getMediaUri()); 206 207 Bundle extras = new Bundle(); 208 if (metadataDescription.getExtras() != null) { 209 extras.putAll(metadataDescription.getExtras()); 210 } 211 212 extras.putInt(CONTENT_STYLE_PLAYABLE_HINT, mPlayableStyle.mBundleValue); 213 extras.putInt(CONTENT_STYLE_BROWSABLE_HINT, mBrowsableStyle.mBundleValue); 214 215 bob.setExtras(extras); 216 return bob.build(); 217 } 218 } 219