15cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan/* 25cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * Copyright (C) 2017 The Android Open Source Project 35cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * 45cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * Licensed under the Apache License, Version 2.0 (the "License"); 55cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * you may not use this file except in compliance with the License. 65cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * You may obtain a copy of the License at 75cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * 85cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * http://www.apache.org/licenses/LICENSE-2.0 95cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * 105cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * Unless required by applicable law or agreed to in writing, software 115cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * distributed under the License is distributed on an "AS IS" BASIS, 125cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * See the License for the specific language governing permissions and 145cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * limitations under the License. 155cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan */ 165cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathanpackage com.android.car.media.drawer; 175cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 185f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chenimport android.content.Context; 195cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathanimport android.graphics.PorterDuff; 205cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathanimport android.graphics.drawable.Drawable; 215f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chenimport android.media.MediaDescription; 225cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathanimport android.media.browse.MediaBrowser; 235cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathanimport android.media.session.MediaSession; 245cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathanimport android.util.Log; 25bff2cd02e423e6819b0568d5fa23c54b96c967d9Anthony Chen 265cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathanimport com.android.car.media.MediaPlaybackModel; 275cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathanimport com.android.car.media.R; 285cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 295cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathanimport java.util.ArrayList; 305cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathanimport java.util.List; 315cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 32ddd67d6fbdfcebec8c40a1f8c7d9d68ec2c9fd17Aurimas Liutikasimport androidx.car.drawer.DrawerItemViewHolder; 33ddd67d6fbdfcebec8c40a1f8c7d9d68ec2c9fd17Aurimas Liutikas 345cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan/** 355cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * {@link MediaItemsFetcher} implementation that fetches items from a specific {@link MediaBrowser} 365cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * node. 375cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * <p> 385cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * It optionally supports surfacing the Media app's queue as the last item. 395cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan */ 405cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathanclass MediaBrowserItemsFetcher implements MediaItemsFetcher { 415cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan private static final String TAG = "Media.BrowserFetcher"; 425cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 435f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen /** 445f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen * An id that can be returned from {@link MediaBrowser.MediaItem#getMediaId()} to indicate that 455f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen * a {@link android.media.browse.MediaBrowser.MediaItem} representing the play queue has been 465f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen * clicked. 475f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen */ 485f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen static final String PLAY_QUEUE_MEDIA_ID = "com.android.car.media.drawer.PLAY_QUEUE"; 495f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen 505f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen private final Context mContext; 515cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan private final MediaPlaybackModel mMediaPlaybackModel; 525cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan private final String mMediaId; 535cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan private final boolean mShowQueueItem; 545f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen private final MediaItemOnClickListener mItemClickListener; 555cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan private ItemsUpdatedCallback mCallback; 565cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan private List<MediaBrowser.MediaItem> mItems = new ArrayList<>(); 575cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan private boolean mQueueAvailable; 585cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 595f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen MediaBrowserItemsFetcher(Context context, MediaPlaybackModel model, 605f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen MediaItemOnClickListener listener, String mediaId, boolean showQueueItem) { 615f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen mContext = context; 625cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mMediaPlaybackModel = model; 635f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen mItemClickListener = listener; 645cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mMediaId = mediaId; 655cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mShowQueueItem = showQueueItem; 665cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 675cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 685cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan @Override 695cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan public void start(ItemsUpdatedCallback callback) { 705cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mCallback = callback; 715cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan updateQueueAvailability(); 72d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (mMediaPlaybackModel.isConnected()) { 73d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mMediaPlaybackModel.getMediaBrowser().subscribe(mMediaId, mSubscriptionCallback); 74d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } else { 75d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mItems.clear(); 76d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez callback.onItemsUpdated(); 77d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 785cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mMediaPlaybackModel.addListener(mModelListener); 795cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 805cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 815cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan private final MediaPlaybackModel.Listener mModelListener = 825cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan new MediaPlaybackModel.AbstractListener() { 835cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan @Override 845cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan public void onQueueChanged(List<MediaSession.QueueItem> queue) { 855cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan updateQueueAvailability(); 865cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 875cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan @Override 885cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan public void onSessionDestroyed(CharSequence destroyedMediaClientName) { 895cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan updateQueueAvailability(); 905cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 91d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 92d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public void onMediaConnectionSuspended() { 93d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (mCallback != null) { 94d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mCallback.onItemsUpdated(); 95d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 96d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 975cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan }; 985cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 995cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan private final MediaBrowser.SubscriptionCallback mSubscriptionCallback = 1005cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan new MediaBrowser.SubscriptionCallback() { 1015cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan @Override 1025cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) { 1035cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mItems.clear(); 1045cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mItems.addAll(children); 1055cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mCallback.onItemsUpdated(); 1065cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 1075cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 1085cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan @Override 1095cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan public void onError(String parentId) { 1105cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mItems.clear(); 1115cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mCallback.onItemsUpdated(); 1125cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 1135cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan }; 1145cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 1155cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan private void updateQueueAvailability() { 1165cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan if (mShowQueueItem && !mMediaPlaybackModel.getQueue().isEmpty()) { 1175cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mQueueAvailable = true; 1185cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 1195cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 1205cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 1215cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan @Override 1225cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan public int getItemCount() { 1235cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan int size = mItems.size(); 1245cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan if (mQueueAvailable) { 1255cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan size++; 1265cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 1275cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan return size; 1285cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 1295cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 1305cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan @Override 1312575070e9de72bde07e5bc1c7ece95bd7f78ac27Srinivas Visvanathan public boolean usesSmallLayout(int position) { 1322575070e9de72bde07e5bc1c7ece95bd7f78ac27Srinivas Visvanathan if (mQueueAvailable && position == mItems.size()) { 1332575070e9de72bde07e5bc1c7ece95bd7f78ac27Srinivas Visvanathan return true; 1342575070e9de72bde07e5bc1c7ece95bd7f78ac27Srinivas Visvanathan } 1352575070e9de72bde07e5bc1c7ece95bd7f78ac27Srinivas Visvanathan return MediaItemsFetcher.usesSmallLayout(mItems.get(position).getDescription()); 1362575070e9de72bde07e5bc1c7ece95bd7f78ac27Srinivas Visvanathan } 1372575070e9de72bde07e5bc1c7ece95bd7f78ac27Srinivas Visvanathan 1382575070e9de72bde07e5bc1c7ece95bd7f78ac27Srinivas Visvanathan @Override 1395cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan public void populateViewHolder(DrawerItemViewHolder holder, int position) { 1405cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan if (mQueueAvailable && position == mItems.size()) { 1415cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan holder.getTitle().setText(mMediaPlaybackModel.getQueueTitle()); 1425cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan return; 1435cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 1445cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan MediaBrowser.MediaItem item = mItems.get(position); 1455cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan MediaItemsFetcher.populateViewHolderFrom(holder, item.getDescription()); 1465cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 147bff2cd02e423e6819b0568d5fa23c54b96c967d9Anthony Chen if (holder.getEndIcon() == null) { 1485f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen return; 1495f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen } 1505f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen 1515cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan if (item.isBrowsable()) { 1525f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen int iconColor = mContext.getColor(R.color.car_tint); 1535f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen Drawable drawable = mContext.getDrawable(R.drawable.ic_chevron_right); 1545cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan drawable.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN); 155bff2cd02e423e6819b0568d5fa23c54b96c967d9Anthony Chen holder.getEndIcon().setImageDrawable(drawable); 1565cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } else { 157bff2cd02e423e6819b0568d5fa23c54b96c967d9Anthony Chen holder.getEndIcon().setImageDrawable(null); 1585cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 1595cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 1605cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 1615cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan @Override 1625cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan public void onItemClick(int position) { 1635f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen if (mItemClickListener == null) { 1645cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan return; 1655cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 1665cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 1675f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen MediaBrowser.MediaItem item = mQueueAvailable && position == mItems.size() 1685f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen ? createPlayQueueMediaItem() 1695f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen : mItems.get(position); 1705f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen 1715f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen mItemClickListener.onMediaItemClicked(item); 1725cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 1735cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 1745f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen /** 1755f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen * Creates and returns a {@link android.media.browse.MediaBrowser.MediaItem} that represents an 1765f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen * entry for the play queue. A play queue media item will have a media id of 1775f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen * {@link #PLAY_QUEUE_MEDIA_ID} and is {@link MediaBrowser.MediaItem#FLAG_BROWSABLE}. 1785f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen */ 1795f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen private MediaBrowser.MediaItem createPlayQueueMediaItem() { 1805f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen MediaDescription description = new MediaDescription.Builder() 1815f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen .setMediaId(PLAY_QUEUE_MEDIA_ID) 1825f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen .setTitle(mMediaPlaybackModel.getQueueTitle()) 1835f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen .build(); 1845f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen 1855f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen return new MediaBrowser.MediaItem(description, MediaBrowser.MediaItem.FLAG_BROWSABLE); 1865cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 1875cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan 1885cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan @Override 1895cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan public void cleanup() { 1905cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mMediaPlaybackModel.removeListener(mModelListener); 1915cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mMediaPlaybackModel.getMediaBrowser().unsubscribe(mMediaId); 1925cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan mCallback = null; 1935cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan } 194b435de51965d13509bd5a1a8e2a127470c0aaa11Rakesh Iyer 195b435de51965d13509bd5a1a8e2a127470c0aaa11Rakesh Iyer @Override 196b435de51965d13509bd5a1a8e2a127470c0aaa11Rakesh Iyer public int getScrollPosition() { 197b435de51965d13509bd5a1a8e2a127470c0aaa11Rakesh Iyer return MediaItemsFetcher.DONT_SCROLL; 198b435de51965d13509bd5a1a8e2a127470c0aaa11Rakesh Iyer } 1995f33f82fb1a5907fbd8bd286dcdaa689d2d192d6Anthony Chen} 200