164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer/*
264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Copyright (C) 2016 The Android Open Source Project
364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer *
464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Licensed under the Apache License, Version 2.0 (the "License");
564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * you may not use this file except in compliance with the License.
664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * You may obtain a copy of the License at
764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer *
864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer *      http://www.apache.org/licenses/LICENSE-2.0
964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer *
1064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Unless required by applicable law or agreed to in writing, software
1164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * distributed under the License is distributed on an "AS IS" BASIS,
1264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * See the License for the specific language governing permissions and
1464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * limitations under the License.
1564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */
1664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerpackage com.android.car.media;
1764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer
18450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perezimport android.annotation.NonNull;
19450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perezimport android.car.Car;
2064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.content.ComponentName;
21450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perezimport android.content.Context;
2264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.content.Intent;
23450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perezimport android.content.SharedPreferences;
24d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.graphics.Bitmap;
258a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perezimport android.media.session.MediaController;
2664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.os.Bundle;
27d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.support.v4.app.Fragment;
28d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.support.v4.app.FragmentManager;
291d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perezimport android.support.v4.widget.DrawerLayout;
3060561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perezimport android.transition.Fade;
3164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.util.Log;
32d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.util.TypedValue;
33d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.view.View;
345289168183d65b16813447c2b160adab799f1ac8Roberto Perezimport android.view.ViewGroup;
35bff2cd02e423e6819b0568d5fa23c54b96c967d9Anthony Chen
368a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perezimport com.android.car.media.common.ActiveMediaSourceManager;
37d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport com.android.car.media.common.CrossfadeImageView;
38d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport com.android.car.media.common.MediaItemMetadata;
39cade0ffb8f53aee68f7d453c30871d1c32b53632Roberto Perezimport com.android.car.media.common.MediaSource;
4060561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perezimport com.android.car.media.common.MediaSourcesManager;
415289168183d65b16813447c2b160adab799f1ac8Roberto Perezimport com.android.car.media.common.PlaybackControls;
42d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport com.android.car.media.common.PlaybackModel;
435cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathanimport com.android.car.media.drawer.MediaDrawerController;
445a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perezimport com.android.car.media.widgets.AppBarView;
455289168183d65b16813447c2b160adab799f1ac8Roberto Perezimport com.android.car.media.widgets.MetadataView;
46a51d23d9109f44187200495fdb4523d47255b14aRoberto Perezimport com.android.car.media.widgets.ViewUtils;
47d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
48d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport java.util.ArrayList;
49d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport java.util.List;
50d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport java.util.Objects;
51d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport java.util.stream.Collectors;
525cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan
53ddd67d6fbdfcebec8c40a1f8c7d9d68ec2c9fd17Aurimas Liutikasimport androidx.car.drawer.CarDrawerActivity;
54ddd67d6fbdfcebec8c40a1f8c7d9d68ec2c9fd17Aurimas Liutikasimport androidx.car.drawer.CarDrawerAdapter;
55ddd67d6fbdfcebec8c40a1f8c7d9d68ec2c9fd17Aurimas Liutikas
5664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer/**
5764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * This activity controls the UI of media. It also updates the connection status for the media app
585cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan * by broadcast. Drawer menu is controlled by {@link MediaDrawerController}.
5964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */
6060561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perezpublic class MediaActivity extends CarDrawerActivity implements BrowseFragment.Callbacks,
611d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        AppSelectionFragment.Callbacks, PlaybackFragment.Callbacks {
6264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    private static final String TAG = "MediaActivity";
6364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer
64450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    /** Intent extra specifying the package with the MediaBrowser */
65d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    public static final String KEY_MEDIA_PACKAGE = "media_package";
66450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    /** Shared preferences files */
67450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    public static final String SHARED_PREF = "com.android.car.media";
68450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    /** Shared preference containing the last controlled source */
69450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    public static final String LAST_MEDIA_SOURCE_SHARED_PREF_KEY = "last_media_source";
7060561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez
71d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    /** Configuration (controlled from resources) */
72d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private boolean mContentForwardBrowseEnabled;
73d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private float mBackgroundBlurRadius;
74d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private float mBackgroundBlurScale;
75a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez    private int mFadeDuration;
76d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
77d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    /** Models */
785cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan    private MediaDrawerController mDrawerController;
798a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez    private ActiveMediaSourceManager mActiveMediaSourceManager;
80d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private MediaSource mMediaSource;
81d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private PlaybackModel mPlaybackModel;
8260561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    private MediaSourcesManager mMediaSourcesManager;
83450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    private SharedPreferences mSharedPreferences;
84d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
85d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    /** Layout views */
865a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez    private AppBarView mAppBarView;
87d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private CrossfadeImageView mAlbumBackground;
888189d452317e04e1c1852d2d3a2e9da3e0295147Roberto Perez    private PlaybackFragment mPlaybackFragment;
8960561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    private AppSelectionFragment mAppSelectionFragment;
905289168183d65b16813447c2b160adab799f1ac8Roberto Perez    private PlaybackControls mPlaybackControls;
915289168183d65b16813447c2b160adab799f1ac8Roberto Perez    private MetadataView mMetadataView;
925289168183d65b16813447c2b160adab799f1ac8Roberto Perez    private ViewGroup mBrowseControlsContainer;
93fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez    private EmptyFragment mEmptyFragment;
94a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez    private ViewGroup mBrowseContainer;
95a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez    private ViewGroup mPlaybackContainer;
965289168183d65b16813447c2b160adab799f1ac8Roberto Perez
97d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    /** Current state */
98d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private Fragment mCurrentFragment;
99d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private Mode mMode = Mode.BROWSING;
10060561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    private boolean mIsAppSelectorOpen;
101a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez    private MediaItemMetadata mCurrentMetadata;
102d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
103d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private MediaSource.Observer mMediaSourceObserver = new MediaSource.Observer() {
104d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        @Override
105d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        protected void onBrowseConnected(boolean success) {
106d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            MediaActivity.this.onBrowseConnected(success);
107d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        }
108d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
109d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        @Override
110d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        protected void onBrowseDisconnected() {
111d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            MediaActivity.this.onBrowseConnected(false);
112d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        }
113d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    };
114d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private PlaybackModel.PlaybackObserver mPlaybackObserver =
115d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            new PlaybackModel.PlaybackObserver() {
1168a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        @Override
1178a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        protected void onSourceChanged() {
1188a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            updateMetadata();
1198a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        }
120d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
1218a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        @Override
1228a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        public void onMetadataChanged() {
1238a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            mCurrentMetadata = null;
1248a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            updateMetadata();
1258a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        }
1268a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez    };
1278a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez    private ActiveMediaSourceManager.Observer mActiveSourceObserver = () -> {
1288a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        // If the active media source changes and it is the one currently being browsed, then
1298a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        // we should capture the controller.
1308a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        MediaController controller = mActiveMediaSourceManager.getMediaController();
1318a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        if (mPlaybackModel.getMediaController() == null
1328a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                && mMediaSource != null
1338a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                && controller != null
1348a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                && Objects.equals(controller.getPackageName(), mMediaSource.getPackageName())) {
1358a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            mPlaybackModel.setMediaController(controller);
1368a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        }
1378a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez    };
138fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez    private MediaSource.ItemsSubscription mItemsSubscription =
139fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            new MediaSource.ItemsSubscription() {
1408a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        @Override
1418a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        public void onChildrenLoaded(MediaSource mediaSource, String parentId,
1428a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                List<MediaItemMetadata> items) {
1438a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            if (mediaSource == mMediaSource) {
1448a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                updateTabs(items);
1458a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            } else {
1468a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                Log.w(TAG, "Received items for a wrong source: " +
1478a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                        mediaSource.getPackageName());
1488a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            }
1498a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        }
1508a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez    };
1515a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez    private AppBarView.AppBarListener mAppBarListener = new AppBarView.AppBarListener() {
152d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        @Override
1535a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez        public void onTabSelected(MediaItemMetadata item) {
154fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            updateBrowseFragment(BrowseState.LOADED, item);
155a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            switchToMode(Mode.BROWSING);
156d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        }
157d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
158d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        @Override
1595a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez        public void onBack() {
1605a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez            if (mCurrentFragment != null && mCurrentFragment instanceof BrowseFragment) {
1615a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez                BrowseFragment fragment = (BrowseFragment) mCurrentFragment;
1625a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez                fragment.navigateBack();
1635a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez            }
164d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        }
165d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
166d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        @Override
1675a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez        public void onCollapse() {
1685a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez            switchToMode(Mode.BROWSING);
1695a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez        }
1705a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez
1715a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez        @Override
1725a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez        public void onAppSelection() {
1731d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez            Log.d(TAG, "onAppSelection clicked");
17460561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez            if (mIsAppSelectorOpen) {
17560561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                closeAppSelector();
17660561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez            } else {
17760561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                openAppSelector();
17860561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez            }
179d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        }
180d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    };
18160561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    private MediaSourcesManager.Observer mMediaSourcesManagerObserver = () -> {
18260561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        mAppBarView.setAppSelection(!mMediaSourcesManager.getMediaSources().isEmpty());
18360561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        mAppSelectionFragment.refresh();
18460561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    };
1851d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez    private DrawerLayout.DrawerListener mDrawerListener = new DrawerLayout.DrawerListener() {
1861d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        @Override
1871d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        public void onDrawerSlide(@android.support.annotation.NonNull View view, float v) {
1881d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        }
1891d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez
1901d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        @Override
1911d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        public void onDrawerOpened(@android.support.annotation.NonNull View view) {
1921d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez            closeAppSelector();
1931d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        }
1941d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez
1951d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        @Override
1961d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        public void onDrawerClosed(@android.support.annotation.NonNull View view) {
1971d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        }
1981d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez
1991d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        @Override
2001d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        public void onDrawerStateChanged(int i) {
2011d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        }
2021d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez    };
203d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
204fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez    /**
205fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     * Possible modes of the application UI
206fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     */
207d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private enum Mode {
208fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        /** The user is browsing a media source */
209d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        BROWSING,
210fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        /** The user is interacting with the full screen playback UI */
211d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        PLAYBACK
212d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
21364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer
214fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez    /**
215fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     * Possible states of the application UI
216fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     */
217fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez    public enum BrowseState {
218fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        /** There is no content to show */
219fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        EMPTY,
220fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        /** We are still in the process of obtaining data */
221fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        LOADING,
222fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        /** Data has been loaded */
223fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        LOADED,
224fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        /** The content can't be shown due an error */
225fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        ERROR
226fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez    }
227fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez
22864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    @Override
22964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    protected void onCreate(Bundle savedInstanceState) {
23064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer        super.onCreate(savedInstanceState);
231d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
232d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        setMainContent(R.layout.media_activity);
233fd135a91a13b55d23768370dc81c2d349b19f6b7Anthony Chen        setToolbarElevation(0f);
2345cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan
235d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        mContentForwardBrowseEnabled = getResources()
236d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez                .getBoolean(R.bool.forward_content_browse_enabled);
237d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        mDrawerController = new MediaDrawerController(this, getDrawerController());
238bff2cd02e423e6819b0568d5fa23c54b96c967d9Anthony Chen        getDrawerController().setRootAdapter(getRootAdapter());
2391d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        getDrawerController().addDrawerListener(mDrawerListener);
2401d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        if (mContentForwardBrowseEnabled) {
2411d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez            getSupportActionBar().hide();
2421d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        }
2435a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez        mAppBarView = findViewById(R.id.app_bar);
2445a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez        mAppBarView.setListener(mAppBarListener);
2451d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        mAppBarView.setContentForwardEnabled(mContentForwardBrowseEnabled);
246d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        mPlaybackFragment = new PlaybackFragment();
24760561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        mAppSelectionFragment = new AppSelectionFragment();
24860561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        int fadeDuration = getResources().getInteger(R.integer.app_selector_fade_duration);
24960561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        mAppSelectionFragment.setEnterTransition(new Fade().setDuration(fadeDuration));
25060561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        mAppSelectionFragment.setExitTransition(new Fade().setDuration(fadeDuration));
2518a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        mActiveMediaSourceManager = new ActiveMediaSourceManager(this);
252d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        mPlaybackModel = new PlaybackModel(this);
25360561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        mMediaSourcesManager = new MediaSourcesManager(this);
254d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        mAlbumBackground = findViewById(R.id.media_background);
255a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez        mPlaybackControls = findViewById(R.id.browse_controls);
2565289168183d65b16813447c2b160adab799f1ac8Roberto Perez        mPlaybackControls.setModel(mPlaybackModel);
257a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez        mMetadataView = findViewById(R.id.browse_metadata);
2585289168183d65b16813447c2b160adab799f1ac8Roberto Perez        mMetadataView.setModel(mPlaybackModel);
2595289168183d65b16813447c2b160adab799f1ac8Roberto Perez        mBrowseControlsContainer = findViewById(R.id.browse_controls_container);
2605289168183d65b16813447c2b160adab799f1ac8Roberto Perez        mBrowseControlsContainer.setOnClickListener(view -> switchToMode(Mode.PLAYBACK));
261d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        TypedValue outValue = new TypedValue();
262d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        getResources().getValue(R.dimen.playback_background_blur_radius, outValue, true);
263d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        mBackgroundBlurRadius = outValue.getFloat();
264d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        getResources().getValue(R.dimen.playback_background_blur_scale, outValue, true);
265d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        mBackgroundBlurScale = outValue.getFloat();
266450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        mSharedPreferences = getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
267a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez        mFadeDuration = getResources().getInteger(
268a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                R.integer.new_album_art_fade_in_duration);
269a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez        mEmptyFragment = new EmptyFragment();
270a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez        mBrowseContainer = findViewById(R.id.fragment_container);
271a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez        mPlaybackContainer = findViewById(R.id.playback_container);
272a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez        getSupportFragmentManager().beginTransaction()
273a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                .replace(R.id.playback_container, mPlaybackFragment)
274a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                .commit();
275d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
276bff2cd02e423e6819b0568d5fa23c54b96c967d9Anthony Chen
277d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    @Override
27860561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    public void onResume() {
27960561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        super.onResume();
280d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        mPlaybackModel.registerObserver(mPlaybackObserver);
2818a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        mActiveMediaSourceManager.registerObserver(mActiveSourceObserver);
28260561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        mMediaSourcesManager.registerObserver(mMediaSourcesManagerObserver);
283fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        handleIntent();
284d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
2858189d452317e04e1c1852d2d3a2e9da3e0295147Roberto Perez
286d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    @Override
28760561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    public void onPause() {
28860561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        super.onPause();
289d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        mPlaybackModel.unregisterObserver(mPlaybackObserver);
2908a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        mActiveMediaSourceManager.unregisterObserver(mActiveSourceObserver);
29160561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        mMediaSourcesManager.unregisterObserver(mMediaSourcesManagerObserver);
29264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    }
29364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer
29464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    @Override
29564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    public void onDestroy() {
29664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer        super.onDestroy();
2975cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan        mDrawerController.cleanup();
2985289168183d65b16813447c2b160adab799f1ac8Roberto Perez        mPlaybackControls.setModel(null);
2995289168183d65b16813447c2b160adab799f1ac8Roberto Perez        mMetadataView.setModel(null);
3005cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan    }
3015cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan
3025cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan    @Override
3035cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan    protected CarDrawerAdapter getRootAdapter() {
304bff2cd02e423e6819b0568d5fa23c54b96c967d9Anthony Chen        return mDrawerController == null ? null : mDrawerController.getRootAdapter();
30564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    }
30664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer
30764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    @Override
30864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    protected void onNewIntent(Intent intent) {
30964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer        super.onNewIntent(intent);
31064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer        if (Log.isLoggable(TAG, Log.VERBOSE)) {
31164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer            Log.v(TAG, "onNewIntent(); intent: " + (intent == null ? "<< NULL >>" : intent));
31264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer        }
31364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer
31464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer        setIntent(intent);
315d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        handleIntent();
31664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    }
31764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer
31864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    @Override
31964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    public void onBackPressed() {
3208189d452317e04e1c1852d2d3a2e9da3e0295147Roberto Perez        mPlaybackFragment.closeOverflowMenu();
32164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer        super.onBackPressed();
32264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer    }
32364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer
324d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private void onBrowseConnected(boolean success) {
325d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        if (!success) {
326fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            updateTabs(null);
327fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            mMediaSource.unsubscribeChildren(null, mItemsSubscription);
328fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            mMediaSource.unsubscribe(mMediaSourceObserver);
329fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            updateBrowseFragment(BrowseState.ERROR, null);
330d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            return;
33164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer        }
332fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        mMediaSource.subscribeChildren(null, mItemsSubscription);
3338a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        if (mPlaybackModel.getMediaController() == null) {
3348a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            mPlaybackModel.setMediaController(mMediaSource.getMediaController());
3358a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        }
336d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
33764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer
338d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private void handleIntent() {
339450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        Intent intent = getIntent();
340450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        String action = intent != null ? intent.getAction() : null;
341450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez
342450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        getDrawerController().closeDrawer();
343450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez
344450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        if (Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE.equals(action)) {
345450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            // The user either wants to browse a particular media source or switch to the
346450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            // playback UI.
347450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            String packageName = intent.getStringExtra(KEY_MEDIA_PACKAGE);
348450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            if (packageName != null) {
349450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez                // We were told to navigate to a particular package: we open browse for it.
350a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                closeAppSelector();
3518a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                changeMediaSource(new MediaSource(this, packageName), null);
352450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez                switchToMode(Mode.BROWSING);
353450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez                return;
354450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            }
355450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez
3568a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            // If we didn't receive a package name and we are playing something: show the playback
357450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            // UI for the playing media source.
3588a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            MediaController controller = mActiveMediaSourceManager.getMediaController();
3598a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            if (controller != null) {
360a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                closeAppSelector();
3618a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                changeMediaSource(new MediaSource(this, controller.getPackageName()), controller);
362450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez                switchToMode(Mode.PLAYBACK);
363450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez                return;
364450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            }
365450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        }
366450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez
367450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        // In any other case, if we were already browsing something: just close drawers/overlays
368450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        // and display what we have.
369450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        if (mMediaSource != null) {
370450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            closeAppSelector();
371450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            return;
372450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        }
373450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez
374450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        // If we don't have a current media source, we try with the last one we remember.
375450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        MediaSource lastMediaSource = getLastMediaSource();
376450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        if (lastMediaSource != null) {
377450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            closeAppSelector();
3788a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            changeMediaSource(lastMediaSource, null);
379fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            switchToMode(Mode.BROWSING);
380450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        } else {
381450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            // If we don't have anything from before: open the app selector.
382450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            openAppSelector();
383450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        }
384d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
385cade0ffb8f53aee68f7d453c30871d1c32b53632Roberto Perez
386d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    /**
3878a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez     * Sets the media source being browsed.
3888a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez     *
3898a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez     * @param mediaSource the media source we are going to try to browse
3908a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez     * @param controller a controller we can use to control the playback state of the given
3918a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez     *                   source. If not provided, we will try to obtain it from the session manager.
3928a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez     *                   Otherwise, we will obtain a controller once the media browser is connected.
393d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez     */
3948a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez    private void changeMediaSource(MediaSource mediaSource, MediaController controller) {
395d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        if (Objects.equals(mediaSource, mMediaSource)) {
396d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            // No change, nothing to do.
397d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            return;
398d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        }
399d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        if (mMediaSource != null) {
400fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            mMediaSource.unsubscribeChildren(null, mItemsSubscription);
401d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            mMediaSource.unsubscribe(mMediaSourceObserver);
402d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            updateTabs(new ArrayList<>());
403d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        }
404d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        mMediaSource = mediaSource;
4058a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        mPlaybackModel.setMediaController(controller != null ? controller
4068a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                : mActiveMediaSourceManager.getControllerForPackage(mediaSource.getPackageName()));
407450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        setLastMediaSource(mMediaSource);
408d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        if (mMediaSource != null) {
409d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            if (Log.isLoggable(TAG, Log.INFO)) {
410d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez                Log.i(TAG, "Browsing: " + mediaSource.getName());
411d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            }
412e8f6fb8b486c5ae99452d2fc93a922d80df1d76dRoberto Perez            // Prepare the media source for playback
413e8f6fb8b486c5ae99452d2fc93a922d80df1d76dRoberto Perez            mPlaybackModel.onPrepare();
414e8f6fb8b486c5ae99452d2fc93a922d80df1d76dRoberto Perez            // Make the drawer display browse information of the selected source
415d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            ComponentName component = mMediaSource.getBrowseServiceComponentName();
4165cb58af61618d6c457685a5feba630540f25567cSrinivas Visvanathan            MediaManager.getInstance(this).setMediaClientComponent(component);
417d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            // If content forward browsing is disabled, then no need to subscribe to this media
418e8f6fb8b486c5ae99452d2fc93a922d80df1d76dRoberto Perez            // source, we will use the drawer instead.
419d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            if (mContentForwardBrowseEnabled) {
420d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez                Log.i(TAG, "Content forward is enabled: subscribing to " +
421d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez                        mMediaSource.getPackageName());
4221d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez                updateBrowseFragment(BrowseState.LOADING, null);
423d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez                mMediaSource.subscribe(mMediaSourceObserver);
424d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            }
42560561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez            mAppBarView.setAppIcon(mMediaSource.getRoundPackageIcon());
4265a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez            mAppBarView.setTitle(mMediaSource.getName());
4275a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez        } else {
4285a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez            mAppBarView.setAppIcon(null);
4295a5a06b91563988c671319c68d9ce1a66f352a95Roberto Perez            mAppBarView.setTitle(null);
43064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer        }
431d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
43264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer
433d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private boolean isCurrentMediaSourcePlaying() {
4348a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        return mMediaSource != null
4358a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                && mActiveMediaSourceManager.isPlaying(mMediaSource.getPackageName());
436d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
437d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
438fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez    /**
439fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     * Updates the tabs displayed on the app bar, based on the top level items on the browse tree.
440fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     * If there is at least one browsable item, we show the browse content of that node.
441fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     * If there are only playable items, then we show those items.
442fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     * If there are not items at all, we show the empty message.
443fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     * If we receive null, we show the error message.
444fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     *
445fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     * @param items top level items, or null if there was an error trying load those items.
446fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     */
447d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private void updateTabs(List<MediaItemMetadata> items) {
448fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        if (items == null || items.isEmpty()) {
449fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            mAppBarView.setItems(null);
450fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            updateBrowseFragment(items == null ? BrowseState.ERROR : BrowseState.EMPTY, null);
451fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            return;
452fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        }
453fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez
454450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        items = customizeTabs(mMediaSource, items);
455d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        List<MediaItemMetadata> browsableTopLevel = items.stream()
456d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez                .filter(item -> item.isBrowsable())
457d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez                .collect(Collectors.toList());
458d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
459a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez        if (!browsableTopLevel.isEmpty()) {
460a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            // If we have at least a few browsable items, we show the tabs
461a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            mAppBarView.setItems(browsableTopLevel);
462fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            updateBrowseFragment(BrowseState.LOADED, browsableTopLevel.get(0));
463d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        } else {
464a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            // Otherwise, we show the top of the tree with no fabs
465a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            mAppBarView.setItems(null);
466fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            updateBrowseFragment(BrowseState.LOADED, null);
467d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        }
468d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
469d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
470450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    /**
471450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez     * Extension point used to customize media items displayed on the tabs.
472450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez     *
473450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez     * @param mediaSource media source these items belong to.
474450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez     * @param items items to override.
475450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez     * @return an updated list of items.
476450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez     * @deprecated This method will be removed on b/79089344
477450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez     */
478450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    @Deprecated
479450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    protected List<MediaItemMetadata> customizeTabs(MediaSource mediaSource,
480450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            List<MediaItemMetadata> items) {
481450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        return items;
482450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    }
483450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez
484d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private void switchToMode(Mode mode) {
4851d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        // If content forward is not enable, then we always show the playback UI (browse will be
4861d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        // done in the drawer)
4871d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        mMode = mContentForwardBrowseEnabled ? mode : Mode.PLAYBACK;
4885289168183d65b16813447c2b160adab799f1ac8Roberto Perez        updateMetadata();
4891d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        switch (mMode) {
490a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            case PLAYBACK:
491a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                ViewUtils.showViewAnimated(mPlaybackContainer, mFadeDuration);
492a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                ViewUtils.hideViewAnimated(mBrowseContainer, mFadeDuration);
493a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                mAppBarView.setState(AppBarView.State.PLAYING);
494a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                break;
495a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            case BROWSING:
496a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                ViewUtils.hideViewAnimated(mPlaybackContainer, mFadeDuration);
497a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                ViewUtils.showViewAnimated(mBrowseContainer, mFadeDuration);
498a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                mAppBarView.setState(AppBarView.State.BROWSING);
499a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                break;
500a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez        }
501d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
502d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
503a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez    /**
504a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez     * Updates the browse area with either a loading state, the root node content, or the
505a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez     * content of a particular media item.
506a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez     *
507fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     * @param state state in the process of loading browse information.
508fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     * @param topItem if state == IDLE, this will contain the item to display,
509fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez     *                or null to display the root node.
510a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez     */
511fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez    private void updateBrowseFragment(BrowseState state, MediaItemMetadata topItem) {
512fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        switch(state) {
513fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            case LOADED:
514fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                if (topItem != null) {
515fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                    mCurrentFragment = BrowseFragment.newInstance(mMediaSource, topItem);
516fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                    mAppBarView.setActiveItem(topItem);
517fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                } else {
518fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                    mCurrentFragment = BrowseFragment.newInstance(mMediaSource, null);
519fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                    mAppBarView.setActiveItem(null);
520fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                }
521fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                break;
522fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            case EMPTY:
523fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            case LOADING:
524fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            case ERROR:
525fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                mCurrentFragment = mEmptyFragment;
526fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                mEmptyFragment.setState(state, mMediaSource);
527fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                mAppBarView.setActiveItem(null);
528fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                break;
52964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer        }
530a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez        getSupportFragmentManager().beginTransaction()
531a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                .replace(R.id.fragment_container, mCurrentFragment)
532fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                .commitAllowingStateLoss();
533d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
534d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
535d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private void updateMetadata() {
536d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        if (isCurrentMediaSourcePlaying()) {
537a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            if (mMode == Mode.PLAYBACK) {
538a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                ViewUtils.hideViewAnimated(mBrowseControlsContainer, mFadeDuration);
539a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            } else {
540a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                ViewUtils.showViewAnimated(mBrowseControlsContainer, mFadeDuration);
541a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            }
542a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            MediaItemMetadata metadata = mPlaybackModel.getMetadata();
543a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            if (Objects.equals(mCurrentMetadata, metadata)) {
544a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez                return;
545a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            }
546a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            mCurrentMetadata = metadata;
54760561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez            mUpdateAlbumArtRunnable.run();
54860561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        } else {
54960561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez            mAlbumBackground.setImageBitmap(null, true);
550a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez            ViewUtils.hideViewAnimated(mBrowseControlsContainer, mFadeDuration);
55160561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        }
55260561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    }
55360561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez
55460561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    /**
55560561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez     * We might receive new album art before we are ready to display it. If that situation happens
55660561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez     * we will retrieve and render the album art when the views are already laid out.
55760561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez     */
55860561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    private Runnable mUpdateAlbumArtRunnable = new Runnable() {
55960561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        @Override
56060561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        public void run() {
56160561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez            MediaItemMetadata metadata = mPlaybackModel.getMetadata();
562d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            if (metadata != null) {
56360561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                if (mAlbumBackground.getWidth() == 0 || mAlbumBackground.getHeight() == 0) {
56460561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                    // We need to wait for the view to be measured before we can render this
56560561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                    // album art.
56660561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                    mAlbumBackground.setImageBitmap(null, false);
56760561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                    mAlbumBackground.post(this);
56860561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                } else {
56960561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                    mAlbumBackground.removeCallbacks(this);
57060561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                    metadata.getAlbumArt(MediaActivity.this,
57160561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                            mAlbumBackground.getWidth(),
57260561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                            mAlbumBackground.getHeight(),
57360561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                            false)
57460561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                            .thenAccept(bitmap -> setBackgroundImage(bitmap));
57560561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                }
576d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            } else {
57760561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                mAlbumBackground.removeCallbacks(this);
578d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez                mAlbumBackground.setImageBitmap(null, true);
579d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            }
580d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        }
58160561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    };
58264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer
583d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    private void setBackgroundImage(Bitmap bitmap) {
584d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        // TODO(b/77551865): Implement image blurring once the following issue is solved:
585d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        // b/77551557
586d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        // bitmap = ImageUtils.blur(getContext(), bitmap, mBackgroundBlurScale,
587d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        //        mBackgroundBlurRadius);
588d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        mAlbumBackground.setImageBitmap(bitmap, true);
589d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
590d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
591d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    @Override
592d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    public MediaSource getMediaSource(String packageName) {
593d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        if (mMediaSource != null && mMediaSource.getPackageName().equals(packageName)) {
594d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez            return mMediaSource;
595d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        }
596d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        return new MediaSource(this, packageName);
597d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
59864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer
599d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    @Override
600d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    public void onBackStackChanged() {
601d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        // TODO: Update ActionBar
602d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
603d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez
604d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    @Override
605d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    public void onPlayableItemClicked(MediaSource mediaSource, MediaItemMetadata item) {
606d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        mPlaybackModel.onStop();
6078a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        if (!Objects.equals(mediaSource, mPlaybackModel.getMediaSource())) {
6088a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            Log.w(TAG, "Trying to play an item from a different source "
6098a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                + "(expected: " + mPlaybackModel.getMediaSource() + ", received"
6108a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez                + mediaSource + ")");
6118a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            changeMediaSource(mediaSource, mediaSource.getMediaController());
6128a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        }
6138a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        mPlaybackModel.onPlayItem(item.getId());
614d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez        setIntent(null);
615d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez    }
61660561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez
61760561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    private void openAppSelector() {
61860561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        mIsAppSelectorOpen = true;
61960561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        FragmentManager manager = getSupportFragmentManager();
62060561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        mAppBarView.setState(AppBarView.State.APP_SELECTION);
62160561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        manager.beginTransaction()
62260561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                .replace(R.id.app_selection_container, mAppSelectionFragment)
62360561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                .commit();
62460561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    }
62560561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez
62660561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    private void closeAppSelector() {
62760561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        mIsAppSelectorOpen = false;
62860561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        FragmentManager manager = getSupportFragmentManager();
629fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez        mAppBarView.setState(mMode == Mode.PLAYBACK ? AppBarView.State.PLAYING
630fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez                : AppBarView.State.BROWSING);
63160561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        manager.beginTransaction()
63260561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                .remove(mAppSelectionFragment)
63360561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                .commit();
63460561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    }
63560561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez
63660561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    @Override
63760561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    public List<MediaSource> getMediaSources() {
63860561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        return mMediaSourcesManager.getMediaSources()
63960561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                .stream()
640e8f6fb8b486c5ae99452d2fc93a922d80df1d76dRoberto Perez                .filter(source -> source.getMediaBrowser() != null || source.isCustom())
64160561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez                .collect(Collectors.toList());
64260561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    }
64360561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez
64460561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    @Override
64560561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    public void onMediaSourceSelected(MediaSource mediaSource) {
64660561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        closeAppSelector();
647450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        if (mediaSource.getMediaBrowser() != null && !mediaSource.isCustom()) {
648fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez            mCurrentMetadata = null;
6498a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez            changeMediaSource(mediaSource, null);
650450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            switchToMode(Mode.BROWSING);
65160561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        } else {
652450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            String packageName = mediaSource.getPackageName();
65360561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez            Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
65460561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez            startActivity(intent);
65560561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez        }
65660561a06d4b8f10ca8c6f09f20c91b50fb6d3fa3Roberto Perez    }
657450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez
658450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    private MediaSource getLastMediaSource() {
659450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        String packageName = mSharedPreferences.getString(LAST_MEDIA_SOURCE_SHARED_PREF_KEY, null);
660450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        if (packageName == null) {
661450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            return null;
662450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        }
663450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        // Verify that the stored package name corresponds to a currently installed media source.
664450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        for (MediaSource mediaSource : mMediaSourcesManager.getMediaSources()) {
665450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            if (mediaSource.getPackageName().equals(packageName)) {
666450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez                return mediaSource;
667450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez            }
668450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        }
669450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        return null;
670450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    }
671450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez
672450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    private void setLastMediaSource(@NonNull MediaSource mediaSource) {
673450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez        mSharedPreferences.edit()
674450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez                .putString(LAST_MEDIA_SOURCE_SHARED_PREF_KEY, mediaSource.getPackageName())
675450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez                .apply();
676450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez    }
6771d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez
6781d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez
6791d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez    @Override
6808a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez    public PlaybackModel getPlaybackModel() {
6818a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez        return mPlaybackModel;
6828a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez    }
6838a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez
6848a97fbcb2a74f960798dac29bca522cc8870daf3Roberto Perez    @Override
6851d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez    public void onQueueButtonClicked() {
6861d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        if (mContentForwardBrowseEnabled) {
6871d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez            mPlaybackFragment.toggleQueueVisibility();
6881d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        } else {
6891d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez            mDrawerController.showPlayQueue();
6901d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez        }
6911d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez    }
692349880a1749d601dea9f20106b3dd09ee47f8eb9Anthony Chen}
693