Building Android TV Apps With WebViews (Part 1)

Queuify is a new app that has been off-and-on developed for the past three years. I have just published the app in the beta track on Google Play while developing its fourth iteration.

One reason that this project has been in the pipeline for so long has been scope. This time, I’m not trying to add in too many features, just the ones that I actually use. Over time though, I have built up a good knowledge of YouTube’s data and player APIs, as well as designed a practical database on my own server.

While this has certainly helped with the web app, I’d like my app to extend beyond just my laptop. Notably, I want a great experience on Android TV, the platform which originally inspired the app’s development all those years ago.

I’d love to watch some YouTube videos on the big screen.

Android TV does not have a web browser, but I can inject my app into a WebView. That will work for most of the app’s UI (minus the back button and a few other system-level functions). However, the biggest problem is one that has yet to have a good solution: authentication.

On the web it’s a piece of cake to open up a new window, enter your login credentials, and then return to the original window. On Android TV, window management in a WebView is a bit of a challenge. Even then, I can’t use the dpad to click or type in any fields. It’s not possible to do Oauth through the WebView.

The question is how to make the website know my name on all my devices

This process is easier on a phone as you can focus and type in each field. Yet that creates an unnecessary barrier as one could instead use Android APIs for authentication.

For these reasons I created WebGameBridge.js, a library that lets you design apps in HTML and compile as Android apps without adding unnecessary complexity and giving developers a lot of flexibility and simplicity. This project was originally designed with Epic Table Tennis, incorporating not only authentication but also Google Play Games leaderboards and achievements. It’s also available on Google Play.

To delve under the hood, the system uses Android’s WebView APIs to allow Javascript to execute a Java method and allow Java to execute Javascript. Both sets of APIs are tied together to allow commands to be executed through the webpage cycle like connecting to Google Play Services and authenticating the user. Then, Javascript functions that do things like unlock achievements or pass leaderboards are instead executed using native APIs. You’ll notice that the file size for this game is under 3MB. Though most games often take up dozens or hundreds of megabytes in storage, all the assets can be loaded from the web at runtime and save users a lot of space.

This is all well and good. It seems like a variety of other HTML5 to Android services. The difference is pretty cool though. Epic Table Tennis on the web is exactly the same as in the app. When I’m developing it, it uses the same exact code. It uses the same exact APIs. On the frontend, there’s literally no difference.

As an example, here’s what happens when a game ends and I submit the leaderboard time:

1
GPGLeaderboard.update(GPGLeaderboard.getList().Time, endtime - starttime, function(r) { console.log(r) });

I don’t need to do any checking for whether the user is in an app or on the web. That is handled in the background. In fact, all of the APIs in this library are designed to be cross-platform. Using the website will work as expected. By tying it into the app though, it works in a way that feels native even though all of the code is the same.

Extending this ideology to controls, this library takes native device inputs and translates them into ordinary web inputs. Take for example the back button. In an ordinary WebView app the back button would instantly close the app. However, this library allows us to handle special cases.

The library uses a system of menus that is especially optimized for gaming. This had to be hacked a little bit to work with Queuify, as it’s not a game and therefore won’t use certain menus, but it works as expected on Android.

1
2
3
4
5
6
7
8
9
10
11
12
//Back key activates the Pause if ingame
if(GamePad.isDown(GamePad.KEYS.Back)) {
    console.log("Back button pressed");
if(menus.current() == MENUS.GAME) { //Pause if ingame
    menus.open(MENUS.PAUSE);
} else if(menus.current() == MENUS.PAUSE || menus.current() == MENUS.ACHIEVEMENTS || menus.current() == MENUS.LEADERBOARD) { //Go to main menu
    menus.open(MENUS.MAIN);
} else if(menus.current() == MENUS.MAIN) { //Exit app if on Android
    if(ANDROID)
       Android.exit();
    }
}

When the back button is pressed, it gets mapped to a Javascript key (Escape). If you want to test your back button implementation you can use the escape key and get the same effect.

In the code snippet above, which is from the library itself, you can see how the progression goes from the game, to the pause menu, to the main menu, and then to the homescreen. This is just how it should work, and it’s implemented in a way that works seamlessly on both platforms.

So this is cool, but often these things are difficult to implement in an already-existing project. In this case though, it’s very easy to implement and forget. Here’s how I intregated it into Queuify’s web app:

1
2
3
var clientId = 'xxx.apps.googleusercontent.com';
var apiKey = 'xxxxxx-xxxx-xxxxxxxxxxxxx';
var scopes = 'https://www.googleapis.com/auth/plus.me';

 

That’s it. I just include two scripts, declare a few variables, and I’m done. This is the barebones implementation, but you can get pretty deep into the library if you choose.

1
2
3
function onConnected() {
    ...
}
1
2
3
function onGetUserInfo(r) {
    ...
}
1
2
3
function onKeyTap(keycode) {
    ...
}

 

Here are three functions I’ve also added into the app to do specific things. I can run specific code when the app is first authenticated, when I retrieve user profile data, and when the app receives a key tap. These functions work exactly the same on all the platforms.

I can tell a button to execute handleClientLoad on clicking it, with the id “authorize-button”, to initialize a login session. However, after the first time the process is automatic on both the web and Android.

Now that I’ve finished my web app and have integrated the library as much as I want, it’s time to extend it into an Android app. I just need to download the library and then create a single launcher activity.

1
2
3
4
5
6
7
public class EpicTableTennis extends WebViewActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        initialize("http://felkerdigitalmedia.com/pong/retro.php", new String[]{"Games", "Plus"});
        super.onCreate(savedInstanceState);
    }
}

This is all the code I need to write for the game to run on Android. I call initialize with two parameters, the URL I’m trying to open, and a String array of scopes that I’m requesting. If you just want a barebones web app that takes advantage of the native Android platform, you essentially only need to write one line.

What’s great is that if you want to go beyond a simple barebones app, you can do that as well by leveraging custom platform features. If I was to use a library like Unity, I’d end up being bogged down by cross-platform. Even something as simple as notifications requires an expensive plugin. This library is meant to support cross-platform without limiting flexibility or the unique properties of each platform. Nothing is being hidden or preventing you from customization. All this code is yours to optimize as you want.

Queuify is a video app, and it runs on Android TV. I’d like to to support recommendations, that row of videos at the top of the launcher. Though there may not be any web APIs for this, I can easily extend my Android app. It just requires me to add another service to the app and call it periodically for updates. This service makes an HTTP request for the user, which can be stored using SharedPreferences, and parses the data for use in the recommendations row.

WebGameBridge.js gives you the best of the web: quick updates, simple programming, and cross-platform support with the best of native platforms: familiar user interactions, integrated APIs, and unique experiences. I recommend web developers and Android developers alike take a look at this library the next time they plan on doing a project. It turns building an app into building a responsive website.

The second part of this article will go into adding custom support for Android TV while controlling most of it in JavaScript.

Nick Felker

Nick Felker

Nick Felker is a student Electrical & Computer Engineering student at Rowan University (C/O 2017) and the student IEEE webmaster. When he's not studying, he is a software developer for the web and Android (Felker Tech). He has several open source projects on GitHub (http://github.com/fleker)Devices: Moto G-2013 Moto G-2015, Moto 360, Google ADT-1, Nexus 7-2013 (x2), Lenovo Laptop, Custom Desktop.Although he was an intern at Google, the content of this blog is entirely independent and his own thoughts.

More Posts - Website

Follow Me:
TwitterLinkedInGoogle PlusReddit