From 8d89d89966d8bad04ea9b8413c82c8ed5786f55a Mon Sep 17 00:00:00 2001 From: Krzysztof Ciombor Date: Thu, 9 Nov 2017 14:53:43 +0100 Subject: [PATCH] Add support for hasTVPreferredFocus on Android TV devices --- Libraries/Components/AppleTV/TVViewPropTypes.js | 10 +++------- Libraries/Components/Button.js | 7 +++++++ .../Touchable/TouchableNativeFeedback.android.js | 7 +++++++ Libraries/Components/Touchable/TouchableOpacity.js | 5 +++++ .../View/PlatformViewPropTypes.android.js | 13 ------------- ...iewPropTypes.ios.js => PlatformViewPropTypes.js} | 2 +- .../facebook/react/views/view/ReactViewManager.java | 9 +++++++++ 7 files changed, 32 insertions(+), 21 deletions(-) delete mode 100644 Libraries/Components/View/PlatformViewPropTypes.android.js rename Libraries/Components/View/{PlatformViewPropTypes.ios.js => PlatformViewPropTypes.js} (95%) diff --git a/Libraries/Components/AppleTV/TVViewPropTypes.js b/Libraries/Components/AppleTV/TVViewPropTypes.js index b2c426d86241e3..092c828e20d009 100644 --- a/Libraries/Components/AppleTV/TVViewPropTypes.js +++ b/Libraries/Components/AppleTV/TVViewPropTypes.js @@ -17,17 +17,13 @@ var PropTypes = require('prop-types'); */ var TVViewPropTypes = { /** - * *(Apple TV only)* When set to true, this view will be focusable - * and navigable using the Apple TV remote. - * - * @platform ios + * When set to true, this view will be focusable + * and navigable using the TV remote. */ isTVSelectable: PropTypes.bool, /** - * *(Apple TV only)* May be set to true to force the Apple TV focus engine to move focus to this view. - * - * @platform ios + * May be set to true to force the TV focus engine to move focus to this view. */ hasTVPreferredFocus: PropTypes.bool, diff --git a/Libraries/Components/Button.js b/Libraries/Components/Button.js index 9729f8832bd440..473d9772544b87 100644 --- a/Libraries/Components/Button.js +++ b/Libraries/Components/Button.js @@ -55,6 +55,7 @@ class Button extends React.Component<{ title: string, onPress: () => any, color?: ?string, + hasTVPreferredFocus?: ?boolean, accessibilityLabel?: ?string, disabled?: ?boolean, testID?: ?string, @@ -76,6 +77,10 @@ class Button extends React.Component<{ * If true, disable all interactions for this component. */ disabled: PropTypes.bool, + /** + * TV preferred focus (see documentation for the View component). + */ + hasTVPreferredFocus: PropTypes.bool, /** * Handler to be called when the user taps the button */ @@ -93,6 +98,7 @@ class Button extends React.Component<{ onPress, title, disabled, + hasTVPreferredFocus, testID, } = this.props; const buttonStyles = [styles.button]; @@ -123,6 +129,7 @@ class Button extends React.Component<{ accessibilityTraits={accessibilityTraits} testID={testID} disabled={disabled} + hasTVPreferredFocus={hasTVPreferredFocus} onPress={onPress}> {formattedTitle} diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js index 482024edf4db83..d2e09a5ff23ad1 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js @@ -84,6 +84,11 @@ var TouchableNativeFeedback = createReactClass({ */ background: backgroundPropType, + /** + * TV preferred focus (see documentation for the View component). + */ + hasTVPreferredFocus: PropTypes.bool, + /** * Set to true to add the ripple effect to the foreground of the view, instead of the * background. This is useful if one of your child views has a background of its own, or you're @@ -248,6 +253,8 @@ var TouchableNativeFeedback = createReactClass({ testID: this.props.testID, onLayout: this.props.onLayout, hitSlop: this.props.hitSlop, + isTVSelectable: true, + hasTVPreferredFocus: this.props.hasTVPreferredFocus, onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder, onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest, onResponderGrant: this.touchableHandleResponderGrant, diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 4a46cdab939f62..a28e891efb32d4 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -129,6 +129,10 @@ var TouchableOpacity = createReactClass({ * active. Defaults to 0.2. */ activeOpacity: PropTypes.number, + /** + * TV preferred focus (see documentation for the View component). + */ + hasTVPreferredFocus: PropTypes.bool, /** * Apple TV parallax effects */ @@ -246,6 +250,7 @@ var TouchableOpacity = createReactClass({ testID={this.props.testID} onLayout={this.props.onLayout} isTVSelectable={true} + hasTVPreferredFocus={this.props.hasTVPreferredFocus} tvParallaxProperties={this.props.tvParallaxProperties} hitSlop={this.props.hitSlop} onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder} diff --git a/Libraries/Components/View/PlatformViewPropTypes.android.js b/Libraries/Components/View/PlatformViewPropTypes.android.js deleted file mode 100644 index 7d4734bdaa5f84..00000000000000 --- a/Libraries/Components/View/PlatformViewPropTypes.android.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule PlatformViewPropTypes - * @flow - */ - -module.export = {}; diff --git a/Libraries/Components/View/PlatformViewPropTypes.ios.js b/Libraries/Components/View/PlatformViewPropTypes.js similarity index 95% rename from Libraries/Components/View/PlatformViewPropTypes.ios.js rename to Libraries/Components/View/PlatformViewPropTypes.js index 7957c7b4a90ecf..cf3d6d02ba75e7 100644 --- a/Libraries/Components/View/PlatformViewPropTypes.ios.js +++ b/Libraries/Components/View/PlatformViewPropTypes.js @@ -13,7 +13,7 @@ const Platform = require('Platform'); var TVViewPropTypes = {}; -if (Platform.isTVOS) { +if (Platform.isTV) { TVViewPropTypes = require('TVViewPropTypes'); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java index 77104de53b8b7e..4ceccd7e2337d0 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java @@ -52,6 +52,15 @@ public void setAccessible(ReactViewGroup view, boolean accessible) { view.setFocusable(accessible); } + @ReactProp(name = "hasTVPreferredFocus") + public void setTVPreferredFocus(ReactViewGroup view, boolean hasTVPreferredFocus) { + if (hasTVPreferredFocus) { + view.setFocusable(true); + view.setFocusableInTouchMode(true); + view.requestFocus(); + } + } + @ReactPropGroup(names = { ViewProps.BORDER_RADIUS, ViewProps.BORDER_TOP_LEFT_RADIUS,