react-native-tvos 0.76.0-0rc5 → 0.76.1-0
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.
- package/Libraries/AppDelegate/RCTAppDelegate.mm +3 -2
- package/Libraries/Components/Pressable/Pressable.js +3 -36
- package/Libraries/Components/Pressable/useAndroidRippleForView.js +4 -4
- package/Libraries/Components/TV/TVViewPropTypes.js +1 -0
- package/Libraries/Core/ReactNativeVersion.js +2 -2
- package/Libraries/Pressability/Pressability.js +37 -0
- package/Libraries/ReactNative/AppRegistry.js +3 -3
- package/README.md +38 -26
- package/React/Base/RCTVersion.m +2 -2
- package/React/CoreModules/RCTDeviceInfo.mm +2 -0
- package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +2 -0
- package/ReactAndroid/build.gradle.kts +0 -1
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/HeadlessJsTaskService.java +4 -8
- package/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.kt +2 -1
- package/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +0 -1
- package/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/AlertFragment.java +5 -57
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +2 -2
- package/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.kt +2 -2
- package/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerDelegate.kt +19 -2
- package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
- package/package.json +8 -8
- package/scripts/cocoapods/utils.rb +4 -2
- package/scripts/codegen/generate-artifacts-executor.js +19 -4
- package/sdks/hermesc/osx-bin/hermes +0 -0
- package/sdks/hermesc/osx-bin/hermesc +0 -0
- package/sdks/hermesc/win64-bin/hermesc.exe +0 -0
- package/sdks/hermesc/win64-bin/msvcp140.dll +0 -0
- package/sdks/hermesc/win64-bin/vcruntime140.dll +0 -0
- package/sdks/hermesc/win64-bin/vcruntime140_1.dll +0 -0
- package/ReactAndroid/src/main/res/views/alert/layout/alert_title_layout.xml +0 -22
|
@@ -76,7 +76,8 @@
|
|
|
76
76
|
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
|
77
77
|
UIViewController *rootViewController = [self createRootViewController];
|
|
78
78
|
[self setRootView:rootView toRootViewController:rootViewController];
|
|
79
|
-
|
|
79
|
+
_window.windowScene.delegate = self;
|
|
80
|
+
_window.rootViewController = rootViewController;
|
|
80
81
|
#if TARGET_OS_TV
|
|
81
82
|
UIUserInterfaceStyle style = rootViewController.traitCollection.userInterfaceStyle;
|
|
82
83
|
if (style == UIUserInterfaceStyleDark) {
|
|
@@ -85,7 +86,7 @@
|
|
|
85
86
|
rootView.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
|
|
86
87
|
}
|
|
87
88
|
#endif
|
|
88
|
-
[
|
|
89
|
+
[_window makeKeyAndVisible];
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
- (void)applicationDidEnterBackground:(UIApplication *)application
|
|
@@ -392,52 +392,19 @@ function Pressable(
|
|
|
392
392
|
// $FlowFixMe[incompatible-call]
|
|
393
393
|
const eventHandlers = usePressability(config);
|
|
394
394
|
|
|
395
|
-
const pressableTVFocusEventHandler = React.useCallback(
|
|
396
|
-
(evt: any) => {
|
|
397
|
-
if (isTVSelectable !== false || focusable !== false) {
|
|
398
|
-
// $FlowFixMe[prop-missing]
|
|
399
|
-
if (evt?.eventType === 'focus') {
|
|
400
|
-
shouldUpdatePressed && setFocused(true);
|
|
401
|
-
onFocus && onFocus(evt);
|
|
402
|
-
// $FlowFixMe[prop-missing]
|
|
403
|
-
} else if (evt.eventType === 'blur') {
|
|
404
|
-
onBlur && onBlur(evt);
|
|
405
|
-
shouldUpdatePressed && setFocused(false);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
// $FlowFixMe[prop-missing]
|
|
409
|
-
if (evt.eventType === 'select') {
|
|
410
|
-
// $FlowFixMe[incompatible-exact]
|
|
411
|
-
onPress && onPress(evt);
|
|
412
|
-
}
|
|
413
|
-
// $FlowFixMe[prop-missing]
|
|
414
|
-
if (evt.eventType === 'longSelect') {
|
|
415
|
-
// $FlowFixMe[incompatible-exact]
|
|
416
|
-
onLongPress && onLongPress(evt);
|
|
417
|
-
}
|
|
418
|
-
},
|
|
419
|
-
[
|
|
420
|
-
onBlur,
|
|
421
|
-
onFocus,
|
|
422
|
-
onLongPress,
|
|
423
|
-
onPress,
|
|
424
|
-
focusable,
|
|
425
|
-
isTVSelectable,
|
|
426
|
-
shouldUpdatePressed,
|
|
427
|
-
],
|
|
428
|
-
);
|
|
429
|
-
|
|
430
395
|
React.useEffect(() => {
|
|
431
396
|
if (!tvFocusEventHandler) {
|
|
432
397
|
return;
|
|
433
398
|
}
|
|
399
|
+
const pressableTVFocusEventHandler = (evt: any) =>
|
|
400
|
+
eventHandlers?.onTVEvent(evt);
|
|
434
401
|
// $FlowFixMe[prop-missing]
|
|
435
402
|
const viewTag = tagForComponentOrHandle(viewRef?.current);
|
|
436
403
|
tvFocusEventHandler.register(viewTag, pressableTVFocusEventHandler);
|
|
437
404
|
return () => {
|
|
438
405
|
tvFocusEventHandler.unregister(viewTag);
|
|
439
406
|
};
|
|
440
|
-
}, [
|
|
407
|
+
}, [eventHandlers, viewRef]);
|
|
441
408
|
|
|
442
409
|
return (
|
|
443
410
|
<View
|
|
@@ -80,8 +80,8 @@ export default function useAndroidRippleForView(
|
|
|
80
80
|
if (view != null) {
|
|
81
81
|
Commands.hotspotUpdate(
|
|
82
82
|
view,
|
|
83
|
-
event.nativeEvent
|
|
84
|
-
event.nativeEvent
|
|
83
|
+
event.nativeEvent?.locationX ?? 0,
|
|
84
|
+
event.nativeEvent?.locationY ?? 0,
|
|
85
85
|
);
|
|
86
86
|
Commands.setPressed(view, true);
|
|
87
87
|
}
|
|
@@ -91,8 +91,8 @@ export default function useAndroidRippleForView(
|
|
|
91
91
|
if (view != null) {
|
|
92
92
|
Commands.hotspotUpdate(
|
|
93
93
|
view,
|
|
94
|
-
event.nativeEvent
|
|
95
|
-
event.nativeEvent
|
|
94
|
+
event.nativeEvent?.locationX ?? 0,
|
|
95
|
+
event.nativeEvent?.locationY ?? 0,
|
|
96
96
|
);
|
|
97
97
|
}
|
|
98
98
|
},
|
|
@@ -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,7 @@ export type EventHandlers = $ReadOnly<{|
|
|
|
152
157
|
onResponderTerminate: (event: PressEvent) => void,
|
|
153
158
|
onResponderTerminationRequest: () => boolean,
|
|
154
159
|
onStartShouldSetResponder: () => boolean,
|
|
160
|
+
onTVEvent: (event: any) => void,
|
|
155
161
|
|}>;
|
|
156
162
|
|
|
157
163
|
type TouchState =
|
|
@@ -430,6 +436,35 @@ export default class Pressability {
|
|
|
430
436
|
}
|
|
431
437
|
|
|
432
438
|
_createEventHandlers(): EventHandlers {
|
|
439
|
+
const tvEventHandlers = {
|
|
440
|
+
onTVEvent: (evt: any): void => {
|
|
441
|
+
if (this._config.disabled !== false) {
|
|
442
|
+
// $FlowFixMe[prop-missing]
|
|
443
|
+
if (evt?.eventType === 'focus') {
|
|
444
|
+
const {onFocus} = this._config;
|
|
445
|
+
onFocus && onFocus(evt);
|
|
446
|
+
// $FlowFixMe[prop-missing]
|
|
447
|
+
} else if (evt.eventType === 'blur') {
|
|
448
|
+
const {onBlur} = this._config;
|
|
449
|
+
onBlur && onBlur(evt);
|
|
450
|
+
} else if (evt.eventType === 'select') {
|
|
451
|
+
const {onPress, onPressIn, onPressOut} = this._config;
|
|
452
|
+
// $FlowFixMe[incompatible-exact]
|
|
453
|
+
onPressIn && onPressIn(evt);
|
|
454
|
+
onPress && onPress(evt);
|
|
455
|
+
setTimeout(() => {
|
|
456
|
+
onPressOut && onPressOut(evt);
|
|
457
|
+
}, this._config.minPressDuration ?? DEFAULT_MIN_PRESS_DURATION);
|
|
458
|
+
} else if (evt.eventType === 'longSelect') {
|
|
459
|
+
const {onLongPress, onPressIn, onPressOut} = this._config;
|
|
460
|
+
onLongPress && onLongPress(evt);
|
|
461
|
+
evt?.eventKeyAction === 0
|
|
462
|
+
? onPressIn && onPressIn(evt)
|
|
463
|
+
: onPressOut && onPressOut(evt);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
};
|
|
433
468
|
const focusEventHandlers = {
|
|
434
469
|
onBlur: (event: BlurEvent): void => {
|
|
435
470
|
const {onBlur} = this._config;
|
|
@@ -610,6 +645,7 @@ export default class Pressability {
|
|
|
610
645
|
};
|
|
611
646
|
}
|
|
612
647
|
return {
|
|
648
|
+
...tvEventHandlers,
|
|
613
649
|
...focusEventHandlers,
|
|
614
650
|
...responderEventHandlers,
|
|
615
651
|
...hoverPointerEvents,
|
|
@@ -662,6 +698,7 @@ export default class Pressability {
|
|
|
662
698
|
},
|
|
663
699
|
};
|
|
664
700
|
return {
|
|
701
|
+
...tvEventHandlers,
|
|
665
702
|
...focusEventHandlers,
|
|
666
703
|
...responderEventHandlers,
|
|
667
704
|
...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
|
|
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;
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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:
|
|
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
|
|
57
|
-
- API level
|
|
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_:
|
|
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
|
-
##
|
|
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.
|
|
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
|
-
-
|
|
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
|
|
package/React/Base/RCTVersion.m
CHANGED
|
@@ -23,8 +23,8 @@ NSDictionary* RCTGetReactNativeVersion(void)
|
|
|
23
23
|
__rnVersion = @{
|
|
24
24
|
RCTVersionMajor: @(0),
|
|
25
25
|
RCTVersionMinor: @(76),
|
|
26
|
-
RCTVersionPatch: @(
|
|
27
|
-
RCTVersionPrerelease: @"
|
|
26
|
+
RCTVersionPatch: @(1),
|
|
27
|
+
RCTVersionPrerelease: @"0",
|
|
28
28
|
};
|
|
29
29
|
});
|
|
30
30
|
return __rnVersion;
|
|
@@ -56,6 +56,7 @@ RCT_EXPORT_MODULE()
|
|
|
56
56
|
|
|
57
57
|
_currentInterfaceDimensions = [self _exportedDimensions];
|
|
58
58
|
|
|
59
|
+
#if !TARGET_OS_TV
|
|
59
60
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
60
61
|
selector:@selector(interfaceOrientationDidChange)
|
|
61
62
|
name:UIApplicationDidBecomeActiveNotification
|
|
@@ -70,6 +71,7 @@ RCT_EXPORT_MODULE()
|
|
|
70
71
|
selector:@selector(interfaceFrameDidChange)
|
|
71
72
|
name:RCTWindowFrameDidChangeNotification
|
|
72
73
|
object:nil];
|
|
74
|
+
#endif
|
|
73
75
|
|
|
74
76
|
#if TARGET_OS_IOS
|
|
75
77
|
|
|
@@ -1479,6 +1479,8 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
|
|
|
1479
1479
|
_backgroundColorLayer.mask = maskLayer;
|
|
1480
1480
|
_backgroundColorLayer.cornerRadius = 0;
|
|
1481
1481
|
}
|
|
1482
|
+
|
|
1483
|
+
[_backgroundColorLayer removeAllAnimations];
|
|
1482
1484
|
}
|
|
1483
1485
|
|
|
1484
1486
|
// borders
|
|
@@ -124,15 +124,11 @@ public abstract class HeadlessJsTaskService extends Service implements HeadlessJ
|
|
|
124
124
|
@Override
|
|
125
125
|
public void onDestroy() {
|
|
126
126
|
super.onDestroy();
|
|
127
|
+
ReactContext reactContext = getReactContext();
|
|
127
128
|
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (reactContext != null) {
|
|
132
|
-
HeadlessJsTaskContext headlessJsTaskContext =
|
|
133
|
-
HeadlessJsTaskContext.getInstance(reactContext);
|
|
134
|
-
headlessJsTaskContext.removeTaskEventListener(this);
|
|
135
|
-
}
|
|
129
|
+
if (reactContext != null) {
|
|
130
|
+
HeadlessJsTaskContext headlessJsTaskContext = HeadlessJsTaskContext.getInstance(reactContext);
|
|
131
|
+
headlessJsTaskContext.removeTaskEventListener(this);
|
|
136
132
|
}
|
|
137
133
|
if (sWakeLock != null) {
|
|
138
134
|
sWakeLock.release();
|
|
@@ -12,6 +12,7 @@ import androidx.annotation.AnyThread
|
|
|
12
12
|
import androidx.annotation.UiThread
|
|
13
13
|
import com.facebook.infer.annotation.ThreadConfined
|
|
14
14
|
import com.facebook.react.common.annotations.UnstableReactNativeAPI
|
|
15
|
+
import com.facebook.react.uimanager.events.EventDispatcher
|
|
15
16
|
|
|
16
17
|
@OptIn(UnstableReactNativeAPI::class)
|
|
17
18
|
public interface UIManager : PerformanceCounter {
|
|
@@ -78,7 +79,7 @@ public interface UIManager : PerformanceCounter {
|
|
|
78
79
|
public fun dispatchCommand(reactTag: Int, commandId: String, commandArgs: ReadableArray?)
|
|
79
80
|
|
|
80
81
|
/** @return the [EventDispatcher] object that is used by this class. */
|
|
81
|
-
public
|
|
82
|
+
public val eventDispatcher: EventDispatcher
|
|
82
83
|
|
|
83
84
|
/**
|
|
84
85
|
* Used by native animated module to bypass the process of updating the values through the shadow
|
|
@@ -12,20 +12,11 @@ import android.app.Dialog;
|
|
|
12
12
|
import android.content.Context;
|
|
13
13
|
import android.content.DialogInterface;
|
|
14
14
|
import android.content.res.TypedArray;
|
|
15
|
-
import android.os.Build;
|
|
16
15
|
import android.os.Bundle;
|
|
17
|
-
import android.view.LayoutInflater;
|
|
18
|
-
import android.view.View;
|
|
19
|
-
import android.widget.TextView;
|
|
20
16
|
import androidx.annotation.Nullable;
|
|
21
17
|
import androidx.appcompat.app.AlertDialog;
|
|
22
|
-
import androidx.core.view.AccessibilityDelegateCompat;
|
|
23
|
-
import androidx.core.view.ViewCompat;
|
|
24
|
-
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
|
25
18
|
import androidx.fragment.app.DialogFragment;
|
|
26
|
-
import com.facebook.infer.annotation.Assertions;
|
|
27
19
|
import com.facebook.infer.annotation.Nullsafe;
|
|
28
|
-
import com.facebook.react.R;
|
|
29
20
|
|
|
30
21
|
/** A fragment used to display the dialog. */
|
|
31
22
|
@Nullsafe(Nullsafe.Mode.LOCAL)
|
|
@@ -75,55 +66,15 @@ public class AlertFragment extends DialogFragment implements DialogInterface.OnC
|
|
|
75
66
|
return isAppCompat;
|
|
76
67
|
}
|
|
77
68
|
|
|
78
|
-
/**
|
|
79
|
-
* Creates a custom dialog title View that has the role of "Heading" and focusable for
|
|
80
|
-
* accessibility purposes.
|
|
81
|
-
*
|
|
82
|
-
* @returns accessible TextView title
|
|
83
|
-
*/
|
|
84
|
-
private static View getAccessibleTitle(Context activityContext, String titleText) {
|
|
85
|
-
LayoutInflater inflater = LayoutInflater.from(activityContext);
|
|
86
|
-
|
|
87
|
-
// This layout matches the sizing and styling of AlertDialog's title_template (minus the icon)
|
|
88
|
-
// since the whole thing gets tossed out when setting a custom title
|
|
89
|
-
View titleContainer = inflater.inflate(R.layout.alert_title_layout, null);
|
|
90
|
-
|
|
91
|
-
TextView accessibleTitle =
|
|
92
|
-
Assertions.assertNotNull(titleContainer.findViewById(R.id.alert_title));
|
|
93
|
-
accessibleTitle.setText(titleText);
|
|
94
|
-
accessibleTitle.setFocusable(true);
|
|
95
|
-
|
|
96
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
97
|
-
accessibleTitle.setAccessibilityHeading(true);
|
|
98
|
-
} else {
|
|
99
|
-
ViewCompat.setAccessibilityDelegate(
|
|
100
|
-
accessibleTitle,
|
|
101
|
-
new AccessibilityDelegateCompat() {
|
|
102
|
-
@Override
|
|
103
|
-
public void onInitializeAccessibilityNodeInfo(
|
|
104
|
-
View view, AccessibilityNodeInfoCompat info) {
|
|
105
|
-
super.onInitializeAccessibilityNodeInfo(accessibleTitle, info);
|
|
106
|
-
info.setHeading(true);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return titleContainer;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
69
|
/**
|
|
115
70
|
* Creates a dialog compatible only with AppCompat activities. This function should be kept in
|
|
116
71
|
* sync with {@link createAppDialog}.
|
|
117
72
|
*/
|
|
118
73
|
private static Dialog createAppCompatDialog(
|
|
119
74
|
Context activityContext, Bundle arguments, DialogInterface.OnClickListener fragment) {
|
|
120
|
-
AlertDialog.Builder builder =
|
|
75
|
+
AlertDialog.Builder builder =
|
|
76
|
+
new AlertDialog.Builder(activityContext).setTitle(arguments.getString(ARG_TITLE));
|
|
121
77
|
|
|
122
|
-
if (arguments.containsKey(ARG_TITLE)) {
|
|
123
|
-
String title = Assertions.assertNotNull(arguments.getString(ARG_TITLE));
|
|
124
|
-
View accessibleTitle = getAccessibleTitle(activityContext, title);
|
|
125
|
-
builder.setCustomTitle(accessibleTitle);
|
|
126
|
-
}
|
|
127
78
|
if (arguments.containsKey(ARG_BUTTON_POSITIVE)) {
|
|
128
79
|
builder.setPositiveButton(arguments.getString(ARG_BUTTON_POSITIVE), fragment);
|
|
129
80
|
}
|
|
@@ -154,13 +105,10 @@ public class AlertFragment extends DialogFragment implements DialogInterface.OnC
|
|
|
154
105
|
@Deprecated(since = "0.75.0", forRemoval = true)
|
|
155
106
|
private static Dialog createAppDialog(
|
|
156
107
|
Context activityContext, Bundle arguments, DialogInterface.OnClickListener fragment) {
|
|
157
|
-
android.app.AlertDialog.Builder builder =
|
|
108
|
+
android.app.AlertDialog.Builder builder =
|
|
109
|
+
new android.app.AlertDialog.Builder(activityContext)
|
|
110
|
+
.setTitle(arguments.getString(ARG_TITLE));
|
|
158
111
|
|
|
159
|
-
if (arguments.containsKey(ARG_TITLE)) {
|
|
160
|
-
String title = Assertions.assertNotNull(arguments.getString(ARG_TITLE));
|
|
161
|
-
View accessibleTitle = getAccessibleTitle(activityContext, title);
|
|
162
|
-
builder.setCustomTitle(accessibleTitle);
|
|
163
|
-
}
|
|
164
112
|
if (arguments.containsKey(ARG_BUTTON_POSITIVE)) {
|
|
165
113
|
builder.setPositiveButton(arguments.getString(ARG_BUTTON_POSITIVE), fragment);
|
|
166
114
|
}
|
|
@@ -22,7 +22,7 @@ import com.facebook.yoga.YogaConstants
|
|
|
22
22
|
public abstract class BaseViewManagerDelegate<T : View, U : BaseViewManagerInterface<T>>(
|
|
23
23
|
@Suppress("NoHungarianNotation") @JvmField protected val mViewManager: U
|
|
24
24
|
) : ViewManagerDelegate<T> {
|
|
25
|
-
override public fun setProperty(view: T, propName: String
|
|
25
|
+
override public fun setProperty(view: T, propName: String?, value: Any?) {
|
|
26
26
|
when (propName) {
|
|
27
27
|
ViewProps.ACCESSIBILITY_ACTIONS ->
|
|
28
28
|
mViewManager.setAccessibilityActions(view, value as ReadableArray?)
|
|
@@ -104,6 +104,6 @@ public abstract class BaseViewManagerDelegate<T : View, U : BaseViewManagerInter
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
override public fun receiveCommand(view: T, commandName: String
|
|
107
|
+
override public fun receiveCommand(view: T, commandName: String?, args: ReadableArray?): Unit =
|
|
108
108
|
Unit
|
|
109
109
|
}
|
|
@@ -18,7 +18,24 @@ import com.facebook.react.bridge.ReadableArray
|
|
|
18
18
|
* @param <T> the type of the view supported by this delegate </T>
|
|
19
19
|
*/
|
|
20
20
|
public interface ViewManagerDelegate<T : View?> {
|
|
21
|
-
public fun setProperty(view: T, propName: String, value: Any?)
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Sets a property on a view managed by this view manager.
|
|
24
|
+
*
|
|
25
|
+
* @param view the view to set the property on
|
|
26
|
+
* @param propName the name of the property to set (NOTE: should be `String` but is kept as
|
|
27
|
+
* `String?` to avoid breaking changes)
|
|
28
|
+
* @param value the value to set the property to
|
|
29
|
+
*/
|
|
30
|
+
public fun setProperty(view: T, propName: String?, value: Any?)
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Executes a command from JS to the view
|
|
34
|
+
*
|
|
35
|
+
* @param view the view to execute the command on
|
|
36
|
+
* @param commandName the name of the command to execute (NOTE: should be `String` but is kept as
|
|
37
|
+
* `String?` to avoid breaking changes)
|
|
38
|
+
* @param args the arguments to pass to the command
|
|
39
|
+
*/
|
|
40
|
+
public fun receiveCommand(view: T, commandName: String?, args: ReadableArray?)
|
|
24
41
|
}
|
|
@@ -17,8 +17,8 @@ namespace facebook::react {
|
|
|
17
17
|
constexpr struct {
|
|
18
18
|
int32_t Major = 0;
|
|
19
19
|
int32_t Minor = 76;
|
|
20
|
-
int32_t Patch =
|
|
21
|
-
std::string_view Prerelease = "
|
|
20
|
+
int32_t Patch = 1;
|
|
21
|
+
std::string_view Prerelease = "0";
|
|
22
22
|
} ReactNativeVersion;
|
|
23
23
|
|
|
24
24
|
} // namespace facebook::react
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-tvos",
|
|
3
|
-
"version": "0.76.0
|
|
3
|
+
"version": "0.76.1-0",
|
|
4
4
|
"description": "A framework for building native apps using React",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -110,13 +110,13 @@
|
|
|
110
110
|
},
|
|
111
111
|
"dependencies": {
|
|
112
112
|
"@jest/create-cache-key-function": "^29.6.3",
|
|
113
|
-
"@react-native/assets-registry": "0.76.
|
|
114
|
-
"@react-native/codegen": "0.76.
|
|
115
|
-
"@react-native/community-cli-plugin": "0.76.
|
|
116
|
-
"@react-native/gradle-plugin": "0.76.
|
|
117
|
-
"@react-native/js-polyfills": "0.76.
|
|
118
|
-
"@react-native/normalize-colors": "0.76.
|
|
119
|
-
"@react-native-tvos/virtualized-lists": "0.76.0
|
|
113
|
+
"@react-native/assets-registry": "0.76.1",
|
|
114
|
+
"@react-native/codegen": "0.76.1",
|
|
115
|
+
"@react-native/community-cli-plugin": "0.76.1",
|
|
116
|
+
"@react-native/gradle-plugin": "0.76.1",
|
|
117
|
+
"@react-native/js-polyfills": "0.76.1",
|
|
118
|
+
"@react-native/normalize-colors": "0.76.1",
|
|
119
|
+
"@react-native-tvos/virtualized-lists": "0.76.1-0",
|
|
120
120
|
"abort-controller": "^3.0.0",
|
|
121
121
|
"anser": "^1.4.9",
|
|
122
122
|
"ansi-regex": "^5.0.0",
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
# This source code is licensed under the MIT license found in the
|
|
4
4
|
# LICENSE file in the root directory of this source tree.
|
|
5
5
|
|
|
6
|
+
require 'shellwords'
|
|
7
|
+
|
|
6
8
|
require_relative "./helpers.rb"
|
|
7
9
|
|
|
8
10
|
# Utilities class for React Native Cocoapods
|
|
@@ -237,8 +239,8 @@ class ReactNativePodsUtils
|
|
|
237
239
|
# When installing pods with a yarn alias, yarn creates a fake yarn and node executables
|
|
238
240
|
# in a temporary folder.
|
|
239
241
|
# Using `node --print "process.argv[0]";` we are able to retrieve the actual path from which node is running.
|
|
240
|
-
# see https://github.com/facebook/react-native/issues/43285 for more info
|
|
241
|
-
node_binary = `node --print "process.argv[0]"
|
|
242
|
+
# see https://github.com/facebook/react-native/issues/43285 for more info. We've tweaked this slightly.
|
|
243
|
+
node_binary = Shellwords.escape(`node --print "process.argv[0]"`.strip)
|
|
242
244
|
system("echo 'export NODE_BINARY=#{node_binary}' > #{file_path}.local")
|
|
243
245
|
end
|
|
244
246
|
end
|
|
@@ -490,11 +490,22 @@ function rootCodegenTargetNeedsThirdPartyComponentProvider(pkgJson, platform) {
|
|
|
490
490
|
return !pkgJsonIncludesGeneratedCode(pkgJson) && platform === 'ios';
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
-
function dependencyNeedsThirdPartyComponentProvider(
|
|
493
|
+
function dependencyNeedsThirdPartyComponentProvider(
|
|
494
|
+
schemaInfo,
|
|
495
|
+
platform,
|
|
496
|
+
appCondegenConfigSpec,
|
|
497
|
+
) {
|
|
494
498
|
// Filter the react native core library out.
|
|
495
499
|
// In the future, core library and third party library should
|
|
496
500
|
// use the same way to generate/register the fabric components.
|
|
497
|
-
|
|
501
|
+
// We also have to filter out the the components defined in the app
|
|
502
|
+
// because the RCTThirdPartyComponentProvider is generated inside Fabric,
|
|
503
|
+
// which lives in a different target from the app and it has no visibility over
|
|
504
|
+
// the symbols defined in the app.
|
|
505
|
+
return (
|
|
506
|
+
!isReactNativeCoreLibrary(schemaInfo.library.config.name, platform) &&
|
|
507
|
+
schemaInfo.library.config.name !== appCondegenConfigSpec
|
|
508
|
+
);
|
|
498
509
|
}
|
|
499
510
|
|
|
500
511
|
function mustGenerateNativeCode(includeLibraryPath, schemaInfo) {
|
|
@@ -705,8 +716,12 @@ function execute(projectRoot, targetPlatform, baseOutputPath) {
|
|
|
705
716
|
if (
|
|
706
717
|
rootCodegenTargetNeedsThirdPartyComponentProvider(pkgJson, platform)
|
|
707
718
|
) {
|
|
708
|
-
const filteredSchemas = schemaInfos.filter(
|
|
709
|
-
dependencyNeedsThirdPartyComponentProvider
|
|
719
|
+
const filteredSchemas = schemaInfos.filter(schemaInfo =>
|
|
720
|
+
dependencyNeedsThirdPartyComponentProvider(
|
|
721
|
+
schemaInfo,
|
|
722
|
+
platform,
|
|
723
|
+
pkgJson.codegenConfig?.appCondegenConfigSpec,
|
|
724
|
+
),
|
|
710
725
|
);
|
|
711
726
|
const schemas = filteredSchemas.map(schemaInfo => schemaInfo.schema);
|
|
712
727
|
const supportedApplePlatforms = filteredSchemas.map(
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
-
android:layout_width="match_parent"
|
|
4
|
-
android:layout_height="wrap_content"
|
|
5
|
-
android:gravity="center_vertical|start"
|
|
6
|
-
android:orientation="horizontal"
|
|
7
|
-
android:paddingStart="?android:attr/dialogPreferredPadding"
|
|
8
|
-
android:paddingTop="18dp"
|
|
9
|
-
android:paddingEnd="?android:attr/dialogPreferredPadding">
|
|
10
|
-
|
|
11
|
-
<TextView
|
|
12
|
-
android:id="@+id/alert_title"
|
|
13
|
-
style="?android:attr/windowTitleStyle"
|
|
14
|
-
android:layout_width="match_parent"
|
|
15
|
-
android:layout_height="wrap_content"
|
|
16
|
-
android:ellipsize="end"
|
|
17
|
-
android:singleLine="true"
|
|
18
|
-
android:textAlignment="viewStart"
|
|
19
|
-
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
20
|
-
android:textSize="18sp" />
|
|
21
|
-
|
|
22
|
-
</LinearLayout>
|