@Override
  public void play(QueueItem item) {
    mPlayOnFocusGain = true;
    tryToGetAudioFocus();
    registerAudioNoisyReceiver();
    String mediaId = item.getDescription().getMediaId();
    boolean mediaHasChanged = !TextUtils.equals(mediaId, mCurrentMediaId);
    if (mediaHasChanged) {
      mCurrentPosition = 0;
      mCurrentMediaId = mediaId;
    }

    if (mState == PlaybackStateCompat.STATE_PAUSED && !mediaHasChanged && mMediaPlayer != null) {
      configMediaPlayerState();
    } else {
      mState = PlaybackStateCompat.STATE_STOPPED;
      relaxResources(false); // release everything except MediaPlayer
      MediaMetadataCompat track =
          mMusicProvider.getMusic(
              MediaIDHelper.extractMusicIDFromMediaID(item.getDescription().getMediaId()));

      String source =
          MusicProvider.getTrackSource(track.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID));

      try {
        createMediaPlayerIfNeeded();

        mState = PlaybackStateCompat.STATE_BUFFERING;

        mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mMediaPlayer.setDataSource(source);

        // Starts preparing the media player in the background. When
        // it's done, it will call our OnPreparedListener (that is,
        // the onPrepared() method on this class, since we set the
        // listener to 'this'). Until the media player is prepared,
        // we *cannot* call start() on it!
        mMediaPlayer.prepareAsync();

        // If we are streaming from the internet, we want to hold a
        // Wifi lock, which prevents the Wifi radio from going to
        // sleep while the song is playing.
        mWifiLock.acquire();

        if (mCallback != null) {
          mCallback.onPlaybackStatusChanged(mState);
        }

      } catch (IOException ex) {
        LogHelper.e(TAG, ex, "Exception playing song");
        if (mCallback != null) {
          mCallback.onError(ex.getMessage());
        }
      }
    }
  }
 @Override
 public void onCustomAction(@NonNull String action, Bundle extras) {
   if (CUSTOM_ACTION_THUMBS_UP.equals(action)) {
     LogHelper.i(TAG, "onCustomAction: favorite for current track");
     MediaMetadataCompat track = getCurrentPlayingMusic();
     if (track != null) {
       String musicId = track.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID);
       mMusicProvider.setFavorite(musicId, !mMusicProvider.isFavorite(musicId));
     }
     // playback state needs to be updated because the "Favorite" icon on the
     // custom action will change to reflect the new favorite state.
     updatePlaybackState(null);
   } else {
     LogHelper.e(TAG, "Unsupported action: ", action);
   }
 }
 private void setCustomAction(PlaybackStateCompat.Builder stateBuilder) {
   MediaMetadataCompat currentMusic = getCurrentPlayingMusic();
   if (currentMusic != null) {
     // Set appropriate "Favorite" icon on Custom action:
     String musicId = currentMusic.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID);
     int favoriteIcon = R.drawable.ic_star_off;
     if (mMusicProvider.isFavorite(musicId)) {
       favoriteIcon = R.drawable.ic_star_on;
     }
     LogHelper.d(
         TAG,
         "updatePlaybackState, setting Favorite custom action of music ",
         musicId,
         " current favorite=",
         mMusicProvider.isFavorite(musicId));
     Bundle customActionExtras = new Bundle();
     WearHelper.setShowCustomActionOnWear(customActionExtras, true);
     stateBuilder.addCustomAction(
         new PlaybackStateCompat.CustomAction.Builder(
                 CUSTOM_ACTION_THUMBS_UP, getString(R.string.favorite), favoriteIcon)
             .setExtras(customActionExtras)
             .build());
   }
 }
  private void updateMetadata() {
    if (!QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
      LogHelper.e(TAG, "Can't retrieve current metadata.");
      updatePlaybackState(getResources().getString(R.string.error_no_metadata));
      return;
    }
    MediaSessionCompat.QueueItem queueItem = mPlayingQueue.get(mCurrentIndexOnQueue);
    String musicId =
        MediaIDHelper.extractMusicIDFromMediaID(queueItem.getDescription().getMediaId());
    MediaMetadataCompat track = mMusicProvider.getMusic(musicId);
    if (track == null) {
      throw new IllegalArgumentException("Invalid musicId " + musicId);
    }
    final String trackId = track.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID);
    if (!TextUtils.equals(musicId, trackId)) {
      IllegalStateException e = new IllegalStateException("track ID should match musicId.");
      LogHelper.e(
          TAG,
          "track ID should match musicId.",
          " musicId=",
          musicId,
          " trackId=",
          trackId,
          " mediaId from queueItem=",
          queueItem.getDescription().getMediaId(),
          " title from queueItem=",
          queueItem.getDescription().getTitle(),
          " mediaId from track=",
          track.getDescription().getMediaId(),
          " title from track=",
          track.getDescription().getTitle(),
          " source.hashcode from track=",
          track.getString(MusicProvider.CUSTOM_METADATA_TRACK_SOURCE).hashCode(),
          e);
      throw e;
    }
    LogHelper.d(TAG, "Updating metadata for MusicID= " + musicId);
    mSession.setMetadata(track);

    // Set the proper album artwork on the media session, so it can be shown in the
    // locked screen and in other places.
    if (track.getDescription().getIconBitmap() == null
        && track.getDescription().getIconUri() != null) {
      String albumUri = track.getDescription().getIconUri().toString();
      AlbumArtCache.getInstance()
          .fetch(
              albumUri,
              new AlbumArtCache.FetchListener() {
                @Override
                public void onFetched(String artUrl, Bitmap bitmap, Bitmap icon) {
                  MediaSessionCompat.QueueItem queueItem = mPlayingQueue.get(mCurrentIndexOnQueue);
                  MediaMetadataCompat track = mMusicProvider.getMusic(trackId);
                  track =
                      new MediaMetadataCompat.Builder(track)

                          // set high resolution bitmap in METADATA_KEY_ALBUM_ART. This is used, for
                          // example, on the lockscreen background when the media session is active.
                          .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap)

                          // set small version of the album art in the DISPLAY_ICON. This is used on
                          // the MediaDescriptionCompat and thus it should be small to be serialized
                          // if
                          // necessary..
                          .putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, icon)
                          .build();

                  mMusicProvider.updateMusic(trackId, track);

                  // If we are still playing the same music
                  String currentPlayingId =
                      MediaIDHelper.extractMusicIDFromMediaID(
                          queueItem.getDescription().getMediaId());
                  if (trackId.equals(currentPlayingId)) {
                    mSession.setMetadata(track);
                  }
                }
              });
    }
  }