Skip to content

Android TV and the Sorcerer's Code: Making TV Apps Demystified (Part 1)

Android TV apps explained
Making Android TV apps

So, you're interested in making an Android TV app? Maybe you're a developer who has no idea where to start? In that case, I was in your shoes when our Tech Lead announced we're making just that.

I had zero knowledge about Android TV at the time, and I've never used it nor read about it. Of course I was excited to learn something new, but also sceptical about the amount of unknowns I had on the subject. Little did I know, the days I spent on this project would end up being the most enjoyable development experience I've had to date.

So worry not! Let me help you on your first step in the upcoming journey ahead. This post will serve as an introduction to the topic highlighting different challenges and tips based on our experience with developing a production TV app.

This is Part 1 of more to come. Here's what we'll cover this time:

  • An introduction to our published product and requirements
  • How to get started
  • Getting familiar with the Leanback libraries offered by Google

The app

TV 2 Regions is a white-label mobile app solution we developed at Shape for providing news and video content. We've published 28 mobile news apps based on this while-label architecture that you can read more about here.

From embedded videos inside news articles to news broadcasts and full blown series, videos live at the very heart of the app. This made it the perfect candidate for a TV app where users can enjoy their favourite video content on a large TV screen.

We started planning this new project, and from the get go we realised two requirements:

  • First, we needed to reuse as much code as we could. We already had so much logic in the mobile app and it didn’t make sense to bill the client for a completely new solution when we could leverage what we already had. Not to mention the maintenance headache in the future if we needed to change common parts of the two codebases. The intention for the new TV app is to be a different UI layer on top of existing business and data layers.
  • Great native user experience. We've built a high quality and award winning product for mobile, and we intended to keep the bar high when targeting TV. TV platforms are different from what we're used to on mobile, so we had to learn about what makes a TV experience unique. We also had to be mindful of how Apple and Google do things differently, so we can also deliver an authentic native user experience to our users.

Our TV apps are already published on the Play and App stores. Here's a demo of the Android app:

Now that we'd set our requirements, it was time to get started.

Getting started

Android TV vs Google TV

When I first started looking into Android TV, I kept encountering this term – Google TV. And I quickly got confused about what the difference is and whether it's going to affect developing the app. A few Google searches later, the answer turned out to be pretty simple. Basically, Google TV is just a rebranding of the Android TV OS. Google has improved the system by focusing on a tailored user experience and a better looking interface, but it's just a new shell on top of an existing system. The same apps work on both systems and share the same Play Store.

Google TV, however, does offer content discovery features which your app can integrate with to get more visibility and engagement. You can read more about the difference from a developer's perspective here.

If you're also curious about the difference from a user perspective, Tom's Guide has a good article about it. 

Documentation: Google's got your back!

One of the best things about working on an Android TV app for the first time was the amount of help and information Google has prepared for getting started. I remember having so many questions and just diving into the documentation for days to understand what makes TV apps special and be prepared to make the right technical choices from the start.

Get started by looking at these resources:

Having said that, there is always a risk of information overload and spending too much time researching instead of starting the implementation earlier. So it's important to draw a line where initial research becomes sufficient and coding should start. You'll continue to learn as you go and your project manager will thank you 😊

Look at the market

TV users are already familiar with specific usage patterns that we had to pay attention to, so our app can provide a friendly and familiar experience to the users. Naturally, we found ourselves drawing inspiration from existing TV apps which helped us identify good choices to ensure seamless design.

Lean back with Leanback

Leanback is a collection of support libraries that make it easier to create TV apps for Android.

You can read more about it here. You can also get a better understanding of how the libraries work by looking at the sample apps.

Leanback has made making interactive pages much easier than building them from scratch. The UI templates it offers are customisable and allowed us to preserve a unique identity within our app.

Let's take a look at the most commonly used classes and use cases.

BrowseSupportFragment

Home page of our TV SYD PLAY app

 

This fragment is useful for browsing through a TV catalogue and it can serve as your app's main screen. It's basically composed of two fragments. One is a HeadersSupportFragment which plays the role of a bottom navigation bar on mobile. The other is a RowsSupportFragment which supports scrolling over a vertical list of items.

By default, the browse fragment supports collapsing the headers fragment when the user moves to the rows fragment, as well as expanding it when the back button is clicked.

You can also provide your own implementation of both fragments if you need more customisations on top of what the BrowseSupportFragment API already offers.

As you can see, the fragment also provides a search button you can use and customise the colour of to match your app style.

Basically, this fragment is a scaffold for your screen that helps you bring the elements of your page together while you focus on making the individual items that populate it.

RowsSupportFragment

This will be your go-to fragment to showcase the catalogue of content your app has to offer. The advantage of this fragment over just building a regular list fragment from scratch is that it handles remote control navigation out of the box. So as long as your items are focusable, the user will be able to navigate between them. It will also respond to the user navigating down with the remote by scrolling the list down.

The API for setting and displaying the data is also quite simple and similar to using a RecyclerView. You basically need to set an ObjectAdapter that is responsible for managing the data. The adapter takes a Presenter object that is responsible for creating the item view holders and binding/unbinding them.

You can either create custom ObjectAdapter and Presenter classes, or use one of their pre-made subclasses such as ArrayObjectAdapter and RowPresenter.

The adapter can also accept a PresentorSelector instead, which allows you to select a different Presenter class for different items.

Dependency diagram for the classes used in RowsSupportFragment

 

VerticalGridView & HorizontalGridView

These are very useful subclasses of RecyclerView that offer scrolling the items based on user interactions with the remote control as well as keeping track of a selected position. They also have functions for setting item spacing, item alignment, and focus scroll strategy (such as making sure the focused item is always at a specific position, or scrolling a page of items when scrolling outside the list).

Handling focus changes is a huge part of the user experience in a TV app. And these classes are designed to make it easier for the developer to support that.

SearchSupportFragment

Search page for our TV SYD PLAY app

 

Implementing a search feature is made easy with Leanback. You can leverage the SearchSupportFragment by extending it and calling the following functions:

  • setBadgeDrawable(): sets an image to show inside the embedded search bar.
  • setTitle(): to set the placeholder text for the search field.
  • setSearchResultProvider(): sets the interface instance that provides the ObjectAdapter for the search result items that are rendered in a RowsSupportFragment. The interface also has callbacks for when the search query is updated or submitted.
  • setOnItemViewClickedListener(): sets a click listener for the search result items.

Typing using the remote control doesn’t provide the best user experience, so this fragment also comes with an out of the box support for speech recognition. Next to the search field, there is a speech recognition button. To support this, you need only declare the android.permission.RECORD_AUDIO permission and react to the result in onActivityResult.

You may need to support showing other UI elements in the search page. For this, one trick is to wrap the support fragment in an activity for example and overlay it with more UI elements. You may need to interface with the views embedded inside the fragment to adjust their margins or paddings for example to leave space for these additional views. For this, you'll have to dig into the fragment's source code and understand a bit of its inner workings. Using the Layout Inspector would be helpful to understand the internal view structure the fragment is using.

Video playback

Imagine creating a video player from just a handful of lines.

class MyVideoFragment : VideoSupportFragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val videoUriPath = "<http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4>"
        val mediaAdapter = MediaPlayerAdapter(context)

        val playerGlue = PlaybackTransportControlGlue(context, mediaAdapter)
        playerGlue.host = VideoSupportFragmentGlueHost(this)
        playerGlue.title = "Leanback is awesome"
        playerGlue.subtitle = "Leanback video example"

        mediaAdapter.setDataSource(videoUriPath.toUri())
        playerGlue.playWhenPrepared()
    }
}

 

Code for a simple and functional video player page

 

The Leanback library offers an array of classes that simplify implementing a video playback feature.

Dependency diagram for the classes required to make a video player page

 

Let's go through them:

  • VideoSupportFragment: This fragment is responsible for rendering videos on a SurfaceView as well as displaying the playback controls and related content, such as title and subtitle.
  • VideoSupportFragmentGlueHost: This class is a bridge that propagates UI events (lifecycle events, user interactions, …) to PlaybackTransportControlGlue and player events as well as playback controls definitions to VideoSupportFragment.
  • PlaybackTransportControlGlue: This class provides a playback controls implementation that includes by default play/pause, skip next/previous, and a seek bar. We can also add our own custom controls by subclassing it. It acts as a connection between these controls and the video player.
  • PlayerAdapter: This is a wrapper class over a video player, and provides an interface that's consumed by PlaybackTransportControlGlue.
    • MediaPlayerAdapter: An implementation of PlayerAdapter based on a MediaPlayer.
    • LeanbackPlayerAdapter: Another implementation based on ExoPlayer which is part of the Jetpack Media3 library and provides a number of advantages over the classic MediaPlayer, such as extensibility and support for playlists.

Chromecast Support

You can enable casting to your Android TV app from your mobile app through Chromecast. Google has made a nice codelab about this. Check it out for more details.

Don't lean back too much

Even though the Leanback libraries offer a lot of useful classes and templates that simplify making an Android TV app, if your design does not conform exactly to the default behaviours of these classes, you'll find yourself often digging into the source code trying to understand what's going on behind the scenes and how you can customise these classes. Leanback is pretty extensible but to achieve that you have to acquire a good understanding of its inner-workings. I've personally never spent as much time digging into Google's source code and resources as when I was developing our Android TV app 😅. It's a bit of an uphill climb in the beginning, but once you've overcome it, you'll find yourself empowered by the journey and clear about the way ahead.

Coming up

In the next part, we'll talk about the architecture we used that allowed us to reuse the mobile app code and have a more maintainable solution. We'll also cover some interesting UI challenges we faced and how we solved them.

Blogposts

What's up