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