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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Android TV: Getting Started
25 mins
- Getting Started
- Netderlix Code Structure
- Setting up Android TV Emulator
- The Leanback Library
- Meeting Android TV App Criteria
- Creating Video Catalog
- Using BrowseSupportFragment from Leanback
- Implementing a Presenter for Showing Video Thumbnails
- Creating Video Catalog With Playlists
- Setting up Click Listeners
- Presenting Video Details
- Creating Details View for Selected Playlist
- Previewing Recommendations
- Previewing Recommendation Video Details
- Setting Details View Background
- Playing a Video
- Introducing the Glue Mechanisms
- Where to Go From Here?
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:
- You set the title. It’s a property in
BrowseSupportFragment
. - 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.
- 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:
Build and run. Notice the added title and new color of the section on the 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:
- The argument item is cast to
VideoItem
. Then, you create a localVideo
object that stores video data. - You fetch
View
ofViewHolder
and cast it toImageCardView
. Next, you set its title text, content text and dimensions. - Using
loadBitmapIntoImageView()
from Util.kt, you use a local drawable and load it intocardView
.
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:
- Create a new instance of
ArrayObjectAdapter
that usescardPresenter
. - Populate an item list for adapter with all videos of the playlist.
- 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 torowsAdapter
. - 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.
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!
Looks awesome, doesn’t it?
Last, in CatalogFragment.kt, call initializeBackground()
at the end of onActivityCreated()
. Your code will look like this:
Build and run. You can see the background changing as you select videos.
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.
Build and run. Select a video, and you’ll see a black screen. This will be your next task.