Developing for the Living Room: How to Build an Android App for Fire TV (Part 6)

Playing the content: The PlaybackOverlayActivity

Mario Viviani
Amazon Developers
Published in
5 min readJan 9, 2017

--

In parts 1–5 of this series we followed the user journey on Fire TV from browsing and content discovery to reading the details of specific content and performing an action. Now we end our journey on the best part: how to play the video!

The PlaybackOverlayActivity

In a Leanback-enabled project, playing video content is performed within the PlaybackOverlayActivity. The UI of the PlaybackOverlayActivity is simple. We have a full-screen video player that is responsible for playing the content. On top of the video player is the PlaybackOverlayFragment, which is responsible for displaying all the media controls and managing the underlying content play back.

There are many different video players that can be used. When we first deploy the Leanback-enabled project, the default video player is VideoView.

VideoViewis a very basic video player that is perfect if we just want to display non-encrypted video files in an easy way. That being said, most developers prefer to opt for a more powerful and feature-rich player, and for Fire TV the usual choice is the Amazon-customized version of ExoPlayer. For the sake of simplicity, we’ll stick with what we find in the default Leanback template, VideoView.

When we analyze the xml file of the UI in the PlaybackOverlayActivity, we’ll see that there’s nothing more than these two components:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<VideoView
android:id="@+id/videoView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"/>
<fragment
android:id="@+id/playback_controls_fragment"
android:name="PlaybackOverlayFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>

Main components of the PlaybackOverlayActivity

The basic setup of the PlaybackOverlayActivity is quite simple, and focuses on four main components:

public class PlaybackOverlayActivity extends Activity implements
PlaybackOverlayFragment.OnPlayPauseClickedListener {

private VideoView mVideoView;
private LeanbackPlaybackState mPlaybackState;
private MediaSession mSession;



}
  1. Listeners: we can add plenty of Listeners to the PlaybackOverlayActivity, which is useful for adding callbacks for all the actions performed by the user on their remote control (Play, Pause, Rewind, etc). For simplicity, we’ll review the only Listener available on the Leanback template, the OnPlayPauseClickedListener.
  2. VideoView: is the player we’ll use to play the video content.
  3. LeanbackPlaybackState: is just a flag that is used to keep track of what the status of the app is (e.g. LeanbackPlaybackState.PLAYING, LeanbackPlaybackState.PAUSED).
  4. MediaSession: the main task of MediaSession is to talk to the underlying Android framework and manage the ownership of the actions performed with the remote.

The remote

One of the main differences between Fire TV and other Android devices is that the only way to interact with Fire TV is through its remote.

Because Fire OS 5 is based on Android Lollipop, the KeyEvents generated on the Fire TV remote are the same KeyEvents that are generated on a classic Android device, but with physical buttons.

First of all, we need to make sure that our application is in control of the remote. We do it easily by setting a couple flags in the MediaSession.

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.playback_controls);
...

mSession = new MediaSession(this, "LeanbackSampleApp");
mSession.setCallback(new MediaSessionCallback());
mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS
| MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS
);
mSession.setActive(true);}

When MediaSession.setFlags() is invoked, our application takes control of the actions performed on the buttons of the remote. In particular, the FLAG_HANDLES_MEDIA_BUTTONSallows users to take control of the buttons on the bottom of the remote (Rewind, Play/Pause, and Forward), and FLAG_HANDLES_TRANSPORT_CONTROLS allows our app to listen for the Navigation buttons that control the movement inside the views of the app (Up, Right, Down, Left).

Listen to KeyEvents on the remote

Now that we have set the MediaSession of our app, we can start listening for actions performed on the remote.

To do this, we need to react to KeyEvents:

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
PlaybackOverlayFragment playbackOverlayFragment =
findFragmentById(R.id.playback_controls_fragment);
switch (keyCode) {
...
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
if (mPlaybackState == LeanbackPlaybackState.PLAYING) {
playbackOverlayFragment.togglePlayback(false);
} else {
playbackOverlayFragment.togglePlayback(true);
}
return true;
...
}
}

We need to override onKeyUp(), as this is the event that is triggered when the user clicks on a button and lifts their finger (single click/action concluded).

In this case we demonstrate how to react when the user presses the button Play/Pause. We react to the KeyEvent: KEYCODE_MEDIA_PLAY_PAUSE then check the current LeanbackPlaybackState. If we are PLAYING, we toggle playback to false on the PlaybackOverlayFragment (to stop playing). Otherwise we set it to true, triggering the callback on the underlying fragment.

Finally, we need to trigger playback on the VideoView. To do this, we need to implement the callback provided by the ClickListener in the Activity (in our case, PlaybackOverlayFragment.OnPlayPauseClickedListener).

public void onFragmentPlayPause(Movie movie, int position, Boolean playPause) {
mVideoView.setVideoPath(movie.getVideoUrl());
...
if (mPlaybackState != LeanbackPlaybackState.PLAYING) {
mPlaybackState = LeanbackPlaybackState.PLAYING;
if (position > 0) {
mVideoView.seekTo(position);
mVideoView.start();
}
...
}

Let’s see step by step what happens here:

  1. Set the URL of the video to play. To do this, we use VideoView.setVideoPath(), bypassing the URL of the video. It could be a simple video file on our cloud repository or an embedded video in the app. The important bit is that the URL needs to point to a video file.
  2. We check the LeanbackPlaybackState.
  3. If we need to start playing the content, we use seekTo() to set the position in the video where we want it to start playing (in milliseconds, typically 0).
  4. Call VideoView.start() to finally make the video play!

Other features of Leanback-enabled projects

The Leanback template is quite complex but allows the creation of rich TV experiences for our users. For example, by default you can define recommended videos when a piece of content is displayed. Also, developers can implement the SearchFragment to create custom search experiences.

Beyond Leanback: The Fire App Builder

Leanback is just the first step when it comes to creating rich media streaming experiences. At Amazon, we’ve been listening closely to the feedback coming from TV app developers and we recently released a new template for creating native TV apps the easiest way possible: the Fire App Builder.

With the Fire App Builder you can create a rich TV Android App in a few minutes, easily connecting it to a streaming service and with plug-and-play modules for IAP, Video Ads, Social Login, Closed Captioning and much more.

To learn more, visit the Github repository at github.com/amzn/fire-app-builder and the complete documentation at bit.ly/FireAppBuilderDoc

We are very excited to see your TV app on Fire TV!

-Mario Viviani (@mariuxtheone)

Originally published at developer.amazon.com.

--

--