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.ComponentName; 19 import android.media.browse.MediaBrowser; 20 import android.media.session.MediaController; 21 import android.media.session.MediaSession; 22 import android.os.Bundle; 23 import android.support.annotation.Nullable; 24 import android.support.v4.widget.DrawerLayout; 25 import android.util.Log; 26 import android.view.View; 27 import com.android.car.app.CarDrawerActivity; 28 import com.android.car.app.CarDrawerAdapter; 29 import com.android.car.media.MediaManager; 30 import com.android.car.media.MediaPlaybackModel; 31 import com.android.car.media.R; 32 33 /** 34 * Manages drawer navigation and item selection. 35 * <p> 36 * Maintains separate MediaPlaybackModel for media browsing and control. Sets up root Drawer 37 * adapter with root of media-browse tree (using MediaBrowserItemsFetcher). Supports switching the 38 * rootAdapter to show the queue-items (using MediaQueueItemsFetcher). 39 */ 40 public class MediaDrawerController implements MediaDrawerAdapter.MediaFetchCallback, 41 MediaItemOnClickListener { 42 private static final String TAG = "MediaDrawerController"; 43 44 private static final String EXTRA_ICON_SIZE = 45 "com.google.android.gms.car.media.BrowserIconSize"; 46 47 private final CarDrawerActivity mActivity; 48 private final MediaPlaybackModel mMediaPlaybackModel; 49 private MediaDrawerAdapter mRootAdapter; 50 MediaDrawerController(CarDrawerActivity activity)51 public MediaDrawerController(CarDrawerActivity activity) { 52 mActivity = activity; 53 Bundle extras = new Bundle(); 54 extras.putInt(EXTRA_ICON_SIZE, 55 mActivity.getResources().getDimensionPixelSize(R.dimen.car_list_item_icon_size)); 56 mMediaPlaybackModel = new MediaPlaybackModel(mActivity, extras); 57 mMediaPlaybackModel.addListener(mModelListener); 58 59 mRootAdapter = new MediaDrawerAdapter(mActivity); 60 // Start with a empty title since we depend on the mMediaManagerListener callback to 61 // know which app is being used and set the actual title there. 62 mRootAdapter.setTitle(""); 63 mRootAdapter.setFetchCallback(this); 64 65 // Kick off MediaBrowser/MediaController connection. 66 mMediaPlaybackModel.start(); 67 } 68 69 @Override onQueueItemClicked(MediaSession.QueueItem queueItem)70 public void onQueueItemClicked(MediaSession.QueueItem queueItem) { 71 MediaController.TransportControls controls = mMediaPlaybackModel.getTransportControls(); 72 73 if (controls != null) { 74 controls.skipToQueueItem(queueItem.getQueueId()); 75 } 76 77 mActivity.closeDrawer(); 78 } 79 80 @Override onMediaItemClicked(MediaBrowser.MediaItem item)81 public void onMediaItemClicked(MediaBrowser.MediaItem item) { 82 if (item.isBrowsable()) { 83 MediaItemsFetcher fetcher; 84 if (MediaBrowserItemsFetcher.PLAY_QUEUE_MEDIA_ID.equals(item.getMediaId())) { 85 fetcher = createMediaQueueItemsFetcher(); 86 } else { 87 fetcher = createMediaBrowserItemFetcher(item.getMediaId(), 88 false /* showQueueItem */); 89 } 90 setupAdapterAndSwitch(fetcher, item.getDescription().getTitle()); 91 } else if (item.isPlayable()) { 92 MediaController.TransportControls controls = mMediaPlaybackModel.getTransportControls(); 93 if (controls != null) { 94 controls.pause(); 95 controls.playFromMediaId(item.getMediaId(), item.getDescription().getExtras()); 96 } 97 mActivity.closeDrawer(); 98 } else { 99 Log.w(TAG, "Unknown item type; don't know how to handle!"); 100 } 101 } 102 103 @Override onFetchStart()104 public void onFetchStart() { 105 // Initially there will be no items and we don't want to show empty-list indicator 106 // briefly until items are fetched. 107 mActivity.showLoadingProgressBar(true); 108 } 109 110 @Override onFetchEnd()111 public void onFetchEnd() { 112 mActivity.showLoadingProgressBar(false); 113 } 114 115 /** 116 * Creates a new sub-level in the drawer and switches to that as the currently displayed view. 117 * 118 * @param fetcher The {@link MediaItemsFetcher} that is responsible for fetching the items to be 119 * displayed in the new view. 120 * @param title The title text of the new view in the drawer. 121 */ setupAdapterAndSwitch(MediaItemsFetcher fetcher, CharSequence title)122 private void setupAdapterAndSwitch(MediaItemsFetcher fetcher, CharSequence title) { 123 MediaDrawerAdapter subAdapter = new MediaDrawerAdapter(mActivity); 124 subAdapter.setFetcher(fetcher); 125 subAdapter.setTitle(title); 126 mActivity.switchToAdapter(subAdapter); 127 } 128 129 /** 130 * Opens the drawer and displays the current playing queue of items. When the drawer is closed, 131 * the view is switched back to the drawer root. 132 */ showPlayQueue()133 public void showPlayQueue() { 134 mRootAdapter.setFetcher(createMediaQueueItemsFetcher()); 135 mRootAdapter.setTitle(mMediaPlaybackModel.getQueueTitle()); 136 mActivity.openDrawer(); 137 mRootAdapter.scrollToCurrent(); 138 mActivity.addDrawerListener(mQueueDrawerListener); 139 } 140 cleanup()141 public void cleanup() { 142 mActivity.removeDrawerListener(mQueueDrawerListener); 143 mRootAdapter.cleanup(); 144 mMediaPlaybackModel.removeListener(mModelListener); 145 mMediaPlaybackModel.stop(); 146 } 147 148 /** 149 * @return Adapter to display root items of MediaBrowse tree. {@link #showPlayQueue()} can 150 * be used to display items from the queue. 151 */ getRootAdapter()152 public CarDrawerAdapter getRootAdapter() { 153 return mRootAdapter; 154 } 155 156 /** 157 * Creates a {@link MediaBrowserItemsFetcher} that whose root is the given {@code mediaId}. 158 */ createMediaBrowserItemFetcher(String mediaId, boolean showQueueItem)159 private MediaBrowserItemsFetcher createMediaBrowserItemFetcher(String mediaId, 160 boolean showQueueItem) { 161 return new MediaBrowserItemsFetcher(mActivity, mMediaPlaybackModel, this /* listener */, 162 mediaId, showQueueItem); 163 } 164 165 /** 166 * Creates a {@link MediaQueueItemsFetcher} that is responsible for fetching items in the user's 167 * current play queue. 168 */ createMediaQueueItemsFetcher()169 private MediaQueueItemsFetcher createMediaQueueItemsFetcher() { 170 return new MediaQueueItemsFetcher(mActivity, mMediaPlaybackModel, this /* listener */); 171 } 172 173 /** 174 * Creates a {@link MediaItemsFetcher} that will display the top-most level of the drawer. 175 */ createRootMediaItemsFetcher()176 private MediaItemsFetcher createRootMediaItemsFetcher() { 177 return createMediaBrowserItemFetcher(mMediaPlaybackModel.getMediaBrowser().getRoot(), 178 true /* showQueueItem */); 179 } 180 181 /** 182 * A {@link android.support.v4.widget.DrawerLayout.DrawerListener} specifically to be used when 183 * the play queue has been shown in the drawer. When the drawer is closed following this 184 * display, this listener will reset the drawer to display the root view. 185 */ 186 private final DrawerLayout.DrawerListener mQueueDrawerListener = 187 new DrawerLayout.DrawerListener() { 188 @Override 189 public void onDrawerClosed(View drawerView) { 190 mRootAdapter.setFetcher(createRootMediaItemsFetcher()); 191 mRootAdapter.setTitle( 192 MediaManager.getInstance(mActivity).getMediaClientName()); 193 mActivity.removeDrawerListener(this); 194 } 195 196 @Override 197 public void onDrawerSlide(View drawerView, float slideOffset) {} 198 @Override 199 public void onDrawerOpened(View drawerView) {} 200 @Override 201 public void onDrawerStateChanged(int newState) {} 202 }; 203 204 private final MediaPlaybackModel.Listener mModelListener = 205 new MediaPlaybackModel.AbstractListener() { 206 @Override 207 public void onMediaAppChanged(@Nullable ComponentName currentName, 208 @Nullable ComponentName newName) { 209 // Only store MediaManager instance to a local variable when it is short lived. 210 MediaManager mediaManager = MediaManager.getInstance(mActivity); 211 mRootAdapter.setTitle(mediaManager.getMediaClientName()); 212 } 213 214 @Override 215 public void onMediaConnected() { 216 mRootAdapter.setFetcher(createRootMediaItemsFetcher()); 217 } 218 }; 219 } 220