summaryrefslogtreecommitdiff
path: root/src/com/android/car/media/drawer/MediaDrawerController.java
blob: 06b20685cfd1c6247c9254c7580925be836fb5f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.car.media.drawer;

import android.content.ComponentName;
import android.content.Context;
import android.media.browse.MediaBrowser;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.View;

import com.android.car.media.MediaManager;
import com.android.car.media.MediaPlaybackModel;
import com.android.car.media.R;

import androidx.car.drawer.CarDrawerAdapter;
import androidx.car.drawer.CarDrawerController;

/**
 * Manages drawer navigation and item selection.
 * <p>
 * Maintains separate MediaPlaybackModel for media browsing and control. Sets up root Drawer
 * adapter with root of media-browse tree (using MediaBrowserItemsFetcher). Supports switching the
 * rootAdapter to show the queue-items (using MediaQueueItemsFetcher).
 */
public class MediaDrawerController implements MediaDrawerAdapter.MediaFetchCallback,
        MediaItemOnClickListener {
    private static final String TAG = "MediaDrawerController";

    private static final String EXTRA_ICON_SIZE =
            "com.google.android.gms.car.media.BrowserIconSize";

    private final Context mContext;
    private final CarDrawerController mDrawerController;
    private final MediaPlaybackModel mMediaPlaybackModel;
    private MediaDrawerAdapter mRootAdapter;

    public MediaDrawerController(Context context, CarDrawerController drawerController) {
        mContext = context;
        mDrawerController = drawerController;

        Bundle extras = new Bundle();
        extras.putInt(EXTRA_ICON_SIZE,
                mContext.getResources().getDimensionPixelSize(R.dimen.car_primary_icon_size));

        mMediaPlaybackModel = new MediaPlaybackModel(mContext, extras);
        mMediaPlaybackModel.addListener(mModelListener);

        mRootAdapter = new MediaDrawerAdapter(mContext, mDrawerController);
        // Start with a empty title since we depend on the mMediaManagerListener callback to
        // know which app is being used and set the actual title there.
        mRootAdapter.setTitle("");
        mRootAdapter.setFetchCallback(this);

        // Kick off MediaBrowser/MediaController connection.
        mMediaPlaybackModel.start();
    }

    @Override
    public void onQueueItemClicked(MediaSession.QueueItem queueItem) {
        MediaController.TransportControls controls = mMediaPlaybackModel.getTransportControls();

        if (controls != null) {
            controls.skipToQueueItem(queueItem.getQueueId());
        }

        mDrawerController.closeDrawer();
    }

    @Override
    public void onMediaItemClicked(MediaBrowser.MediaItem item) {
        if (item.isBrowsable()) {
            MediaItemsFetcher fetcher;
            if (MediaBrowserItemsFetcher.PLAY_QUEUE_MEDIA_ID.equals(item.getMediaId())) {
                fetcher = createMediaQueueItemsFetcher();
            } else {
                fetcher = createMediaBrowserItemFetcher(item.getMediaId(),
                        false /* showQueueItem */);
            }
            setupAdapterAndSwitch(fetcher, item.getDescription().getTitle());
        } else if (item.isPlayable()) {
            MediaController.TransportControls controls = mMediaPlaybackModel.getTransportControls();
            if (controls != null) {
                controls.playFromMediaId(item.getMediaId(), item.getDescription().getExtras());
            }
            mDrawerController.closeDrawer();
        } else {
            Log.w(TAG, "Unknown item type; don't know how to handle!");
        }
    }

    @Override
    public void onFetchStart() {
        // Initially there will be no items and we don't want to show empty-list indicator
        // briefly until items are fetched.
        mDrawerController.showLoadingProgressBar(true);
    }

    @Override
    public void onFetchEnd() {
        mDrawerController.showLoadingProgressBar(false);
    }

    /**
     * Creates a new sub-level in the drawer and switches to that as the currently displayed view.
     *
     * @param fetcher The {@link MediaItemsFetcher} that is responsible for fetching the items to be
     *                displayed in the new view.
     * @param title The title text of the new view in the drawer.
     */
    private void setupAdapterAndSwitch(MediaItemsFetcher fetcher, CharSequence title) {
        MediaDrawerAdapter subAdapter = new MediaDrawerAdapter(mContext, mDrawerController);
        subAdapter.setFetcher(fetcher);
        subAdapter.setTitle(title);
        subAdapter.setFetchCallback(this);
        mDrawerController.pushAdapter(subAdapter);
    }

    /**
     * Opens the drawer and displays the current playing queue of items. When the drawer is closed,
     * the view is switched back to the drawer root.
     */
    public void showPlayQueue() {
        mRootAdapter.setFetcherAndInvoke(createMediaQueueItemsFetcher());
        mRootAdapter.setTitle(mMediaPlaybackModel.getQueueTitle());
        mDrawerController.openDrawer();
        mRootAdapter.scrollToCurrent();
        mDrawerController.addDrawerListener(mQueueDrawerListener);
    }

    public void cleanup() {
        mDrawerController.removeDrawerListener(mQueueDrawerListener);
        mRootAdapter.cleanup();
        mMediaPlaybackModel.removeListener(mModelListener);
         mMediaPlaybackModel.stop();
    }

    /**
     * @return Adapter to display root items of MediaBrowse tree. {@link #showPlayQueue()} can
     *      be used to display items from the queue.
     */
    public CarDrawerAdapter getRootAdapter() {
        return mRootAdapter;
    }

    /**
     * Creates a {@link MediaBrowserItemsFetcher} that whose root is the given {@code mediaId}.
     */
    private MediaBrowserItemsFetcher createMediaBrowserItemFetcher(String mediaId,
            boolean showQueueItem) {
        return new MediaBrowserItemsFetcher(mContext, mMediaPlaybackModel, this /* listener */,
                mediaId, showQueueItem);
    }

    /**
     * Creates a {@link MediaQueueItemsFetcher} that is responsible for fetching items in the user's
     * current play queue.
     */
    private MediaQueueItemsFetcher createMediaQueueItemsFetcher() {
        return new MediaQueueItemsFetcher(mContext, mMediaPlaybackModel, this /* listener */);
    }

    /**
     * Creates a {@link MediaItemsFetcher} that will display the top-most level of the drawer.
     */
    private MediaItemsFetcher createRootMediaItemsFetcher() {
        return createMediaBrowserItemFetcher(mMediaPlaybackModel.getMediaBrowser().getRoot(),
                true /* showQueueItem */);
    }

    /**
     * A {@link android.support.v4.widget.DrawerLayout.DrawerListener} specifically to be used when
     * the play queue has been shown in the drawer. When the drawer is closed following this
     * display, this listener will reset the drawer to display the root view.
     */
    private final DrawerLayout.DrawerListener mQueueDrawerListener =
            new DrawerLayout.DrawerListener() {
        @Override
        public void onDrawerClosed(View drawerView) {
            mRootAdapter.setFetcherAndInvoke(createRootMediaItemsFetcher());
            mRootAdapter.setTitle(
                    MediaManager.getInstance(mContext).getMediaClientName());
            mDrawerController.removeDrawerListener(this);
        }

        @Override
        public void onDrawerSlide(View drawerView, float slideOffset) {}
        @Override
        public void onDrawerOpened(View drawerView) {}
        @Override
        public void onDrawerStateChanged(int newState) {}
    };

    private final MediaPlaybackModel.Listener mModelListener =
            new MediaPlaybackModel.AbstractListener() {
        @Override
        public void onMediaAppChanged(@Nullable ComponentName currentName,
                @Nullable ComponentName newName) {
            // Only store MediaManager instance to a local variable when it is short lived.
            MediaManager mediaManager = MediaManager.getInstance(mContext);
            mRootAdapter.cleanup();
            mRootAdapter.setTitle(mediaManager.getMediaClientName());
        }

        @Override
        public void onMediaConnected() {
            mRootAdapter.setFetcherAndInvoke(createRootMediaItemsFetcher());
        }
    };
}