Making your OpenFL game Android TV ready
I have been using a Raspberry PI as a media center with Kodi (OpenELEC) since it came out. It is a wonderfully (cheap) media player, but the performance can be lacking from time to time. Last year I decided to look for an alternative and because I am developing games, I wanted a media player that can also be used as a kind of console/testing device with a controller for the big screen. After some searching on the net I read that Google had been working on the Android TV platform and was going to release the Nexus Player. Unfortunately there where no plans for a Europe release, so I would have to import one. Then I discovered that Google handed out developer kits named ADT-1 to developers in the US. Since I had to import the device anyway, I went to eBay and bought one.
Got my ADT-1 development kit. Curious about the capabilities of this device! pic.twitter.com/SjvjR4opes
— Jip Spinnewijn (@spipnl) December 20, 2014
I hooked it up to my TV and installed the Kodi apk via wifi. It was working perfectly. My first game, although not ready for controller input, worked surprisingly well thanks to the OpenFL platform. The only downsides where that the game icon was not visible on the home screen and that it was not available in the Android TV Play Store version. This post explains how I learned to make apps Android TV ready.
Android TV OpenFL example
To make this explanation easier to understand, I decided to create an example application on GitHub.
Extending the AndroidManifest.xml
The app needs to edit the AndroidManifest.xml. OpenFL provides some settings to be set trough the project xml of your Haxe application like sdk-versions, but not (yet) the extra settings we need for Android TV, so we need to overwrite the template used for generating the AndroidManifest.xml. Using this guide as background info copy the original template from lime to templates/AndroidManifest.xml
Then set the template path in your OpenFL project xml:
1 |
<template path="templates/AndroidManifest.xml" rename="AndroidManifest.xml" if="android"/> |
Following the official guide to make your app Android TV ready and visible in the Play Store on your device, you need to alter the xml-file on multiple places.
Adding the leanback launcher intent filter
First you need to add this filter to your MainActivity to enable your app for TV and make the app icon available on the TV home screen.
1 |
<category android:name="android.intent.category.LEANBACK_LAUNCHER" /> |
Adding the TV features
You need to tell the app that it can be used on other devices than mobile. Add the following lines to the template.
1 2 |
<uses-feature android:name="android.hardware.touchscreen" android:required="false" /> <uses-feature android:name="android.software.leanback" android:required="false" /> |
The first adds leanback launcher support for the app to be visible on the launcher menu. Set this to required=”true” when your are making a TV-only app. The second declares that your app is compatible with devices without touch screens (TV’s obviously).
Adding leanback banner
By default the leanback icon will use your app icon to show in the TV menu. To use a special image for the TV shortcut, we need to create an image resource of 320 by 180 pixels and place it in the res/drawable-xhdpi folder. Create a banner image, place it in the assets folder and tell OpenFL to copy it to the right folder by adding the following line to your project file:
1 |
<assets path="assets/appbanner.png" rename="../res/drawable-xhdpi/banner.png" if="android" /> |
(I have searched for other ways to do this, but eventually this was working fine for me. If anyone knows a better way of doing this. Please place a comment below!)
Then add the android:logo attribute to the application element in the AndroidManifest.xml. The official guide suggests using android:banner , but this was not working for me. Also the native test application in Android Studio uses android:logo , so I would stick to that for now.
1 2 3 4 5 |
<application ... android:logo="@drawable/banner" > ... </application> |
Place in games category
If you now run the app it will show up nicely with our banner image on the menu. By default it will show in the ‘Apps’ category. If you are using OpenFL you are probably creating a game and want your app in the ‘Games’ section. How do we do that?
You have to add another attribute to our application element:
1 2 3 4 5 |
<application ... android:isGame="true" > ... </application> |
This is a new setting introduced in Android 5.0, so be aware that your app now needs SDK 21 or it will give an error on compilation. You can set the SDK version in your project xml.
1 |
<android target-sdk-version="21" /> |
Install the app again on the Android TV device and it is properly placed in the ‘Games’ section:
You now have made your OpenFL game Android TV ready!
Is running on Android TV?
TV’s are obviously very different devices from mobile phones and tablets. They require a different type of interaction (a controller instead of touch input) and because you are sitting at a distance, you are advised to build a different interface for your TV apps. Therefore we need to know when the app is running on an Android TV or not. The Android Developers guide explains that you have to check the Current Mode Type of the main activity. This mode type can also be used to check if it is running on Android Watch/Android Car. Who knows in the future? 🙂
You can skip this section and go to Using the Extension if you are not interested in the juicy details of developing the extension 😉
Example code from Android Developers guide to check the Mode Type:
1 2 3 4 5 6 7 8 |
public static final String TAG = "DeviceTypeRuntimeCheck"; UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE); if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) { Log.d(TAG, "Running on a TV Device") } else { Log.d(TAG, "Running on a non-TV Device") } |
I have created a simple native extension that reads the Mode Type and checks if it is equal to the Android TV constant (UI_MODE_TYPE_TELEVISION).
Creating the Extension
This was my first attempt of creating an OpenFl native extension, so I needed some help. I followed this tutorial and generated the example extension from lime and renamed it to android-mode-type.
Using the example code from the Android Developers guide I created a method to retrieve the Mode Type. The variable mainContext is a static variable in the org.haxe.extension.Extension class which contains the context of the current Activity. It simply returns the integer currentModeType.
1 2 3 4 5 6 7 8 9 |
... public static int getAndroidModeType() { UiModeManager uiModeManager = (UiModeManager) mainContext.getSystemService(Context.UI_MODE_SERVICE); int currentModeType = (int)uiModeManager.getCurrentModeType(); return currentModeType; } ... |
And I created a second method to check the mode type against the Configuration.UI_MODE_TYPE_TELEVISION constant.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
... public static boolean isAndroidTV() { boolean result = false; int currentModeType = AndroidModeType.getAndroidModeType(); if (currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) { result = true; } return result; } ... |
The Haxe part of the extension has a method called isAndroidTV() that calls the Java method with the same name if the app is running on Android and sets the result. If it is not running Android it will always return false.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
... public static function isAndriodTV():Bool { // Default is false var result:Bool = false; // Only make the native call if running on Android #if (android && openfl) result = is_android_tv(); #end return result; } #if (android && openfl) private static var is_android_tv = JNI.createStaticMethod("nl.spip.extension.AndroidModeType", "isAndroidTV", "()Z"); #end ... |
Using the Extension
Now we can include the extension in our project xml:
1 |
<haxelib name="android-mode-type" /> |
And use it in our application:
1 2 3 4 5 6 7 8 9 10 11 12 |
... // Call the native extension to check if the app is running on Android TV if (AndroidModeType.isAndriodTV()) { textFormat.color = 0x3C763D; deviceTextField.defaultTextFormat = textFormat; deviceTextField.text = 'Running on Android TV'; } else { textFormat.color = 0xA94442; deviceTextField.defaultTextFormat = textFormat; deviceTextField.text = 'Not running on Android TV'; } ... |
The result of the app running on Android TV:
On other (Android) devices and platforms it will show a red text saying ‘Not running on Android TV’.
Play Store Example
I wanted to verify that my example app was visible in the Android TV version of the Google Play Store, so I submitted it to the Play Store with the appropriate details. After everything was done and I pressed ‘submit’ I received an email from Google telling me that the app was violating the spam provisions of the Content Policy and it was suspended from the Play Store.
It probably had to do with the title of the app containing ‘Android TV’. I have made an appeal against the removal at Google, but I got a very vague reply that they could not help me any further. In order for the app to work I should resubmit it under a different title in order to work, but I decided to not take the risk. I will implement it on one of my next games and in the mean time you have to take my word for it and try it yourself! 😉
So here is the source of the example app:
https://github.com/spipnl/android-tv-openfl-example
And here is the source of the extension:
https://github.com/spipnl/adroid-mode-type
Happy coding and if you have any questions or if this helped you out, you can always leave a comment below.