Update News

***TOP NEWS * How to delete google search? Give the offer GOOGLE

Monday 16 March 2015

Building TV Playback Apps

Building TV Playback Apps

GET STARTED

DEPENDENCIES AND PREREQUISITES

  • Android 5.0 (API level 21) or higher

YOU SHOULD ALSO READ


VIDEO

DevBytes: Android TV — Using the Leanback library

Browsing and playing media files is frequently part of the user experience provided by a TV app. Building such an experience from scratch, while making sure that it is fast, fluid, and attractive can be quite challenging. Whether your app provides access to a small or large media catalog, it is important to allow users to quickly browse options and get to the content they want.
The Android framework provides classes for building user interfaces for these types of apps with the v17 leanback support library. This library provides a framework of classes for creating an efficient and familiar interface for browsing and playing media files with minimal coding. The classes are designed be extended and customized so you can create an experience that is unique to your app.
This class shows you how to build a TV app for browsing and playing media content using the Leanback support libraries for TV.

Topics


Creating a Catalog Browser
Learn how to use the Leanback support library to build a browsing interface for media catalogs.
Providing a Card View
Learn how to use the Leanback support library to build a card view for content items.
Building a Details View
Learn how to use the Leanback support library to build a details page for media items.
Displaying a Now Playing Card
Learn how to use a MediaSession to display a Now Playing card on the home screen.

Creating a Catalog Browser

PREVIOUS NEXT

THIS LESSON TEACHES YOU TO

  1. Create a Media Browse Layout
  2. Display Media Lists
  3. Update the Background
Media apps that run on TV need to allow users to browse its content offerings, make a selection, and start playing content. The content browsing experience for apps of this type should be simple and intuitive, as well as visually pleasing and engaging.
This lesson discusses how to use the classes provided by thev17 leanback support library to implement a user interface for browsing music or videos from your app's media catalog.

Create a Media Browse Layout


The BrowseFragment class in the leanback library allows you to create a primary layout for browsing categories and rows of media items with a minimum of code. The following example shows how to create a layout that contains a BrowseFragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  >

  <fragment
      android:name="android.support.v17.leanback.app.BrowseFragment"
      android:id="@+id/browse_fragment"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      />
</LinearLayout>
In order to work with this layout in an activity, retrieve the BrowseFragment element from the layout. Use the methods in this class to set display parameters such as the icon, title, and whether category headers are enabled. The following code sample demonstrates how to set the layout parameters for a BrowseFragment in a layout:
public class BrowseMediaActivity extends Activity {

    public static final String TAG ="BrowseActivity";

    protected BrowseFragment mBrowseFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.browse_fragment);

        final FragmentManager fragmentManager = getFragmentManager();
        mBrowseFragment = (BrowseFragment) fragmentManager.findFragmentById(
                R.id.browse_fragment);

        // Set display parameters for the BrowseFragment
        mBrowseFragment.setHeadersState(BrowseFragment.HEADERS_ENABLED);
        mBrowseFragment.setTitle(getString(R.string.app_name));
        mBrowseFragment.setBadgeDrawable(getResources().getDrawable(
                R.drawable.ic_launcher));
        mBrowseFragment.setBrowseParams(params);

    }
}

Displaying Media Lists


The BrowseFragment allows you to define and display browsable media content categories and media items from a media catalog using adapters and presenters. Adapters enable you to connect to local or online data sources that contain your media catalog information. Presenters hold data about media items and provide layout information for displaying an item on screen.
The following example code shows an implementation of a Presenter for displaying string data:
public class StringPresenter extends Presenter {
    private static final String TAG = "StringPresenter";

    public ViewHolder onCreateViewHolder(ViewGroup parent) {
        TextView textView = new TextView(parent.getContext());
        textView.setFocusable(true);
        textView.setFocusableInTouchMode(true);
        textView.setBackground(
                parent.getContext().getResources().getDrawable(R.drawable.text_bg));
        return new ViewHolder(textView);
    }

    public void onBindViewHolder(ViewHolder viewHolder, Object item) {
        ((TextView) viewHolder.view).setText(item.toString());
    }

    public void onUnbindViewHolder(ViewHolder viewHolder) {
        // no op
    }
}
Once you have constructed a presenter class for your media items, you can build and attach an adapter to theBrowseFragment to display those items on screen for browsing by the user. The following example code demonstrates how to construct an adapter to display categories and items in those categories using theStringPresenter class shown in the previous code example:
private ArrayObjectAdapter mRowsAdapter;
private static final int NUM_ROWS = 4;
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    buildRowsAdapter();
}
private void buildRowsAdapter() {
    mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());

    for (int i = 0; i < NUM_ROWS; ++i) {
        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(i, "Category " + i, null);
        mRowsAdapter.add(new ListRow(header, listRowAdapter));
    }

    mBrowseFragment.setAdapter(mRowsAdapter);
}
This example shows a static implementation of the adapters. A typical media browsing application uses data from an online database or web service. For an example of a browsing application that uses data retrieved from the web, see the Android TV sample app.

Update the Background


In order to add visual interest to a media-browsing app on TV, you can update the background image as users browse through content. This technique can make interaction with your app feel more cinematic and enjoyable for users.
The Leanback support library provides a BackgroundManager class for changing the background of your TV app activity. The following example shows how to create a simple method for updating the background within your TV app activity:
protected void updateBackground(Drawable drawable) {
    BackgroundManager.getInstance(this).setDrawable(drawable);
}
Many of the existing media-browse apps automatically update the background as the user navigates through media listings. In order to do this, you can set up a selection listener to automatically update the background based on the user's current selection. The following example shows you how to set up anOnItemViewSelectedListener class to catch selection events and update the background:
protected void clearBackground() {
    BackgroundManager.getInstance(this).setDrawable(mDefaultBackground);
}
protected OnItemViewSelectedListener getDefaultItemViewSelectedListener() {
    return new OnItemViewSelectedListener() {
        @Override
        public void onItemSelected(Object item, Row row) {
            if (item instanceof Movie ) {
                URI uri = ((Movie)item).getBackdropURI();
                updateBackground(uri);
            } else {
                clearBackground();
            }
        }
    };
}
Note: The implementation above is a simple example shown for purposes of illustration. When creating this function in your own app, you should consider running the background update action in a separate thread for better performance. In addition, if you are planning on updating the background in response to users scrolling through items, consider adding a time to delay a background image update until the user settles on an item. This technique avoids excessive background image updates.
Providing a Card View

THIS LESSON TEACHES YOU TO

  1. Create a Card Presenter
  2. Create a Card View

TRY IT OUT

  • Android Leanback sample app
In the previous lesson, you created a catalog browser, implemented in a browse fragment, that displays a list of media items. In this lesson, you create the card views for your media items and present them in the browse fragment.
The BaseCardView class and subclasses display the meta data associated with a media item. The ImageCardView class used in this lesson displays an image for the content along with the media item's title.
This lesson describes code from the Android Leanback sample app, available on GitHub. Use this sample code to start your own app.

Figure 1. The Leanback sample app browse fragment with a card presenter displaying card view objects.

Create a Card Presenter


Presenter generates views and binds objects to them on demand. In the browse fragment where your app presents its content to the user, you create a Presenter for the content cards and pass it to the adapter that adds the content to the screen. In the following code, the CardPresenter is created in the onLoadFinished()callback of the LoaderManager.
@Override
public void onLoadFinished(Loader<HashMap<String, List<Movie>>> arg0,
                           HashMap<String, List<Movie>> data) {

    mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
    CardPresenter cardPresenter = new CardPresenter();

    int i = 0;

    for (Map.Entry<String, List<Movie>> entry : data.entrySet()) {
        ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
        List<Movie> list = entry.getValue();

        for (int j = 0; j < list.size(); j++) {
            listRowAdapter.add(list.get(j));
        }
        HeaderItem header = new HeaderItem(i, entry.getKey(), null);
        i++;
        mRowsAdapter.add(new ListRow(header, listRowAdapter));
    }

    HeaderItem gridHeader = new HeaderItem(i, getString(R.string.more_samples),
            null);

    GridItemPresenter gridPresenter = new GridItemPresenter();
    ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(gridPresenter);
    gridRowAdapter.add(getString(R.string.grid_view));
    gridRowAdapter.add(getString(R.string.error_fragment));
    gridRowAdapter.add(getString(R.string.personal_settings));
    mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));

    setAdapter(mRowsAdapter);

    updateRecommendations();
}

Create a Card View


In this step, you build the card presenter with a view holder for the card view that describes your media content items. Note that each presenter must only create one view type. If you have two different card view types then you need two different card presenters.
In the Presenter, implement an onCreateViewHolder() callback that creates a view holder that can be used to display a content item.
@Override
public class CardPresenter extends Presenter {

    private Context mContext;
    private static int CARD_WIDTH = 313;
    private static int CARD_HEIGHT = 176;
    private Drawable mDefaultCardImage;

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent) {
        mContext = parent.getContext();
        mDefaultCardImage = mContext.getResources().getDrawable(R.drawable.movie);
...
In the onCreateViewHolder() method, create a card view for content items. The sample below uses anImageCardView.
When a card is selected, the default behavior expands it to a larger size. If you want to designate a different color for the selected card, call setSelected() as shown here.
...
    ImageCardView cardView = new ImageCardView(mContext) {
        @Override
        public void setSelected(boolean selected) {
            int selected_background = mContext.getResources().getColor(R.color.detail_background);
            int default_background = mContext.getResources().getColor(R.color.default_background);
            int color = selected ? selected_background : default_background;
            findViewById(R.id.info_field).setBackgroundColor(color);
            super.setSelected(selected);
        }
    };
...
When the user opens your app, the Presenter.ViewHolder displays the CardView objects for your content items. You need to set these to receive focus from the D-pad controller by calling setFocusable(true) andsetFocusableInTouchMode(true).
...
    cardView.setFocusable(true);
    cardView.setFocusableInTouchMode(true);
    return new ViewHolder(cardView);
}
When the user selects the ImageCardView, it expands to reveal its text area with the background color you specify, as shown in figure 2.

Figure 2. The Leanback sample app image card view when selected.
Building a Details View

THIS LESSON TEACHES YOU TO

  1. Build a Details Presenter
  2. Extend the Details Fragment
  3. Create a Details Activity
  4. Define a Listener for Clicked Items
The media browsing interface classes provided by the v17 leanback support library include classes for displaying additional information about a media item, such as a description or reviews, and for taking action on that item, such as purchasing it or playing its content.
This lesson discusses how to create a presenter class for media item details, and 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. For more information on using fragment transactions, see the Building a Dynamic UI with Fragments training.

Build a Details Presenter


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.");
    }
}

Extend the Details Fragment


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.
The following example code demonstrates how to use the presenter class shown in the previous section, to add a preview image and actions for the media item being viewed. This example also shows the addition of a related media items row, which appears below the details listing.
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:
        DetailsOverviewRowPresenter rowPresenter =
            new DetailsOverviewRowPresenter(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);
    }
}

Create a Details Activity

Fragments such as the DetailsFragment must be contained within an activity in order to be used for display. Creating an activity for your details view, separate from the browse activity, enables you to invoke your details view using an Intent. This section explains how to build an activity to contain your implementation of the detail view for your media items.
Start creating the details activity by building a layout that references your implementation of theDetailsFragment:
<!-- file: res/layout/details.xml -->
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:name="com.example.android.mediabrowser.MediaItemDetailsFragment"
    android:id="@+id/details_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>
Next, create an activity class that uses the layout shown in the previous code example:
public class DetailsActivity extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.details);
    }
}
Finally, add this new activity to the manifest. Remember to apply the Leanback theme to ensure that the user interface is consistent with the media browse activity:
<application>
  ...

  <activity android:name=".DetailsActivity"
    android:exported="true"
    android:theme="@style/Theme.Leanback"/>
</application>

Define a Listener for Clicked Items

After you have implemented the DetailsFragment, modify your main media browsing view to move to your details view when a user clicks on a media item. In order to enable this behavior, add anOnItemViewClickedListener object to the BrowseFragment that fires an intent to start the item details activity.
The following example shows how to implement a listener to start the details view when a user clicks a media item in the main media browsing activity:
public class BrowseMediaActivity extends Activity {
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        // create the media item rows
        buildRowsAdapter();

        // add a listener for selected items
        mBrowseFragment.OnItemViewClickedListener(
            new OnItemViewClickedListener() {
                @Override
                public void onItemClicked(Object item, Row row) {
                    System.out.println("Media Item clicked: " + item.toString());
                    Intent intent = new Intent(BrowseMediaActivity.this,
                            DetailsActivity.class);
                    // pass the item information
                    intent.getExtras().putLong("id", item.getId());
                    startActivity(intent);
                }
            });
    }
}


Displaying a Now Playing Card

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 MediaSession class to implement the Now Playing card.

Start a Media Session


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 MediaBrowserService.
In your service's onCreate() method, create a new MediaSession, set the callback and flags appropriate to a media app, and set the session token for the 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 FLAG_HANDLES_TRANSPORT_CONTROLSflag set.

Display a Now Playing Card


The Now Playing card shows up after 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 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.

Update the Playback State


As with any media app, update the playback state in the 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;
}

Display the Media Metadata


For the track currently playing, 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. The following example assumes your track's data is stored in a custom data class, 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());
}

Respond to User Action


When the user selects the Now Playing card, the system opens the app that owns the session. If your app provides a PendingIntent to pass 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);

Helping Users Find Your Content on TV

DEPENDENCIES AND PREREQUISITES

  • Android 5.0 (API level 21) or higher

YOU SHOULD ALSO READ

TV devices offer many entertainment options for users. They have thousands of content options from apps and related content services. At the same time, most users prefer to use TVs with the least amount of input possible. With the number of choices available to users, it is important for app developers to provide quick and easy paths for users to discover and enjoy your content.
The Android framework helps you provide a number of paths for users to discover your content, including recommendations on the home screen and searching within your app's content catalog.
This class shows you how to help users discover your app's content through recommendations and in-app searching.

Topics


Recommending TV Content
Learn how to recommend content for users so that it appears in the recommendations row on the home screen of a TV device.
Making TV Apps Searchable
Learn how to make your content searchable from the Android TV home screen.
Searching within TV Apps
Learn how to use a built-for-TV user interface for searching within your app.

No comments:

Post a Comment