react-native-tvos 0.76.0-0rc6 → 0.76.1-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/Libraries/AppDelegate/RCTAppDelegate.mm +3 -2
  2. package/Libraries/Components/Pressable/Pressable.d.ts +1 -1
  3. package/Libraries/Components/Pressable/Pressable.js +0 -48
  4. package/Libraries/Components/Pressable/useAndroidRippleForView.js +4 -4
  5. package/Libraries/Components/TV/TVViewPropTypes.js +2 -0
  6. package/Libraries/Components/TextInput/TextInput.d.ts +1 -1
  7. package/Libraries/Components/Touchable/Touchable.js +0 -43
  8. package/Libraries/Components/Touchable/TouchableBounce.js +0 -33
  9. package/Libraries/Components/Touchable/TouchableHighlight.js +12 -47
  10. package/Libraries/Components/Touchable/TouchableNativeFeedback.js +0 -33
  11. package/Libraries/Components/Touchable/TouchableOpacity.js +12 -44
  12. package/Libraries/Components/Touchable/TouchableWithoutFeedback.js +0 -19
  13. package/Libraries/Components/View/ViewNativeComponent.js +6 -0
  14. package/Libraries/Components/View/ViewPropTypes.d.ts +12 -1
  15. package/Libraries/Components/View/ViewPropTypes.js +7 -0
  16. package/Libraries/Core/ReactNativeVersion.js +2 -2
  17. package/Libraries/NativeComponent/BaseViewConfig.android.js +19 -0
  18. package/Libraries/NativeComponent/BaseViewConfig.ios.js +6 -0
  19. package/Libraries/NativeComponent/TVViewConfig.js +4 -0
  20. package/Libraries/Pressability/Pressability.js +54 -0
  21. package/Libraries/ReactNative/AppRegistry.js +3 -3
  22. package/Libraries/Types/CoreEventTypes.d.ts +21 -0
  23. package/Libraries/Types/CoreEventTypes.js +6 -0
  24. package/README.md +38 -26
  25. package/React/Base/RCTVersion.m +2 -2
  26. package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +41 -25
  27. package/React/Views/RCTTVView.h +13 -0
  28. package/React/Views/RCTTVView.m +54 -34
  29. package/React/Views/RCTViewManager.m +4 -0
  30. package/ReactAndroid/gradle.properties +1 -1
  31. package/ReactAndroid/src/main/java/com/facebook/react/HeadlessJsTaskService.java +4 -8
  32. package/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt +1 -1
  33. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +2 -2
  34. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java +16 -0
  35. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java +20 -0
  36. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlurEvent.kt +16 -0
  37. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FocusEvent.kt +16 -0
  38. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PressInEvent.kt +16 -0
  39. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PressOutEvent.kt +16 -0
  40. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +212 -4
  41. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +47 -4
  42. package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
  43. package/ReactCommon/react/renderer/components/view/BaseViewEventEmitter.cpp +18 -0
  44. package/ReactCommon/react/renderer/components/view/BaseViewEventEmitter.h +8 -0
  45. package/package.json +8 -8
  46. package/scripts/cocoapods/utils.rb +4 -2
  47. package/scripts/codegen/generate-artifacts-executor.js +19 -4
  48. package/sdks/hermesc/osx-bin/hermes +0 -0
  49. package/sdks/hermesc/osx-bin/hermesc +0 -0
  50. package/sdks/hermesc/win64-bin/hermesc.exe +0 -0
  51. package/types/public/ReactNativeTVTypes.d.ts +1 -1
  52. package/Libraries/Components/Touchable/TVTouchable.js +0 -71
@@ -110,6 +110,18 @@ const bubblingEventTypes = {
110
110
  bubbled: 'onClick',
111
111
  },
112
112
  },
113
+ topFocus: {
114
+ phasedRegistrationNames: {
115
+ captured: 'onFocusCapture',
116
+ bubbled: 'onFocus',
117
+ },
118
+ },
119
+ topBlur: {
120
+ phasedRegistrationNames: {
121
+ captured: 'onBlurCapture',
122
+ bubbled: 'onBlur',
123
+ },
124
+ },
113
125
  };
114
126
 
115
127
  const directEventTypes = {
@@ -123,6 +135,13 @@ const directEventTypes = {
123
135
  registrationName: 'onGestureHandlerStateChange',
124
136
  }),
125
137
 
138
+ topPressIn: {
139
+ registrationName: 'onPressIn',
140
+ },
141
+ topPressOut: {
142
+ registrationName: 'onPressOut',
143
+ },
144
+
126
145
  // Direct events from UIManagerModuleConstants.java
127
146
  topContentSizeChange: {
128
147
  registrationName: 'onContentSizeChange',
@@ -181,6 +181,12 @@ const directEventTypes = {
181
181
  onGestureHandlerStateChange: DynamicallyInjectedByGestureHandler({
182
182
  registrationName: 'onGestureHandlerStateChange',
183
183
  }),
184
+ topPressIn: {
185
+ registrationName: 'onPressIn',
186
+ },
187
+ topPressOut: {
188
+ registrationName: 'onPressOut',
189
+ },
184
190
  };
185
191
 
186
192
  const validAttributesForNonEventProps = {
@@ -16,6 +16,10 @@ export const validAttributesForTVProps = {
16
16
  nextFocusLeft: true,
17
17
  nextFocusRight: true,
18
18
  nextFocusUp: true,
19
+ onFocus: true,
20
+ onBlur: true,
21
+ onPressIn: true,
22
+ onPressOut: true,
19
23
  trapFocusLeft: true,
20
24
  trapFocusRight: true,
21
25
  trapFocusDown: true,
@@ -131,6 +131,11 @@ export type PressabilityConfig = $ReadOnly<{|
131
131
  */
132
132
  onPressOut?: ?(event: PressEvent) => mixed,
133
133
 
134
+ /**
135
+ * Called when a TV event is passed to the config.
136
+ */
137
+ onTVEvent?: ?(event: any) => void,
138
+
134
139
  /**
135
140
  * Whether to prevent any other native components from becoming responder
136
141
  * while this pressable is responder.
@@ -152,6 +157,8 @@ export type EventHandlers = $ReadOnly<{|
152
157
  onResponderTerminate: (event: PressEvent) => void,
153
158
  onResponderTerminationRequest: () => boolean,
154
159
  onStartShouldSetResponder: () => boolean,
160
+ onPressIn: (event: any) => void,
161
+ onPressOut: (event: any) => void,
155
162
  |}>;
156
163
 
157
164
  type TouchState =
@@ -391,6 +398,7 @@ export default class Pressability {
391
398
  |}>;
392
399
  _touchActivateTime: ?number;
393
400
  _touchState: TouchState = 'NOT_RESPONDER';
401
+ _longPressSent: boolean = false;
394
402
 
395
403
  constructor(config: PressabilityConfig) {
396
404
  this.configure(config);
@@ -430,6 +438,50 @@ export default class Pressability {
430
438
  }
431
439
 
432
440
  _createEventHandlers(): EventHandlers {
441
+ const tvPressEventHandlers = {
442
+ onPressIn: (evt: any): void => {
443
+ if (this._config.disabled === false) {
444
+ return;
445
+ }
446
+
447
+ this._longPressSent = false;
448
+
449
+ const {onPressIn, onLongPress} = this._config;
450
+ onPressIn && onPressIn(evt);
451
+
452
+ const delayPressIn = normalizeDelay(this._config.delayPressIn);
453
+ const delayLongPress = normalizeDelay(
454
+ this._config.delayLongPress,
455
+ 10,
456
+ DEFAULT_LONG_PRESS_DELAY_MS - delayPressIn,
457
+ );
458
+ this._longPressDelayTimeout = setTimeout(() => {
459
+ onLongPress && onLongPress(evt);
460
+ this._longPressSent = true;
461
+ }, delayLongPress + delayPressIn);
462
+ },
463
+ onPressOut: (evt: any): void => {
464
+ if (this._config.disabled === false) {
465
+ return;
466
+ }
467
+ this._cancelLongPressDelayTimeout();
468
+ const {onPress, onLongPress, onPressOut, android_disableSound} =
469
+ this._config;
470
+ onPressOut && onPressOut(evt);
471
+
472
+ if (onPress != null) {
473
+ const isPressCanceledByLongPress =
474
+ onLongPress != null && this._longPressSent;
475
+ if (!isPressCanceledByLongPress) {
476
+ if (Platform.OS === 'android' && android_disableSound !== true) {
477
+ SoundManager.playTouchSound();
478
+ }
479
+ onPress(evt);
480
+ }
481
+ }
482
+ },
483
+ };
484
+
433
485
  const focusEventHandlers = {
434
486
  onBlur: (event: BlurEvent): void => {
435
487
  const {onBlur} = this._config;
@@ -610,6 +662,7 @@ export default class Pressability {
610
662
  };
611
663
  }
612
664
  return {
665
+ ...tvPressEventHandlers,
613
666
  ...focusEventHandlers,
614
667
  ...responderEventHandlers,
615
668
  ...hoverPointerEvents,
@@ -662,6 +715,7 @@ export default class Pressability {
662
715
  },
663
716
  };
664
717
  return {
718
+ ...tvPressEventHandlers,
665
719
  ...focusEventHandlers,
666
720
  ...responderEventHandlers,
667
721
  ...mouseEventHandlers,
@@ -13,7 +13,7 @@ import type {RootTag} from '../Types/RootTagTypes';
13
13
  import type {IPerformanceLogger} from '../Utilities/createPerformanceLogger';
14
14
  import type {DisplayModeType} from './DisplayMode';
15
15
 
16
- import BatchedBridge from '../BatchedBridge/BatchedBridge';
16
+ import registerCallableModule from '../Core/registerCallableModule';
17
17
  import BugReporting from '../BugReporting/BugReporting';
18
18
  import createPerformanceLogger from '../Utilities/createPerformanceLogger';
19
19
  import infoLog from '../Utilities/infoLog';
@@ -363,8 +363,8 @@ global.RN$SurfaceRegistry = {
363
363
 
364
364
  if (global.RN$Bridgeless === true) {
365
365
  console.log('Bridgeless mode is enabled');
366
- } else {
367
- BatchedBridge.registerCallableModule('AppRegistry', AppRegistry);
368
366
  }
369
367
 
368
+ registerCallableModule('AppRegistry', AppRegistry);
369
+
370
370
  module.exports = AppRegistry;
@@ -246,6 +246,15 @@ export interface GestureResponderEvent
246
246
 
247
247
  export interface MouseEvent extends NativeSyntheticEvent<NativeMouseEvent> {}
248
248
 
249
+ export interface NativeFocusEvent {}
250
+ export interface FocusEvent extends NativeSyntheticEvent<NativeFocusEvent> {}
251
+
252
+ export interface NativeBlurEvent {}
253
+ export interface BlurEvent extends NativeSyntheticEvent<NativeBlurEvent> {}
254
+
255
+ export interface NativePressEvent {}
256
+ export interface PressEvent extends NativeSyntheticEvent<NativePressEvent> {}
257
+
249
258
  export interface TargetedEvent {
250
259
  target: number;
251
260
  }
@@ -265,6 +274,18 @@ export interface PointerEvents {
265
274
  onPointerUpCapture?: ((event: PointerEvent) => void) | undefined;
266
275
  }
267
276
 
277
+ export interface PressEvents {
278
+ onPressIn?: ((event: PressEvent) => void) | undefined;
279
+ onPressOut?: ((event: PressEvent) => void) | undefined;
280
+ }
281
+
282
+ export interface FocusEvents {
283
+ onFocus?: ((event: FocusEvent) => void) | undefined;
284
+ onFocusCapture?: ((event: FocusEvent) => void) | undefined;
285
+ onBlur?: ((event: BlurEvent) => void) | undefined;
286
+ onBlurCapture?: ((event: BlurEvent) => void) | undefined;
287
+ }
288
+
268
289
  export interface TVRemoteEvent {
269
290
  tag?: number | undefined;
270
291
  target?: number | undefined;
@@ -282,6 +282,12 @@ export type FocusEvent = SyntheticEvent<
282
282
  |}>,
283
283
  >;
284
284
 
285
+ export type RemotePressEvent = SyntheticEvent<
286
+ $ReadOnly<{|
287
+ target: number,
288
+ |}>,
289
+ >;
290
+
285
291
  export type MouseEvent = SyntheticEvent<
286
292
  $ReadOnly<{|
287
293
  clientX: number,
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Apple TV and Android TV support for React Native are maintained here and in the corresponding `react-native-tvos` NPM package, and not in the [core repo](https://github.com/facebook/react-native/). This is a full fork of the main repository, with only the changes needed to support Apple TV and Android TV.
4
4
 
5
- Releases of `react-native-tvos` will be based on a public release of `react-native`; e.g. the 0.75.2-0 release of this package will be derived from the 0.75.0 release of `react-native`. All releases of this repo will follow the 0.xx.x-y format, where x digits are from a specific RN core release, and y represents the additional versioning from this repo.
5
+ Releases of `react-native-tvos` will be based on a public release of `react-native`; e.g. the 0.76.0-0 release of this package will be derived from the 0.76.0 release of `react-native`. All releases of this repo will follow the 0.xx.x-y format, where x digits are from a specific RN core release, and y represents the additional versioning from this repo.
6
6
 
7
7
  Releases will be published on npmjs.org and you may find the latest release version here: https://www.npmjs.com/package/react-native-tvos?activeTab=versions or use the tag `@latest`
8
8
 
@@ -23,20 +23,29 @@ This README covers only TV-specific features. For more general documentation and
23
23
 
24
24
  ### Hermes JS support
25
25
 
26
- As of the 0.71 release, Hermes is fully working on both Apple TV and Android TV, and is enabled by default.
26
+ - As of the 0.71 release, Hermes is fully working on both Apple TV and Android TV, and is enabled by default.
27
27
 
28
28
  ### React Native new architecture (Fabric) support
29
29
 
30
- - _Apple TV_: Modify your app's Podfile to set the `:fabric_enabled` value to `true` in both iOS and tvOS targets. After that, run `pod install` to pick up the additional pods needed for the new architecture. Components that have not been reimplemented in the new architecture will show up as an "unimplemented component".
31
- - _Android TV_: To enable Fabric, modify `android/gradle.properties` in your app and set `newArchEnabled=true`, then rebuild your app.
30
+ React Native TV 0.76 enables the New Architecture by default. You can read more about it in this blog post from the RN core team: [The New Architecture Is Here](https://reactnative.dev/blog/2024/10/23/the-new-architecture-is-here)
32
31
 
33
- As of the 0.74 release, bridgeless is the default when Fabric is enabled. If you need to use Fabric without bridgeless on Apple TV, you can override the default by adding the method below in `AppDelegate.mm`:
32
+ If, for any reason, the New Architecture is not behaving properly in your application, there is always the option to opt-out from it until you are ready to turn it on again.
34
33
 
35
- ```objc
36
- - (BOOL)bridgelessEnabled
37
- {
38
- return false;
39
- }
34
+ To opt-out from the New Architecture:
35
+
36
+ - _Expo apps_: RN 0.76 will be supported in SDK 52. See the [SDK 52 beta release notes](https://expo.dev/changelog/2024/10-24-sdk-52-beta) for more information on new arch support in Expo.
37
+
38
+ - _Apple TV_: You can reinstall the dependencies by running the command:
39
+
40
+ ```sh
41
+ RCT_NEW_ARCH_ENABLED=0 bundle exec pod install
42
+ ```
43
+
44
+ - _Android TV_: On Android, modify the android/gradle.properties file and turn off the newArchEnabled flag, then do a clean rebuild:
45
+
46
+ ```diff
47
+ -newArchEnabled=true
48
+ +newArchEnabled=false
40
49
  ```
41
50
 
42
51
  ### Typescript
@@ -51,17 +60,19 @@ The RNTester app supports Apple TV and Android TV. In this repo, `RNTester/Podf
51
60
 
52
61
  Minimum operating system versions:
53
62
 
54
- - Apple TV: tvOS 13.4
63
+ - Apple TV:
64
+ - tvOS 13.4 (for the 0.74 and 0.75 releases)
65
+ - tvOS 15.1 (for the 0.76 release)
55
66
  - Android TV:
56
- - API level 21 (for the 0.73 releases)
57
- - API level 23 (for the 0.74 release)
67
+ - API level 23 (for the 0.74 and 0.75 releases)
68
+ - API level 24 (for the 0.76 release)
58
69
 
59
70
  ## Build changes
60
71
 
61
- - _Native layer for Apple TV_: React Native Xcode projects all now have Apple TV build targets, with names ending in the string '-tvOS'. Changes in the React Native podspecs in 0.73 now require that your application `Podfile` only have one target. This repo supports either an iOS target or a tvOS target, but both targets should not be active at the same time. The new app template now has the iOS target commented out.
72
+ - _Native layer for Apple TV_: Changes in the React Native podspecs (in 0.73 and later) require that your application `Podfile` only have one target. This repo supports either an iOS target or a tvOS target.
62
73
  - _Maven artifacts for Android TV_: In 0.71 and later releases, the React Native Android prebuilt archives are published to Maven instead of being included in the NPM. We are following the same model, except that the Maven artifacts will be in group `io.github.react-native-tvos` instead of `com.facebook.react`. The `@react-native/gradle-plugin` module has been upgraded so that the Android dependencies will be detected correctly during build.
63
74
 
64
- ## _(New)_ TV project creation in React Native 0.75 and later
75
+ ## TV project creation in React Native 0.75 and later
65
76
 
66
77
  > _Warning:_ Make sure you do not globally install `react-native` or `react-native-tvos`. If you have done this the wrong way, you may get error messages like `ld: library not found for -lPods-TestApp-tvOS`.
67
78
 
@@ -81,7 +92,7 @@ As of React Native 0.75.x, the template that used to reside in the `react-native
81
92
 
82
93
  > _Note:_ The new TV template will only build apps for Apple TV and Android TV. Multiple platform targets are no longer supported in React Native app Podfiles.
83
94
 
84
- To create a new project:
95
+ To create a new project for RNTV 0.76:
85
96
 
86
97
  ```sh
87
98
  #
@@ -115,7 +126,7 @@ $ npx @react-native-community/cli@latest init TVTest --template @react-native-tv
115
126
  ###### ######
116
127
 
117
128
 
118
- Welcome to React Native 0.75.2!
129
+ Welcome to React Native 0.76!
119
130
  Learn once, write anywhere
120
131
 
121
132
  ✔ Downloading template
@@ -135,8 +146,6 @@ npx react-native run-ios --simulator "Apple TV"
135
146
  npx react-native run:android --device tv_api_31
136
147
  ```
137
148
 
138
- See [this document](https://docs.expo.dev/bare/using-expo-cli/) for more details on Expo CLI functionality. (Note that many of these features require that Expo SDK modules be built into your app. Expo SDK support requires a different project configuration as described below.)
139
-
140
149
  ## How to support TV specific file extensions
141
150
 
142
151
  The template contains an [example Metro configuration](./packages/react-native/template/metro.config.js) that allows Metro to resolve application source files with TV-specific code, indicated by specific file extensions (e.g. `*.ios.tv.tsx`, `*.android.tv.tsx`, `*.tv.tsx`). The config will work the same way with the other standard source file extensions (`.js`, etc.), as documented in [Metro docs](https://metrobundler.dev/docs/configuration/#sourceexts)
@@ -169,13 +178,18 @@ var running_on_apple_tv = Platform.isTVOS;
169
178
 
170
179
  - _Access to touchable controls_: 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 `TouchableWithoutFeedback`, `TouchableHighlight` and `TouchableOpacity` will "just work" on both Apple TV and Android TV. In particular:
171
180
 
172
- - `onFocus` will be executed when the touchable view goes into focus
173
- - `onBlur` will be executed when the touchable view goes out of focus
174
- - `onPress` will be executed when the touchable view is actually selected by pressing the "select" button on the TV remote (center button on Apple TV remote, or center button on Android TV DPad).
175
- - `onLongPress` will be executed twice if the "select" button is held down for a length of time. The two events passed into `onLongPress()` will have different values for their `eventKeyAction` property, 0 for key down (start) and 1 for key up (end).
181
+ - `onFocus()` will be executed when the touchable view goes into focus
182
+ - `onBlur()` will be executed when the touchable view goes out of focus
183
+ - `onPress()` will be executed when the touchable view is actually selected by pressing the "select" button on the TV remote (center button on Apple TV remote, or center button on Android TV DPad).
184
+ - `onLongPress()` will be executed twice if the "select" button is held down for a length of time. The two events passed into `onLongPress()` will have different values for their `eventKeyAction` property, 0 for key down (start) and 1 for key up (end).
176
185
 
177
186
  - _Pressable controls_: The `Pressable` API works with TV. Additional `onFocus` and `onBlur` props are provided to allow you to customize behavior when a Pressable enters or leaves focus. Similar to the `pressed` state that is true while a user is pressing the component on a touchscreen, the `focused` state will be true when it is focused on TV. `PressableExample` in RNTester has been modified appropriately. The `onPress()` and `onLongPress()` methods work the same way as with `Touchable` components.
178
187
 
188
+ - _Tailwind styles for Pressable controls_: For the 0.76 release, the `Pressable` component also generates the `onPressIn()` and `onPressOut()` events needed to support the [`active:` pseudo class for Tailwind styles](https://www.nativewind.dev/v4/core-concepts/states#hover-focus-and-active-).
189
+ - For `onPress()` events (the "select" button on the remote is pressed once), `onPressIn()` is generated, then `onPressOut()` is generated a short time later.
190
+ - For `onLongPress()` events (the "select" button on the remote is held down for a length of time), `onPressIn()` is generated once the press down is detected, and `onPressOut()` is generated when the button is released.
191
+ - The `focus:` pseudo class is also supported via the `onFocus()` and `onBlur()` events.
192
+
179
193
  - _TV remote/keyboard input_: Application code that needs to implement custom handling of TV remote events can create an instance of `TVEventHandler` and listen for these events. For a more convenient API, we provide `useTVEventHandler`.
180
194
 
181
195
  ```javascript
@@ -244,8 +258,6 @@ class Game2048 extends React.Component {
244
258
  }
245
259
  ```
246
260
 
247
- - _Flipper_: We do not support Flipper.
248
-
249
261
  - _LogBox_: The LogBox error/warning display (which replaced YellowBox in 0.63) is working as expected on TV platforms, after a few adjustments to make the controls accessible to the focus engine.
250
262
 
251
263
  - _Dev Menu support_: On the Apple TV simulator, cmd-D will bring up the developer menu, just like on iOS. To bring it up on a real Apple TV device, make a long press on the play/pause button on the remote. (Please do not shake the Apple TV device, that will not work :) ). Android TV dev menu behavior is the same as on Android phone.
@@ -259,7 +271,7 @@ class Game2048 extends React.Component {
259
271
  - `enableTVPanGesture`/`disableTVPanGesture`: Methods to enable and disable detection of finger touches that pan across the touch surface of the Siri remote. See `TVEventHandlerExample` in the `RNTester` app for a demo.
260
272
  - `enableGestureHandlersCancelTouches`/`disableGestureHandlersCancelTouches`: Methods to turn on and turn off cancellation of touches by the gesture handlers in `RCTTVRemoteHandler` (see #366). Cancellation of touches is turned on (enabled) by default in 0.69 and earlier releases.
261
273
 
262
- - Accessibility: We have an additional `accessibilityFocus` [accessibility action](https://reactnative.dev/docs/accessibility#accessibility-actions) on Android that you can use for detecting focus changes on every *accessible* element (like a regular `Text`) when `TalkBack` is enabled.
274
+ - _Accessibility_: We have an additional `accessibilityFocus` [accessibility action](https://reactnative.dev/docs/accessibility#accessibility-actions) on Android that you can use for detecting focus changes on every *accessible* element (like a regular `Text`) when `TalkBack` is enabled.
263
275
 
264
276
  - _TVFocusGuideView_: This component provides support for Apple's `UIFocusGuide` API and is implemented in the same way for Android TV, to help ensure that focusable controls can be navigated to, even if they are not directly in line with other controls. An example is provided in `RNTester` that shows two different ways of using this component.
265
277
 
@@ -23,8 +23,8 @@ NSDictionary* RCTGetReactNativeVersion(void)
23
23
  __rnVersion = @{
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(76),
26
- RCTVersionPatch: @(0),
27
- RCTVersionPrerelease: @"0rc6",
26
+ RCTVersionPatch: @(1),
27
+ RCTVersionPrerelease: @"1",
28
28
  };
29
29
  });
30
30
  return __rnVersion;
@@ -63,8 +63,7 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
63
63
  BOOL _removeClippedSubviews;
64
64
  NSMutableArray<UIView *> *_reactSubviews;
65
65
  BOOL _motionEffectsAdded;
66
- UITapGestureRecognizer *_selectRecognizer;
67
- UILongPressGestureRecognizer * _longSelectRecognizer;
66
+ UILongPressGestureRecognizer * _pressRecognizer;
68
67
  NSSet<NSString *> *_Nullable _propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN;
69
68
  UIView *_containerView;
70
69
  BOOL _useCustomContainerView;
@@ -278,7 +277,7 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
278
277
  [[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventLongSelect keyAction:recognizer.eventKeyAction tag:@(self.tag) target:@(self.tag)];
279
278
  }
280
279
 
281
- - (void)handleSelect:(UIGestureRecognizer *)r
280
+ - (void)animatePress
282
281
  {
283
282
  if (_tvParallaxProperties.enabled == YES) {
284
283
  float magnification = _tvParallaxProperties.magnification;
@@ -302,18 +301,26 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
302
301
  self.transform = CGAffineTransformMakeScale(magnification, magnification);
303
302
  }
304
303
  completion:^(__unused BOOL finished2) {
305
- [self sendSelectNotification:r];
306
304
  }];
307
305
  }];
308
306
 
309
- } else {
310
- [self sendSelectNotification:r];
311
307
  }
312
308
  }
313
309
 
314
- - (void)handleLongSelect:(UIGestureRecognizer *)r
310
+ - (void)handlePress:(UIGestureRecognizer *)r
315
311
  {
316
- [self sendLongSelectNotification:r];
312
+ switch (r.state) {
313
+ case UIGestureRecognizerStateBegan:
314
+ _eventEmitter->onPressIn();
315
+ break;
316
+ case UIGestureRecognizerStateEnded:
317
+ case UIGestureRecognizerStateCancelled:
318
+ [self animatePress];
319
+ _eventEmitter->onPressOut();
320
+ break;
321
+ default:
322
+ break;
323
+ }
317
324
  }
318
325
 
319
326
  - (void)addParallaxMotionEffects
@@ -576,6 +583,8 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
576
583
  }
577
584
 
578
585
  if (context.nextFocusedView == self && self.isUserInteractionEnabled && ![self isTVFocusGuide]) {
586
+ if(_eventEmitter) _eventEmitter->onFocus();
587
+
579
588
  [self becomeFirstResponder];
580
589
  [self enableDirectionalFocusGuides];
581
590
  [coordinator addCoordinatedAnimations:^(void){
@@ -583,6 +592,8 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
583
592
  [self sendFocusNotification:context];
584
593
  } completion:^(void){}];
585
594
  } else {
595
+ if (_eventEmitter) _eventEmitter->onBlur();
596
+
586
597
  [self disableDirectionalFocusGuides];
587
598
  [coordinator addCoordinatedAnimations:^(void){
588
599
  [self removeParallaxMotionEffects];
@@ -1029,24 +1040,15 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1029
1040
  // `isTVSelectable`
1030
1041
  if (oldViewProps.isTVSelectable != newViewProps.isTVSelectable) {
1031
1042
  if (newViewProps.isTVSelectable && ![self isTVFocusGuide]) {
1032
- UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
1033
- action:@selector(handleSelect:)];
1034
- recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
1035
- _selectRecognizer = recognizer;
1036
-
1037
- UILongPressGestureRecognizer *longRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongSelect:)];
1038
- recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
1039
- [self addGestureRecognizer:longRecognizer];
1040
- _longSelectRecognizer = longRecognizer;
1041
-
1042
- [self addGestureRecognizer:_selectRecognizer];
1043
- [self addGestureRecognizer:_longSelectRecognizer];
1043
+ UILongPressGestureRecognizer *pressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlePress:)];
1044
+ pressRecognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
1045
+ pressRecognizer.minimumPressDuration = 0;
1046
+
1047
+ [self addGestureRecognizer:pressRecognizer];
1048
+ _pressRecognizer = pressRecognizer;
1044
1049
  } else {
1045
- if (_selectRecognizer) {
1046
- [self removeGestureRecognizer:_selectRecognizer];
1047
- }
1048
- if (_longSelectRecognizer) {
1049
- [self removeGestureRecognizer:_longSelectRecognizer];
1050
+ if (_pressRecognizer) {
1051
+ [self removeGestureRecognizer:_pressRecognizer];
1050
1052
  }
1051
1053
  }
1052
1054
  }
@@ -1077,6 +1079,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1077
1079
  _nextFocusUp = [rootView viewWithTag:newViewProps.nextFocusUp.value()];
1078
1080
  [self enableDirectionalFocusGuides];
1079
1081
  } else {
1082
+ if (self.focusGuideUp != nil) {
1083
+ [[self containingRootView] removeLayoutGuide:self.focusGuideUp];
1084
+ }
1080
1085
  _nextFocusUp = nil;
1081
1086
  }
1082
1087
  }
@@ -1087,6 +1092,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1087
1092
  _nextFocusDown = [rootView viewWithTag:newViewProps.nextFocusDown.value()];
1088
1093
  [self enableDirectionalFocusGuides];
1089
1094
  } else {
1095
+ if (self.focusGuideDown != nil) {
1096
+ [[self containingRootView] removeLayoutGuide:self.focusGuideDown];
1097
+ }
1090
1098
  _nextFocusDown = nil;
1091
1099
  }
1092
1100
  }
@@ -1097,6 +1105,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1097
1105
  _nextFocusLeft = [rootView viewWithTag:newViewProps.nextFocusLeft.value()];
1098
1106
  [self enableDirectionalFocusGuides];
1099
1107
  } else {
1108
+ if (self.focusGuideLeft != nil) {
1109
+ [[self containingRootView] removeLayoutGuide:self.focusGuideLeft];
1110
+ }
1100
1111
  _nextFocusLeft = nil;
1101
1112
  }
1102
1113
  }
@@ -1107,6 +1118,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1107
1118
  _nextFocusRight = [rootView viewWithTag:newViewProps.nextFocusRight.value()];
1108
1119
  [self enableDirectionalFocusGuides];
1109
1120
  } else {
1121
+ if (self.focusGuideRight != nil) {
1122
+ [[self containingRootView] removeLayoutGuide:self.focusGuideRight];
1123
+ }
1110
1124
  _nextFocusRight = nil;
1111
1125
  }
1112
1126
  }
@@ -1479,6 +1493,8 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
1479
1493
  _backgroundColorLayer.mask = maskLayer;
1480
1494
  _backgroundColorLayer.cornerRadius = 0;
1481
1495
  }
1496
+
1497
+ [_backgroundColorLayer removeAllAnimations];
1482
1498
  }
1483
1499
 
1484
1500
  // borders
@@ -52,6 +52,19 @@
52
52
  @property (nonatomic, assign) BOOL trapFocusLeft;
53
53
  @property (nonatomic, assign) BOOL trapFocusRight;
54
54
 
55
+
56
+ /**
57
+ * Focus
58
+ */
59
+ @property (nonatomic, copy) RCTBubblingEventBlock onFocus;
60
+ @property (nonatomic, copy) RCTBubblingEventBlock onBlur;
61
+
62
+ /**
63
+ * TV Press Handlers
64
+ */
65
+ @property (nonatomic, copy) RCTDirectEventBlock onPressIn;
66
+ @property (nonatomic, copy) RCTDirectEventBlock onPressOut;
67
+
55
68
  - (instancetype)initWithBridge:(RCTBridge *)bridge;
56
69
 
57
70
  /**