From c416b40542ece64e26fb2298485ae42eeebc352a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Rit?= Date: Wed, 20 Feb 2019 23:34:39 -0800 Subject: [PATCH] Add setNextFocus support (#22082) Summary: Add properties to be able to set the nextFocus. It can be very useful with Android TV. New android View properties : * nextFocusDown binded to [setNextFocusDownId](https://developer.android.com/reference/android/view/View.html#setNextFocusDownId(int)) * nextFocusForward binded to [setNextFocusForwardId](https://developer.android.com/reference/android/view/View.html#setNextFocusForwardId(int)) * nextFocusLeft binded to [setNextFocusLeftId](https://developer.android.com/reference/android/view/View.html#setNextFocusLeftId(int)) * nextFocusRight binded to [setNextFocusRightId](https://developer.android.com/reference/android/view/View.html#setNextFocusRightId(int)) * nextFocusUp binded to [setNextFocusUpId](https://developer.android.com/reference/android/view/View.html#setNextFocusUpId(int)) Can be used to fix : * Fixes #20593 * Fixes #20100 Same PR as #22080 but accorded to changes on master Pull Request resolved: https://github.com/facebook/react-native/pull/22082 Differential Revision: D14162740 Pulled By: cpojer fbshipit-source-id: 9a13a185d4e8307ce67014fb076c62d135c487c3 --- Libraries/Components/Button.js | 45 +++++++++++++++ .../Touchable/TouchableHighlight.js | 57 ++++++++++++++++++- .../TouchableNativeFeedback.android.js | 30 ++++++++++ .../Components/Touchable/TouchableOpacity.js | 40 +++++++++++++ Libraries/Components/View/ViewPropTypes.js | 35 ++++++++++++ .../react/views/view/ReactViewManager.java | 25 ++++++++ 6 files changed, 231 insertions(+), 1 deletion(-) diff --git a/Libraries/Components/Button.js b/Libraries/Components/Button.js index 78e6bdd636d..7a233cfc584 100644 --- a/Libraries/Components/Button.js +++ b/Libraries/Components/Button.js @@ -43,6 +43,41 @@ type ButtonProps = $ReadOnly<{| */ hasTVPreferredFocus?: ?boolean, + /** + * TV next focus down (see documentation for the View component). + * + * @platform android + */ + nextFocusDown?: ?number, + + /** + * TV next focus forward (see documentation for the View component). + * + * @platform android + */ + nextFocusForward?: ?number, + + /** + * TV next focus left (see documentation for the View component). + * + * @platform android + */ + nextFocusLeft?: ?number, + + /** + * TV next focus right (see documentation for the View component). + * + * @platform android + */ + nextFocusRight?: ?number, + + /** + * TV next focus up (see documentation for the View component). + * + * @platform android + */ + nextFocusUp?: ?number, + /** * Text to display for blindness accessibility features */ @@ -95,6 +130,11 @@ class Button extends React.Component { onPress, title, hasTVPreferredFocus, + nextFocusDown, + nextFocusForward, + nextFocusLeft, + nextFocusRight, + nextFocusUp, disabled, testID, } = this.props; @@ -127,6 +167,11 @@ class Button extends React.Component { accessibilityRole="button" accessibilityStates={accessibilityStates} hasTVPreferredFocus={hasTVPreferredFocus} + nextFocusDown={nextFocusDown} + nextFocusForward={nextFocusForward} + nextFocusLeft={nextFocusLeft} + nextFocusRight={nextFocusRight} + nextFocusUp={nextFocusUp} testID={testID} disabled={disabled} onPress={onPress}> diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 5cc22939042..fa0e083fa8a 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -43,9 +43,18 @@ type IOSProps = $ReadOnly<{| tvParallaxProperties?: ?TVParallaxPropertiesType, |}>; +type AndroidProps = $ReadOnly<{| + nextFocusDown?: ?number, + nextFocusForward?: ?number, + nextFocusLeft?: ?number, + nextFocusRight?: ?number, + nextFocusUp?: ?number, +|}>; + type Props = $ReadOnly<{| ...TouchableWithoutFeedbackProps, ...IOSProps, + ...AndroidProps, activeOpacity?: ?number, underlayColor?: ?ColorValue, @@ -189,7 +198,48 @@ const TouchableHighlight = ((createReactClass({ */ hasTVPreferredFocus: PropTypes.bool, /** - * Apple TV parallax effects + * TV next focus down (see documentation for the View component). + * + * @platform android + */ + nextFocusDown: PropTypes.number, + /** + * TV next focus forward (see documentation for the View component). + * + * @platform android + */ + nextFocusForward: PropTypes.number, + /** + * TV next focus left (see documentation for the View component). + * + * @platform android + */ + nextFocusLeft: PropTypes.number, + /** + * TV next focus right (see documentation for the View component). + * + * @platform android + */ + nextFocusRight: PropTypes.number, + /** + * TV next focus up (see documentation for the View component). + * + * @platform android + */ + nextFocusUp: PropTypes.number, + /** + * *(Apple TV only)* Object with properties to control Apple TV parallax effects. + * + * enabled: If true, parallax effects are enabled. Defaults to true. + * shiftDistanceX: Defaults to 2.0. + * shiftDistanceY: Defaults to 2.0. + * tiltAngle: Defaults to 0.05. + * magnification: Defaults to 1.0. + * pressMagnification: Defaults to 1.0. + * pressDuration: Defaults to 0.3. + * pressDelay: Defaults to 0.0. + * + * @platform ios */ tvParallaxProperties: PropTypes.object, /** @@ -367,6 +417,11 @@ const TouchableHighlight = ((createReactClass({ isTVSelectable={true} tvParallaxProperties={this.props.tvParallaxProperties} hasTVPreferredFocus={this.props.hasTVPreferredFocus} + nextFocusDown={this.props.nextFocusDown} + nextFocusForward={this.props.nextFocusForward} + nextFocusLeft={this.props.nextFocusLeft} + nextFocusRight={this.props.nextFocusRight} + nextFocusUp={this.props.nextFocusUp} onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder} onResponderTerminationRequest={ this.touchableHandleResponderTerminationRequest diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js index 16beec536b4..46e7d2d9c3f 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js @@ -93,6 +93,31 @@ const TouchableNativeFeedback = createReactClass({ */ hasTVPreferredFocus: PropTypes.bool, + /** + * TV next focus down (see documentation for the View component). + */ + nextFocusDown: PropTypes.number, + + /** + * TV next focus forward (see documentation for the View component). + */ + nextFocusForward: PropTypes.number, + + /** + * TV next focus left (see documentation for the View component). + */ + nextFocusLeft: PropTypes.number, + + /** + * TV next focus right (see documentation for the View component). + */ + nextFocusRight: PropTypes.number, + + /** + * TV next focus up (see documentation for the View component). + */ + nextFocusUp: PropTypes.number, + /** * 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 @@ -303,6 +328,11 @@ const TouchableNativeFeedback = createReactClass({ onLayout: this.props.onLayout, hitSlop: this.props.hitSlop, isTVSelectable: true, + nextFocusDown: this.props.nextFocusDown, + nextFocusForward: this.props.nextFocusForward, + nextFocusLeft: this.props.nextFocusLeft, + nextFocusRight: this.props.nextFocusRight, + nextFocusUp: this.props.nextFocusUp, hasTVPreferredFocus: this.props.hasTVPreferredFocus, onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder, onResponderTerminationRequest: this diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index ad97528a737..98a2c5d0afd 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -32,6 +32,11 @@ const PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30}; type TVProps = $ReadOnly<{| hasTVPreferredFocus?: ?boolean, + nextFocusDown?: ?number, + nextFocusForward?: ?number, + nextFocusLeft?: ?number, + nextFocusRight?: ?number, + nextFocusUp?: ?number, tvParallaxProperties?: ?TVParallaxPropertiesType, |}>; @@ -148,6 +153,36 @@ const TouchableOpacity = ((createReactClass({ * TV preferred focus (see documentation for the View component). */ hasTVPreferredFocus: PropTypes.bool, + /** + * TV next focus down (see documentation for the View component). + * + * @platform android + */ + nextFocusDown: PropTypes.number, + /** + * TV next focus forward (see documentation for the View component). + * + * @platform android + */ + nextFocusForward: PropTypes.number, + /** + * TV next focus left (see documentation for the View component). + * + * @platform android + */ + nextFocusLeft: PropTypes.number, + /** + * TV next focus right (see documentation for the View component). + * + * @platform android + */ + nextFocusRight: PropTypes.number, + /** + * TV next focus up (see documentation for the View component). + * + * @platform android + */ + nextFocusUp: PropTypes.number, /** * Apple TV parallax effects */ @@ -281,6 +316,11 @@ const TouchableOpacity = ((createReactClass({ testID={this.props.testID} onLayout={this.props.onLayout} isTVSelectable={true} + nextFocusDown={this.props.nextFocusDown} + nextFocusForward={this.props.nextFocusForward} + nextFocusLeft={this.props.nextFocusLeft} + nextFocusRight={this.props.nextFocusRight} + nextFocusUp={this.props.nextFocusUp} hasTVPreferredFocus={this.props.hasTVPreferredFocus} tvParallaxProperties={this.props.tvParallaxProperties} hitSlop={this.props.hitSlop} diff --git a/Libraries/Components/View/ViewPropTypes.js b/Libraries/Components/View/ViewPropTypes.js index 259dd91ea91..ed23042dacd 100644 --- a/Libraries/Components/View/ViewPropTypes.js +++ b/Libraries/Components/View/ViewPropTypes.js @@ -284,6 +284,41 @@ type AndroidViewProps = $ReadOnly<{| * See http://facebook.github.io/react-native/docs/view.html#importantforaccessibility */ importantForAccessibility?: ?('auto' | 'yes' | 'no' | 'no-hide-descendants'), + + /** + * TV next focus down (see documentation for the View component). + * + * @platform android + */ + nextFocusDown?: ?number, + + /** + * TV next focus forward (see documentation for the View component). + * + * @platform android + */ + nextFocusForward?: ?number, + + /** + * TV next focus left (see documentation for the View component). + * + * @platform android + */ + nextFocusLeft?: ?number, + + /** + * TV next focus right (see documentation for the View component). + * + * @platform android + */ + nextFocusRight?: ?number, + + /** + * TV next focus up (see documentation for the View component). + * + * @platform android + */ + nextFocusUp?: ?number, |}>; type IOSViewProps = $ReadOnly<{| 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 5d286a6518a..31178517860 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 @@ -65,6 +65,31 @@ public class ReactViewManager extends ViewGroupManager { } } + @ReactProp(name = "nextFocusDown", defaultInt = View.NO_ID) + public void nextFocusDown(ReactViewGroup view, int viewId) { + view.setNextFocusDownId(viewId); + } + + @ReactProp(name = "nextFocusForward", defaultInt = View.NO_ID) + public void nextFocusForward(ReactViewGroup view, int viewId) { + view.setNextFocusForwardId(viewId); + } + + @ReactProp(name = "nextFocusLeft", defaultInt = View.NO_ID) + public void nextFocusLeft(ReactViewGroup view, int viewId) { + view.setNextFocusLeftId(viewId); + } + + @ReactProp(name = "nextFocusRight", defaultInt = View.NO_ID) + public void nextFocusRight(ReactViewGroup view, int viewId) { + view.setNextFocusRightId(viewId); + } + + @ReactProp(name = "nextFocusUp", defaultInt = View.NO_ID) + public void nextFocusUp(ReactViewGroup view, int viewId) { + view.setNextFocusUpId(viewId); + } + @ReactPropGroup(names = { ViewProps.BORDER_RADIUS, ViewProps.BORDER_TOP_LEFT_RADIUS,