Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add support for Android TV devices #1

Open
wants to merge 49 commits into
base: master
Choose a base branch
from

Conversation

krzysztofciombor
Copy link

@krzysztofciombor krzysztofciombor commented Oct 16, 2017

Add support for Android TV devices

Motivation

  • To be on a par with Apple TV support, this makes it possible to run React Native apps on Android TV devices (See also: https://react-native.canny.io/feature-requests/p/android-tv-support)
  • These changes also make it possible to navigate through the app using D-PAD buttons that are present on some mobile devices
  • Since these changes affect, among others, ReactRootView.java and Touchable.js code and are closely related to Apple TV implementation, it makes sense for them to be included in the core

Supported Android TV features

  • React native apps can be launched on Android TV devices and properly render their content
  • Navigation is possible using left, right, top, bottom arrows from the remote (or D-PAD)
  • Touchable components can handle D-PAD center button press events and correctly fire their onPress handlers
  • Touchable components will receive onPressIn and onPressOut events and can react to focus/blur changes appropriately (just like on Apple TV)
  • Platform constants allow to check if the react-native app is running on TV (Platform.isTV)
  • ScrollViews behave correctly (same as native implementation) when switching to view outside bounds – that is, the container would scroll such that the newly focused element is fully visible
  • Native "clicking" sounds are played when moving between focusable elements
  • Play/Pause click event is send to TVEventHandler
  • Rewind and FastForward events are send to TVEventHandler
  • Back button behaves as a normal Android back button
  • Diagonal buttons work correctly on Android TV, e.g. if there is no button directly to the right from the focused one, but there is one to the right but a bit higher/lower it will grab focus
  • Dev menu can be accessed by long pressing fast forward button

A demo showing RNTester app running on Android TV device (Amazon Fire TV Stick) can be found here:
RNAndroidTVDemo

Caveats

  • TextInput will not work on Android TV devices. There's an issue with native ReactEditText implementation that prevents it from receiving focus. This makes it impossible to navigate to TextInput.
    This will be fixed next, but will be included in a separate Pull Request
  • Overlay permissions cannot be granted on Android TV devices running Android version >= 6.0
    This is because the overlay permission can only be granted by firing an Intent to open settings page (ACTION_MANAGE_OVERLAY_PERMISSION). Since this page does not exist on TV devices the permission cannot be requested. This will make the app crash when trying to open dev menu (⌘+M) or displaying a redbox error.
    Note: This does not affect devices running Android version < 6.0 (for example Amazon Fire TV Stick)

Test Plan

  • Launch the RNTester app on Android TV device.
    • Ensure it launches without a crash
    • Ensure basic navigation is possible
    • Ensure Touchable components can receive select events
  • Ensure the changes do not break current Android and iOS mobile devices functionality.

RNAndroidTVDemo video

Changes

  • Added ReactAndroidTVViewManager that handles TV KeyEvents and dispatches events to JS - This is the core that enables basic navigation functionality on Android TV devices
  • Following the above change we copy TVEventHandler.ios.js into TVEventHandler.android.js to enable JS to pick up those native navigation events and dispatch them further to subscribed views. (Note: We do not have a native TVNavigationEventEmitter implementation on Android, thus this file is slightly modified, e.g. it does pass null to NativeEventEmitter constructor)
  • Added uiMode to AndroidInfoModule. (Note: This required changing extends BaseJavaModule to extends ReactContextBaseJavaModule to be able to use getSystemService which requires Context instance!
  • Added isTV constants to both Platform.ios.js (keeping the deprecated isTVOS as well) and Platform.android.js
  • Changed condition check on Touchable.js to use the newly added isTV flag to properly handle TV navigation events on Android as well
  • Added LEANBACK_LAUNCHER to RNTester intent-filter so that it is possible to launch it on Android TV devices.
  • Added documentation page for Android TV and merged Android and Apple TV docs into one

Future work

  • Fix TextInput components handling by allowing them to be focused and making a proper navigation between them (and/or other components) possible. One thing to note here that the default behavior to immediately open software keyboard when focused on TextInput field will need to be adjusted on Android TV as well)
  • Fix overlay permissions issue by changing the way redbox/dev menu are displayed
  • Adjust placement of TV-related files (e.g. the TVEventHandler.js file is placed inside AppleTV directory which is not accurate, since it does handle Android TV events as well)

Previous discussion: #1

Release Notes

[ANDROID] [FEATURE] [TV] - Added support for Android TV devices

@krzysztofciombor
Copy link
Author

@kmagiera I've added checks to prevent RNTester app from crashing when run on Android TV, though I'm not 100% convinced if they should make it into the official implementation.

@@ -65,7 +67,7 @@ public ReactActivityDelegate(
}

protected ReactRootView createRootView() {
return new ReactRootView(getContext());
return new ReactAndroidTVViewManager(getContext());
Copy link

@kmagiera kmagiera Oct 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should find some other way instead of making changes here. I'd suggest that we:

  1. Add necessary overrides to ReactRootView (seems like just adding dispatchKeyEvent should be enough)
  2. Proxy call to this method to a new class called ReactAndroidTVRootViewHelper where all the TV related logic from TVViewMagaer will be put

pt 2 will help with not bloating the rootview class with a lot of code that is specific to some small fraction of devices

@@ -34,6 +34,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add comment here what is this needed for (android TV)

@@ -56,4 +62,9 @@ private static String getServerIpAddress(int port) {

return String.format(Locale.US, "%s:%d", ipAddress, port);
}

public static boolean isRunningOnTV(ReactContext reactContext) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code does not belong here. This class provides methods that are used by the code loader (most typically in debug mode). You can just put this code directly in AndroidInfoModule as it is not being used anywhere outside of that module (as opposed to other methods from this class).

Also I'd change the naming strategy here. With "running" it looks weird as we don't use "isRunningOnAndroid", just "platform == 'android'". Perhaps we could find something similar to "interfaceIdiom" exported on iOS. It looks like maybe "uiMode" would be a good choice as it could be TV, NORMAL, CAR etc (https://developer.android.com/reference/android/content/res/Configuration.html#uiMode) much like interfaceIdiom on iOS

@kmagiera
Copy link

Nice progress there! Can you also add a section to this PR description outlining the feature set that your PR makes available on TV (e.g. navigating with arrows, play button, scrollview paging, auto focus)


public static boolean isRunningOnTV(ReactContext reactContext) {
UiModeManager uiModeManager = (UiModeManager) reactContext.getSystemService(UI_MODE_SERVICE);
return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION;
Copy link

@kmagiera kmagiera Oct 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -11,10 +11,35 @@
*/
'use strict';

function TVEventHandler() {}
const React = require('React');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we consolidate TVEventHandler.android.js and TVEventHandler.ios.js? From what I understand the only difference is that the native module we pass to NativeEventEmitter for iOS does not exists on android (in which case its undefined which seems to be ok with this code)

ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
mAndroidTVRootViewHelper.handleKeyEvent(ev, eventEmitter);
return super.dispatchKeyEvent(ev);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we return true here? similarly to what we do with touches we can pretty safely assume the key press event is going to be consumed by the react app. Calling into super will just default to returning false.
This is only relevant to apps that embed react native root views, but if we both handle the key and return false the "master" app may also pass the same key event to some other view which is undesirable

@kmagiera kmagiera changed the title Feature/android tv [WIP] Add support for Android TV devices Oct 16, 2017
@@ -158,7 +158,9 @@ var TouchableNativeFeedback = createReactClass({
touchableHandleActivePressIn: function(e: Event) {
this.props.onPressIn && this.props.onPressIn(e);
this._dispatchPressedStateChange(true);
this._dispatchHotspotUpdate(this.pressInLocation.locationX, this.pressInLocation.locationY);
if (!Platform.isTV) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check instead if this.pressInLocation is defined

Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getContext().getPackageName()));
FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
Toast.makeText(getContext(), REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
((Activity) getContext()).startActivityForResult(serviceIntent, REQUEST_OVERLAY_PERMISSION_CODE);
if (canHandleIntent(serviceIntent)) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we perhaps want to put the log and a toast from the lines above into this if block

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I said, this is just a temporary workaround and the app will still crash when trying to display redbox alert. Do you think we can come up with a more bullet-proof solution for this?


private static final String IS_TESTING = "IS_TESTING";

private ReactContext mReactContext;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When extending ContextBaseJavaModule I think you don't need to keep reference to the context yourselves – you should be able to call getReactApplicationContext() to get reference to the context


private ReactRootView mReactRootView;

public ReactAndroidTVRootViewHelper(ReactRootView mReactRootView) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename argument to reactRootView, then you don't have to use this.sth = sth below

emitter.emit("onTVNavEvent", event);
}

private View getFocusedView(ViewGroup viewGroup) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to findFocusedView ("get" is reserved for operations that are rather simple whereas this performs a search)

@@ -331,7 +331,7 @@ var TouchableMixin = {
} else if (evt.eventType === 'blur') {
cmp.touchableHandleActivePressOut && cmp.touchableHandleActivePressOut(evt);
} else if (evt.eventType === 'select') {
cmp.touchableHandlePress && cmp.touchableHandlePress(evt);
cmp.touchableHandlePress && !cmp.props.disabled && cmp.touchableHandlePress(evt);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kmagiera Note: I'm not exactly sure this is the correct way to handle that, as it relies on cmp.props directly

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe call to this touchableHandleStartShouldSetResponder() instead?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or nevermind, this looks ok to me, TouchableMixin is already accesing that prop in this method: touchableHandleStartShouldSetResponder

KeyEvent.KEYCODE_DPAD_RIGHT
);

private View mLastFocusedView = null;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that we only need view ID for sending blur event later on. In that case I'd prefer not to keep a reference to a view but just the ID

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, are you sure there is no other way for the view to lose focus? If not maybe there is a way for the rootview to notify this handler class when it looses focus so we can send the blur event earlier

super.requestChildFocus(child, focused);
}

private DeviceEventManagerModule.RCTDeviceEventEmitter getDeviceEventEmitter() {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kmagiera Do you think this is a good idea to separate this (and maybe also the part with null checking catalyst instance) to a separate method, since it is repeated multiple times in these proxy methods?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we will save much by doing this. Not worth changing at this moment

mLastFocusedViewId = View.NO_ID;
}

private void handlePlayPauseEvent(RCTDeviceEventEmitter emitter) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about inlining handlePlayPauseEvent and handleSelectEvent directly into handleKeyEvent switch statement.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
return;
}
mAndroidTVRootViewHelper.clearFocus(getDeviceEventEmitter());
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I'm not sure about this but I assume that since it is the RootView that looses its focus that means another (native?) part of the app is now focused and we should send blur event to react-native views.

int eventKeyAction = ev.getAction();
View targetView = findFocusedView(mReactRootView);
if (targetView != null) {
if (KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE == eventKeyCode && eventKeyAction == KeyEvent.ACTION_UP) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kmagiera Now that I think about it, maybe we should dispatch the "playPause" event event without having a focused view? It does not include the view id after all.
At first I thought that it would make no sense to send playPause events without having any view focused, but maybe I'm wrong.

@krzysztofciombor
Copy link
Author

@kmagiera I've added an experimental support for opening dev menu by long pressing the playPause button on remote controller.
Note that, however, our TVHelper will still dispatch a playPause event. I'm now wondering if we shouldn't move the keyEvent recognition code from ReactRootView into ReactActivityDelegate and use the onKeyDown/onKeyUp to proxy keyEvents to ReactAndroidTVRootHelper? What do you think?

@kmagiera
Copy link

@krzysztofciombor I think its ok. Events should be processed in rootview as it should be possible to have multiple rootviews each providing events to a correct react views subtree (e.g. usecase when native app has few react native screens embedded).

As for long play press we shouldn't delay it being sent to RN because of the fact we may want to show the dev menu. I don't think it matters if we dispatch it to JS in case you want to open the menu, as usually after that step you will reload the app

@@ -23,6 +23,12 @@ The RNTester app supports Apple TV; use the `RNTester-tvOS` build target to buil

```js
var Platform = require('Platform');
var running_on_apple_tv = Platform.isTV;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should perhaps be changed to sth like:

var running_on_tv = Platform.isTV

// If you want to be more specific and only detect devices running tvOS (but no Android TV devices) you can use:
var running_on_apple_tv = Platform.isTVOS


## Code changes

- *Access to touchable controls*: When running on Android TV the Android framework will automatically apply a directional navigation scheme based on relative position of focusable elements in your views. The `Touchable` mixin has code added to detect focus changes and use existing methods to style the components properly and initiate the proper actions when the view is selected using the TV remote, so `TouchableHighlight`, `TouchableOpacity` and `TouchableNativeFeedback` will "just work". In particular:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there is a point in duplicating this information. Maybe we can add a section "Building for TV devices" and move most of this stuff there. We can split the section re installation by platform in a way its done here: http://facebook.github.io/react-native/docs/running-on-device.html

@@ -0,0 +1,112 @@
package com.facebook.react.views.androidtv;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add copyright headers

@kmagiera
Copy link

This looks good! I added a few minor comments inline. I think its quite ready to be submitted as a PR upstream.

Can you also restructure the description of this PR in a following way:

  1. title
  2. motivation
  3. what Android TV features are supported (current "What is supported by this set of changes" section)
  4. caveats (what is still broken on android TV)
    • mention textinput here
    • mention redbox and explain what the issue is
    • other things?
  5. describe changes made in this PR (current "changes" section)
  6. test plan (make it a bit more comprehensive though)
  7. future work:
    • mention some naming changes as parts of the code are no longer specific to Apple TV or tvOS
    • issues with overlay permission need to be address (this will come as a separate, unrelated code)
    • fixes for textinput and whatnot

@kmagiera kmagiera closed this Oct 23, 2017
@kmagiera kmagiera reopened this Oct 23, 2017
facebook-github-bot pushed a commit to facebook/react-native that referenced this pull request Mar 6, 2018
Summary:
<!--
Thank you for sending the PR! We appreciate you spending the time to work on these changes.

Help us understand your motivation by explaining why you decided to make this change.

You can learn more about contributing to React Native here: http://facebook.github.io/react-native/docs/contributing.html

Happy contributing!

-->

* To be on par with Apple TV support, this makes it possible to run React Native apps on Android TV devices (See also: https://react-native.canny.io/feature-requests/p/android-tv-support)
* These changes also make it possible to navigate through the app using D-PAD buttons that are present on some mobile devices
* Since these changes affect, among others, `ReactRootView.java` and `Touchable.js` code and are closely related to Apple TV implementation, it makes sense for them to be included in the core

 - React native apps can be launched on Android TV devices and properly render their content
 - Navigation is possible using left, right, top, bottom arrows from the remote (or D-PAD)
 - Touchable components can handle D-PAD center button press events and correctly fire their `onPress` handlers
 - Touchable components will receive `onPressIn` and `onPressOut` events and can react to focus/blur changes appropriately (just like on Apple TV)
 - `Platform` constants allow to check if the react-native app is running on TV (`Platform.isTV`)
 - `ScrollView`s behave correctly (same as native implementation) when switching to view outside bounds – that is, the container would scroll such that the newly focused element is fully visible
 - Native "clicking" sounds are played when moving between focusable elements
 - Play/Pause click event is send to `TVEventHandler`
 - Rewind and FastForward events are send to `TVEventHandler`
 - Back button behaves as a normal Android back button
 - Diagonal buttons work correctly on Android TV, e.g. if there is no button directly to the right from the focused one, but there is one to the right but a bit higher/lower it will grab focus
 - Dev menu can be accessed by long pressing fast forward button

A demo showing RNTester app running on Android TV device (Amazon Fire TV Stick) can be found here:
[![RNAndroidTVDemo](http://img.youtube.com/vi/EzIQErHhY20/0.jpg)](http://www.youtube.com/watch?v=EzIQErHhY20)

- `TextInput` will not work on Android TV devices. There's an issue with native `ReactEditText` implementation that prevents it from receiving focus. This makes it impossible to navigate to `TextInput`.
This will be fixed next, but will be included in a separate Pull Request
- ~Overlay permissions cannot be granted on Android TV devices running Android version >= 6.0
This is because the overlay permission can only be granted by firing an Intent to open settings page (`ACTION_MANAGE_OVERLAY_PERMISSION`). Since this page does not exist on TV devices the permission cannot be requested. This will make the app crash when trying to open dev menu (⌘+M) or displaying a redbox error.
Note: This does not affect devices running Android version < 6.0 (for example Amazon Fire TV Stick)~
This is now fixed by: #16596

* Launch the RNTester app on Android TV device.
  * Ensure it launches without a crash
  * Ensure basic navigation is possible
  * Ensure Touchable components can receive select events
* Ensure the changes do not break current Android and iOS mobile devices functionality.
* Ensure the changes do not break current Apple TV functionality.

[RNAndroidTVDemo video](http://img.youtube.com/vi/EzIQErHhY20/0.jpg)

* Added `ReactAndroidTVViewManager` that handles TV `KeyEvent`s and dispatches events to JS - This is the core that enables basic navigation functionality on Android TV devices
* Following the above change we copy `TVEventHandler.ios.js` into `TVEventHandler.android.js` to enable JS to pick up those native navigation events and dispatch them further to subscribed views. (Note: We do not have a native `TVNavigationEventEmitter` implementation on Android, thus this file is slightly modified, e.g. it does pass `null` to `NativeEventEmitter` constructor)
* Added `uiMode` to `AndroidInfoModule`. (**Note**: This required changing `extends BaseJavaModule` to `extends ReactContextBaseJavaModule` to be able to use `getSystemService` which requires `Context` instance!
* Added `isTV` constants to both `Platform.ios.js` (keeping the deprecated `isTVOS` as well) and `Platform.android.js`
* Changed condition check on `Touchable.js` to use the newly added `isTV` flag to properly handle TV navigation events on Android as well
* Added `LEANBACK_LAUNCHER` to `RNTester` `intent-filter` so that it is possible to launch it on Android TV devices.
* See also a PR to `react-native-website` repo with updated docs for Android TV: facebook/react-native-website#59

 - [ ] Fix `TextInput` components handling by allowing them to be focused and making a proper navigation between them (and/or other components) possible. One thing to note here that the default behavior to immediately open software keyboard when focused on `TextInput` field will need to be adjusted on Android TV as well)
 - [x] Fix overlay permissions issue by changing the way redbox/dev menu are displayed (see: #16596)
 - [ ] Adjust placement of TV-related files (e.g. the `TVEventHandler.js` file is placed inside `AppleTV` directory which is not accurate, since it does handle Android TV events as well)

Previous discussion: software-mansion-labs#1
<!--
Help reviewers and the release process by writing your own release notes

**INTERNAL and MINOR tagged notes will not be included in the next version's final release notes.**

  CATEGORY
[----------]        TYPE
[ CLI      ]   [-------------]      LOCATION
[ DOCS     ]   [ BREAKING    ]   [-------------]
[ GENERAl  ]   [ BUGFIX      ]   [-{Component}-]
[ INTERNAL ]   [ ENHANCEMENT ]   [ {File}      ]
[ IOS      ]   [ FEATURE     ]   [ {Directory} ]   |-----------|
[ ANDROID  ]   [ MINOR       ]   [ {Framework} ] - | {Message} |
[----------]   [-------------]   [-------------]   |-----------|

[CATEGORY] [TYPE] [LOCATION] - MESSAGE

 EXAMPLES:

 [IOS] [BREAKING] [FlatList] - Change a thing that breaks other things
 [ANDROID] [BUGFIX] [TextInput] - Did a thing to TextInput
 [CLI] [FEATURE] [local-cli/info/info.js] - CLI easier to do things with
 [DOCS] [BUGFIX] [GettingStarted.md] - Accidentally a thing/word
 [GENERAL] [ENHANCEMENT] [Yoga] - Added new yoga thing/position
 [INTERNAL] [FEATURE] [./scripts] - Added thing to script that nobody will see
-->

[ANDROID] [FEATURE] [TV] - Added support for Android TV devices
Closes #16500

Differential Revision: D6536847

Pulled By: hramos

fbshipit-source-id: 17bbb11e8583b97f195ced5fd9762f8902fb8a3d
rozele pushed a commit to microsoft/react-native-windows that referenced this pull request Apr 5, 2018
Summary:
<!--
Thank you for sending the PR! We appreciate you spending the time to work on these changes.

Help us understand your motivation by explaining why you decided to make this change.

You can learn more about contributing to React Native here: http://facebook.github.io/react-native/docs/contributing.html

Happy contributing!

-->

* To be on par with Apple TV support, this makes it possible to run React Native apps on Android TV devices (See also: https://react-native.canny.io/feature-requests/p/android-tv-support)
* These changes also make it possible to navigate through the app using D-PAD buttons that are present on some mobile devices
* Since these changes affect, among others, `ReactRootView.java` and `Touchable.js` code and are closely related to Apple TV implementation, it makes sense for them to be included in the core

 - React native apps can be launched on Android TV devices and properly render their content
 - Navigation is possible using left, right, top, bottom arrows from the remote (or D-PAD)
 - Touchable components can handle D-PAD center button press events and correctly fire their `onPress` handlers
 - Touchable components will receive `onPressIn` and `onPressOut` events and can react to focus/blur changes appropriately (just like on Apple TV)
 - `Platform` constants allow to check if the react-native app is running on TV (`Platform.isTV`)
 - `ScrollView`s behave correctly (same as native implementation) when switching to view outside bounds – that is, the container would scroll such that the newly focused element is fully visible
 - Native "clicking" sounds are played when moving between focusable elements
 - Play/Pause click event is send to `TVEventHandler`
 - Rewind and FastForward events are send to `TVEventHandler`
 - Back button behaves as a normal Android back button
 - Diagonal buttons work correctly on Android TV, e.g. if there is no button directly to the right from the focused one, but there is one to the right but a bit higher/lower it will grab focus
 - Dev menu can be accessed by long pressing fast forward button

A demo showing RNTester app running on Android TV device (Amazon Fire TV Stick) can be found here:
[![RNAndroidTVDemo](http://img.youtube.com/vi/EzIQErHhY20/0.jpg)](http://www.youtube.com/watch?v=EzIQErHhY20)

- `TextInput` will not work on Android TV devices. There's an issue with native `ReactEditText` implementation that prevents it from receiving focus. This makes it impossible to navigate to `TextInput`.
This will be fixed next, but will be included in a separate Pull Request
- ~Overlay permissions cannot be granted on Android TV devices running Android version >= 6.0
This is because the overlay permission can only be granted by firing an Intent to open settings page (`ACTION_MANAGE_OVERLAY_PERMISSION`). Since this page does not exist on TV devices the permission cannot be requested. This will make the app crash when trying to open dev menu (⌘+M) or displaying a redbox error.
Note: This does not affect devices running Android version < 6.0 (for example Amazon Fire TV Stick)~
This is now fixed by: facebook/react-native#16596

* Launch the RNTester app on Android TV device.
  * Ensure it launches without a crash
  * Ensure basic navigation is possible
  * Ensure Touchable components can receive select events
* Ensure the changes do not break current Android and iOS mobile devices functionality.
* Ensure the changes do not break current Apple TV functionality.

[RNAndroidTVDemo video](http://img.youtube.com/vi/EzIQErHhY20/0.jpg)

* Added `ReactAndroidTVViewManager` that handles TV `KeyEvent`s and dispatches events to JS - This is the core that enables basic navigation functionality on Android TV devices
* Following the above change we copy `TVEventHandler.ios.js` into `TVEventHandler.android.js` to enable JS to pick up those native navigation events and dispatch them further to subscribed views. (Note: We do not have a native `TVNavigationEventEmitter` implementation on Android, thus this file is slightly modified, e.g. it does pass `null` to `NativeEventEmitter` constructor)
* Added `uiMode` to `AndroidInfoModule`. (**Note**: This required changing `extends BaseJavaModule` to `extends ReactContextBaseJavaModule` to be able to use `getSystemService` which requires `Context` instance!
* Added `isTV` constants to both `Platform.ios.js` (keeping the deprecated `isTVOS` as well) and `Platform.android.js`
* Changed condition check on `Touchable.js` to use the newly added `isTV` flag to properly handle TV navigation events on Android as well
* Added `LEANBACK_LAUNCHER` to `RNTester` `intent-filter` so that it is possible to launch it on Android TV devices.
* See also a PR to `react-native-website` repo with updated docs for Android TV: facebook/react-native-website#59

 - [ ] Fix `TextInput` components handling by allowing them to be focused and making a proper navigation between them (and/or other components) possible. One thing to note here that the default behavior to immediately open software keyboard when focused on `TextInput` field will need to be adjusted on Android TV as well)
 - [x] Fix overlay permissions issue by changing the way redbox/dev menu are displayed (see: facebook/react-native#16596)
 - [ ] Adjust placement of TV-related files (e.g. the `TVEventHandler.js` file is placed inside `AppleTV` directory which is not accurate, since it does handle Android TV events as well)

Previous discussion: software-mansion-labs/react-native#1
<!--
Help reviewers and the release process by writing your own release notes

**INTERNAL and MINOR tagged notes will not be included in the next version's final release notes.**

  CATEGORY
[----------]        TYPE
[ CLI      ]   [-------------]      LOCATION
[ DOCS     ]   [ BREAKING    ]   [-------------]
[ GENERAl  ]   [ BUGFIX      ]   [-{Component}-]
[ INTERNAL ]   [ ENHANCEMENT ]   [ {File}      ]
[ IOS      ]   [ FEATURE     ]   [ {Directory} ]   |-----------|
[ ANDROID  ]   [ MINOR       ]   [ {Framework} ] - | {Message} |
[----------]   [-------------]   [-------------]   |-----------|

[CATEGORY] [TYPE] [LOCATION] - MESSAGE

 EXAMPLES:

 [IOS] [BREAKING] [FlatList] - Change a thing that breaks other things
 [ANDROID] [BUGFIX] [TextInput] - Did a thing to TextInput
 [CLI] [FEATURE] [local-cli/info/info.js] - CLI easier to do things with
 [DOCS] [BUGFIX] [GettingStarted.md] - Accidentally a thing/word
 [GENERAL] [ENHANCEMENT] [Yoga] - Added new yoga thing/position
 [INTERNAL] [FEATURE] [./scripts] - Added thing to script that nobody will see
-->

[ANDROID] [FEATURE] [TV] - Added support for Android TV devices
Closes facebook/react-native#16500

Differential Revision: D6536847

Pulled By: hramos

fbshipit-source-id: 17bbb11e8583b97f195ced5fd9762f8902fb8a3d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants