void refreshRoute() {
    if (mAttachedToWindow) {
      final MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
      final boolean isRemote = !route.isDefaultOrBluetooth() && route.matchesSelector(mSelector);
      final boolean isConnecting = isRemote && route.isConnecting();

      boolean needsRefresh = false;
      if (mRemoteActive != isRemote) {
        mRemoteActive = isRemote;
        needsRefresh = true;
      }
      if (mIsConnecting != isConnecting) {
        mIsConnecting = isConnecting;
        needsRefresh = true;
      }

      if (needsRefresh) {
        updateContentDescription();
        refreshDrawableState();
        if (mRemoteIndicator.getCurrent() instanceof AnimationDrawable) {
          AnimationDrawable curDrawable = (AnimationDrawable) mRemoteIndicator.getCurrent();
          if (!curDrawable.isRunning()) {
            curDrawable.start();
          }
        }
      }
    }
  }
  /**
   * Show the route chooser or controller dialog.
   *
   * <p>If the default route is selected or if the currently selected route does not match the
   * {@link #getRouteSelector selector}, then shows the route chooser dialog. Otherwise, shows the
   * route controller dialog to offer the user a choice to disconnect from the route or perform
   * other control actions such as setting the route's volume.
   *
   * <p>The application can customize the dialogs by calling {@link #setDialogFactory} to provide a
   * customized dialog factory.
   *
   * @return True if the dialog was actually shown.
   * @throws IllegalStateException if the activity is not a subclass of {@link FragmentActivity}.
   */
  public boolean showDialog() {
    if (!mAttachedToWindow) {
      return false;
    }

    final FragmentManager fm = getFragmentManager();
    if (fm == null) {
      throw new IllegalStateException("The activity must be a subclass of FragmentActivity");
    }

    MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
    if (route.isDefaultOrBluetooth() || !route.matchesSelector(mSelector)) {
      if (fm.findFragmentByTag(CHOOSER_FRAGMENT_TAG) != null) {
        Log.w(TAG, "showDialog(): Route chooser dialog already showing!");
        return false;
      }
      MediaRouteChooserDialogFragment f = mDialogFactory.onCreateChooserDialogFragment();
      f.setRouteSelector(mSelector);
      f.show(fm, CHOOSER_FRAGMENT_TAG);
    } else {
      if (fm.findFragmentByTag(CONTROLLER_FRAGMENT_TAG) != null) {
        Log.w(TAG, "showDialog(): Route controller dialog already showing!");
        return false;
      }
      MediaRouteControllerDialogFragment f = mDialogFactory.onCreateControllerDialogFragment();
      f.show(fm, CONTROLLER_FRAGMENT_TAG);
    }
    return true;
  }
  @Override
  public void unregisterRoute(MediaRouter router) {
    if (mRemoteControl == null) {
      return;
    }

    router.removeRemoteControlClient(mRemoteControl);
  }
  /**
   * Test method for {@link AbstractMediaRouteController#addMediaStateListener(MediaStateListener)}
   * and {@link AbstractMediaRouteController#removeMediaStateListener(MediaStateListener)}
   *
   * <p>Makes sure that listeners gets notified when they are added and don't get notified when
   * removed.
   */
  @Test
  @Feature({"MediaRemote"})
  public void testAddRemoveMediaStateListener() {
    MediaStateListener listener = mock(MediaStateListener.class);
    MediaStateListener otherListener = mock(MediaStateListener.class);
    MediaRouter mediaRouter = ShadowMediaRouter.createCapturingMock();
    when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt())).thenReturn(true);
    ShadowMediaRouter.sMediaRouter = mediaRouter;

    // Creating the MediaRouteController
    AbstractMediaRouteController mediaRouteController = new DummyMediaRouteController();
    assertNotNull(mediaRouteController.getMediaRouter());

    // 1. #addMediaStateListener()
    mediaRouteController.addMediaStateListener(listener);

    // The route selector should get notified of new states.
    verify(mediaRouter).addCallback(any(MediaRouteSelector.class), any(Callback.class), anyInt());
    verify(listener).onRouteAvailabilityChanged(true);

    // Check the behavior difference between the first and subsequent additions.
    mediaRouteController.addMediaStateListener(otherListener);
    verify(otherListener).onRouteAvailabilityChanged(true);
    // The call count should not change.
    verify(mediaRouter).addCallback(any(MediaRouteSelector.class), any(Callback.class), anyInt());
    verify(listener).onRouteAvailabilityChanged(true);

    // 2. #removeMediaStateListener()
    mediaRouteController.removeMediaStateListener(otherListener);

    // The removed listener should not be notified of changes anymore.
    when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt())).thenReturn(false);
    ShadowMediaRouter.sCallback.onRouteRemoved(mediaRouter, null);
    verifyNoMoreInteractions(otherListener);
    verify(listener).onRouteAvailabilityChanged(false);
    verify(mediaRouter, times(0)).removeCallback(any(Callback.class));

    mediaRouteController.removeMediaStateListener(listener);

    when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt())).thenReturn(true);
    ShadowMediaRouter.sCallback.onRouteAdded(mediaRouter, null);
    verifyNoMoreInteractions(otherListener);
    verifyNoMoreInteractions(listener);
    verify(mediaRouter).removeCallback(any(Callback.class));
  }
 @Override
 public void onDisconnected() {
   LogHelper.d(TAG, "onDisconnected");
   mSessionExtras.remove(EXTRA_CONNECTED_CAST);
   mSession.setExtras(mSessionExtras);
   Playback playback = new LocalPlayback(MusicService.this, mMusicProvider);
   mMediaRouter.setMediaSession(null);
   switchToPlayer(playback, false);
 }
  @Override
  public void onDetachedFromWindow() {
    mAttachedToWindow = false;
    if (!mSelector.isEmpty()) {
      mRouter.removeCallback(mCallback);
    }

    super.onDetachedFromWindow();
  }
  /**
   * Sets the media route selector for filtering the routes that the user can select using the media
   * route chooser dialog.
   *
   * @param selector The selector, must not be null.
   */
  public void setRouteSelector(MediaRouteSelector selector) {
    if (selector == null) {
      throw new IllegalArgumentException("selector must not be null");
    }

    if (!mSelector.equals(selector)) {
      if (mAttachedToWindow) {
        if (!mSelector.isEmpty()) {
          mRouter.removeCallback(mCallback);
        }
        if (!selector.isEmpty()) {
          mRouter.addCallback(selector, mCallback);
        }
      }
      mSelector = selector;
      refreshRoute();
    }
  }
  @Override
  public void onAttachedToWindow() {
    super.onAttachedToWindow();

    mAttachedToWindow = true;
    if (!mSelector.isEmpty()) {
      mRouter.addCallback(mSelector, mCallback);
    }
    refreshRoute();
  }
 @Override
 public void onApplicationConnected(
     ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
   // In case we are casting, send the device name as an extra on MediaSession metadata.
   mSessionExtras.putString(EXTRA_CONNECTED_CAST, mCastManager.getDeviceName());
   mSession.setExtras(mSessionExtras);
   // Now we can switch to CastPlayback
   Playback playback = new CastPlayback(MusicService.this, mMusicProvider);
   mMediaRouter.setMediaSession(mSession);
   switchToPlayer(playback, true);
 }
  /** Test method for {@link AbstractMediaRouteController#isRemotePlaybackAvailable()}. */
  @Test
  @Feature({"MediaRemote"})
  public void testIsRemotePlaybackAvailable() {
    MediaRouter mediaRouter = mock(MediaRouter.class);
    AbstractMediaRouteController mediaRouteCtrl = new DummyMediaRouteController();
    when(mediaRouter.getSelectedRoute())
        .thenReturn(createRouteInfo(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE));
    when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt())).thenReturn(true);

    // Default
    assertFalse(mediaRouteCtrl.isRemotePlaybackAvailable());

    ShadowMediaRouter.sMediaRouter = mediaRouter;
    mediaRouteCtrl = spy(new DummyMediaRouteController());
    assertTrue(mediaRouteCtrl.isRemotePlaybackAvailable());

    when(mediaRouter.getSelectedRoute())
        .thenReturn(createRouteInfo(MediaRouter.RouteInfo.PLAYBACK_TYPE_LOCAL));
    assertTrue(mediaRouteCtrl.isRemotePlaybackAvailable());

    when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt())).thenReturn(false);
    assertFalse(mediaRouteCtrl.isRemotePlaybackAvailable());

    when(mediaRouter.getSelectedRoute())
        .thenReturn(createRouteInfo(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE));
    assertTrue(mediaRouteCtrl.isRemotePlaybackAvailable());
  }
  public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) {
    super(MediaRouterThemeHelper.createThemedContext(context, defStyleAttr), attrs, defStyleAttr);
    context = getContext();

    mRouter = MediaRouter.getInstance(context);
    mCallback = new MediaRouterCallback();

    TypedArray a =
        context.obtainStyledAttributes(attrs, R.styleable.MediaRouteButton, defStyleAttr, 0);
    setRemoteIndicatorDrawable(
        a.getDrawable(R.styleable.MediaRouteButton_externalRouteEnabledDrawable));
    mMinWidth = a.getDimensionPixelSize(R.styleable.MediaRouteButton_android_minWidth, 0);
    mMinHeight = a.getDimensionPixelSize(R.styleable.MediaRouteButton_android_minHeight, 0);
    a.recycle();

    updateContentDescription();
    setClickable(true);
    setLongClickable(true);
  }
  /*
   * (non-Javadoc)
   * @see android.app.Service#onCreate()
   */
  @Override
  public void onCreate() {
    super.onCreate();
    LogHelper.d(TAG, "onCreate");

    mPlayingQueue = new ArrayList<>();
    mMusicProvider = new MusicProvider();
    mPackageValidator = new PackageValidator(this);

    // Start a new MediaSession
    mSession = new MediaSession(this, "MusicService");
    setSessionToken(mSession.getSessionToken());
    mSession.setCallback(new MediaSessionCallback());
    mSession.setFlags(
        MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);

    mPlayback = new LocalPlayback(this, mMusicProvider);
    mPlayback.setState(PlaybackState.STATE_NONE);
    mPlayback.setCallback(this);
    mPlayback.start();

    Context context = getApplicationContext();
    Intent intent = new Intent(context, NowPlayingActivity.class);
    PendingIntent pi =
        PendingIntent.getActivity(
            context, 99 /*request code*/, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    mSession.setSessionActivity(pi);

    mSessionExtras = new Bundle();
    CarHelper.setSlotReservationFlags(mSessionExtras, true, true, true);
    mSession.setExtras(mSessionExtras);

    updatePlaybackState(null);

    mMediaNotificationManager = new MediaNotificationManager(this);
    mCastManager = ((UAMPApplication) getApplication()).getCastManager(getApplicationContext());

    mCastManager.addVideoCastConsumer(mCastConsumer);
    mMediaRouter = MediaRouter.getInstance(getApplicationContext());
  }
 public void removeFromMediaRouter(MediaRouter router) {
   if (null != mActualRemoteControlClient) {
     router.removeRemoteControlClient(mActualRemoteControlClient);
   }
 }
 /**
  * Registers with {@link MediaRouter}
  *
  * @param router
  */
 public void addToMediaRouter(MediaRouter router) {
   if (null != mActualRemoteControlClient) {
     router.addRemoteControlClient(mActualRemoteControlClient);
   }
 }
  /*
   * (non-Javadoc)
   * @see android.app.Service#onCreate()
   */
  @Override
  public void onCreate() {
    super.onCreate();
    LogHelper.d(TAG, "onCreate");

    mPlayingQueue = new ArrayList<>();
    mMusicProvider = new MusicProvider();
    mPackageValidator = new PackageValidator(this);

    ComponentName mediaButtonReceiver = new ComponentName(this, RemoteControlReceiver.class);

    Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    mediaButtonIntent.setComponent(mediaButtonReceiver);
    PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0);

    // Start a new MediaSessionCompat
    mSession =
        new MediaSessionCompat(this, "MusicService", mediaButtonReceiver, mediaPendingIntent);

    /*
    final MediaSessionCallback cb = new MediaSessionCallback();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // Shouldn't really have to do this but the MediaSessionCompat method uses
        // an internal proxy class, which doesn't forward events such as
        // onPlayFromMediaId when running on Lollipop.
        final MediaSession session = (MediaSession) mSession.getMediaSession();
        session.setCallback(new MediaSessionCallbackProxy(cb));
    } else {
        mSession.setCallback(cb);
    }
    */

    setSessionToken(mSession.getSessionToken());
    mSession.setCallback(new MediaSessionCallback());
    mSession.setFlags(
        MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
            | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

    mPlayback = new LocalPlayback(this, mMusicProvider);
    mPlayback.setState(PlaybackStateCompat.STATE_NONE);
    mPlayback.setCallback(this);
    mPlayback.start();

    Context context = getApplicationContext();
    Intent intent = new Intent(context, NowPlayingActivity.class);
    PendingIntent pi =
        PendingIntent.getActivity(
            context, 99 /*request code*/, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    mSession.setSessionActivity(pi);

    mSessionExtras = new Bundle();
    CarHelper.setSlotReservationFlags(mSessionExtras, true, true, true);
    WearHelper.setSlotReservationFlags(mSessionExtras, true, true);
    WearHelper.setUseBackgroundFromTheme(mSessionExtras, true);
    mSession.setExtras(mSessionExtras);

    updatePlaybackState(null);

    mMediaNotificationManager = new MediaNotificationManager(this);
    mCastManager = VideoCastManager.getInstance();
    mCastManager.addVideoCastConsumer(mCastConsumer);
    mMediaRouter = MediaRouter.getInstance(getApplicationContext());

    IntentFilter filter = new IntentFilter(CarHelper.ACTION_MEDIA_STATUS);
    mCarConnectionReceiver =
        new BroadcastReceiver() {
          @Override
          public void onReceive(Context context, Intent intent) {
            String connectionEvent = intent.getStringExtra(CarHelper.MEDIA_CONNECTION_STATUS);
            mIsConnectedToCar = CarHelper.MEDIA_CONNECTED.equals(connectionEvent);
            LogHelper.i(
                TAG,
                "Connection event to Android Auto: ",
                connectionEvent,
                " isConnectedToCar=",
                mIsConnectedToCar);
          }
        };
    registerReceiver(mCarConnectionReceiver, filter);
  }