> Building a Details View
how to extend the DetailsFragment
class to implement a details view for a media item when it is selected by a user.
Note: The implementation example shown here uses an additional activity to contain the DetailsFragment
. However, it is possible to avoid creating a second activity by replacing the current BrowseFragment
with aDetailsFragment
within the same activity using fragment transactions.
In the media browsing framework provided by the leanback library, you use presenter objects to control the display of data on screen, including media item details. The framework provides theAbstractDetailsDescriptionPresenter
class for this purpose, which is a nearly complete implementation of the presenter for media item details. All you have to do is implement the onBindDescription()
method to bind the view fields to your data objects, as shown in the following code sample:
public class DetailsDescriptionPresenter extends AbstractDetailsDescriptionPresenter { @Override protected void onBindDescription(ViewHolder viewHolder, Object itemData) { MyMediaItemDetails details = (MyMediaItemDetails) itemData; // In a production app, the itemData object contains the information // needed to display details for the media item: // viewHolder.getTitle().setText(details.getShortTitle()); // Here we provide static data for testing purposes: viewHolder.getTitle().setText(itemData.toString()); viewHolder.getSubtitle().setText("2014 Drama TV-14"); viewHolder.getBody().setText("Lorem ipsum dolor sit amet, consectetur " + "adipisicing elit, sed do eiusmod tempor incididunt ut labore " + " et dolore magna aliqua. Ut enim ad minim veniam, quis " + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea " + "commodo consequat."); } }> When using the
DetailsFragment
class for displaying your media item details, extend that class to provide additional content such as a preview image and actions for the media item. You can also provide additional content, such as a list of related media items.
public class MediaItemDetailsFragment extends DetailsFragment { private static final String TAG = "MediaItemDetailsFragment"; private ArrayObjectAdapter mRowsAdapter; @Override public void onCreate(Bundle savedInstanceState) { Log.i(TAG, "onCreate"); super.onCreate(savedInstanceState); buildDetails(); } private void buildDetails() { ClassPresenterSelector selector = new ClassPresenterSelector(); // Attach your media item details presenter to the row presenter: FullWidthDetailsOverviewRowPresenter rowPresenter = new FullWidthDetailsOverviewRowPresenter( new DetailsDescriptionPresenter()); selector.addClassPresenter(DetailsOverviewRow.class, rowPresenter); selector.addClassPresenter(ListRow.class, new ListRowPresenter()); mRowsAdapter = new ArrayObjectAdapter(selector); Resources res = getActivity().getResources(); DetailsOverviewRow detailsOverview = new DetailsOverviewRow( "Media Item Details"); // Add images and action buttons to the details view detailsOverview.setImageDrawable(res.getDrawable(R.drawable.jelly_beans)); detailsOverview.addAction(new Action(1, "Buy $9.99")); detailsOverview.addAction(new Action(2, "Rent $2.99")); mRowsAdapter.add(detailsOverview); // Add a Related items row ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter( new StringPresenter()); listRowAdapter.add("Media Item 1"); listRowAdapter.add("Media Item 2"); listRowAdapter.add("Media Item 3"); HeaderItem header = new HeaderItem(0, "Related Items", null); mRowsAdapter.add(new ListRow(header, listRowAdapter)); setAdapter(mRowsAdapter); } }> Displaying a Now Playing Card
TV apps must display a Now Playing card when playing media behind the launcher or in the background. This card allows users to return to the app that is currently playing media.
The Android framework displays a Now Playing card on the home screen when there is an active MediaSession
. The card includes media metadata such as album art, title, and app icon. When the user selects the card, the system opens the app.
Create a MediaSession
when your app is preparing to play media. The following code snippet is an example of how to set the appropriate callback and flags:
mSession = new MediaSession(this, "MusicService"); mSession.setCallback(new MediaSessionCallback()); mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
The Now Playing card only appears for active sessions. You must call setActive(true)
when playback begins. Your app must also request audio focus, as described in Managing Audio Focus.
private void handlePlayRequest() { tryToGetAudioFocus(); if (!mSession.isActive()) { mSession.setActive(true); } ...The card is removed from the launcher screen when a
setActive(false)
call deactivates the media session, or when another app initiates media playback. If playback is completely stopped and there is no active media, your app should deactivate the media session immediately. If playback is paused, your app should deactivate the media session after a delay, usually between 5 to 30 minutes.
Update the Playback State
Update the playback state in the MediaSession
so the card can show the state of the current media.
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_PAUSE | 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; } else { actions |= PlaybackState.ACTION_PLAY; } if (mCurrentIndexOnQueue > 0) { actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS; } if (mCurrentIndexOnQueue < mPlayingQueue.size() - 1) { actions |= PlaybackState.ACTION_SKIP_TO_NEXT; } return actions; }> Set the
MediaMetadata
with the
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.
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.putBitmap(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 PendingIntent
to 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);