@Override
 public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) {
   LogHelper.d(
       TAG,
       "OnGetRoot: clientPackageName=" + clientPackageName,
       "; clientUid=" + clientUid + " ; rootHints=",
       rootHints);
   // To ensure you are not allowing any arbitrary app to browse your app's contents, you
   // need to check the origin:
   if (!mPackageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
     // If the request comes from an untrusted package, return null. No further calls will
     // be made to other media browsing methods.
     LogHelper.w(TAG, "OnGetRoot: IGNORING request from untrusted package " + clientPackageName);
     return null;
   }
   //noinspection StatementWithEmptyBody
   if (CarHelper.isValidCarPackage(clientPackageName)) {
     // Optional: if your app needs to adapt the music library to show a different subset
     // when connected to the car, this is where you should handle it.
     // If you want to adapt other runtime behaviors, like tweak ads or change some behavior
     // that should be different on cars, you should instead use the boolean flag
     // set by the BroadcastReceiver mCarConnectionReceiver (mIsConnectedToCar).
   }
   //noinspection StatementWithEmptyBody
   if (WearHelper.isValidWearCompanionPackage(clientPackageName)) {
     // Optional: if your app needs to adapt the music library for when browsing from a
     // Wear device, you should return a different MEDIA ROOT here, and then,
     // on onLoadChildren, handle it accordingly.
   }
   return new BrowserRoot(MEDIA_ID_ROOT, null);
 }
  /**
   * Actual implementation of onLoadChildren that assumes that MusicProvider is already initialized.
   */
  private void loadChildrenImpl(
      final String parentMediaId, final Result<List<MediaBrowser.MediaItem>> result) {
    LogHelper.d(TAG, "OnLoadChildren: parentMediaId=", parentMediaId);

    List<MediaBrowser.MediaItem> mediaItems = new ArrayList<>();

    if (MEDIA_ID_ROOT.equals(parentMediaId)) {
      LogHelper.d(TAG, "OnLoadChildren.ROOT");
      mediaItems.add(
          new MediaBrowser.MediaItem(
              new MediaDescription.Builder()
                  .setMediaId(MEDIA_ID_MUSICS_BY_GENRE)
                  .setTitle(getString(R.string.browse_genres))
                  .setIconUri(
                      Uri.parse(
                          "android.resource://" + "com.example.android.uamp/drawable/ic_by_genre"))
                  .setSubtitle(getString(R.string.browse_genre_subtitle))
                  .build(),
              MediaBrowser.MediaItem.FLAG_BROWSABLE));

    } else if (MEDIA_ID_MUSICS_BY_GENRE.equals(parentMediaId)) {
      LogHelper.d(TAG, "OnLoadChildren.GENRES");
      for (String genre : mMusicProvider.getGenres()) {
        MediaBrowser.MediaItem item =
            new MediaBrowser.MediaItem(
                new MediaDescription.Builder()
                    .setMediaId(createBrowseCategoryMediaID(MEDIA_ID_MUSICS_BY_GENRE, genre))
                    .setTitle(genre)
                    .setSubtitle(getString(R.string.browse_musics_by_genre_subtitle, genre))
                    .build(),
                MediaBrowser.MediaItem.FLAG_BROWSABLE);
        mediaItems.add(item);
      }

    } else if (parentMediaId.startsWith(MEDIA_ID_MUSICS_BY_GENRE)) {
      String genre = MediaIDHelper.getHierarchy(parentMediaId)[1];
      LogHelper.d(TAG, "OnLoadChildren.SONGS_BY_GENRE  genre=", genre);
      for (MediaMetadata track : mMusicProvider.getMusicsByGenre(genre)) {
        // Since mediaMetadata fields are immutable, we need to create a copy, so we
        // can set a hierarchy-aware mediaID. We will need to know the media hierarchy
        // when we get a onPlayFromMusicID call, so we can create the proper queue based
        // on where the music was selected from (by artist, by genre, random, etc)
        String hierarchyAwareMediaID =
            MediaIDHelper.createMediaID(
                track.getDescription().getMediaId(), MEDIA_ID_MUSICS_BY_GENRE, genre);
        MediaMetadata trackCopy =
            new MediaMetadata.Builder(track)
                .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, hierarchyAwareMediaID)
                .build();
        MediaBrowser.MediaItem bItem =
            new MediaBrowser.MediaItem(trackCopy.getDescription(), MediaItem.FLAG_PLAYABLE);
        mediaItems.add(bItem);
      }
    } else {
      LogHelper.w(TAG, "Skipping unmatched parentMediaId: ", parentMediaId);
    }
    LogHelper.d(TAG, "OnLoadChildren sending ", mediaItems.size(), " results for ", parentMediaId);
    result.sendResult(mediaItems);
  }
  private void onMetadataChanged(MediaMetadataCompat metadata) {
    LogHelper.d(TAG, "onMetadataChanged ", metadata);
    if (getActivity() == null) {
      LogHelper.w(
          TAG,
          "onMetadataChanged called when getActivity null,"
              + "this should not happen if the callback was properly unregistered. Ignoring.");
      return;
    }
    if (metadata == null) {
      return;
    }

    mTitle.setText(metadata.getDescription().getTitle());
    mSubtitle.setText(metadata.getDescription().getSubtitle());
    String artUrl = null;
    if (metadata.getDescription().getIconUri() != null) {
      artUrl = metadata.getDescription().getIconUri().toString();
    }
    if (!TextUtils.equals(artUrl, mArtUrl)) {
      mArtUrl = artUrl;
      Bitmap art = metadata.getDescription().getIconBitmap();
      AlbumArtCache cache = AlbumArtCache.getInstance();
      if (art == null) {
        art = cache.getIconImage(mArtUrl);
      }
      if (art != null) {
        mAlbumArt.setImageBitmap(art);
      } else {
        cache.fetch(
            artUrl,
            new AlbumArtCache.FetchListener() {
              @Override
              public void onFetched(String artUrl, Bitmap bitmap, Bitmap icon) {
                if (icon != null) {
                  LogHelper.d(
                      TAG, "album art icon of w=", icon.getWidth(), " h=", icon.getHeight());
                  if (isAdded()) {
                    mAlbumArt.setImageBitmap(icon);
                  }
                }
              }
            });
      }
    }
  }
  private void onPlaybackStateChanged(PlaybackStateCompat state) {
    LogHelper.d(TAG, "onPlaybackStateChanged ", state);
    if (getActivity() == null) {
      LogHelper.w(
          TAG,
          "onPlaybackStateChanged called when getActivity null,"
              + "this should not happen if the callback was properly unregistered. Ignoring.");
      return;
    }
    if (state == null) {
      return;
    }
    boolean enablePlay = false;
    switch (state.getState()) {
      case PlaybackStateCompat.STATE_PAUSED:
      case PlaybackStateCompat.STATE_STOPPED:
        enablePlay = true;
        break;
      case PlaybackStateCompat.STATE_ERROR:
        LogHelper.e(TAG, "error playbackstate: ", state.getErrorMessage());
        Toast.makeText(getActivity(), state.getErrorMessage(), Toast.LENGTH_LONG).show();
        break;
    }

    if (enablePlay) {
      mPlayPause.setImageDrawable(
          ContextCompat.getDrawable(getActivity(), R.drawable.ic_play_arrow_black_36dp));
    } else {
      mPlayPause.setImageDrawable(
          ContextCompat.getDrawable(getActivity(), R.drawable.ic_pause_black_36dp));
    }

    MediaControllerCompat controller =
        ((FragmentActivity) getActivity()).getSupportMediaController();
    String extraInfo = null;
    if (controller != null && controller.getExtras() != null) {
      String castName = controller.getExtras().getString(MusicService.EXTRA_CONNECTED_CAST);
      if (castName != null) {
        extraInfo = getResources().getString(R.string.casting_to_device, castName);
      }
    }
    setExtraInfo(extraInfo);
  }
 @Override
 public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
   LogHelper.d(
       TAG,
       "OnGetRoot: clientPackageName=" + clientPackageName,
       "; clientUid=" + clientUid + " ; rootHints=",
       rootHints);
   // To ensure you are not allowing any arbitrary app to browse your app's contents, you
   // need to check the origin:
   if (!mPackageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
     // If the request comes from an untrusted package, return null. No further calls will
     // be made to other media browsing methods.
     LogHelper.w(TAG, "OnGetRoot: IGNORING request from untrusted package " + clientPackageName);
     return null;
   }
   //noinspection StatementWithEmptyBody
   if (CarHelper.isValidCarPackage(clientPackageName)) {
     // Optional: if your app needs to adapt ads, music library or anything else that
     // needs to run differently when connected to the car, this is where you should handle
     // it.
   }
   return new BrowserRoot(MEDIA_ID_ROOT, null);
 }