• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Displaying a Now Playing Card
2page.tags="nowplaying","mediasession"
3
4trainingnavtop=true
5
6@jd:body
7
8<div id="tb-wrapper">
9<div id="tb">
10  <h2>This lesson teaches you to</h2>
11  <ol>
12    <li><a href="#session">Start a Media Session</a></li>
13    <li><a href="#card">Display a Now Playing Card</a></li>
14    <li><a href="#state">Update the Playback State</a></li>
15    <li><a href="#respond">Respond to User Action</a></li>
16  </ol>
17
18</div>
19</div>
20
21<p>TV apps may allow users to play music or other media in the background while using other
22applications. If your app allows this type of use, it must must
23provide a means for the user to return to the app to pause the music or switch to a new song. The
24Android framework enables TV apps to do this by displaying a <em>Now Playing</em> card on the home
25screen in the recommendations row.</p>
26
27<p>The Now Playing card is a system artifact that displays on the
28home screen in the recommendations row for an active media session. It includes the media metadata
29such as the album art, title, and app icon. When the user selects it, the system opens the the app
30that owns the session.</p>
31
32<p>This lesson shows how to use the {@link android.media.session.MediaSession} class to implement
33the Now Playing card.</p>
34
35<h2 id="session">Start a Media Session</h2>
36
37<p>A playback app can run as an <a href="{@docRoot}guide/components/activities">activity</a> or
38as a <a href="{@docRoot}guide/components/services">service</a>. The service is required for
39background playback because it can continue to play media even after the activity that launched it
40has been destroyed. For this discussion, the media playback app is assumed to be running in a
41{@link android.service.media.MediaBrowserService}.</p>
42
43<p>In your service's {@link android.service.media.MediaBrowserService#onCreate() onCreate()}
44method, create a new {@link android.media.session.MediaSession#MediaSession(android.content.Context, java.lang.String) MediaSession},
45set the callback and flags appropriate to a media app, and set the session token for the
46{@link android.service.media.MediaBrowserService}.</p>
47
48<pre>
49mSession = new MediaSession(this, "MusicService");
50mSession.setCallback(new MediaSessionCallback());
51mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
52        MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
53
54// for the MediaBrowserService
55setSessionToken(mSession.getSessionToken());
56</pre>
57
58<p class="note"<strong>Note:</strong> The Now Playing card will display only for a media session with
59the {@link android.media.session.MediaSession#FLAG_HANDLES_TRANSPORT_CONTROLS} flag set.</p>
60
61<h2 id="card">Display a Now Playing Card</h2>
62
63<p>The Now Playing card shows up after {@link android.media.session.MediaSession#setActive(boolean) setActive(true)}
64is called, if the session is the highest priority session in the system. Also, note that your app
65must request the audio focus, as described in <a href="{@docRoot}training/managing-audio/audio-focus">
66Managing Audio Focus</a>.</p>
67
68<pre>
69private void handlePlayRequest() {
70
71    tryToGetAudioFocus();
72
73    if (!mSession.isActive()) {
74        mSession.setActive(true);
75    }
76...
77</pre>
78
79<p>The card is removed from the home screen when {@link android.media.session.MediaSession#setActive(boolean) setActive(false)}
80is called or if another app initiates media playback. You may want to remove the card from the home
81screen some time after playback is paused, depending on how long you want to keep the card up,
82usually 5 to 30 minutes.</p>
83
84<h2 id="state">Update the Playback State</h2>
85
86<p>As with any media app, update the playback state in the {@link android.media.session.MediaSession}
87so that the card can display the current metadata, as shown in the following example:</p>
88
89<pre>
90private void updatePlaybackState() {
91    long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
92    if (mMediaPlayer != null &amp;&amp; mMediaPlayer.isPlaying()) {
93        position = mMediaPlayer.getCurrentPosition();
94    }
95    PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
96            .setActions(getAvailableActions());
97    stateBuilder.setState(mState, position, 1.0f);
98    mSession.setPlaybackState(stateBuilder.build());
99}
100private long getAvailableActions() {
101    long actions = PlaybackState.ACTION_PLAY |
102            PlaybackState.ACTION_PLAY_FROM_MEDIA_ID |
103            PlaybackState.ACTION_PLAY_FROM_SEARCH;
104    if (mPlayingQueue == null || mPlayingQueue.isEmpty()) {
105        return actions;
106    }
107    if (mState == PlaybackState.STATE_PLAYING) {
108        actions |= PlaybackState.ACTION_PAUSE;
109    }
110    if (mCurrentIndexOnQueue &gt; 0) {
111        actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS;
112    }
113    if (mCurrentIndexOnQueue &lt; mPlayingQueue.size() - 1) {
114        actions |= PlaybackState.ACTION_SKIP_TO_NEXT;
115    }
116    return actions;
117}
118</pre>
119
120<h2 id="metadata">Display the Media Metadata</h2>
121
122<p>For the track currently playing, set the {@link android.media.MediaMetadata} with the
123{@link android.media.session.MediaSession#setMetadata(android.media.MediaMetadata) setMetadata()}
124method. This method of the media session object lets you provide information to the Now Playing card
125about the track such as the title, subtitle, and various icons. The following example assumes your
126track's data is stored in a custom data class, {@code MediaData}.</p>
127
128<pre>
129private void updateMetadata(MediaData myData) {
130    MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
131    // To provide most control over how an item is displayed set the
132    // display fields in the metadata
133    metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE,
134            myData.displayTitle);
135    metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE,
136            myData.displaySubtitle);
137    metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI,
138            myData.artUri);
139    // And at minimum the title and artist for legacy support
140    metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE,
141            myData.title);
142    metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST,
143            myData.artist);
144    // A small bitmap for the artwork is also recommended
145    metadataBuilder.putString(MediaMetadata.METADATA_KEY_ART,
146            myData.artBitmap);
147    // Add any other fields you have for your data as well
148    mSession.setMetadata(metadataBuilder.build());
149}
150</pre>
151
152<h2 id="respond">Respond to User Action</h2>
153
154<p>When the user selects the Now Playing card, the system opens the app that owns the session.
155If your app provides a {@link android.app.PendingIntent} to pass to
156{@link android.media.session.MediaSession#setSessionActivity(android.app.PendingIntent) setSessionActivity()},
157the system launches the activity you specify, as demonstrated below. If not, the default system
158intent opens. The activity you specify must provide playback controls that allow users to pause or
159stop playback.</p>
160
161<pre>
162Intent intent = new Intent(mContext, MyActivity.class);
163    PendingIntent pi = PendingIntent.getActivity(context, 99 /*request code*/,
164            intent, PendingIntent.FLAG_UPDATE_CURRENT);
165    mSession.setSessionActivity(pi);
166</pre>
167
168