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