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