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