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 android.os.Bundle; 26 import android.support.v4.media.MediaBrowserCompat.MediaItem; 27 import android.support.v4.media.MediaDescriptionCompat; 28 import android.support.v4.media.MediaMetadataCompat; 29 import android.support.v4.media.session.MediaSessionCompat; 30 import android.support.v4.media.session.MediaSessionCompat.QueueItem; 31 32 import java.util.ArrayList; 33 import java.util.Collections; 34 import java.util.List; 35 36 /** Our internal representation of media items. */ 37 public class TmaMediaItem { 38 39 private static final String CUSTOM_ACTION_PREFIX = "com.android.car.media.testmediaapp."; 40 41 /** The name of each entry is the value used in the json file. */ 42 public enum ContentStyle { 43 NONE (0), 44 LIST (MediaKeys.CONTENT_STYLE_LIST_ITEM_HINT_VALUE), 45 GRID (MediaKeys.CONTENT_STYLE_GRID_ITEM_HINT_VALUE), 46 LIST_CATEGORY(MediaKeys.CONTENT_STYLE_CATEGORY_LIST_ITEM_HINT_VALUE), 47 GRID_CATEGORY(MediaKeys.CONTENT_STYLE_CATEGORY_GRID_ITEM_HINT_VALUE); 48 final int mBundleValue; ContentStyle(int value)49 ContentStyle(int value) { 50 mBundleValue = value; 51 } 52 } 53 54 public enum TmaCustomAction { 55 HEART_PLUS_PLUS(CUSTOM_ACTION_PREFIX + "heart_plus_plus", R.string.heart_plus_plus, 56 R.drawable.ic_heart_plus_plus), 57 HEART_LESS_LESS(CUSTOM_ACTION_PREFIX + "heart_less_less", R.string.heart_less_less, 58 R.drawable.ic_heart_less_less), 59 REQUEST_LOCATION(CUSTOM_ACTION_PREFIX + "location", R.string.location, 60 R.drawable.ic_location); 61 62 final String mId; 63 final int mNameId; 64 final int mIcon; 65 TmaCustomAction(String id, int name, int icon)66 TmaCustomAction(String id, int name, int icon) { 67 mId = id; 68 mNameId = name; 69 mIcon = icon; 70 } 71 } 72 73 private final int mFlags; 74 private final MediaMetadataCompat mMediaMetadata; 75 private final ContentStyle mPlayableStyle; 76 private final ContentStyle mBrowsableStyle; 77 private final int mSelfUpdateMs; 78 79 80 /** Internally modifiable list (for includes). */ 81 private final List<TmaMediaItem> mChildren = new ArrayList<>(); 82 /** Internally modifiable list (for includes). */ 83 private final List<TmaMediaItem> mPlayableChildren = new ArrayList<>(); 84 /** Read only list. */ 85 final List<TmaCustomAction> mCustomActions; 86 /** Read only list. Events triggered when starting the playback. */ 87 final List<TmaMediaEvent> mMediaEvents; 88 /** References another json file where to get extra children from. */ 89 final String mInclude; 90 91 private @Nullable TmaMediaItem mParent; 92 int mHearts; 93 int mRevealCounter; 94 boolean mIsHidden = false; 95 96 TmaMediaItem(int flags, ContentStyle playableStyle, ContentStyle browsableStyle, MediaMetadataCompat metadata, int selfUpdateMs, List<TmaCustomAction> customActions, List<TmaMediaEvent> mediaEvents, List<TmaMediaItem> children, String include)97 public TmaMediaItem(int flags, ContentStyle playableStyle, ContentStyle browsableStyle, 98 MediaMetadataCompat metadata, int selfUpdateMs, 99 List<TmaCustomAction> customActions, List<TmaMediaEvent> mediaEvents, 100 List<TmaMediaItem> children, String include) { 101 mFlags = flags; 102 mPlayableStyle = playableStyle; 103 mBrowsableStyle = browsableStyle; 104 mMediaMetadata = metadata; 105 mSelfUpdateMs = selfUpdateMs; 106 mCustomActions = Collections.unmodifiableList(customActions); 107 mMediaEvents = Collections.unmodifiableList(mediaEvents); 108 mInclude = include; 109 setChildren(children); 110 } 111 setParent(@ullable TmaMediaItem parent)112 private void setParent(@Nullable TmaMediaItem parent) { 113 mParent = parent; 114 } 115 getSelfUpdateDelay()116 int getSelfUpdateDelay() { 117 return mSelfUpdateMs; 118 } 119 getChildren()120 List<TmaMediaItem> getChildren() { 121 return Collections.unmodifiableList(mChildren); 122 } 123 124 @Nullable getParent()125 TmaMediaItem getParent() { 126 return mParent; 127 } 128 129 @Nullable getPlayableByIndex(long index)130 TmaMediaItem getPlayableByIndex(long index) { 131 if (index < 0 || index >= mPlayableChildren.size()) { 132 return null; 133 } 134 return mPlayableChildren.get((int)index); 135 } 136 137 @Nullable getPrevious()138 TmaMediaItem getPrevious() { 139 if (mParent == null) return null; 140 List<TmaMediaItem> queueItems = mParent.mPlayableChildren; 141 int myIndex = queueItems.indexOf(this); 142 return (myIndex > 0) ? queueItems.get(myIndex - 1) : null; 143 } 144 145 @Nullable getNext()146 TmaMediaItem getNext() { 147 if (mParent == null) return null; 148 List<TmaMediaItem> queueItems = mParent.mPlayableChildren; 149 int myIndex = queueItems.indexOf(this); 150 return (myIndex < queueItems.size() - 1) ? queueItems.get(myIndex + 1) : null; 151 } 152 getMediaId()153 String getMediaId() { 154 return mMediaMetadata.getString(METADATA_KEY_MEDIA_ID); 155 } 156 157 /** Returns -1 if the duration key is unspecified or <= 0. */ getDuration()158 long getDuration() { 159 long result = mMediaMetadata.getLong(METADATA_KEY_DURATION); 160 if (result <= 0) return -1; 161 return result; 162 } 163 setChildren(List<TmaMediaItem> children)164 void setChildren(List<TmaMediaItem> children) { 165 mChildren.clear(); 166 mChildren.addAll(children); 167 168 List<TmaMediaItem> playableChildren = new ArrayList<>(children.size()); 169 for (TmaMediaItem child: mChildren) { 170 child.setParent(this); 171 if ((child.mFlags & FLAG_PLAYABLE) != 0) { 172 playableChildren.add(child); 173 } 174 } 175 mPlayableChildren.clear(); 176 mPlayableChildren.addAll(playableChildren); 177 } 178 updateSessionMetadata(MediaSessionCompat session)179 void updateSessionMetadata(MediaSessionCompat session) { 180 session.setMetadata(mMediaMetadata); 181 } 182 toMediaItem()183 MediaItem toMediaItem() { 184 return new MediaItem(buildDescription(), mFlags); 185 } 186 buildQueue()187 List<QueueItem> buildQueue() { 188 int count = mPlayableChildren.size(); 189 List<QueueItem> queue = new ArrayList<>(count); 190 for (int i = 0 ; i < count; i++) { 191 TmaMediaItem child = mPlayableChildren.get(i); 192 queue.add(new QueueItem(child.buildDescription(), i)); 193 } 194 return queue; 195 } 196 197 /** Returns the id of the item in the queue. */ getQueueId()198 long getQueueId() { 199 if (mParent != null) { 200 int index = mParent.mPlayableChildren.indexOf(this); 201 if (index >= 0) return index; 202 } 203 return MediaSessionCompat.QueueItem.UNKNOWN_ID; 204 } 205 buildDescription()206 private MediaDescriptionCompat buildDescription() { 207 208 // Use the default media description but add our extras. 209 MediaDescriptionCompat metadataDescription = mMediaMetadata.getDescription(); 210 211 MediaDescriptionCompat.Builder bob = new MediaDescriptionCompat.Builder(); 212 bob.setMediaId(metadataDescription.getMediaId()); 213 bob.setTitle(metadataDescription.getTitle()); 214 bob.setSubtitle(metadataDescription.getSubtitle()); 215 bob.setDescription(metadataDescription.getDescription()); 216 bob.setIconBitmap(metadataDescription.getIconBitmap()); 217 bob.setIconUri(metadataDescription.getIconUri()); 218 bob.setMediaUri(metadataDescription.getMediaUri()); 219 220 Bundle extras = new Bundle(); 221 if (metadataDescription.getExtras() != null) { 222 extras.putAll(metadataDescription.getExtras()); 223 } 224 225 extras.putInt(MediaKeys.CONTENT_STYLE_PLAYABLE_HINT, mPlayableStyle.mBundleValue); 226 extras.putInt(MediaKeys.CONTENT_STYLE_BROWSABLE_HINT, mBrowsableStyle.mBundleValue); 227 228 bob.setExtras(extras); 229 return bob.build(); 230 } 231 } 232