Amazon Fire TV Stick App in React Native
In short
Today as some of you could have expected after our short movie with “Hello world” written in React Native running on Amazon Fire TV Stick (referred to AF later), Jakub Stasiak and I are going to describe our journey with an AF.
First steps with AF
Our adventure with an AF has started from a request from a one of our client to build an app which would be responsible for displaying important information on TV displays in shops. Fire TV Stick is a small device (in the size of a bigger memory stick) which is probably the cheapest option to make non-smart TVs “smarter”. The most important thing for us is that it’s powered by Android.
When our AF arrived, we had to decide which technology stack we would like to choose. A final app would consist of 3–4 screens with input areas, list and details view.
How can we write an app for AF?
After some research we came up with the following options:
- HTML5 app
- native Android app
- React Native app
The order of the list above is not a coincidence, the first option is probably the easiest and you can find relatively many docs about that solution. RN app is on the last position because we haven’t found anyone who had done this before. As it’s not too hard to spot the answer we wanted to try with RN on AF.
For the purpose of this article, we want to go through a process of creating an RSS reader in RN for AF.
How to enable adb?
The first step to become a React Native developer for Amazon Fire TV Stick is to enable adb on a device. On a Fire Stick, it’s even easier than on an Android phone. We just have to go to the device settings and find the adb switch. Next, we can verify whether the adb was enabled or not, we can do that from CLI (if our AF is connected to our computer) using command <rte-code>adb devices<rte-code>.
Will an empty app work?
Now we can generate and start an empty RN app, we have to run the following two commands:
- <rte-code>react-native init FirestickDemo && cd FirestickDemo<rte-code>
- <rte-code>react-native run-android<rte-code>
As a result, we should see a standard RN app, be able to show developers the menu and navigate through options. 🎉
Can we navigate through buttons?
At this point, it seems like we can start developing what we planned. Let’s add some buttons to test if it works.
White rectangle should change background color.
Why can’t we click on anything?
We are clicking center button on remote control but nothing happens. It’s possible to navigate between touchable components and buttons but there is no possibility to call <rte-code>onPress<rte-code> method. We tried few different approaches. We even wrote native apps in Java and Native Script with few buttons (those apps works great). Turned out that <rte-code>Button<rte-code> component doesn’t use native button underneath. That’s an unfortunate.
How does AF’s remote work?
While writing our first app for AF we realized that remote control sends classic keyboard events. React Native doesn’t support those events by default but we found a library for that. Great, so there is still hope. It looks like we have everything we need — possibility to navigate between every <rte-code>touchable<rte-code> and possibility to listen to keyboard events. We could manually call <rte-code>onPress<rte-code> after user press center button on a remote control.
Yes, we could… but we can’t.
Another problem — there is no possibility to find out which <rte-code>touchable<rte-code> is focused. Ah, React Native, dear friend why you have to make everything harder. Now we must write something big and ugly that will become better <rte-code>touchable<rte-code> and keep in our state information about currently focused component.
How to live with that?
Our idea was to create two things:
- <rte-code>SelectableContainer<rte-code> - a component which will store all information about selectable components in current view and react to keyboard events.
- <rte-code>selectable<rte-code> - a Higher Order Component , something that can transform classic <rte-code>TouchableOpacity<rte-code>, <rte-code>Button<rte-code>, <rte-code>View<rte-code>, etc. into a component that can be focused and selected using a keyboard or a remote control.
<rte-code>SelectableContainer<rte-code> should be a stateful component with a state in that shape:
So we can properly react on every up/down button click and change <rte-code>activeSelectable<rte-code> to one above/below. There is a list of coordinates in a component state so we can easily find out which selectable should we select. On every change of <rte-code>activeSelectable<rte-code> we will call <rte-code>onFocus<rte-code>, <rte-code>onBlur<rte-code> or <rte-code>onPress<rte-code> method. The code which is responsible for all of that:
How does SelectableContainer have the access to buttons inside?
The answer could frighten you — through <rte-code>context<rte-code>. Yes, we know… it’s an experimental API which will probably change but still, it’s the best solution for us. Of course, we don’t want to write context “magic“ every time we want to add simple button. It will be ugly and hard to maintain.
That’s why we created <rte-code>selectable<rte-code> HOC. It will register our component in <rte-code>SelectableContainer<rte-code> and make sure that we can use <rte-code>onPress<rte-code> prop on every component wrapped by it. It will also read position of our component so <rte-code>SelectableContainer<rte-code> will know the proper visual order of selectable components.
Okay, we can now easily create selectable <rte-code>Button<rte-code> or <rte-code>View<rte-code>. Let’s do that by modifying our first attempt to AF buttons:
Now we can select buttons!
Will inputs work?
The first part of our work is done! We are ready to create a list of RSS feeds with buttons. Now we only have to add some possibility to create custom RSS feed. And there is no surprise, classic <rte-code>TextInput<rte-code> doesn’t work well on AF. It’s impossible to navigate from another component to it. Fortunately, we created <rte-code>selectable<rte-code> before so we can us it here. Just simply wrap <rte-code>TextInput<rte-code> into <rte-code>selectable<rte-code> and call <rte-code>textInput.focus()<rte-code> inside <rte-code>onPress<rte-code> function prop. Just like this:
Now we truly have all pieces we need to build AF app!
RSS feed app
As you can see our app consists of 2 screens, for navigation we use exponent/ex-navigation which handle back button for us. The most interesting part of an app (of course besides problems described above) was fetching RSS news in an easy to read from javascript form. Not that long time ago we would use Google Feed API to translate xmlish RSSes to jsons, but sadly Google decided to shut down this service and we had to find an alternative solution. Finally, we came across YQL (Yahoo query language) which works exactly as we expected. For displaying RSS content which sometimes is HTML we use WebView component from RN.
Link to source code.
Conclusion
Should you use React Native as a technology to build AF app? If you are familiar with React Native definitely yes, it was a great adventure for us and we learned a lot of things. If not, maybe stay with native stuff or write an app in HTML5 which is kind of a recommended solution for Fire TV Stick.