Home | History | Annotate | Download | only in drawer
      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 
     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
     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
     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
    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
    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      */
    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      */
    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 
    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      */
    159     public CarDrawerAdapter getRootAdapter() {
    160         return mRootAdapter;
    161     }
    162 
    163     /**
    164      * Creates a {@link MediaBrowserItemsFetcher} that whose root is the given {@code mediaId}.
    165      */
    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      */
    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      */
    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