David Gonzalez

Android Software Craftsman

Building Applications for Android TV

By now you all have heard about the new Android release and almost all of you have played with new API and probably experimented with Material Design.

I have to say though, that one of the things that I’m most excited about is Android TV. I was very much into it before it was cool but never got the chance to actually get hands on. Happily this has changed now and I’ll be writing a series of posts about developing for Android TV, how to start, how to make your existing app compatible and how to make the most of it.

If you are interested, please read on!

Building applications for Android TV is not just developing and designing for an extremely big tablet. Yes, it is a much bigger display but that’s not the point. The interaction with the device is completely different compared to a phone, you are not going to scroll through a 55 inches TV, are you? Same goes for the use cases, so far no one has been spotted reading the news in the underground on a Sony Bravia.

Support Libraries

To make things a bit easier for us, Google has created a Leanback Support Library which contains APIs to support building user interfaces on TV devices. It provides a number of important widgets for TV apps which we’ll make use in this project.

1
2
3
4
5
dependencies {
  compile 'com.android.support:recyclerview-v7:21.0.+'
  compile 'com.android.support:leanback-v17:21.0.+'
  compile 'com.android.support:appcompat-v7:21.0.+'
}

Demo project: The Android TV Explorer

It’s always easier to show and share while building stuff so I’m creating this example project called Android TV Explorer where all the knowledge from this series will be pushed.

By the end of this series the project will be a great starting point for anyone interested in developing a media library application for Android TV.

Creating a library layout using the Browse Fragment

One of the widgets that the Leanback Support Library provides is the BrowseFragment. A fragment for creating leanback browse screens, it is composed of a RowsFragment and a HeadersFragment.

As you can see in the library screen above, the header corresponds to the list on the left and the rows to the grid on the right. BrowseFragment allows us to customise several actions and some UI elements, like changing the default visibility of the HeadersFragment or changing the behaviour of the transition when entering one of the rows.

For the purpose of this example we are not going to change anything today, indeed we are going to take a closer look at how the items are displayed.

1
2
3
4
5
6
7
private void setupUIElements() {
  setTitle(getString(R.string.browse_title));
  setHeadersState(HEADERS_ENABLED);
  setHeadersTransitionOnBackEnabled(true);
  setBrandColor(getResources().getColor(R.color.fastlane_background));
  setSearchAffordanceColor(getResources().getColor(R.color.search_opaque));
}

There several helper methods that the BrowseFragment exposes that make it very simple to customise its look and feel. For example, setBrandColor() and setSearchAffordanceColor() allow us to change the background of the HeadersFragment and the color of the rounded search icon.

Presenter

BrowseFragment renders the elements of its ArrayObjectAdapter as a sequence of rows and a vertical list. This ObjectAdapter makes use of a Presenter, concept which those familiar with the Model-view-presenter will quickly understand.

A Presenter is used to generate Views and bind Objects to them on demand. It is closely related to concept of an RecyclerView.Adapter, but is non position-based.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

@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));
  }

  setAdapter(mRowsAdapter);
}

We are using a CardPresenter which will display all the movies from each category into cards. The elements of the BrowseFragment adapter must be subclasses of ‘Row’, that’s the reason why the mRowsAdapter uses the ListRowPresenter. Again, this is just a wrapper around normal fragments and its goal is to make things easy to develop from scratch. Obviously, one can make use of them or not but for the purpose of this project we’ll stick to the simplicity… for now!

Setting up event listeners

There are two exposed Listeners in the BrowseFragment. It’s very interesting to see how the selection and click are two different events, the reason why is very simple and you’ll get to understand it after reading the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

protected OnItemViewSelectedListener getDefaultItemSelectedListener() {
  return new OnItemViewSelectedListener() {
    @Override
    public void onItemSelected(Presenter.ViewHolder viewHolder, Object item, RowPresenter.ViewHolder                  viewHolder2, Row row) {
      mBackgroundURI = ((Movie) item).getBackgroundImageURI();
      updateBackgroundPicture();
    }
  };
}

protected OnItemViewClickedListener getDefaultItemClickedListener() {
  return new OnItemViewClickedListener() {
    @Override
    public void onItemClicked(Presenter.ViewHolder viewHolder, Object item, RowPresenter.ViewHolder viewHolder2, Row row) {
      Toast.makeText(getActivity(), movie.getTitle(), Toast.LENGTH_LONG);
    }
  };
}

When a video is selected we can use the BackgroundManager to update the background picture.

Background Manager

This is a complete different approach to what a mobile or tablet application would do but in my honest opinion makes a lot of sense. Android TV supports background image continuity between multiple Activities. We are used to have transitions between all the Activities but this can be confusing in bigger form factors and when there is not a context switch, making use of the Background Manager we can deliver smoother transitions that won’t disturb the user when looking at a big screen.

1
2
3
4

protected void updateBackground(Drawable drawable) {
  BackgroundManager.getInstance(getActivity()).setDrawable(drawable);
}

Tip

Setting up the environment might be a bit tricky when talking about Android TV. Some of you may have an ADT Developer Kit but if you don’t you’ll need to create an emulator to start with (or wait for the brand new Nexus Player). It’s pretty straight forward to create an emulator but it’s very helpful to activate hardware acceleration in order to have better performance in the Android TV emulator.

Show me the code

You’ll find the entire project on Github and I’ll be adding more features along with all the blog posts of this series. As always, all trolling feedback is more than welcome!

Comments