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.graphics.PorterDuff; 19 import android.graphics.drawable.Drawable; 20 import android.media.browse.MediaBrowser; 21 import android.media.session.MediaController; 22 import android.media.session.MediaSession; 23 import android.util.Log; 24 25 import com.android.car.app.CarDrawerActivity; 26 import com.android.car.app.DrawerItemViewHolder; 27 import com.android.car.media.MediaPlaybackModel; 28 import com.android.car.media.R; 29 30 import java.util.ArrayList; 31 import 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 */ 39 class MediaBrowserItemsFetcher implements MediaItemsFetcher { 40 private static final String TAG = "Media.BrowserFetcher"; 41 42 private final CarDrawerActivity mActivity; 43 private final MediaPlaybackModel mMediaPlaybackModel; 44 private final String mMediaId; 45 private final boolean mShowQueueItem; 46 private ItemsUpdatedCallback mCallback; 47 private List<MediaBrowser.MediaItem> mItems = new ArrayList<>(); 48 private boolean mQueueAvailable; 49 MediaBrowserItemsFetcher(CarDrawerActivity activity, MediaPlaybackModel model, String mediaId, boolean showQueueItem)50 MediaBrowserItemsFetcher(CarDrawerActivity activity, MediaPlaybackModel model, String mediaId, 51 boolean showQueueItem) { 52 mActivity = activity; 53 mMediaPlaybackModel = model; 54 mMediaId = mediaId; 55 mShowQueueItem = showQueueItem; 56 } 57 58 @Override start(ItemsUpdatedCallback callback)59 public void start(ItemsUpdatedCallback callback) { 60 mCallback = callback; 61 updateQueueAvailability(); 62 mMediaPlaybackModel.getMediaBrowser().subscribe(mMediaId, mSubscriptionCallback); 63 mMediaPlaybackModel.addListener(mModelListener); 64 } 65 66 private final MediaPlaybackModel.Listener mModelListener = 67 new MediaPlaybackModel.AbstractListener() { 68 @Override 69 public void onQueueChanged(List<MediaSession.QueueItem> queue) { 70 updateQueueAvailability(); 71 } 72 @Override 73 public void onSessionDestroyed(CharSequence destroyedMediaClientName) { 74 updateQueueAvailability(); 75 } 76 }; 77 78 private final MediaBrowser.SubscriptionCallback mSubscriptionCallback = 79 new MediaBrowser.SubscriptionCallback() { 80 @Override 81 public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) { 82 mItems.clear(); 83 mItems.addAll(children); 84 mCallback.onItemsUpdated(); 85 } 86 87 @Override 88 public void onError(String parentId) { 89 Log.e(TAG, "Error loading children of: " + mMediaId); 90 mItems.clear(); 91 mCallback.onItemsUpdated(); 92 } 93 }; 94 updateQueueAvailability()95 private void updateQueueAvailability() { 96 if (mShowQueueItem && !mMediaPlaybackModel.getQueue().isEmpty()) { 97 mQueueAvailable = true; 98 } 99 } 100 101 @Override getItemCount()102 public int getItemCount() { 103 int size = mItems.size(); 104 if (mQueueAvailable) { 105 size++; 106 } 107 return size; 108 } 109 110 @Override populateViewHolder(DrawerItemViewHolder holder, int position)111 public void populateViewHolder(DrawerItemViewHolder holder, int position) { 112 if (mQueueAvailable && position == mItems.size()) { 113 holder.getTitle().setText(mMediaPlaybackModel.getQueueTitle()); 114 return; 115 } 116 MediaBrowser.MediaItem item = mItems.get(position); 117 MediaItemsFetcher.populateViewHolderFrom(holder, item.getDescription()); 118 119 // TODO(sriniv): Once we use smallLayout, text and rightIcon fields may be unavailable. 120 // Related to b/36573125. 121 if (item.isBrowsable()) { 122 int iconColor = mActivity.getColor(R.color.car_tint); 123 Drawable drawable = mActivity.getDrawable(R.drawable.ic_chevron_right); 124 drawable.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN); 125 holder.getRightIcon().setImageDrawable(drawable); 126 } else { 127 holder.getRightIcon().setImageDrawable(null); 128 } 129 } 130 131 @Override onItemClick(int position)132 public void onItemClick(int position) { 133 if (mQueueAvailable && position == mItems.size()) { 134 MediaItemsFetcher fetcher = new MediaQueueItemsFetcher(mActivity, mMediaPlaybackModel); 135 setupAdapterAndSwitch(fetcher, mMediaPlaybackModel.getQueueTitle()); 136 return; 137 } 138 139 MediaBrowser.MediaItem item = mItems.get(position); 140 if (item.isBrowsable()) { 141 MediaItemsFetcher fetcher = new MediaBrowserItemsFetcher( 142 mActivity, mMediaPlaybackModel, item.getMediaId(), false /* showQueueItem */); 143 setupAdapterAndSwitch(fetcher, item.getDescription().getTitle()); 144 } else if (item.isPlayable()) { 145 MediaController.TransportControls controls = mMediaPlaybackModel.getTransportControls(); 146 if (controls != null) { 147 controls.pause(); 148 controls.playFromMediaId(item.getMediaId(), item.getDescription().getExtras()); 149 } 150 mActivity.closeDrawer(); 151 } else { 152 Log.w(TAG, "Unknown item type; don't know how to handle!"); 153 } 154 } 155 setupAdapterAndSwitch(MediaItemsFetcher fetcher, CharSequence title)156 private void setupAdapterAndSwitch(MediaItemsFetcher fetcher, CharSequence title) { 157 MediaDrawerAdapter subAdapter = new MediaDrawerAdapter(mActivity, false /* smallLayout */); 158 subAdapter.setFetcher(fetcher); 159 subAdapter.setTitle(title); 160 mActivity.switchToAdapter(subAdapter); 161 } 162 163 @Override cleanup()164 public void cleanup() { 165 mMediaPlaybackModel.removeListener(mModelListener); 166 mMediaPlaybackModel.getMediaBrowser().unsubscribe(mMediaId); 167 mCallback = null; 168 } 169 }