/** * Actual implementation of onLoadChildren that assumes that MusicProvider is already initialized. */ private void loadChildrenImpl( final String parentMediaId, final Result<List<MediaBrowserCompat.MediaItem>> result) { LogHelper.d(TAG, "OnLoadChildren: parentMediaId=", parentMediaId); List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>(); if (MEDIA_ID_ROOT.equals(parentMediaId)) { LogHelper.d(TAG, "OnLoadChildren.ROOT"); mediaItems.add( new MediaBrowserCompat.MediaItem( new MediaDescriptionCompat.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(), MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)); } else if (MEDIA_ID_MUSICS_BY_GENRE.equals(parentMediaId)) { LogHelper.d(TAG, "OnLoadChildren.GENRES"); for (String genre : mMusicProvider.getGenres()) { MediaBrowserCompat.MediaItem item = new MediaBrowserCompat.MediaItem( new MediaDescriptionCompat.Builder() .setMediaId(createBrowseCategoryMediaID(MEDIA_ID_MUSICS_BY_GENRE, genre)) .setTitle(genre) .setSubtitle(getString(R.string.browse_musics_by_genre_subtitle, genre)) .build(), MediaBrowserCompat.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 (MediaMetadataCompat 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); MediaMetadataCompat trackCopy = new MediaMetadataCompat.Builder(track) .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, hierarchyAwareMediaID) .build(); MediaBrowserCompat.MediaItem bItem = new MediaBrowserCompat.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); }
@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 onMetadataChanged(MediaMetadataCompat metadata) { if (metadata == null) { return; } LogHelper.d( TAG, "Received metadata state change to mediaId=", metadata.getDescription().getMediaId(), " song=", metadata.getDescription().getTitle()); PlaybackControlsFragment.this.onMetadataChanged(metadata); }
@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 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 static List<MediaSessionCompat.QueueItem> convertToQueue( Iterable<MediaMetadataCompat> tracks, String... categories) { List<MediaSessionCompat.QueueItem> queue = new ArrayList<>(); int count = 0; for (MediaMetadataCompat track : tracks) { // We create a hierarchy-aware mediaID, so we know what the queue is about by looking // at the QueueItem media IDs. String hierarchyAwareMediaID = MediaIDHelper.createMediaID(track.getDescription().getMediaId(), categories); MediaMetadataCompat trackCopy = new MediaMetadataCompat.Builder(track) .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, hierarchyAwareMediaID) .build(); // We don't expect queues to change after created, so we use the item index as the // queueId. Any other number unique in the queue would work. MediaSessionCompat.QueueItem item = new MediaSessionCompat.QueueItem(trackCopy.getDescription(), count++); queue.add(item); } return queue; }
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); } } }); } }
@Override public MediaMetadataCompat getMetadata() { Object metadataObj = MediaControllerCompatApi21.getMetadata(mControllerObj); return metadataObj != null ? MediaMetadataCompat.fromMediaMetadata(metadataObj) : null; }
@Override public void onMetadataChanged(Object metadataObj) { Callback.this.onMetadataChanged(MediaMetadataCompat.fromMediaMetadata(metadataObj)); }