Android TV: Getting Started

Learn how to create your first Android TV app! In this tutorial, you’ll create an Android TV app for viewing RayWenderlich Youtube channel videos! By Ivan Kušt.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Creating Video Catalog

The first screen of the Netderlix app is pretty empty at the moment. The idea is that it shows a catalog of available playlists and videos from the raywenderlich.com YouTube channel. The user can play and select each of the videos to see the details.

Using BrowseSupportFragment from Leanback

Open CatalogFragment.kt. It already extends BrowseSupportFragment and contains code to initialize the YouTube API for fetching videos and playlists. It’s hosted in CatalogActivity and is set up to launch when the app starts on TV.

The first thing to do is to form the title and headers. In CatalogFragment.kt, create setupTitleAndHeaders():

private fun setupTitleAndHeaders() {
  // 1
  title = getString(R.string.browse_title)

  // 2
  headersState = HEADERS_ENABLED
  isHeadersTransitionOnBackEnabled = true

  // 3
  brandColor = ContextCompat.getColor(
    requireContext(), 
    R.color.fastlane_background
  )
}

Now, go over it step by step:

  1. You set the title. It’s a property in BrowseSupportFragment.
  2. You enable headers in the left section and define back navigation. When you select an item from the catalog, headers hide and you can press Back to show headers again.
  3. You set brand color, which is the color of the headers background.

Call it from onActivityCreated() before calling loadPlaylists(). Your code will look like this:

Call site of the setupTitleAndHeaders function

Build and run. Notice the added title and new color of the section on the left.

Catalog screen showing title at top right and header with green background on left

Implementing a Presenter for Showing Video Thumbnails

As mentioned before, you can imagine as if CatalogFragment contains a RecyclerView. The Leanback library uses a subclass of Presenter to render the contents of each item. Presenter manages creating and binding view holders for ObjectAdapter. Each video item in the catalog will be an item of type VideoItem.

Open CatalogCardPresenter.kt and create custom Presenter. There are three functions you have to override:

  • onCreateViewHolder() for creating a new ViewHolder
  • onBindViewHolder() for binding ViewHolder to an item
  • onUnbindViewHolder() for unbinding ViewHolder from an item

The first one is already implemented. It initializes colors and sets up the background.

Add the following to onBindViewHolder():

//1
val videoItem = item as VideoItem
val video = videoItem.video

//2
val cardView = viewHolder?.view as ImageCardView
cardView.titleText = video.title
cardView.contentText = video.channel
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT)

//3
loadBitmapIntoImageView(
    cardView.context,
    video.cardImageUrl,
    R.drawable.movie,
    cardView.mainImageView
)

Following this code:

  1. The argument item is cast to VideoItem. Then, you create a local Video object that stores video data.
  2. You fetch View of ViewHolder and cast it to ImageCardView. Next, you set its title text, content text and dimensions.
  3. Using loadBitmapIntoImageView() from Util.kt, you use a local drawable and load it into cardView.

Add this to onUnbindViewHolder():

val cardView = viewHolder?.view as ImageCardView
// Remove references to images so that the garbage collector can free up memory
cardView.badgeImage = null
cardView.mainImage = null

This clears the image from ImageCardView so garbage collector can free occupied memory.

Build project to make sure there are no compile errors.

Creating Video Catalog With Playlists

The next thing to do is to fill the catalog with videos.

Go back to CatalogFragment.kt and find loadAndShowPlaylists(). Replace all the code from its body with:

val rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
val cardPresenter = CatalogCardPresenter()

This code creates an adapter for catalog rows and a custom presenter.

To make loading of YouTube API playlists easier, find loadPlaylists() and notice it takes two arguments: a success function for providing results and an error function.

The success function has three arguments:

  • Playlist object that contains information about the playlist
  • ArrayList of VideoObject that contains information about videos
  • Boolean value that confirms the last playlist in the batch

The system calls success function for each fetched playlist. Playlists come in batches (or pages) so if the current playlist is the last one in the batch, the last argument is set to true.

Add the following lambda at the end of loadAndShowPlaylists():

val onPlaylistLoaded = { playlist: Playlist,
    videos: ArrayList<Video>,
    lastPlaylistInBatch: Boolean ->

  // 1
  val listRowAdapter = ArrayObjectAdapter(cardPresenter)

  // 2
  videos.forEach { video ->
    listRowAdapter.add(VideoItem(video, videos))
  }

  // 3
  val header = HeaderItem(rowsAdapter.size().toLong(), playlist.title)
  rowsAdapter.add(ListRow(header, listRowAdapter))

  // 4
  if (lastPlaylistInBatch) {
    progressBarManager.hide()
  }
}

Following this code, you:

  1. Create a new instance of ArrayObjectAdapter that uses cardPresenter.
  2. Populate an item list for adapter with all videos of the playlist.
  3. Create a header for the row that will represent the playlist. Then, you define ListRow object with both the header and adapter containing the videos and pass the object to rowsAdapter.
  4. Hide progress bar if this is the last playlist in the batch.

Add this code at the end of loadAndShowPlaylists():

progressBarManager.show()
loadPlaylists(onPlaylistLoaded, ::showError)

Here, you start the progress bar and video loading. Note that you’re passing showError() as a second argument. It’s provided in the starter project and shows a fragment with an error message.

Finally, below the previous code add:

adapter = rowsAdapter

This assigns the newly created rowsAdapter to CatalogFragment adapter.

Build and run. You’ll get an error message because the YouTube API key isn’t set up yet.

App showing 'Error communicating with Youtube API' message

Generating YouTube API Key

Now, follow the steps in the official documentation to get your YouTube API key.

After you’ve set up the YouTube API key, open your local.properties file and add the following line:

youtube.key=yourapikeyvalue

Build and run again. If you attached the key properly, you see all the videos from the raywenderlich.com channel!

App catalog screen with links to videos on the right and their titles on the left

Looks awesome, doesn’t it?

Note: If you still receive an error make sure that you emulator is able to access the internet and your youtube API key is enabled.

Last, in CatalogFragment.kt, call initializeBackground() at the end of onActivityCreated(). Your code will look like this:

InitializeBackground call site

Build and run. You can see the background changing as you select videos.

App catalog screen with background set up

Setting up Click Listeners

Once the video card is selected, you need to show its details. To set up listeners, add the following in onActivityCreated():

onItemViewClickedListener = 
  OnItemViewClickedListener { itemViewHolder, item, _, _ ->
    if (item is VideoItem) {
      showVideoDetails(requireActivity(), itemViewHolder, item)
    }
  }

This listener checks if the clicked item is VideoItem. If so, showVideoDetails() creates Intent for starting VideoDetailsActivity.kt and sets up a shared transition for the thumbnail.

Note: For more details on makeSceneTransitionAnimation() in Util.kt, check the official documentation.

Build and run. Select a video, and you’ll see a black screen. This will be your next task.

Blank details screen