react-native-tvos 0.64.2-0 → 0.64.2-4
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/Components/AppleTV/TVFocusGuideView.js +24 -1
- package/Libraries/Components/AppleTV/TVFocusGuideView.js.orig +61 -0
- package/Libraries/Components/Pressable/Pressable.js +4 -4
- package/Libraries/Components/StatusBar/NativeStatusBarManagerIOS.js +14 -8
- package/Libraries/Components/Touchable/Touchable.js +1 -0
- package/Libraries/NewAppScreen/components/HermesBadge.js +39 -12
- package/README.md +8 -2
- package/React/Base/RCTRootView.m +5 -35
- package/React/Base/RCTTVRemoteHandler.h +5 -2
- package/React/Base/RCTTVRemoteHandler.m +173 -70
- package/React/Modules/RCTTVMenuBridge.m +3 -0
- package/React/Views/RCTFont.mm +4 -0
- package/React/Views/RCTModalHostView.m +6 -9
- package/React/Views/RCTTVFocusGuideView.m +1 -1
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/androidTest/assets/AndroidTestBundle.js +47 -24
- package/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +10 -9
- package/ReactAndroid/src/main/java/com/facebook/react/{ReactAndroidHWInputDeviceHelper.java → modules/core/ReactAndroidHWInputDeviceHelper.java} +27 -20
- package/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK +1 -0
- package/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java +21 -0
- package/ReactCommon/hermes/inspector/tools/msggen/yarn.lock +92 -97
- package/android/com/facebook/react/react-native/{0.64.2-0/react-native-0.64.2-0-sources.jar → 0.64.2-4/react-native-0.64.2-4-sources.jar} +0 -0
- package/android/com/facebook/react/react-native/0.64.2-4/react-native-0.64.2-4-sources.jar.md5 +1 -0
- package/android/com/facebook/react/react-native/0.64.2-4/react-native-0.64.2-4-sources.jar.sha1 +1 -0
- package/android/com/facebook/react/react-native/{0.64.2-0/react-native-0.64.2-0.aar → 0.64.2-4/react-native-0.64.2-4.aar} +0 -0
- package/android/com/facebook/react/react-native/0.64.2-4/react-native-0.64.2-4.aar.md5 +1 -0
- package/android/com/facebook/react/react-native/0.64.2-4/react-native-0.64.2-4.aar.sha1 +1 -0
- package/android/com/facebook/react/react-native/{0.64.2-0/react-native-0.64.2-0.pom → 0.64.2-4/react-native-0.64.2-4.pom} +1 -1
- package/android/com/facebook/react/react-native/0.64.2-4/react-native-0.64.2-4.pom.md5 +1 -0
- package/android/com/facebook/react/react-native/0.64.2-4/react-native-0.64.2-4.pom.sha1 +1 -0
- package/android/com/facebook/react/react-native/maven-metadata.xml +3 -3
- package/android/com/facebook/react/react-native/maven-metadata.xml.md5 +1 -1
- package/android/com/facebook/react/react-native/maven-metadata.xml.sha1 +1 -1
- package/package.json +1 -1
- package/template/App.js +1 -2
- package/template/package.json +1 -1
- package/tvos-types.d.ts +1 -1
- package/ReactAndroid/build.gradle.orig +0 -479
- package/android/com/facebook/react/react-native/0.64.2-0/react-native-0.64.2-0-sources.jar.md5 +0 -1
- package/android/com/facebook/react/react-native/0.64.2-0/react-native-0.64.2-0-sources.jar.sha1 +0 -1
- package/android/com/facebook/react/react-native/0.64.2-0/react-native-0.64.2-0.aar.md5 +0 -1
- package/android/com/facebook/react/react-native/0.64.2-0/react-native-0.64.2-0.aar.sha1 +0 -1
- package/android/com/facebook/react/react-native/0.64.2-0/react-native-0.64.2-0.pom.md5 +0 -1
- package/android/com/facebook/react/react-native/0.64.2-0/react-native-0.64.2-0.pom.sha1 +0 -1
|
@@ -42,9 +42,19 @@ class TVFocusGuideView extends React.Component<FocusGuideProps> {
|
|
|
42
42
|
return (
|
|
43
43
|
// Container view must have nonzero size
|
|
44
44
|
<ReactNative.View style={[{minHeight: 1, minWidth: 1}, this.props.style]}>
|
|
45
|
+
{
|
|
46
|
+
/**
|
|
47
|
+
* The client specified layout(using 'style' prop) should be applied the container view ReactNative.View.
|
|
48
|
+
* And the focusGuide's layout shoule be overrided to wrap it fully inside the container view.
|
|
49
|
+
* For example, if the client specifies 'marginLeft' property in the style prop,
|
|
50
|
+
* then the TVFocusGuideView will apply the 'marginLeft' for both the parentView and the focusGuideView.
|
|
51
|
+
* and so, the left margin is getting added twice and UI becomes incorrect.
|
|
52
|
+
* The same is applicable for other layout properties.
|
|
53
|
+
*/
|
|
54
|
+
}
|
|
45
55
|
{Platform.isTVOS ? (
|
|
46
56
|
<RNFocusGuide
|
|
47
|
-
style={this.props.style}
|
|
57
|
+
style={[this.props.style, styles.focusGuideLayout]}
|
|
48
58
|
ref={ref => (this._focusGuideRef = ref)}
|
|
49
59
|
destinationTags={this._destinationTags}>
|
|
50
60
|
{this.props.children}
|
|
@@ -56,6 +66,19 @@ class TVFocusGuideView extends React.Component<FocusGuideProps> {
|
|
|
56
66
|
}
|
|
57
67
|
}
|
|
58
68
|
|
|
69
|
+
const styles = ReactNative.StyleSheet.create({
|
|
70
|
+
focusGuideLayout: {
|
|
71
|
+
left: 0,
|
|
72
|
+
top: 0,
|
|
73
|
+
right: 0,
|
|
74
|
+
bottom: 0,
|
|
75
|
+
marginLeft: 0,
|
|
76
|
+
marginTop: 0,
|
|
77
|
+
marginRight: 0,
|
|
78
|
+
marginBottom: 0,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
59
82
|
const RNFocusGuide = requireNativeComponent('RCTTVFocusGuideView');
|
|
60
83
|
|
|
61
84
|
module.exports = TVFocusGuideView;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @flow
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const requireNativeComponent = require('../../ReactNative/requireNativeComponent');
|
|
12
|
+
const React = require('react');
|
|
13
|
+
const ReactNative = require('react-native');
|
|
14
|
+
const Platform = require('../../Utilities/Platform');
|
|
15
|
+
import type {ViewProps} from '../View/ViewPropTypes';
|
|
16
|
+
|
|
17
|
+
type FocusGuideProps = $ReadOnly<{
|
|
18
|
+
...ViewProps,
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The views the focus should go to
|
|
22
|
+
*/
|
|
23
|
+
destinations: ?(Object[]),
|
|
24
|
+
}>;
|
|
25
|
+
|
|
26
|
+
class TVFocusGuideView extends React.Component<FocusGuideProps> {
|
|
27
|
+
_focusGuideRef: ?Object;
|
|
28
|
+
_destinationTags: ?(number[]);
|
|
29
|
+
|
|
30
|
+
componentDidUpdate() {
|
|
31
|
+
const destinations = this.props.destinations || [];
|
|
32
|
+
this._destinationTags = destinations.map(
|
|
33
|
+
c => ReactNative.findNodeHandle(c) || 0,
|
|
34
|
+
);
|
|
35
|
+
this._focusGuideRef &&
|
|
36
|
+
this._focusGuideRef.setNativeProps({
|
|
37
|
+
destinationTags: this._destinationTags,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
render(): React.Node {
|
|
42
|
+
return (
|
|
43
|
+
// Container view must have nonzero size
|
|
44
|
+
<ReactNative.View style={[{minHeight: 1, minWidth: 1}, this.props.style]}>
|
|
45
|
+
{Platform.isTVOS ? (
|
|
46
|
+
<RNFocusGuide
|
|
47
|
+
style={this.props.style}
|
|
48
|
+
ref={ref => (this._focusGuideRef = ref)}
|
|
49
|
+
destinationTags={this._destinationTags}>
|
|
50
|
+
{this.props.children}
|
|
51
|
+
</RNFocusGuide>
|
|
52
|
+
) : (
|
|
53
|
+
this.props.children
|
|
54
|
+
)}
|
|
55
|
+
</ReactNative.View>)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const RNFocusGuide = requireNativeComponent('RCTTVFocusGuideView');
|
|
60
|
+
|
|
61
|
+
module.exports = TVFocusGuideView;
|
|
@@ -201,6 +201,7 @@ function Pressable(props: Props, forwardedRef): React.Node {
|
|
|
201
201
|
...android_rippleConfig?.viewProps,
|
|
202
202
|
accessible: accessible !== false,
|
|
203
203
|
focusable: focusable !== false,
|
|
204
|
+
isTVSelectable: isTVSelectable !== false && accessible !== false,
|
|
204
205
|
hitSlop,
|
|
205
206
|
};
|
|
206
207
|
|
|
@@ -257,7 +258,7 @@ function Pressable(props: Props, forwardedRef): React.Node {
|
|
|
257
258
|
|
|
258
259
|
const pressableTVEventHandler = (evt: Event) => {
|
|
259
260
|
if (props.isTVSelectable !== false || props.focusable !== false) {
|
|
260
|
-
if (viewRef.current._nativeTag === evt.target) {
|
|
261
|
+
if (viewRef.current && viewRef.current._nativeTag === evt.target) {
|
|
261
262
|
if (evt?.eventType === 'focus') {
|
|
262
263
|
setFocused(true);
|
|
263
264
|
onFocus && onFocus(evt);
|
|
@@ -285,10 +286,9 @@ function Pressable(props: Props, forwardedRef): React.Node {
|
|
|
285
286
|
{...restPropsWithDefaults}
|
|
286
287
|
{...eventHandlers}
|
|
287
288
|
ref={viewRef}
|
|
288
|
-
|
|
289
|
-
style={typeof style === 'function' ? style({pressed}) : style}
|
|
289
|
+
style={typeof style === 'function' ? style({pressed, focused}) : style}
|
|
290
290
|
collapsable={false}>
|
|
291
|
-
{typeof children === 'function' ? children({pressed}) : children}
|
|
291
|
+
{typeof children === 'function' ? children({pressed, focused}) : children}
|
|
292
292
|
{__DEV__ ? <PressabilityDebugView color="red" hitSlop={hitSlop} /> : null}
|
|
293
293
|
</View>
|
|
294
294
|
);
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
import type {TurboModule} from '../../TurboModule/RCTExport';
|
|
14
14
|
import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry';
|
|
15
15
|
|
|
16
|
+
const Platform = require('../../Utilities/Platform');
|
|
17
|
+
|
|
16
18
|
export interface Spec extends TurboModule {
|
|
17
19
|
+getConstants: () => {|
|
|
18
20
|
+HEIGHT: number,
|
|
@@ -38,7 +40,9 @@ export interface Spec extends TurboModule {
|
|
|
38
40
|
+setHidden: (hidden: boolean, withAnimation: string) => void;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
const NativeModule =
|
|
43
|
+
const NativeModule = Platform.isTVOS
|
|
44
|
+
? null
|
|
45
|
+
: TurboModuleRegistry.getEnforcing<Spec>('StatusBarManager');
|
|
42
46
|
let constants = null;
|
|
43
47
|
|
|
44
48
|
const NativeStatusBarManager = {
|
|
@@ -47,26 +51,28 @@ const NativeStatusBarManager = {
|
|
|
47
51
|
+DEFAULT_BACKGROUND_COLOR?: number,
|
|
48
52
|
|} {
|
|
49
53
|
if (constants == null) {
|
|
50
|
-
constants = NativeModule
|
|
54
|
+
constants = NativeModule
|
|
55
|
+
? NativeModule.getConstants()
|
|
56
|
+
: {HEIGHT: 0, DEFAULT_BACKGROUND_COLOR: 0};
|
|
51
57
|
}
|
|
52
58
|
return constants;
|
|
53
59
|
},
|
|
54
60
|
|
|
55
61
|
// TODO(T47754272) Can we remove this method?
|
|
56
62
|
getHeight(callback: (result: {|height: number|}) => void): void {
|
|
57
|
-
NativeModule.getHeight(callback);
|
|
63
|
+
NativeModule ? NativeModule.getHeight(callback) : callback({height: 0});
|
|
58
64
|
},
|
|
59
65
|
|
|
60
66
|
setNetworkActivityIndicatorVisible(visible: boolean): void {
|
|
61
|
-
NativeModule.setNetworkActivityIndicatorVisible(visible);
|
|
67
|
+
NativeModule && NativeModule.setNetworkActivityIndicatorVisible(visible);
|
|
62
68
|
},
|
|
63
69
|
|
|
64
70
|
addListener(eventType: string): void {
|
|
65
|
-
NativeModule.addListener(eventType);
|
|
71
|
+
NativeModule && NativeModule.addListener(eventType);
|
|
66
72
|
},
|
|
67
73
|
|
|
68
74
|
removeListeners(count: number): void {
|
|
69
|
-
NativeModule.removeListeners(count);
|
|
75
|
+
NativeModule && NativeModule.removeListeners(count);
|
|
70
76
|
},
|
|
71
77
|
|
|
72
78
|
/**
|
|
@@ -76,14 +82,14 @@ const NativeStatusBarManager = {
|
|
|
76
82
|
* - 'light-content'
|
|
77
83
|
*/
|
|
78
84
|
setStyle(statusBarStyle?: ?string, animated: boolean): void {
|
|
79
|
-
NativeModule.setStyle(statusBarStyle, animated);
|
|
85
|
+
NativeModule && NativeModule.setStyle(statusBarStyle, animated);
|
|
80
86
|
},
|
|
81
87
|
|
|
82
88
|
/**
|
|
83
89
|
* - withAnimation can be: 'none' | 'fade' | 'slide'
|
|
84
90
|
*/
|
|
85
91
|
setHidden(hidden: boolean, withAnimation: string): void {
|
|
86
|
-
NativeModule.setHidden(hidden, withAnimation);
|
|
92
|
+
NativeModule && NativeModule.setHidden(hidden, withAnimation);
|
|
87
93
|
},
|
|
88
94
|
};
|
|
89
95
|
|
|
@@ -14,6 +14,7 @@ const BoundingDimensions = require('./BoundingDimensions');
|
|
|
14
14
|
const Platform = require('../../Utilities/Platform');
|
|
15
15
|
const Position = require('./Position');
|
|
16
16
|
const React = require('react');
|
|
17
|
+
const ReactNative = require('../../Renderer/shims/ReactNative');
|
|
17
18
|
const StyleSheet = require('../../StyleSheet/StyleSheet');
|
|
18
19
|
const TVEventHandler = require('../AppleTV/TVEventHandler');
|
|
19
20
|
const UIManager = require('../../ReactNative/UIManager');
|
|
@@ -10,24 +10,51 @@
|
|
|
10
10
|
|
|
11
11
|
import React from 'react';
|
|
12
12
|
import type {Node} from 'react';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
Platform,
|
|
15
|
+
StyleSheet,
|
|
16
|
+
Text,
|
|
17
|
+
useColorScheme,
|
|
18
|
+
useTVEventHandler,
|
|
19
|
+
View,
|
|
20
|
+
} from 'react-native';
|
|
14
21
|
import Colors from './Colors';
|
|
15
22
|
|
|
16
23
|
const HermesBadge = (): Node => {
|
|
24
|
+
const [lastEventType, setLastEventType] = React.useState('');
|
|
25
|
+
const myTVEventHandler = evt => {
|
|
26
|
+
setLastEventType(evt.eventType);
|
|
27
|
+
};
|
|
28
|
+
if (Platform.isTV) {
|
|
29
|
+
useTVEventHandler(myTVEventHandler);
|
|
30
|
+
}
|
|
17
31
|
const isDarkMode = useColorScheme() === 'dark';
|
|
18
|
-
return
|
|
32
|
+
return (
|
|
19
33
|
<View style={styles.badge}>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
{Platform.isTV ? (
|
|
35
|
+
<Text
|
|
36
|
+
style={[
|
|
37
|
+
styles.badgeText,
|
|
38
|
+
{
|
|
39
|
+
color: isDarkMode ? Colors.light : Colors.dark,
|
|
40
|
+
},
|
|
41
|
+
]}>
|
|
42
|
+
TVEvent: {lastEventType}
|
|
43
|
+
</Text>
|
|
44
|
+
) : null}
|
|
45
|
+
{global.HermesInternal ? (
|
|
46
|
+
<Text
|
|
47
|
+
style={[
|
|
48
|
+
styles.badgeText,
|
|
49
|
+
{
|
|
50
|
+
color: isDarkMode ? Colors.light : Colors.dark,
|
|
51
|
+
},
|
|
52
|
+
]}>
|
|
53
|
+
Engine: Hermes
|
|
54
|
+
</Text>
|
|
55
|
+
) : null}
|
|
29
56
|
</View>
|
|
30
|
-
)
|
|
57
|
+
);
|
|
31
58
|
};
|
|
32
59
|
|
|
33
60
|
const styles = StyleSheet.create({
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Going forward, Apple TV support for React Native will be 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.
|
|
4
4
|
|
|
5
|
-
Releases of `react-native-tvos` will be based on a public release of `react-native`; e.g. the 0.64.2-
|
|
5
|
+
Releases of `react-native-tvos` will be based on a public release of `react-native`; e.g. the 0.64.2-2 release of this package will be derived from the 0.64.2 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,6 +23,8 @@ Due to the nature of the typing resolution, the current solution to include type
|
|
|
23
23
|
- install `@types/react-native` as a dev dependency
|
|
24
24
|
- put `import 'react-native/tvos-types.d'` in any of your `.ts` files (root suggested)
|
|
25
25
|
|
|
26
|
+
See the "Build Changes" section below for how to start a new project that will automatically use Typescript.
|
|
27
|
+
|
|
26
28
|
## General support for Apple TV
|
|
27
29
|
|
|
28
30
|
TV devices support has been implemented with the intention of making existing React Native applications "just work" on Apple TV, with few or no changes needed in the JavaScript code for the applications.
|
|
@@ -67,7 +69,11 @@ react-native init TestApp --template=react-native-tvos@latest
|
|
|
67
69
|
cd TestApp && react-native run-ios --simulator "Apple TV" --scheme "TestApp-tvOS"
|
|
68
70
|
```
|
|
69
71
|
|
|
72
|
+
A minimal Typescript starter template can be used to start a new project using the community react-native CLI. The process is the same as above except for the template:
|
|
70
73
|
|
|
74
|
+
```sh
|
|
75
|
+
react-native init TestApp --template=react-native-template-typescript-tv
|
|
76
|
+
```
|
|
71
77
|
|
|
72
78
|
- _JavaScript layer_: Support for Apple TV has been added to `Platform.ios.js`. You can check whether code is running on AppleTV by doing
|
|
73
79
|
|
|
@@ -92,7 +98,7 @@ var running_on_apple_tv = Platform.isTVOS;
|
|
|
92
98
|
- `onBlur` will be executed when the touchable view goes out of focus
|
|
93
99
|
- `onPress` will be executed when the touchable view is actually selected by pressing the "select" button on the TV remote.
|
|
94
100
|
|
|
95
|
-
- _TV remote/keyboard input_: A native class, `RCTTVRemoteHandler`, sets up gesture recognizers for TV remote events. When TV remote events occur, this class fires notifications that are picked up by `RCTTVNavigationEventEmitter` (a subclass of `RCTEventEmitter`), that fires a JS event. This event will be picked up by instances of the `TVEventHandler` JavaScript object. Application code that needs to implement custom handling of TV remote events can create an instance of `TVEventHandler` and listen for these events. In 0.63.1-1, we have added `useTVEventHandler`, which wraps `useEffect` to make this more convenient and simpler for use with functional components.
|
|
101
|
+
- _TV remote/keyboard input_: A native class, `RCTTVRemoteHandler`, sets up gesture recognizers for TV remote events. When TV remote events occur, this class fires notifications that are picked up by `RCTTVNavigationEventEmitter` (a subclass of `RCTEventEmitter`), that fires a JS event. This event will be picked up by instances of the `TVEventHandler` JavaScript object. Application code that needs to implement custom handling of TV remote events can create an instance of `TVEventHandler` and listen for these events. In 0.63.1-1, we have added `useTVEventHandler`, which wraps `useEffect` to make this more convenient and simpler for use with functional components. In 0.64.2-2, we added a TV event display to the new app template using `useTVEventHandler`.
|
|
96
102
|
|
|
97
103
|
```javascript
|
|
98
104
|
|
package/React/Base/RCTRootView.m
CHANGED
|
@@ -98,15 +98,6 @@ NSString *const RCTTVDisableMenuKeyNotification = @"RCTTVDisableMenuKeyNotificat
|
|
|
98
98
|
object:self];
|
|
99
99
|
|
|
100
100
|
#if TARGET_OS_TV
|
|
101
|
-
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
102
|
-
selector:@selector(enableTVMenuKey)
|
|
103
|
-
name:RCTTVEnableMenuKeyNotification
|
|
104
|
-
object:nil];
|
|
105
|
-
|
|
106
|
-
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
107
|
-
selector:@selector(disableTVMenuKey)
|
|
108
|
-
name:RCTTVDisableMenuKeyNotification
|
|
109
|
-
object:nil];
|
|
110
101
|
|
|
111
102
|
#if RCT_DEV
|
|
112
103
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
@@ -115,11 +106,7 @@ NSString *const RCTTVDisableMenuKeyNotification = @"RCTTVDisableMenuKeyNotificat
|
|
|
115
106
|
object:nil];
|
|
116
107
|
#endif
|
|
117
108
|
|
|
118
|
-
self.tvRemoteHandler = [RCTTVRemoteHandler
|
|
119
|
-
for (NSString *key in [self.tvRemoteHandler.tvRemoteGestureRecognizers allKeys]) {
|
|
120
|
-
[self addGestureRecognizer:self.tvRemoteHandler.tvRemoteGestureRecognizers[key]];
|
|
121
|
-
}
|
|
122
|
-
// [self addGestureRecognizer:self.tvRemoteHandler.tvMenuKeyRecognizer];
|
|
109
|
+
self.tvRemoteHandler = [[RCTTVRemoteHandler alloc] initWithView:self];
|
|
123
110
|
#endif
|
|
124
111
|
|
|
125
112
|
[self showLoadingView];
|
|
@@ -134,32 +121,12 @@ NSString *const RCTTVDisableMenuKeyNotification = @"RCTTVDisableMenuKeyNotificat
|
|
|
134
121
|
return self;
|
|
135
122
|
}
|
|
136
123
|
|
|
137
|
-
#if TARGET_OS_TV
|
|
138
|
-
|
|
139
|
-
- (void)enableTVMenuKey {
|
|
140
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
141
|
-
if (![[self gestureRecognizers] containsObject:self.tvRemoteHandler.tvMenuKeyRecognizer]) {
|
|
142
|
-
[self addGestureRecognizer:self.tvRemoteHandler.tvMenuKeyRecognizer];
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
- (void)disableTVMenuKey {
|
|
148
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
149
|
-
if ([[self gestureRecognizers] containsObject:self.tvRemoteHandler.tvMenuKeyRecognizer]) {
|
|
150
|
-
[self removeGestureRecognizer:self.tvRemoteHandler.tvMenuKeyRecognizer];
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
#if RCT_DEV
|
|
124
|
+
#if TARGET_OS_TV && RCT_DEV
|
|
156
125
|
- (void)showDevMenu {
|
|
157
126
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
158
127
|
[[self->_bridge devMenu] show];
|
|
159
128
|
});
|
|
160
129
|
}
|
|
161
|
-
#endif
|
|
162
|
-
|
|
163
130
|
#endif // TARGET_OS_TV
|
|
164
131
|
|
|
165
132
|
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
|
|
@@ -443,6 +410,9 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)
|
|
|
443
410
|
|
|
444
411
|
- (void)dealloc
|
|
445
412
|
{
|
|
413
|
+
#if TARGET_OS_TV
|
|
414
|
+
self.tvRemoteHandler = nil;
|
|
415
|
+
#endif
|
|
446
416
|
[_contentView invalidate];
|
|
447
417
|
}
|
|
448
418
|
|
|
@@ -26,7 +26,10 @@ extern NSString *const RCTTVRemoteEventSwipeDown;
|
|
|
26
26
|
|
|
27
27
|
@interface RCTTVRemoteHandler : NSObject
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
- (instancetype _Nonnull )initWithView:(UIView * _Nonnull)view;
|
|
30
|
+
- (instancetype _Nonnull )init __attribute__((unavailable("init not available, use initWithView:")));
|
|
31
|
+
|
|
32
|
+
+ (BOOL)useMenuKey;
|
|
33
|
+
+ (void)setUseMenuKey:(BOOL)useMenuKey;
|
|
31
34
|
|
|
32
35
|
@end
|
|
@@ -37,87 +37,189 @@ NSString *const RCTTVRemoteEventSwipeRight = @"swipeRight";
|
|
|
37
37
|
NSString *const RCTTVRemoteEventSwipeUp = @"swipeUp";
|
|
38
38
|
NSString *const RCTTVRemoteEventSwipeDown = @"swipeDown";
|
|
39
39
|
|
|
40
|
+
@interface RCTTVRemoteHandler()
|
|
41
|
+
|
|
42
|
+
@property (nonatomic, copy, readonly) NSDictionary *tvRemoteGestureRecognizers;
|
|
43
|
+
@property (nonatomic, strong) UITapGestureRecognizer *tvMenuKeyRecognizer;
|
|
44
|
+
@property (nonatomic, weak) UIView *view;
|
|
45
|
+
|
|
46
|
+
@end
|
|
47
|
+
|
|
40
48
|
@implementation RCTTVRemoteHandler {
|
|
41
49
|
NSMutableDictionary<NSString *, UIGestureRecognizer *> *_tvRemoteGestureRecognizers;
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
-
|
|
52
|
+
#pragma mark -
|
|
53
|
+
#pragma mark Static setting for using menu key
|
|
54
|
+
|
|
55
|
+
static __volatile BOOL __useMenuKey = NO;
|
|
56
|
+
|
|
57
|
+
+ (BOOL)useMenuKey
|
|
58
|
+
{
|
|
59
|
+
return __useMenuKey;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
+ (void)setUseMenuKey:(BOOL)useMenuKey
|
|
63
|
+
{
|
|
64
|
+
__useMenuKey = useMenuKey;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#pragma mark -
|
|
68
|
+
#pragma mark Public methods
|
|
69
|
+
|
|
70
|
+
- (instancetype)initWithView:(UIView *)view
|
|
45
71
|
{
|
|
46
72
|
if ((self = [super init])) {
|
|
73
|
+
_view = view;
|
|
74
|
+
[self setUpGestureRecognizers];
|
|
75
|
+
[self attachToView];
|
|
76
|
+
}
|
|
77
|
+
return self;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
- (void)dealloc
|
|
81
|
+
{
|
|
82
|
+
[self detachFromView];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
#pragma mark -
|
|
86
|
+
#pragma mark Private methods
|
|
87
|
+
|
|
88
|
+
- (void)setUpGestureRecognizers
|
|
89
|
+
{
|
|
47
90
|
_tvRemoteGestureRecognizers = [NSMutableDictionary dictionary];
|
|
91
|
+
// Recognizers for Apple TV remote buttons
|
|
92
|
+
// Menu recognizer
|
|
93
|
+
self.tvMenuKeyRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(menuPressed:)];
|
|
94
|
+
self.tvMenuKeyRecognizer.allowedPressTypes = @[@(UIPressTypeMenu)];
|
|
95
|
+
|
|
96
|
+
// Play/Pause
|
|
97
|
+
[self addTapGestureRecognizerWithSelector:@selector(playPausePressed:)
|
|
98
|
+
pressType:UIPressTypePlayPause
|
|
99
|
+
name:RCTTVRemoteEventPlayPause];
|
|
100
|
+
|
|
101
|
+
// Select
|
|
102
|
+
[self addTapGestureRecognizerWithSelector:@selector(selectPressed:)
|
|
103
|
+
pressType:UIPressTypeSelect
|
|
104
|
+
name:RCTTVRemoteEventSelect];
|
|
105
|
+
|
|
106
|
+
// Up
|
|
107
|
+
[self addTapGestureRecognizerWithSelector:@selector(tappedUp:)
|
|
108
|
+
pressType:UIPressTypeUpArrow
|
|
109
|
+
name:RCTTVRemoteEventUp];
|
|
110
|
+
|
|
111
|
+
// Down
|
|
112
|
+
[self addTapGestureRecognizerWithSelector:@selector(tappedDown:)
|
|
113
|
+
pressType:UIPressTypeDownArrow
|
|
114
|
+
name:RCTTVRemoteEventDown];
|
|
115
|
+
|
|
116
|
+
// Left
|
|
117
|
+
[self addTapGestureRecognizerWithSelector:@selector(tappedLeft:)
|
|
118
|
+
pressType:UIPressTypeLeftArrow
|
|
119
|
+
name:RCTTVRemoteEventLeft];
|
|
120
|
+
|
|
121
|
+
// Right
|
|
122
|
+
[self addTapGestureRecognizerWithSelector:@selector(tappedRight:)
|
|
123
|
+
pressType:UIPressTypeRightArrow
|
|
124
|
+
name:RCTTVRemoteEventRight];
|
|
125
|
+
|
|
126
|
+
// Recognizers for long button presses
|
|
127
|
+
// We don't intercept long menu press -- that's used by the system to go to the home screen
|
|
128
|
+
|
|
129
|
+
[self addLongPressGestureRecognizerWithSelector:@selector(longPlayPausePressed:)
|
|
130
|
+
pressType:UIPressTypePlayPause
|
|
131
|
+
name:RCTTVRemoteEventLongPlayPause];
|
|
132
|
+
|
|
133
|
+
[self addLongPressGestureRecognizerWithSelector:@selector(longSelectPressed:)
|
|
134
|
+
pressType:UIPressTypeSelect
|
|
135
|
+
name:RCTTVRemoteEventLongSelect];
|
|
136
|
+
|
|
137
|
+
// Recognizers for Apple TV remote trackpad swipes
|
|
138
|
+
|
|
139
|
+
// Up
|
|
140
|
+
[self addSwipeGestureRecognizerWithSelector:@selector(swipedUp:)
|
|
141
|
+
direction:UISwipeGestureRecognizerDirectionUp
|
|
142
|
+
name:RCTTVRemoteEventSwipeUp];
|
|
143
|
+
|
|
144
|
+
// Down
|
|
145
|
+
[self addSwipeGestureRecognizerWithSelector:@selector(swipedDown:)
|
|
146
|
+
direction:UISwipeGestureRecognizerDirectionDown
|
|
147
|
+
name:RCTTVRemoteEventSwipeDown];
|
|
148
|
+
|
|
149
|
+
// Left
|
|
150
|
+
[self addSwipeGestureRecognizerWithSelector:@selector(swipedLeft:)
|
|
151
|
+
direction:UISwipeGestureRecognizerDirectionLeft
|
|
152
|
+
name:RCTTVRemoteEventSwipeLeft];
|
|
153
|
+
|
|
154
|
+
// Right
|
|
155
|
+
[self addSwipeGestureRecognizerWithSelector:@selector(swipedRight:)
|
|
156
|
+
direction:UISwipeGestureRecognizerDirectionRight
|
|
157
|
+
name:RCTTVRemoteEventSwipeRight];
|
|
48
158
|
|
|
49
|
-
|
|
50
|
-
// Menu recognizer
|
|
51
|
-
self.tvMenuKeyRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(menuPressed:)];
|
|
52
|
-
self.tvMenuKeyRecognizer.allowedPressTypes = @[@(UIPressTypeMenu)];
|
|
53
|
-
|
|
54
|
-
// Play/Pause
|
|
55
|
-
[self addTapGestureRecognizerWithSelector:@selector(playPausePressed:)
|
|
56
|
-
pressType:UIPressTypePlayPause
|
|
57
|
-
name:RCTTVRemoteEventPlayPause];
|
|
58
|
-
|
|
59
|
-
// Select
|
|
60
|
-
[self addTapGestureRecognizerWithSelector:@selector(selectPressed:)
|
|
61
|
-
pressType:UIPressTypeSelect
|
|
62
|
-
name:RCTTVRemoteEventSelect];
|
|
63
|
-
|
|
64
|
-
// Up
|
|
65
|
-
[self addTapGestureRecognizerWithSelector:@selector(tappedUp:)
|
|
66
|
-
pressType:UIPressTypeUpArrow
|
|
67
|
-
name:RCTTVRemoteEventUp];
|
|
68
|
-
|
|
69
|
-
// Down
|
|
70
|
-
[self addTapGestureRecognizerWithSelector:@selector(tappedDown:)
|
|
71
|
-
pressType:UIPressTypeDownArrow
|
|
72
|
-
name:RCTTVRemoteEventDown];
|
|
73
|
-
|
|
74
|
-
// Left
|
|
75
|
-
[self addTapGestureRecognizerWithSelector:@selector(tappedLeft:)
|
|
76
|
-
pressType:UIPressTypeLeftArrow
|
|
77
|
-
name:RCTTVRemoteEventLeft];
|
|
78
|
-
|
|
79
|
-
// Right
|
|
80
|
-
[self addTapGestureRecognizerWithSelector:@selector(tappedRight:)
|
|
81
|
-
pressType:UIPressTypeRightArrow
|
|
82
|
-
name:RCTTVRemoteEventRight];
|
|
83
|
-
|
|
84
|
-
// Recognizers for long button presses
|
|
85
|
-
// We don't intercept long menu press -- that's used by the system to go to the home screen
|
|
86
|
-
|
|
87
|
-
[self addLongPressGestureRecognizerWithSelector:@selector(longPlayPausePressed:)
|
|
88
|
-
pressType:UIPressTypePlayPause
|
|
89
|
-
name:RCTTVRemoteEventLongPlayPause];
|
|
90
|
-
|
|
91
|
-
[self addLongPressGestureRecognizerWithSelector:@selector(longSelectPressed:)
|
|
92
|
-
pressType:UIPressTypeSelect
|
|
93
|
-
name:RCTTVRemoteEventLongSelect];
|
|
94
|
-
|
|
95
|
-
// Recognizers for Apple TV remote trackpad swipes
|
|
96
|
-
|
|
97
|
-
// Up
|
|
98
|
-
[self addSwipeGestureRecognizerWithSelector:@selector(swipedUp:)
|
|
99
|
-
direction:UISwipeGestureRecognizerDirectionUp
|
|
100
|
-
name:RCTTVRemoteEventSwipeUp];
|
|
101
|
-
|
|
102
|
-
// Down
|
|
103
|
-
[self addSwipeGestureRecognizerWithSelector:@selector(swipedDown:)
|
|
104
|
-
direction:UISwipeGestureRecognizerDirectionDown
|
|
105
|
-
name:RCTTVRemoteEventSwipeDown];
|
|
106
|
-
|
|
107
|
-
// Left
|
|
108
|
-
[self addSwipeGestureRecognizerWithSelector:@selector(swipedLeft:)
|
|
109
|
-
direction:UISwipeGestureRecognizerDirectionLeft
|
|
110
|
-
name:RCTTVRemoteEventSwipeLeft];
|
|
111
|
-
|
|
112
|
-
// Right
|
|
113
|
-
[self addSwipeGestureRecognizerWithSelector:@selector(swipedRight:)
|
|
114
|
-
direction:UISwipeGestureRecognizerDirectionRight
|
|
115
|
-
name:RCTTVRemoteEventSwipeRight];
|
|
116
|
-
}
|
|
159
|
+
}
|
|
117
160
|
|
|
118
|
-
|
|
161
|
+
- (void)attachToView
|
|
162
|
+
{
|
|
163
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
164
|
+
selector:@selector(enableTVMenuKey)
|
|
165
|
+
name:RCTTVEnableMenuKeyNotification
|
|
166
|
+
object:nil];
|
|
167
|
+
|
|
168
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
169
|
+
selector:@selector(disableTVMenuKey)
|
|
170
|
+
name:RCTTVDisableMenuKeyNotification
|
|
171
|
+
object:nil];
|
|
172
|
+
for (NSString *key in [self.tvRemoteGestureRecognizers allKeys]) {
|
|
173
|
+
[_view addGestureRecognizer:self.tvRemoteGestureRecognizers[key]];
|
|
174
|
+
}
|
|
175
|
+
if ([RCTTVRemoteHandler useMenuKey]) {
|
|
176
|
+
[self enableTVMenuKey];
|
|
177
|
+
} else {
|
|
178
|
+
[self disableTVMenuKey];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
- (void)detachFromView
|
|
183
|
+
{
|
|
184
|
+
if ([[self.view gestureRecognizers] containsObject:self.tvMenuKeyRecognizer]) {
|
|
185
|
+
[self.view removeGestureRecognizer:self.tvMenuKeyRecognizer];
|
|
186
|
+
}
|
|
187
|
+
for (NSString *key in [self.tvRemoteGestureRecognizers allKeys]) {
|
|
188
|
+
[_view removeGestureRecognizer:self.tvRemoteGestureRecognizers[key]];
|
|
189
|
+
}
|
|
190
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self
|
|
191
|
+
name:RCTTVEnableMenuKeyNotification
|
|
192
|
+
object:nil];
|
|
193
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self
|
|
194
|
+
name:RCTTVDisableMenuKeyNotification
|
|
195
|
+
object:nil];
|
|
196
|
+
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# pragma mark -
|
|
200
|
+
# pragma mark Notification handlers
|
|
201
|
+
|
|
202
|
+
- (void)enableTVMenuKey
|
|
203
|
+
{
|
|
204
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
205
|
+
if (![[self.view gestureRecognizers] containsObject:self.tvMenuKeyRecognizer]) {
|
|
206
|
+
[self.view addGestureRecognizer:self.tvMenuKeyRecognizer];
|
|
207
|
+
}
|
|
208
|
+
});
|
|
119
209
|
}
|
|
120
210
|
|
|
211
|
+
- (void)disableTVMenuKey
|
|
212
|
+
{
|
|
213
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
214
|
+
if ([[self.view gestureRecognizers] containsObject:self.tvMenuKeyRecognizer]) {
|
|
215
|
+
[self.view removeGestureRecognizer:self.tvMenuKeyRecognizer];
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# pragma mark -
|
|
221
|
+
# pragma mark Gesture handlers
|
|
222
|
+
|
|
121
223
|
- (void)playPausePressed:(UIGestureRecognizer *)r
|
|
122
224
|
{
|
|
123
225
|
[self sendAppleTVEvent:RCTTVRemoteEventPlayPause toView:r.view];
|
|
@@ -189,6 +291,7 @@ NSString *const RCTTVRemoteEventSwipeDown = @"swipeDown";
|
|
|
189
291
|
}
|
|
190
292
|
|
|
191
293
|
#pragma mark -
|
|
294
|
+
#pragma mark Convenience methods for adding gesture recognizers
|
|
192
295
|
|
|
193
296
|
- (void)addLongPressGestureRecognizerWithSelector:(nonnull SEL)selector
|
|
194
297
|
pressType:(UIPressType)pressType
|