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