page.title=Displaying a Now Playing Card page.tags=tv, mediasession helpoutsWidget=true trainingnavtop=true @jd:body
TV apps may allow users to play music or other media in the background while using other applications. If your app allows this type of use, it must must provide a means for the user to return to the app to pause the music or switch to a new song. The Android framework enables TV apps to do this by displaying a Now Playing card on the home screen in the recommendations row.
The Now Playing card is a system artifact that displays on the home screen in the recommendations row for an active media session. It includes the media metadata such as the album art, title, and app icon. When the user selects it, the system opens the the app that owns the session.
This lesson shows how to use the {@link android.media.session.MediaSession} class to implement the Now Playing card.
A playback app can run as an activity or as a service. The service is required for background playback because it can continue to play media even after the activity that launched it has been destroyed. For this discussion, the media playback app is assumed to be running in a {@link android.service.media.MediaBrowserService}.
In your service's {@link android.service.media.MediaBrowserService#onCreate() onCreate()} method, create a new {@link android.media.session.MediaSession#MediaSession(android.content.Context, java.lang.String) MediaSession}, set the callback and flags appropriate to a media app, and set the session token for the {@link android.service.media.MediaBrowserService}.
mSession = new MediaSession(this, "MusicService"); mSession.setCallback(new MediaSessionCallback()); mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); // for the MediaBrowserService setSessionToken(mSession.getSessionToken());
Note: The Now Playing card will display only for a media session with the {@link android.media.session.MediaSession#FLAG_HANDLES_TRANSPORT_CONTROLS} flag set.
The Now Playing card shows up after {@link android.media.session.MediaSession#setActive(boolean) setActive(true)} is called, if the session is the highest priority session in the system. Also, note that your app must request the audio focus, as described in Managing Audio Focus.
private void handlePlayRequest() { tryToGetAudioFocus(); if (!mSession.isActive()) { mSession.setActive(true); } ...
The card is removed from the home screen when {@link android.media.session.MediaSession#setActive(boolean) setActive(false)} is called or if another app initiates media playback. You may want to remove the card from the home screen some time after playback is paused, depending on how long you want to keep the card up, usually 5 to 30 minutes.
As with any media app, update the playback state in the {@link android.media.session.MediaSession} so that the card can display the current metadata, as shown in the following example:
private void updatePlaybackState() { long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN; if (mMediaPlayer != null && mMediaPlayer.isPlaying()) { position = mMediaPlayer.getCurrentPosition(); } PlaybackState.Builder stateBuilder = new PlaybackState.Builder() .setActions(getAvailableActions()); stateBuilder.setState(mState, position, 1.0f); mSession.setPlaybackState(stateBuilder.build()); } private long getAvailableActions() { long actions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PLAY_FROM_MEDIA_ID | PlaybackState.ACTION_PLAY_FROM_SEARCH; if (mPlayingQueue == null || mPlayingQueue.isEmpty()) { return actions; } if (mState == PlaybackState.STATE_PLAYING) { actions |= PlaybackState.ACTION_PAUSE; } if (mCurrentIndexOnQueue > 0) { actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS; } if (mCurrentIndexOnQueue < mPlayingQueue.size() - 1) { actions |= PlaybackState.ACTION_SKIP_TO_NEXT; } return actions; }
For the track currently playing, set the {@link android.media.MediaMetadata} with the {@link android.media.session.MediaSession#setMetadata(android.media.MediaMetadata) setMetadata()} method. This method of the media session object lets you provide information to the Now Playing card about the track such as the title, subtitle, and various icons. The following example assumes your track's data is stored in a custom data class, {@code MediaData}.
private void updateMetadata(MediaData myData) { MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder(); // To provide most control over how an item is displayed set the // display fields in the metadata metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, myData.displayTitle); metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, myData.displaySubtitle); metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, myData.artUri); // And at minimum the title and artist for legacy support metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, myData.title); metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, myData.artist); // A small bitmap for the artwork is also recommended metadataBuilder.putString(MediaMetadata.METADATA_KEY_ART, myData.artBitmap); // Add any other fields you have for your data as well mSession.setMetadata(metadataBuilder.build()); }
When the user selects the Now Playing card, the system opens the app that owns the session. If your app provides a {@link android.app.PendingIntent} to pass to {@link android.media.session.MediaSession#setSessionActivity(android.app.PendingIntent) setSessionActivity()}, the system launches the activity you specify, as demonstrated below. If not, the default system intent opens. The activity you specify must provide playback controls that allow users to pause or stop playback.
Intent intent = new Intent(mContext, MyActivity.class); PendingIntent pi = PendingIntent.getActivity(context, 99 /*request code*/, intent, PendingIntent.FLAG_UPDATE_CURRENT); mSession.setSessionActivity(pi);