1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.car.media; 17 18 import android.annotation.NonNull; 19 import android.car.Car; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.SharedPreferences; 24 import android.graphics.Bitmap; 25 import android.media.session.MediaController; 26 import android.os.Bundle; 27 import android.support.v4.app.Fragment; 28 import android.support.v4.app.FragmentManager; 29 import android.support.v4.widget.DrawerLayout; 30 import android.transition.Fade; 31 import android.util.Log; 32 import android.util.TypedValue; 33 import android.view.View; 34 import android.view.ViewGroup; 35 36 import com.android.car.media.common.ActiveMediaSourceManager; 37 import com.android.car.media.common.CrossfadeImageView; 38 import com.android.car.media.common.MediaItemMetadata; 39 import com.android.car.media.common.MediaSource; 40 import com.android.car.media.common.MediaSourcesManager; 41 import com.android.car.media.common.PlaybackControls; 42 import com.android.car.media.common.PlaybackModel; 43 import com.android.car.media.drawer.MediaDrawerController; 44 import com.android.car.media.widgets.AppBarView; 45 import com.android.car.media.widgets.MetadataView; 46 import com.android.car.media.widgets.ViewUtils; 47 48 import java.util.ArrayList; 49 import java.util.List; 50 import java.util.Objects; 51 import java.util.stream.Collectors; 52 53 import androidx.car.drawer.CarDrawerActivity; 54 import androidx.car.drawer.CarDrawerAdapter; 55 56 /** 57 * This activity controls the UI of media. It also updates the connection status for the media app 58 * by broadcast. Drawer menu is controlled by {@link MediaDrawerController}. 59 */ 60 public class MediaActivity extends CarDrawerActivity implements BrowseFragment.Callbacks, 61 AppSelectionFragment.Callbacks, PlaybackFragment.Callbacks { 62 private static final String TAG = "MediaActivity"; 63 64 /** Intent extra specifying the package with the MediaBrowser */ 65 public static final String KEY_MEDIA_PACKAGE = "media_package"; 66 /** Shared preferences files */ 67 public static final String SHARED_PREF = "com.android.car.media"; 68 /** Shared preference containing the last controlled source */ 69 public static final String LAST_MEDIA_SOURCE_SHARED_PREF_KEY = "last_media_source"; 70 71 /** Configuration (controlled from resources) */ 72 private boolean mContentForwardBrowseEnabled; 73 private float mBackgroundBlurRadius; 74 private float mBackgroundBlurScale; 75 private int mFadeDuration; 76 77 /** Models */ 78 private MediaDrawerController mDrawerController; 79 private ActiveMediaSourceManager mActiveMediaSourceManager; 80 private MediaSource mMediaSource; 81 private PlaybackModel mPlaybackModel; 82 private MediaSourcesManager mMediaSourcesManager; 83 private SharedPreferences mSharedPreferences; 84 85 /** Layout views */ 86 private AppBarView mAppBarView; 87 private CrossfadeImageView mAlbumBackground; 88 private PlaybackFragment mPlaybackFragment; 89 private AppSelectionFragment mAppSelectionFragment; 90 private PlaybackControls mPlaybackControls; 91 private MetadataView mMetadataView; 92 private ViewGroup mBrowseControlsContainer; 93 private EmptyFragment mEmptyFragment; 94 private ViewGroup mBrowseContainer; 95 private ViewGroup mPlaybackContainer; 96 97 /** Current state */ 98 private Fragment mCurrentFragment; 99 private Mode mMode = Mode.BROWSING; 100 private boolean mIsAppSelectorOpen; 101 private MediaItemMetadata mCurrentMetadata; 102 103 private MediaSource.Observer mMediaSourceObserver = new MediaSource.Observer() { 104 @Override 105 protected void onBrowseConnected(boolean success) { 106 MediaActivity.this.onBrowseConnected(success); 107 } 108 109 @Override 110 protected void onBrowseDisconnected() { 111 MediaActivity.this.onBrowseConnected(false); 112 } 113 }; 114 private PlaybackModel.PlaybackObserver mPlaybackObserver = 115 new PlaybackModel.PlaybackObserver() { 116 @Override 117 protected void onSourceChanged() { 118 updateMetadata(); 119 } 120 121 @Override 122 public void onMetadataChanged() { 123 mCurrentMetadata = null; 124 updateMetadata(); 125 } 126 }; 127 private ActiveMediaSourceManager.Observer mActiveSourceObserver = () -> { 128 // If the active media source changes and it is the one currently being browsed, then 129 // we should capture the controller. 130 MediaController controller = mActiveMediaSourceManager.getMediaController(); 131 if (mPlaybackModel.getMediaController() == null 132 && mMediaSource != null 133 && controller != null 134 && Objects.equals(controller.getPackageName(), mMediaSource.getPackageName())) { 135 mPlaybackModel.setMediaController(controller); 136 } 137 }; 138 private MediaSource.ItemsSubscription mItemsSubscription = 139 new MediaSource.ItemsSubscription() { 140 @Override 141 public void onChildrenLoaded(MediaSource mediaSource, String parentId, 142 List<MediaItemMetadata> items) { 143 if (mediaSource == mMediaSource) { 144 updateTabs(items); 145 } else { 146 Log.w(TAG, "Received items for a wrong source: " + 147 mediaSource.getPackageName()); 148 } 149 } 150 }; 151 private AppBarView.AppBarListener mAppBarListener = new AppBarView.AppBarListener() { 152 @Override 153 public void onTabSelected(MediaItemMetadata item) { 154 updateBrowseFragment(BrowseState.LOADED, item); 155 switchToMode(Mode.BROWSING); 156 } 157 158 @Override 159 public void onBack() { 160 if (mCurrentFragment != null && mCurrentFragment instanceof BrowseFragment) { 161 BrowseFragment fragment = (BrowseFragment) mCurrentFragment; 162 fragment.navigateBack(); 163 } 164 } 165 166 @Override 167 public void onCollapse() { 168 switchToMode(Mode.BROWSING); 169 } 170 171 @Override 172 public void onAppSelection() { 173 Log.d(TAG, "onAppSelection clicked"); 174 if (mIsAppSelectorOpen) { 175 closeAppSelector(); 176 } else { 177 openAppSelector(); 178 } 179 } 180 }; 181 private MediaSourcesManager.Observer mMediaSourcesManagerObserver = () -> { 182 mAppBarView.setAppSelection(!mMediaSourcesManager.getMediaSources().isEmpty()); 183 mAppSelectionFragment.refresh(); 184 }; 185 private DrawerLayout.DrawerListener mDrawerListener = new DrawerLayout.DrawerListener() { 186 @Override 187 public void onDrawerSlide(@android.support.annotation.NonNull View view, float v) { 188 } 189 190 @Override 191 public void onDrawerOpened(@android.support.annotation.NonNull View view) { 192 closeAppSelector(); 193 } 194 195 @Override 196 public void onDrawerClosed(@android.support.annotation.NonNull View view) { 197 } 198 199 @Override 200 public void onDrawerStateChanged(int i) { 201 } 202 }; 203 204 /** 205 * Possible modes of the application UI 206 */ 207 private enum Mode { 208 /** The user is browsing a media source */ 209 BROWSING, 210 /** The user is interacting with the full screen playback UI */ 211 PLAYBACK 212 } 213 214 /** 215 * Possible states of the application UI 216 */ 217 public enum BrowseState { 218 /** There is no content to show */ 219 EMPTY, 220 /** We are still in the process of obtaining data */ 221 LOADING, 222 /** Data has been loaded */ 223 LOADED, 224 /** The content can't be shown due an error */ 225 ERROR 226 } 227 228 @Override 229 protected void onCreate(Bundle savedInstanceState) { 230 super.onCreate(savedInstanceState); 231 232 setMainContent(R.layout.media_activity); 233 setToolbarElevation(0f); 234 235 mContentForwardBrowseEnabled = getResources() 236 .getBoolean(R.bool.forward_content_browse_enabled); 237 mDrawerController = new MediaDrawerController(this, getDrawerController()); 238 getDrawerController().setRootAdapter(getRootAdapter()); 239 getDrawerController().addDrawerListener(mDrawerListener); 240 if (mContentForwardBrowseEnabled) { 241 getSupportActionBar().hide(); 242 } 243 mAppBarView = findViewById(R.id.app_bar); 244 mAppBarView.setListener(mAppBarListener); 245 mAppBarView.setContentForwardEnabled(mContentForwardBrowseEnabled); 246 mPlaybackFragment = new PlaybackFragment(); 247 mAppSelectionFragment = new AppSelectionFragment(); 248 int fadeDuration = getResources().getInteger(R.integer.app_selector_fade_duration); 249 mAppSelectionFragment.setEnterTransition(new Fade().setDuration(fadeDuration)); 250 mAppSelectionFragment.setExitTransition(new Fade().setDuration(fadeDuration)); 251 mActiveMediaSourceManager = new ActiveMediaSourceManager(this); 252 mPlaybackModel = new PlaybackModel(this); 253 mMediaSourcesManager = new MediaSourcesManager(this); 254 mAlbumBackground = findViewById(R.id.media_background); 255 mPlaybackControls = findViewById(R.id.browse_controls); 256 mPlaybackControls.setModel(mPlaybackModel); 257 mMetadataView = findViewById(R.id.browse_metadata); 258 mMetadataView.setModel(mPlaybackModel); 259 mBrowseControlsContainer = findViewById(R.id.browse_controls_container); 260 mBrowseControlsContainer.setOnClickListener(view -> switchToMode(Mode.PLAYBACK)); 261 TypedValue outValue = new TypedValue(); 262 getResources().getValue(R.dimen.playback_background_blur_radius, outValue, true); 263 mBackgroundBlurRadius = outValue.getFloat(); 264 getResources().getValue(R.dimen.playback_background_blur_scale, outValue, true); 265 mBackgroundBlurScale = outValue.getFloat(); 266 mSharedPreferences = getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE); 267 mFadeDuration = getResources().getInteger( 268 R.integer.new_album_art_fade_in_duration); 269 mEmptyFragment = new EmptyFragment(); 270 mBrowseContainer = findViewById(R.id.fragment_container); 271 mPlaybackContainer = findViewById(R.id.playback_container); 272 getSupportFragmentManager().beginTransaction() 273 .replace(R.id.playback_container, mPlaybackFragment) 274 .commit(); 275 } 276 277 @Override 278 public void onResume() { 279 super.onResume(); 280 mPlaybackModel.registerObserver(mPlaybackObserver); 281 mActiveMediaSourceManager.registerObserver(mActiveSourceObserver); 282 mMediaSourcesManager.registerObserver(mMediaSourcesManagerObserver); 283 handleIntent(); 284 } 285 286 @Override 287 public void onPause() { 288 super.onPause(); 289 mPlaybackModel.unregisterObserver(mPlaybackObserver); 290 mActiveMediaSourceManager.unregisterObserver(mActiveSourceObserver); 291 mMediaSourcesManager.unregisterObserver(mMediaSourcesManagerObserver); 292 } 293 294 @Override 295 public void onDestroy() { 296 super.onDestroy(); 297 mDrawerController.cleanup(); 298 mPlaybackControls.setModel(null); 299 mMetadataView.setModel(null); 300 } 301 302 @Override 303 protected CarDrawerAdapter getRootAdapter() { 304 return mDrawerController == null ? null : mDrawerController.getRootAdapter(); 305 } 306 307 @Override 308 protected void onNewIntent(Intent intent) { 309 super.onNewIntent(intent); 310 if (Log.isLoggable(TAG, Log.VERBOSE)) { 311 Log.v(TAG, "onNewIntent(); intent: " + (intent == null ? "<< NULL >>" : intent)); 312 } 313 314 setIntent(intent); 315 handleIntent(); 316 } 317 318 @Override 319 public void onBackPressed() { 320 mPlaybackFragment.closeOverflowMenu(); 321 super.onBackPressed(); 322 } 323 324 private void onBrowseConnected(boolean success) { 325 if (!success) { 326 updateTabs(null); 327 mMediaSource.unsubscribeChildren(null, mItemsSubscription); 328 mMediaSource.unsubscribe(mMediaSourceObserver); 329 updateBrowseFragment(BrowseState.ERROR, null); 330 return; 331 } 332 mMediaSource.subscribeChildren(null, mItemsSubscription); 333 if (mPlaybackModel.getMediaController() == null) { 334 mPlaybackModel.setMediaController(mMediaSource.getMediaController()); 335 } 336 } 337 338 private void handleIntent() { 339 Intent intent = getIntent(); 340 String action = intent != null ? intent.getAction() : null; 341 342 getDrawerController().closeDrawer(); 343 344 if (Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE.equals(action)) { 345 // The user either wants to browse a particular media source or switch to the 346 // playback UI. 347 String packageName = intent.getStringExtra(KEY_MEDIA_PACKAGE); 348 if (packageName != null) { 349 // We were told to navigate to a particular package: we open browse for it. 350 closeAppSelector(); 351 changeMediaSource(new MediaSource(this, packageName), null); 352 switchToMode(Mode.BROWSING); 353 return; 354 } 355 356 // If we didn't receive a package name and we are playing something: show the playback 357 // UI for the playing media source. 358 MediaController controller = mActiveMediaSourceManager.getMediaController(); 359 if (controller != null) { 360 closeAppSelector(); 361 changeMediaSource(new MediaSource(this, controller.getPackageName()), controller); 362 switchToMode(Mode.PLAYBACK); 363 return; 364 } 365 } 366 367 // In any other case, if we were already browsing something: just close drawers/overlays 368 // and display what we have. 369 if (mMediaSource != null) { 370 closeAppSelector(); 371 return; 372 } 373 374 // If we don't have a current media source, we try with the last one we remember. 375 MediaSource lastMediaSource = getLastMediaSource(); 376 if (lastMediaSource != null) { 377 closeAppSelector(); 378 changeMediaSource(lastMediaSource, null); 379 switchToMode(Mode.BROWSING); 380 } else { 381 // If we don't have anything from before: open the app selector. 382 openAppSelector(); 383 } 384 } 385 386 /** 387 * Sets the media source being browsed. 388 * 389 * @param mediaSource the media source we are going to try to browse 390 * @param controller a controller we can use to control the playback state of the given 391 * source. If not provided, we will try to obtain it from the session manager. 392 * Otherwise, we will obtain a controller once the media browser is connected. 393 */ 394 private void changeMediaSource(MediaSource mediaSource, MediaController controller) { 395 if (Objects.equals(mediaSource, mMediaSource)) { 396 // No change, nothing to do. 397 return; 398 } 399 if (mMediaSource != null) { 400 mMediaSource.unsubscribeChildren(null, mItemsSubscription); 401 mMediaSource.unsubscribe(mMediaSourceObserver); 402 updateTabs(new ArrayList<>()); 403 } 404 mMediaSource = mediaSource; 405 mPlaybackModel.setMediaController(controller != null ? controller 406 : mActiveMediaSourceManager.getControllerForPackage(mediaSource.getPackageName())); 407 setLastMediaSource(mMediaSource); 408 if (mMediaSource != null) { 409 if (Log.isLoggable(TAG, Log.INFO)) { 410 Log.i(TAG, "Browsing: " + mediaSource.getName()); 411 } 412 // Prepare the media source for playback 413 mPlaybackModel.onPrepare(); 414 // Make the drawer display browse information of the selected source 415 ComponentName component = mMediaSource.getBrowseServiceComponentName(); 416 MediaManager.getInstance(this).setMediaClientComponent(component); 417 // If content forward browsing is disabled, then no need to subscribe to this media 418 // source, we will use the drawer instead. 419 if (mContentForwardBrowseEnabled) { 420 Log.i(TAG, "Content forward is enabled: subscribing to " + 421 mMediaSource.getPackageName()); 422 updateBrowseFragment(BrowseState.LOADING, null); 423 mMediaSource.subscribe(mMediaSourceObserver); 424 } 425 mAppBarView.setAppIcon(mMediaSource.getRoundPackageIcon()); 426 mAppBarView.setTitle(mMediaSource.getName()); 427 } else { 428 mAppBarView.setAppIcon(null); 429 mAppBarView.setTitle(null); 430 } 431 } 432 433 private boolean isCurrentMediaSourcePlaying() { 434 return mMediaSource != null 435 && mActiveMediaSourceManager.isPlaying(mMediaSource.getPackageName()); 436 } 437 438 /** 439 * Updates the tabs displayed on the app bar, based on the top level items on the browse tree. 440 * If there is at least one browsable item, we show the browse content of that node. 441 * If there are only playable items, then we show those items. 442 * If there are not items at all, we show the empty message. 443 * If we receive null, we show the error message. 444 * 445 * @param items top level items, or null if there was an error trying load those items. 446 */ 447 private void updateTabs(List<MediaItemMetadata> items) { 448 if (items == null || items.isEmpty()) { 449 mAppBarView.setItems(null); 450 updateBrowseFragment(items == null ? BrowseState.ERROR : BrowseState.EMPTY, null); 451 return; 452 } 453 454 items = customizeTabs(mMediaSource, items); 455 List<MediaItemMetadata> browsableTopLevel = items.stream() 456 .filter(item -> item.isBrowsable()) 457 .collect(Collectors.toList()); 458 459 if (!browsableTopLevel.isEmpty()) { 460 // If we have at least a few browsable items, we show the tabs 461 mAppBarView.setItems(browsableTopLevel); 462 updateBrowseFragment(BrowseState.LOADED, browsableTopLevel.get(0)); 463 } else { 464 // Otherwise, we show the top of the tree with no fabs 465 mAppBarView.setItems(null); 466 updateBrowseFragment(BrowseState.LOADED, null); 467 } 468 } 469 470 /** 471 * Extension point used to customize media items displayed on the tabs. 472 * 473 * @param mediaSource media source these items belong to. 474 * @param items items to override. 475 * @return an updated list of items. 476 * @deprecated This method will be removed on b/79089344 477 */ 478 @Deprecated 479 protected List<MediaItemMetadata> customizeTabs(MediaSource mediaSource, 480 List<MediaItemMetadata> items) { 481 return items; 482 } 483 484 private void switchToMode(Mode mode) { 485 // If content forward is not enable, then we always show the playback UI (browse will be 486 // done in the drawer) 487 mMode = mContentForwardBrowseEnabled ? mode : Mode.PLAYBACK; 488 updateMetadata(); 489 switch (mMode) { 490 case PLAYBACK: 491 ViewUtils.showViewAnimated(mPlaybackContainer, mFadeDuration); 492 ViewUtils.hideViewAnimated(mBrowseContainer, mFadeDuration); 493 mAppBarView.setState(AppBarView.State.PLAYING); 494 break; 495 case BROWSING: 496 ViewUtils.hideViewAnimated(mPlaybackContainer, mFadeDuration); 497 ViewUtils.showViewAnimated(mBrowseContainer, mFadeDuration); 498 mAppBarView.setState(AppBarView.State.BROWSING); 499 break; 500 } 501 } 502 503 /** 504 * Updates the browse area with either a loading state, the root node content, or the 505 * content of a particular media item. 506 * 507 * @param state state in the process of loading browse information. 508 * @param topItem if state == IDLE, this will contain the item to display, 509 * or null to display the root node. 510 */ 511 private void updateBrowseFragment(BrowseState state, MediaItemMetadata topItem) { 512 switch(state) { 513 case LOADED: 514 if (topItem != null) { 515 mCurrentFragment = BrowseFragment.newInstance(mMediaSource, topItem); 516 mAppBarView.setActiveItem(topItem); 517 } else { 518 mCurrentFragment = BrowseFragment.newInstance(mMediaSource, null); 519 mAppBarView.setActiveItem(null); 520 } 521 break; 522 case EMPTY: 523 case LOADING: 524 case ERROR: 525 mCurrentFragment = mEmptyFragment; 526 mEmptyFragment.setState(state, mMediaSource); 527 mAppBarView.setActiveItem(null); 528 break; 529 } 530 getSupportFragmentManager().beginTransaction() 531 .replace(R.id.fragment_container, mCurrentFragment) 532 .commitAllowingStateLoss(); 533 } 534 535 private void updateMetadata() { 536 if (isCurrentMediaSourcePlaying()) { 537 if (mMode == Mode.PLAYBACK) { 538 ViewUtils.hideViewAnimated(mBrowseControlsContainer, mFadeDuration); 539 } else { 540 ViewUtils.showViewAnimated(mBrowseControlsContainer, mFadeDuration); 541 } 542 MediaItemMetadata metadata = mPlaybackModel.getMetadata(); 543 if (Objects.equals(mCurrentMetadata, metadata)) { 544 return; 545 } 546 mCurrentMetadata = metadata; 547 mUpdateAlbumArtRunnable.run(); 548 } else { 549 mAlbumBackground.setImageBitmap(null, true); 550 ViewUtils.hideViewAnimated(mBrowseControlsContainer, mFadeDuration); 551 } 552 } 553 554 /** 555 * We might receive new album art before we are ready to display it. If that situation happens 556 * we will retrieve and render the album art when the views are already laid out. 557 */ 558 private Runnable mUpdateAlbumArtRunnable = new Runnable() { 559 @Override 560 public void run() { 561 MediaItemMetadata metadata = mPlaybackModel.getMetadata(); 562 if (metadata != null) { 563 if (mAlbumBackground.getWidth() == 0 || mAlbumBackground.getHeight() == 0) { 564 // We need to wait for the view to be measured before we can render this 565 // album art. 566 mAlbumBackground.setImageBitmap(null, false); 567 mAlbumBackground.post(this); 568 } else { 569 mAlbumBackground.removeCallbacks(this); 570 metadata.getAlbumArt(MediaActivity.this, 571 mAlbumBackground.getWidth(), 572 mAlbumBackground.getHeight(), 573 false) 574 .thenAccept(bitmap -> setBackgroundImage(bitmap)); 575 } 576 } else { 577 mAlbumBackground.removeCallbacks(this); 578 mAlbumBackground.setImageBitmap(null, true); 579 } 580 } 581 }; 582 583 private void setBackgroundImage(Bitmap bitmap) { 584 // TODO(b/77551865): Implement image blurring once the following issue is solved: 585 // b/77551557 586 // bitmap = ImageUtils.blur(getContext(), bitmap, mBackgroundBlurScale, 587 // mBackgroundBlurRadius); 588 mAlbumBackground.setImageBitmap(bitmap, true); 589 } 590 591 @Override 592 public MediaSource getMediaSource(String packageName) { 593 if (mMediaSource != null && mMediaSource.getPackageName().equals(packageName)) { 594 return mMediaSource; 595 } 596 return new MediaSource(this, packageName); 597 } 598 599 @Override 600 public void onBackStackChanged() { 601 // TODO: Update ActionBar 602 } 603 604 @Override 605 public void onPlayableItemClicked(MediaSource mediaSource, MediaItemMetadata item) { 606 mPlaybackModel.onStop(); 607 if (!Objects.equals(mediaSource, mPlaybackModel.getMediaSource())) { 608 Log.w(TAG, "Trying to play an item from a different source " 609 + "(expected: " + mPlaybackModel.getMediaSource() + ", received" 610 + mediaSource + ")"); 611 changeMediaSource(mediaSource, mediaSource.getMediaController()); 612 } 613 mPlaybackModel.onPlayItem(item.getId()); 614 setIntent(null); 615 } 616 617 private void openAppSelector() { 618 mIsAppSelectorOpen = true; 619 FragmentManager manager = getSupportFragmentManager(); 620 mAppBarView.setState(AppBarView.State.APP_SELECTION); 621 manager.beginTransaction() 622 .replace(R.id.app_selection_container, mAppSelectionFragment) 623 .commit(); 624 } 625 626 private void closeAppSelector() { 627 mIsAppSelectorOpen = false; 628 FragmentManager manager = getSupportFragmentManager(); 629 mAppBarView.setState(mMode == Mode.PLAYBACK ? AppBarView.State.PLAYING 630 : AppBarView.State.BROWSING); 631 manager.beginTransaction() 632 .remove(mAppSelectionFragment) 633 .commit(); 634 } 635 636 @Override 637 public List<MediaSource> getMediaSources() { 638 return mMediaSourcesManager.getMediaSources() 639 .stream() 640 .filter(source -> source.getMediaBrowser() != null || source.isCustom()) 641 .collect(Collectors.toList()); 642 } 643 644 @Override 645 public void onMediaSourceSelected(MediaSource mediaSource) { 646 closeAppSelector(); 647 if (mediaSource.getMediaBrowser() != null && !mediaSource.isCustom()) { 648 mCurrentMetadata = null; 649 changeMediaSource(mediaSource, null); 650 switchToMode(Mode.BROWSING); 651 } else { 652 String packageName = mediaSource.getPackageName(); 653 Intent intent = getPackageManager().getLaunchIntentForPackage(packageName); 654 startActivity(intent); 655 } 656 } 657 658 private MediaSource getLastMediaSource() { 659 String packageName = mSharedPreferences.getString(LAST_MEDIA_SOURCE_SHARED_PREF_KEY, null); 660 if (packageName == null) { 661 return null; 662 } 663 // Verify that the stored package name corresponds to a currently installed media source. 664 for (MediaSource mediaSource : mMediaSourcesManager.getMediaSources()) { 665 if (mediaSource.getPackageName().equals(packageName)) { 666 return mediaSource; 667 } 668 } 669 return null; 670 } 671 672 private void setLastMediaSource(@NonNull MediaSource mediaSource) { 673 mSharedPreferences.edit() 674 .putString(LAST_MEDIA_SOURCE_SHARED_PREF_KEY, mediaSource.getPackageName()) 675 .apply(); 676 } 677 678 679 @Override 680 public PlaybackModel getPlaybackModel() { 681 return mPlaybackModel; 682 } 683 684 @Override 685 public void onQueueButtonClicked() { 686 if (mContentForwardBrowseEnabled) { 687 mPlaybackFragment.toggleQueueVisibility(); 688 } else { 689 mDrawerController.showPlayQueue(); 690 } 691 } 692 } 693