169 lines
7.1 KiB
Plaintext
169 lines
7.1 KiB
Plaintext
page.title=Displaying a Now Playing Card
|
|
page.tags=tv, mediasession
|
|
helpoutsWidget=true
|
|
|
|
trainingnavtop=true
|
|
|
|
@jd:body
|
|
|
|
<div id="tb-wrapper">
|
|
<div id="tb">
|
|
<h2>This lesson teaches you to</h2>
|
|
<ol>
|
|
<li><a href="#session">Start a Media Session</a></li>
|
|
<li><a href="#card">Display a Now Playing Card</a></li>
|
|
<li><a href="#state">Update the Playback State</a></li>
|
|
<li><a href="#respond">Respond to User Action</a></li>
|
|
</ol>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<p>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 <em>Now Playing</em> card on the home
|
|
screen in the recommendations row.</p>
|
|
|
|
<p>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.</p>
|
|
|
|
<p>This lesson shows how to use the {@link android.media.session.MediaSession} class to implement
|
|
the Now Playing card.</p>
|
|
|
|
<h2 id="session">Start a Media Session</h2>
|
|
|
|
<p>A playback app can run as an <a href="{@docRoot}guide/components/activities">activity</a> or
|
|
as a <a href="{@docRoot}guide/components/services">service</a>. 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}.</p>
|
|
|
|
<p>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}.</p>
|
|
|
|
<pre>
|
|
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());
|
|
</pre>
|
|
|
|
<p class="note"<strong>Note:</strong> The Now Playing card will display only for a media session with
|
|
the {@link android.media.session.MediaSession#FLAG_HANDLES_TRANSPORT_CONTROLS} flag set.</p>
|
|
|
|
<h2 id="card">Display a Now Playing Card</h2>
|
|
|
|
<p>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 <a href="{@docRoot}training/managing-audio/audio-focus">
|
|
Managing Audio Focus</a>.</p>
|
|
|
|
<pre>
|
|
private void handlePlayRequest() {
|
|
|
|
tryToGetAudioFocus();
|
|
|
|
if (!mSession.isActive()) {
|
|
mSession.setActive(true);
|
|
}
|
|
...
|
|
</pre>
|
|
|
|
<p>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.</p>
|
|
|
|
<h2 id="state">Update the Playback State</h2>
|
|
|
|
<p>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:</p>
|
|
|
|
<pre>
|
|
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;
|
|
}
|
|
</pre>
|
|
|
|
<h2 id="metadata">Display the Media Metadata</h2>
|
|
|
|
<p>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}.</p>
|
|
|
|
<pre>
|
|
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());
|
|
}
|
|
</pre>
|
|
|
|
<h2 id="respond">Respond to User Action</h2>
|
|
|
|
<p>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.</p>
|
|
|
|
<pre>
|
|
Intent intent = new Intent(mContext, MyActivity.class);
|
|
PendingIntent pi = PendingIntent.getActivity(context, 99 /*request code*/,
|
|
intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
mSession.setSessionActivity(pi);
|
|
</pre>
|
|
|