• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.car.media.drawer;
17 
18 import android.content.Context;
19 import android.graphics.PorterDuff;
20 import android.graphics.drawable.Drawable;
21 import android.media.MediaDescription;
22 import android.media.browse.MediaBrowser;
23 import android.media.session.MediaSession;
24 import android.util.Log;
25 
26 import com.android.car.media.MediaPlaybackModel;
27 import com.android.car.media.R;
28 
29 import java.util.ArrayList;
30 import java.util.List;
31 
32 import androidx.car.drawer.DrawerItemViewHolder;
33 
34 /**
35  * {@link MediaItemsFetcher} implementation that fetches items from a specific {@link MediaBrowser}
36  * node.
37  * <p>
38  * It optionally supports surfacing the Media app's queue as the last item.
39  */
40 class MediaBrowserItemsFetcher implements MediaItemsFetcher {
41     private static final String TAG = "Media.BrowserFetcher";
42 
43     /**
44      * An id that can be returned from {@link MediaBrowser.MediaItem#getMediaId()} to indicate that
45      * a {@link android.media.browse.MediaBrowser.MediaItem} representing the play queue has been
46      * clicked.
47      */
48     static final String PLAY_QUEUE_MEDIA_ID = "com.android.car.media.drawer.PLAY_QUEUE";
49 
50     private final Context mContext;
51     private final MediaPlaybackModel mMediaPlaybackModel;
52     private final String mMediaId;
53     private final boolean mShowQueueItem;
54     private final MediaItemOnClickListener mItemClickListener;
55     private ItemsUpdatedCallback mCallback;
56     private List<MediaBrowser.MediaItem> mItems = new ArrayList<>();
57     private boolean mQueueAvailable;
58 
MediaBrowserItemsFetcher(Context context, MediaPlaybackModel model, MediaItemOnClickListener listener, String mediaId, boolean showQueueItem)59     MediaBrowserItemsFetcher(Context context, MediaPlaybackModel model,
60             MediaItemOnClickListener listener, String mediaId, boolean showQueueItem) {
61         mContext = context;
62         mMediaPlaybackModel = model;
63         mItemClickListener = listener;
64         mMediaId = mediaId;
65         mShowQueueItem = showQueueItem;
66     }
67 
68     @Override
start(ItemsUpdatedCallback callback)69     public void start(ItemsUpdatedCallback callback) {
70         mCallback = callback;
71         updateQueueAvailability();
72         if (mMediaPlaybackModel.isConnected()) {
73             mMediaPlaybackModel.getMediaBrowser().subscribe(mMediaId, mSubscriptionCallback);
74         } else {
75             mItems.clear();
76             callback.onItemsUpdated();
77         }
78         mMediaPlaybackModel.addListener(mModelListener);
79     }
80 
81     private final MediaPlaybackModel.Listener mModelListener =
82             new MediaPlaybackModel.AbstractListener() {
83         @Override
84         public void onQueueChanged(List<MediaSession.QueueItem> queue) {
85             updateQueueAvailability();
86         }
87         @Override
88         public void onSessionDestroyed(CharSequence destroyedMediaClientName) {
89             updateQueueAvailability();
90         }
91         @Override
92         public void onMediaConnectionSuspended() {
93             if (mCallback != null) {
94                 mCallback.onItemsUpdated();
95             }
96         }
97     };
98 
99     private final MediaBrowser.SubscriptionCallback mSubscriptionCallback =
100         new MediaBrowser.SubscriptionCallback() {
101             @Override
102             public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) {
103                 mItems.clear();
104                 mItems.addAll(children);
105                 mCallback.onItemsUpdated();
106             }
107 
108             @Override
109             public void onError(String parentId) {
110                 mItems.clear();
111                 mCallback.onItemsUpdated();
112             }
113         };
114 
updateQueueAvailability()115     private void updateQueueAvailability() {
116         if (mShowQueueItem && !mMediaPlaybackModel.getQueue().isEmpty()) {
117             mQueueAvailable = true;
118         }
119     }
120 
121     @Override
getItemCount()122     public int getItemCount() {
123         int size = mItems.size();
124         if (mQueueAvailable) {
125             size++;
126         }
127         return size;
128     }
129 
130     @Override
usesSmallLayout(int position)131     public boolean usesSmallLayout(int position) {
132         if (mQueueAvailable && position == mItems.size()) {
133             return true;
134         }
135         return MediaItemsFetcher.usesSmallLayout(mItems.get(position).getDescription());
136     }
137 
138     @Override
populateViewHolder(DrawerItemViewHolder holder, int position)139     public void populateViewHolder(DrawerItemViewHolder holder, int position) {
140         if (mQueueAvailable && position == mItems.size()) {
141             holder.getTitle().setText(mMediaPlaybackModel.getQueueTitle());
142             return;
143         }
144         MediaBrowser.MediaItem item = mItems.get(position);
145         MediaItemsFetcher.populateViewHolderFrom(holder, item.getDescription());
146 
147         if (holder.getEndIcon() == null) {
148             return;
149         }
150 
151         if (item.isBrowsable()) {
152             int iconColor = mContext.getColor(R.color.car_tint);
153             Drawable drawable = mContext.getDrawable(R.drawable.ic_chevron_right);
154             drawable.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN);
155             holder.getEndIcon().setImageDrawable(drawable);
156         } else {
157             holder.getEndIcon().setImageDrawable(null);
158         }
159     }
160 
161     @Override
onItemClick(int position)162     public void onItemClick(int position) {
163         if (mItemClickListener == null) {
164             return;
165         }
166 
167         MediaBrowser.MediaItem item = mQueueAvailable && position == mItems.size()
168                 ? createPlayQueueMediaItem()
169                 : mItems.get(position);
170 
171         mItemClickListener.onMediaItemClicked(item);
172     }
173 
174     /**
175      * Creates and returns a {@link android.media.browse.MediaBrowser.MediaItem} that represents an
176      * entry for the play queue. A play queue media item will have a media id of
177      * {@link #PLAY_QUEUE_MEDIA_ID} and is {@link MediaBrowser.MediaItem#FLAG_BROWSABLE}.
178      */
createPlayQueueMediaItem()179     private MediaBrowser.MediaItem createPlayQueueMediaItem() {
180         MediaDescription description = new MediaDescription.Builder()
181                 .setMediaId(PLAY_QUEUE_MEDIA_ID)
182                 .setTitle(mMediaPlaybackModel.getQueueTitle())
183                 .build();
184 
185         return new MediaBrowser.MediaItem(description, MediaBrowser.MediaItem.FLAG_BROWSABLE);
186     }
187 
188     @Override
cleanup()189     public void cleanup() {
190         mMediaPlaybackModel.removeListener(mModelListener);
191         mMediaPlaybackModel.getMediaBrowser().unsubscribe(mMediaId);
192         mCallback = null;
193     }
194 
195     @Override
getScrollPosition()196     public int getScrollPosition() {
197         return MediaItemsFetcher.DONT_SCROLL;
198     }
199 }
200