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 60 final String mId; 61 final int mNameId; 62 final int mIcon; 63 TmaCustomAction(String id, int name, int icon)64 TmaCustomAction(String id, int name, int icon) { 65 mId = id; 66 mNameId = name; 67 mIcon = icon; 68 } 69 70 } 71 72 private final int mFlags; 73 private final MediaMetadataCompat mMediaMetadata; 74 private final ContentStyle mPlayableStyle; 75 private final ContentStyle mBrowsableStyle; 76 77 /** Read only list. */ 78 final List<TmaMediaItem> mChildren; 79 /** Read only list. */ 80 private final List<TmaMediaItem> mPlayableChildren; 81 /** Read only list. */ 82 final List<TmaCustomAction> mCustomActions; 83 /** Read only list. Events triggered when starting the playback. */ 84 final List<TmaMediaEvent> mMediaEvents; 85 /** References another json file where to get extra children from. */ 86 final String mInclude; 87 88 private @Nullable TmaMediaItem mParent; 89 int mHearts; 90 91 TmaMediaItem(int flags, ContentStyle playableStyle, ContentStyle browsableStyle, MediaMetadataCompat metadata, List<TmaCustomAction> customActions, List<TmaMediaEvent> mediaEvents, List<TmaMediaItem> children, String include)92 public TmaMediaItem(int flags, ContentStyle playableStyle, ContentStyle browsableStyle, 93 MediaMetadataCompat metadata, List<TmaCustomAction> customActions, 94 List<TmaMediaEvent> mediaEvents, 95 List<TmaMediaItem> children, String include) { 96 mFlags = flags; 97 mPlayableStyle = playableStyle; 98 mBrowsableStyle = browsableStyle; 99 mMediaMetadata = metadata; 100 mCustomActions = Collections.unmodifiableList(customActions); 101 mChildren = Collections.unmodifiableList(children); 102 mMediaEvents = Collections.unmodifiableList(mediaEvents); 103 mInclude = include; 104 List<TmaMediaItem> playableChildren = new ArrayList<>(children.size()); 105 for (TmaMediaItem child: mChildren) { 106 child.setParent(this); 107 if ((child.mFlags & FLAG_PLAYABLE) != 0) { 108 playableChildren.add(child); 109 } 110 } 111 mPlayableChildren = Collections.unmodifiableList(playableChildren); 112 } 113 setParent(@ullable TmaMediaItem parent)114 private void setParent(@Nullable TmaMediaItem parent) { 115 mParent = parent; 116 } 117 118 @Nullable getParent()119 TmaMediaItem getParent() { 120 return mParent; 121 } 122 123 @Nullable getPlayableByIndex(long index)124 TmaMediaItem getPlayableByIndex(long index) { 125 if (index < 0 || index >= mPlayableChildren.size()) { 126 return null; 127 } 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(MediaKeys.CONTENT_STYLE_PLAYABLE_HINT, mPlayableStyle.mBundleValue); 213 extras.putInt(MediaKeys.CONTENT_STYLE_BROWSABLE_HINT, mBrowsableStyle.mBundleValue); 214 215 bob.setExtras(extras); 216 return bob.build(); 217 } 218 } 219