react-native-tvos 0.66.3-0 → 0.66.3-3
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/TVFocusEventHandler.js +51 -0
- package/Libraries/Components/AppleTV/TVFocusGuideView.js +30 -35
- package/Libraries/Components/AppleTV/TVViewPropTypes.js +0 -27
- package/Libraries/Components/Pressable/Pressable.js +28 -8
- package/Libraries/Components/Touchable/Touchable.js +14 -18
- package/Libraries/Components/View/ViewNativeComponent.js +6 -1
- package/Libraries/LogBox/UI/LogBoxButton.js +1 -0
- package/React/Base/RCTTVRemoteHandler.h +3 -0
- package/React/Base/RCTTVRemoteHandler.m +24 -0
- package/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm +1 -1
- package/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm +8 -0
- package/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h +6 -0
- package/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm +6 -0
- package/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.h +9 -0
- package/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.mm +18 -0
- package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +2 -0
- package/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm +3 -1
- package/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +4 -1
- package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h +4 -0
- package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +417 -1
- package/React/Fabric/Mounting/RCTComponentViewFactory.mm +2 -0
- package/React/React-RCTFabric.podspec +3 -0
- package/React/Views/RCTTVView.h +5 -0
- package/React/Views/RCTTVView.m +16 -0
- package/React/Views/RCTViewManager.m +17 -0
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +5 -0
- package/ReactAndroid/src/main/java/com/facebook/react/modules/core/ReactAndroidHWInputDeviceHelper.java +10 -4
- package/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +5 -5
- package/ReactCommon/hermes/inspector/tools/msggen/yarn.lock +37 -73
- package/ReactCommon/react/renderer/components/view/ViewProps.cpp +28 -0
- package/ReactCommon/react/renderer/components/view/ViewProps.h +10 -0
- package/ReactCommon/react/renderer/components/view/primitives.h +34 -0
- package/ReactCommon/react/renderer/components/view/propsConversions.h +66 -0
- package/android/com/facebook/react/react-native/{0.66.3-0/react-native-0.66.3-0.aar → 0.66.3-3/react-native-0.66.3-3.aar} +0 -0
- package/android/com/facebook/react/react-native/0.66.3-3/react-native-0.66.3-3.aar.md5 +1 -0
- package/android/com/facebook/react/react-native/0.66.3-3/react-native-0.66.3-3.aar.sha1 +1 -0
- package/android/com/facebook/react/react-native/0.66.3-3/react-native-0.66.3-3.aar.sha256 +1 -0
- package/android/com/facebook/react/react-native/0.66.3-3/react-native-0.66.3-3.aar.sha512 +1 -0
- package/android/com/facebook/react/react-native/{0.66.3-0/react-native-0.66.3-0.module → 0.66.3-3/react-native-0.66.3-3.module} +16 -16
- package/android/com/facebook/react/react-native/0.66.3-3/react-native-0.66.3-3.module.md5 +1 -0
- package/android/com/facebook/react/react-native/0.66.3-3/react-native-0.66.3-3.module.sha1 +1 -0
- package/android/com/facebook/react/react-native/0.66.3-3/react-native-0.66.3-3.module.sha256 +1 -0
- package/android/com/facebook/react/react-native/0.66.3-3/react-native-0.66.3-3.module.sha512 +1 -0
- package/android/com/facebook/react/react-native/{0.66.3-0/react-native-0.66.3-0.pom → 0.66.3-3/react-native-0.66.3-3.pom} +1 -1
- package/android/com/facebook/react/react-native/0.66.3-3/react-native-0.66.3-3.pom.md5 +1 -0
- package/android/com/facebook/react/react-native/0.66.3-3/react-native-0.66.3-3.pom.sha1 +1 -0
- package/android/com/facebook/react/react-native/0.66.3-3/react-native-0.66.3-3.pom.sha256 +1 -0
- package/android/com/facebook/react/react-native/0.66.3-3/react-native-0.66.3-3.pom.sha512 +1 -0
- package/android/com/facebook/react/react-native/maven-metadata.xml +4 -4
- 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/android/com/facebook/react/react-native/maven-metadata.xml.sha256 +1 -1
- package/android/com/facebook/react/react-native/maven-metadata.xml.sha512 +1 -1
- package/package.json +2 -2
- package/template/android/app/src/main/java/com/helloworld/MainApplication.java +5 -0
- package/template/package.json +1 -1
- package/third-party-podspecs/RCT-Folly.podspec +2 -1
- package/React/Views/RCTTVFocusGuideView.h +0 -22
- package/React/Views/RCTTVFocusGuideView.m +0 -60
- package/React/Views/RCTTVFocusGuideViewManager.h +0 -12
- package/React/Views/RCTTVFocusGuideViewManager.m +0 -30
- package/android/com/facebook/react/react-native/0.66.3-0/react-native-0.66.3-0.aar.md5 +0 -1
- package/android/com/facebook/react/react-native/0.66.3-0/react-native-0.66.3-0.aar.sha1 +0 -1
- package/android/com/facebook/react/react-native/0.66.3-0/react-native-0.66.3-0.aar.sha256 +0 -1
- package/android/com/facebook/react/react-native/0.66.3-0/react-native-0.66.3-0.aar.sha512 +0 -1
- package/android/com/facebook/react/react-native/0.66.3-0/react-native-0.66.3-0.module.md5 +0 -1
- package/android/com/facebook/react/react-native/0.66.3-0/react-native-0.66.3-0.module.sha1 +0 -1
- package/android/com/facebook/react/react-native/0.66.3-0/react-native-0.66.3-0.module.sha256 +0 -1
- package/android/com/facebook/react/react-native/0.66.3-0/react-native-0.66.3-0.module.sha512 +0 -1
- package/android/com/facebook/react/react-native/0.66.3-0/react-native-0.66.3-0.pom.md5 +0 -1
- package/android/com/facebook/react/react-native/0.66.3-0/react-native-0.66.3-0.pom.sha1 +0 -1
- package/android/com/facebook/react/react-native/0.66.3-0/react-native-0.66.3-0.pom.sha256 +0 -1
- package/android/com/facebook/react/react-native/0.66.3-0/react-native-0.66.3-0.pom.sha512 +0 -1
|
@@ -13,10 +13,26 @@
|
|
|
13
13
|
#import <React/RCTAssert.h>
|
|
14
14
|
#import <React/RCTBorderDrawing.h>
|
|
15
15
|
#import <React/RCTConversions.h>
|
|
16
|
+
#import <React/RCTDefines.h>
|
|
17
|
+
#import <React/RCTLog.h>
|
|
18
|
+
|
|
19
|
+
#import <React/RCTRootComponentView.h>
|
|
20
|
+
|
|
16
21
|
#import <react/renderer/components/view/ViewComponentDescriptor.h>
|
|
17
22
|
#import <react/renderer/components/view/ViewEventEmitter.h>
|
|
18
23
|
#import <react/renderer/components/view/ViewProps.h>
|
|
19
24
|
|
|
25
|
+
typedef struct {
|
|
26
|
+
BOOL enabled;
|
|
27
|
+
float shiftDistanceX;
|
|
28
|
+
float shiftDistanceY;
|
|
29
|
+
float tiltAngle;
|
|
30
|
+
float magnification;
|
|
31
|
+
float pressMagnification;
|
|
32
|
+
float pressDuration;
|
|
33
|
+
float pressDelay;
|
|
34
|
+
} ParallaxProperties;
|
|
35
|
+
|
|
20
36
|
using namespace facebook::react;
|
|
21
37
|
|
|
22
38
|
@implementation RCTViewComponentView {
|
|
@@ -26,7 +42,16 @@ using namespace facebook::react;
|
|
|
26
42
|
BOOL _isJSResponder;
|
|
27
43
|
BOOL _removeClippedSubviews;
|
|
28
44
|
NSMutableArray<UIView *> *_reactSubviews;
|
|
45
|
+
BOOL _motionEffectsAdded;
|
|
46
|
+
UITapGestureRecognizer *_selectRecognizer;
|
|
29
47
|
NSSet<NSString *> *_Nullable _propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN;
|
|
48
|
+
ParallaxProperties _tvParallaxProperties;
|
|
49
|
+
BOOL _hasTVPreferredFocus;
|
|
50
|
+
UIView *_nextFocusUp;
|
|
51
|
+
UIView *_nextFocusDown;
|
|
52
|
+
UIView *_nextFocusLeft;
|
|
53
|
+
UIView *_nextFocusRight;
|
|
54
|
+
UIView *_nextFocusActiveTarget;
|
|
30
55
|
}
|
|
31
56
|
|
|
32
57
|
- (instancetype)initWithFrame:(CGRect)frame
|
|
@@ -35,7 +60,20 @@ using namespace facebook::react;
|
|
|
35
60
|
static auto const defaultProps = std::make_shared<ViewProps const>();
|
|
36
61
|
_props = defaultProps;
|
|
37
62
|
_reactSubviews = [NSMutableArray new];
|
|
63
|
+
#if TARGET_OS_TV
|
|
64
|
+
_tvParallaxProperties.enabled = NO;
|
|
65
|
+
_tvParallaxProperties.shiftDistanceX = 0.0f;
|
|
66
|
+
_tvParallaxProperties.shiftDistanceY = 0.0f;
|
|
67
|
+
_tvParallaxProperties.tiltAngle = 0.0f;
|
|
68
|
+
_tvParallaxProperties.magnification = 0.0f;
|
|
69
|
+
_tvParallaxProperties.pressMagnification = 0.0f;
|
|
70
|
+
_tvParallaxProperties.pressDuration = 0.0f;
|
|
71
|
+
_tvParallaxProperties.pressDelay = 0.0f;
|
|
72
|
+
self.focusGuide = [UIFocusGuide new];
|
|
73
|
+
[self addLayoutGuide:self.focusGuide];
|
|
74
|
+
#else
|
|
38
75
|
self.multipleTouchEnabled = YES;
|
|
76
|
+
#endif
|
|
39
77
|
}
|
|
40
78
|
return self;
|
|
41
79
|
}
|
|
@@ -58,7 +96,6 @@ using namespace facebook::react;
|
|
|
58
96
|
_contentView.frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame());
|
|
59
97
|
}
|
|
60
98
|
}
|
|
61
|
-
|
|
62
99
|
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
|
|
63
100
|
{
|
|
64
101
|
if (UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero)) {
|
|
@@ -78,6 +115,290 @@ using namespace facebook::react;
|
|
|
78
115
|
_backgroundColor = backgroundColor;
|
|
79
116
|
}
|
|
80
117
|
|
|
118
|
+
#if TARGET_OS_TV
|
|
119
|
+
|
|
120
|
+
#pragma mark - Apple TV methods
|
|
121
|
+
|
|
122
|
+
- (RCTRootComponentView *)containingRootView
|
|
123
|
+
{
|
|
124
|
+
UIView *rootview = self;
|
|
125
|
+
if ([rootview class] == [RCTRootComponentView class]) {
|
|
126
|
+
return (RCTRootComponentView *)rootview;
|
|
127
|
+
}
|
|
128
|
+
do {
|
|
129
|
+
rootview = [rootview superview];
|
|
130
|
+
} while (([rootview class] != [RCTRootComponentView class]) && rootview != nil);
|
|
131
|
+
return (RCTRootComponentView *)rootview;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
- (void)addFocusDestinations:(NSArray*)destinations {
|
|
135
|
+
|
|
136
|
+
self.focusGuide.preferredFocusEnvironments = destinations;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
- (void)sendFocusNotification:(__unused UIFocusUpdateContext *)context
|
|
140
|
+
{
|
|
141
|
+
[self sendNotificationWithEventType:@"focus"];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
- (void)sendBlurNotification:(__unused UIFocusUpdateContext *)context
|
|
145
|
+
{
|
|
146
|
+
[self sendNotificationWithEventType:@"blur"];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
- (void)sendSelectNotification:(UIGestureRecognizer *)recognizer
|
|
150
|
+
{
|
|
151
|
+
[self sendNotificationWithEventType:@"select"];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
- (void)sendNotificationWithEventType:(NSString * __nonnull)eventType
|
|
155
|
+
{
|
|
156
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:@"RCTTVNavigationEventNotification"
|
|
157
|
+
object:@{
|
|
158
|
+
@"eventType":eventType,
|
|
159
|
+
@"tag":@([self tag]),
|
|
160
|
+
@"target":@([self tag])
|
|
161
|
+
}];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
- (void)handleSelect:(__unused UIGestureRecognizer *)r
|
|
165
|
+
{
|
|
166
|
+
if (_tvParallaxProperties.enabled == YES) {
|
|
167
|
+
float magnification = _tvParallaxProperties.magnification;
|
|
168
|
+
float pressMagnification = _tvParallaxProperties.pressMagnification;
|
|
169
|
+
|
|
170
|
+
// Duration of press animation
|
|
171
|
+
float pressDuration = _tvParallaxProperties.pressDuration;
|
|
172
|
+
|
|
173
|
+
// Delay of press animation
|
|
174
|
+
float pressDelay = _tvParallaxProperties.pressDelay;
|
|
175
|
+
|
|
176
|
+
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:pressDelay]];
|
|
177
|
+
|
|
178
|
+
[UIView animateWithDuration:(pressDuration/2)
|
|
179
|
+
animations:^{
|
|
180
|
+
self.transform = CGAffineTransformMakeScale(pressMagnification, pressMagnification);
|
|
181
|
+
}
|
|
182
|
+
completion:^(__unused BOOL finished1){
|
|
183
|
+
[UIView animateWithDuration:(pressDuration/2)
|
|
184
|
+
animations:^{
|
|
185
|
+
self.transform = CGAffineTransformMakeScale(magnification, magnification);
|
|
186
|
+
}
|
|
187
|
+
completion:^(__unused BOOL finished2) {
|
|
188
|
+
[self sendSelectNotification:r];
|
|
189
|
+
}];
|
|
190
|
+
}];
|
|
191
|
+
|
|
192
|
+
} else {
|
|
193
|
+
[self sendSelectNotification:r];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
- (void)addParallaxMotionEffects
|
|
198
|
+
{
|
|
199
|
+
if(!_tvParallaxProperties.enabled) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if(_motionEffectsAdded == YES) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Size of shift movements
|
|
208
|
+
CGFloat const shiftDistanceX = _tvParallaxProperties.shiftDistanceX;
|
|
209
|
+
CGFloat const shiftDistanceY = _tvParallaxProperties.shiftDistanceY;
|
|
210
|
+
|
|
211
|
+
// Make horizontal movements shift the centre left and right
|
|
212
|
+
UIInterpolatingMotionEffect *xShift =
|
|
213
|
+
[[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x"
|
|
214
|
+
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
|
|
215
|
+
xShift.minimumRelativeValue = @(shiftDistanceX * -1.0f);
|
|
216
|
+
xShift.maximumRelativeValue = @(shiftDistanceX);
|
|
217
|
+
|
|
218
|
+
// Make vertical movements shift the centre up and down
|
|
219
|
+
UIInterpolatingMotionEffect *yShift =
|
|
220
|
+
[[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y"
|
|
221
|
+
type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
|
|
222
|
+
yShift.minimumRelativeValue = @(shiftDistanceY * -1.0f);
|
|
223
|
+
yShift.maximumRelativeValue = @(shiftDistanceY);
|
|
224
|
+
|
|
225
|
+
// Size of tilt movements
|
|
226
|
+
CGFloat const tiltAngle = _tvParallaxProperties.tiltAngle;
|
|
227
|
+
|
|
228
|
+
// Now make horizontal movements effect a rotation about the Y axis for side-to-side rotation.
|
|
229
|
+
UIInterpolatingMotionEffect *xTilt =
|
|
230
|
+
[[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"layer.transform"
|
|
231
|
+
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
|
|
232
|
+
|
|
233
|
+
// CATransform3D value for minimumRelativeValue
|
|
234
|
+
CATransform3D transMinimumTiltAboutY = CATransform3DIdentity;
|
|
235
|
+
transMinimumTiltAboutY.m34 = 1.0 / 500;
|
|
236
|
+
transMinimumTiltAboutY = CATransform3DRotate(transMinimumTiltAboutY, tiltAngle * -1.0, 0, 1, 0);
|
|
237
|
+
|
|
238
|
+
// CATransform3D value for minimumRelativeValue
|
|
239
|
+
CATransform3D transMaximumTiltAboutY = CATransform3DIdentity;
|
|
240
|
+
transMaximumTiltAboutY.m34 = 1.0 / 500;
|
|
241
|
+
transMaximumTiltAboutY = CATransform3DRotate(transMaximumTiltAboutY, tiltAngle, 0, 1, 0);
|
|
242
|
+
|
|
243
|
+
// Set the transform property boundaries for the interpolation
|
|
244
|
+
xTilt.minimumRelativeValue = [NSValue valueWithCATransform3D:transMinimumTiltAboutY];
|
|
245
|
+
xTilt.maximumRelativeValue = [NSValue valueWithCATransform3D:transMaximumTiltAboutY];
|
|
246
|
+
|
|
247
|
+
// Now make vertical movements effect a rotation about the X axis for up and down rotation.
|
|
248
|
+
UIInterpolatingMotionEffect *yTilt =
|
|
249
|
+
[[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"layer.transform"
|
|
250
|
+
type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
|
|
251
|
+
|
|
252
|
+
// CATransform3D value for minimumRelativeValue
|
|
253
|
+
CATransform3D transMinimumTiltAboutX = CATransform3DIdentity;
|
|
254
|
+
transMinimumTiltAboutX.m34 = 1.0 / 500;
|
|
255
|
+
transMinimumTiltAboutX = CATransform3DRotate(transMinimumTiltAboutX, tiltAngle * -1.0, 1, 0, 0);
|
|
256
|
+
|
|
257
|
+
// CATransform3D value for minimumRelativeValue
|
|
258
|
+
CATransform3D transMaximumTiltAboutX = CATransform3DIdentity;
|
|
259
|
+
transMaximumTiltAboutX.m34 = 1.0 / 500;
|
|
260
|
+
transMaximumTiltAboutX = CATransform3DRotate(transMaximumTiltAboutX, tiltAngle, 1, 0, 0);
|
|
261
|
+
|
|
262
|
+
// Set the transform property boundaries for the interpolation
|
|
263
|
+
yTilt.minimumRelativeValue = [NSValue valueWithCATransform3D:transMinimumTiltAboutX];
|
|
264
|
+
yTilt.maximumRelativeValue = [NSValue valueWithCATransform3D:transMaximumTiltAboutX];
|
|
265
|
+
|
|
266
|
+
// Add all of the motion effects to this group
|
|
267
|
+
self.motionEffects = @[ xShift, yShift, xTilt, yTilt ];
|
|
268
|
+
|
|
269
|
+
float magnification = _tvParallaxProperties.magnification;
|
|
270
|
+
|
|
271
|
+
[UIView animateWithDuration:0.2 animations:^{
|
|
272
|
+
self.transform = CGAffineTransformScale(self.transform, magnification, magnification);
|
|
273
|
+
}];
|
|
274
|
+
|
|
275
|
+
_motionEffectsAdded = YES;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
- (void)removeParallaxMotionEffects
|
|
279
|
+
{
|
|
280
|
+
if(_motionEffectsAdded == NO) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
[UIView animateWithDuration:0.2 animations:^{
|
|
285
|
+
float magnification = self->_tvParallaxProperties.magnification;
|
|
286
|
+
BOOL enabled = self->_tvParallaxProperties.enabled;
|
|
287
|
+
if (enabled && magnification) {
|
|
288
|
+
self.transform = CGAffineTransformScale(self.transform, 1.0/magnification, 1.0/magnification);
|
|
289
|
+
}
|
|
290
|
+
}];
|
|
291
|
+
|
|
292
|
+
for (UIMotionEffect *effect in [self.motionEffects copy]){
|
|
293
|
+
[self removeMotionEffect:effect];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
_motionEffectsAdded = NO;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
- (BOOL)isUserInteractionEnabled
|
|
301
|
+
{
|
|
302
|
+
return YES;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
- (BOOL)canBecomeFocused
|
|
306
|
+
{
|
|
307
|
+
return _props->isTVSelectable;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
- (NSArray<id<UIFocusEnvironment>> *)preferredFocusEnvironments {
|
|
311
|
+
if (_nextFocusActiveTarget == nil) return [super preferredFocusEnvironments];
|
|
312
|
+
UIView * nextFocusActiveTarget = _nextFocusActiveTarget;
|
|
313
|
+
_nextFocusActiveTarget = nil;
|
|
314
|
+
NSArray<id<UIFocusEnvironment>> * focusEnvironment = @[nextFocusActiveTarget];
|
|
315
|
+
return focusEnvironment;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
- (BOOL) shouldUpdateFocusInContext:(UIFocusUpdateContext *)context {
|
|
319
|
+
if (self.isFocused) {
|
|
320
|
+
if (_nextFocusUp != nil && context.focusHeading == UIFocusHeadingUp) {
|
|
321
|
+
self->_nextFocusActiveTarget = _nextFocusUp;
|
|
322
|
+
[self setNeedsFocusUpdate];
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
if (_nextFocusDown != nil && context.focusHeading == UIFocusHeadingDown) {
|
|
326
|
+
self->_nextFocusActiveTarget = _nextFocusDown;
|
|
327
|
+
[self setNeedsFocusUpdate];
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
if (_nextFocusLeft != nil && context.focusHeading == UIFocusHeadingLeft) {
|
|
331
|
+
self->_nextFocusActiveTarget = _nextFocusLeft;
|
|
332
|
+
[self setNeedsFocusUpdate];
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
if (_nextFocusRight != nil && context.focusHeading == UIFocusHeadingRight) {
|
|
336
|
+
self->_nextFocusActiveTarget = _nextFocusRight;
|
|
337
|
+
[self setNeedsFocusUpdate];
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
self->_nextFocusActiveTarget = nil;
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
self->_nextFocusActiveTarget = nil;
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
|
|
348
|
+
{
|
|
349
|
+
if (context.previouslyFocusedView == context.nextFocusedView) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (context.nextFocusedView == self && self.isUserInteractionEnabled ) {
|
|
353
|
+
[self becomeFirstResponder];
|
|
354
|
+
[coordinator addCoordinatedAnimations:^(void){
|
|
355
|
+
[self addParallaxMotionEffects];
|
|
356
|
+
[self sendFocusNotification:context];
|
|
357
|
+
} completion:^(void){}];
|
|
358
|
+
} else {
|
|
359
|
+
[coordinator addCoordinatedAnimations:^(void){
|
|
360
|
+
[self removeParallaxMotionEffects];
|
|
361
|
+
[self sendBlurNotification:context];
|
|
362
|
+
} completion:^(void){}];
|
|
363
|
+
[self resignFirstResponder];
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
#endif
|
|
368
|
+
|
|
369
|
+
#pragma mark - Native Commands
|
|
370
|
+
|
|
371
|
+
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
|
|
372
|
+
{
|
|
373
|
+
NSLog(@"RCTViewComponentView handleCommand: %@", commandName);
|
|
374
|
+
if ([commandName isEqualToString:@"setDestinations"]) {
|
|
375
|
+
#if TARGET_OS_TV
|
|
376
|
+
if ([args count] != 1) {
|
|
377
|
+
RCTLogError(
|
|
378
|
+
@"%@ command %@ received %d arguments, expected %d.", @"View", commandName, (int)[args count], 1);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (!RCTValidateTypeOfViewCommandArgument(args[0], [NSArray class], @"number", @"View", commandName, @"1st")) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
NSArray *destinationTags = (NSArray<NSNumber *> *)args[0];
|
|
385
|
+
NSMutableArray *destinations = [NSMutableArray new];
|
|
386
|
+
RCTRootComponentView *rootView = [self containingRootView];
|
|
387
|
+
for (NSNumber *tag in destinationTags) {
|
|
388
|
+
UIView *view = [rootView viewWithTag:[tag intValue]];
|
|
389
|
+
if (view != nil) {
|
|
390
|
+
[destinations addObject:view];
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
[self addFocusDestinations:destinations];
|
|
394
|
+
return;
|
|
395
|
+
#endif
|
|
396
|
+
}
|
|
397
|
+
#if RCT_DEBUG
|
|
398
|
+
RCTLogError(@"%@ received command %@, which is not a supported command.", @"View", commandName);
|
|
399
|
+
#endif
|
|
400
|
+
}
|
|
401
|
+
|
|
81
402
|
#pragma mark - RCTComponentViewProtocol
|
|
82
403
|
|
|
83
404
|
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
@@ -359,11 +680,87 @@ using namespace facebook::react;
|
|
|
359
680
|
self.accessibilityIdentifier = RCTNSStringFromString(newViewProps.testId);
|
|
360
681
|
}
|
|
361
682
|
|
|
683
|
+
#if TARGET_OS_TV
|
|
684
|
+
// `isTVSelectable`
|
|
685
|
+
if (oldViewProps.isTVSelectable != newViewProps.isTVSelectable) {
|
|
686
|
+
if (newViewProps.isTVSelectable) {
|
|
687
|
+
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
|
|
688
|
+
action:@selector(handleSelect:)];
|
|
689
|
+
recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
|
|
690
|
+
_selectRecognizer = recognizer;
|
|
691
|
+
[self addGestureRecognizer:_selectRecognizer];
|
|
692
|
+
} else {
|
|
693
|
+
if (_selectRecognizer) {
|
|
694
|
+
[self removeGestureRecognizer:_selectRecognizer];
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
// `tvParallaxProperties
|
|
699
|
+
if (oldViewProps.tvParallaxProperties != newViewProps.tvParallaxProperties) {
|
|
700
|
+
_tvParallaxProperties.enabled = newViewProps.tvParallaxProperties.enabled.has_value() ?
|
|
701
|
+
newViewProps.tvParallaxProperties.enabled.value() :
|
|
702
|
+
newViewProps.isTVSelectable;
|
|
703
|
+
_tvParallaxProperties.shiftDistanceX = [self getFloat:newViewProps.tvParallaxProperties.shiftDistanceX orDefault:2.0];
|
|
704
|
+
_tvParallaxProperties.shiftDistanceY = [self getFloat:newViewProps.tvParallaxProperties.shiftDistanceY orDefault:2.0];
|
|
705
|
+
_tvParallaxProperties.tiltAngle = [self getFloat:newViewProps.tvParallaxProperties.tiltAngle orDefault:0.05];
|
|
706
|
+
_tvParallaxProperties.magnification = [self getFloat:newViewProps.tvParallaxProperties.magnification orDefault:1.0];
|
|
707
|
+
_tvParallaxProperties.pressMagnification = [self getFloat:newViewProps.tvParallaxProperties.pressMagnification orDefault:1.0];
|
|
708
|
+
_tvParallaxProperties.pressDuration = [self getFloat:newViewProps.tvParallaxProperties.pressDuration orDefault:0.3];
|
|
709
|
+
_tvParallaxProperties.pressDelay = [self getFloat:newViewProps.tvParallaxProperties.pressDelay orDefault:0.0];
|
|
710
|
+
}
|
|
711
|
+
// `hasTVPreferredFocus
|
|
712
|
+
if (oldViewProps.hasTVPreferredFocus != newViewProps.hasTVPreferredFocus) {
|
|
713
|
+
_hasTVPreferredFocus = newViewProps.hasTVPreferredFocus;
|
|
714
|
+
}
|
|
715
|
+
// `nextFocusUp`
|
|
716
|
+
if (oldViewProps.nextFocusUp != newViewProps.nextFocusUp) {
|
|
717
|
+
if (newViewProps.nextFocusUp.has_value()) {
|
|
718
|
+
UIView *rootView = [self containingRootView];
|
|
719
|
+
_nextFocusUp = [rootView viewWithTag:newViewProps.nextFocusUp.value()];
|
|
720
|
+
} else {
|
|
721
|
+
_nextFocusUp = nil;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
// `nextFocusDown`
|
|
725
|
+
if (oldViewProps.nextFocusDown != newViewProps.nextFocusDown) {
|
|
726
|
+
if (newViewProps.nextFocusDown.has_value()) {
|
|
727
|
+
UIView *rootView = [self containingRootView];
|
|
728
|
+
_nextFocusDown = [rootView viewWithTag:newViewProps.nextFocusDown.value()];
|
|
729
|
+
} else {
|
|
730
|
+
_nextFocusDown = nil;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
// `nextFocusLeft`
|
|
734
|
+
if (oldViewProps.nextFocusLeft != newViewProps.nextFocusLeft) {
|
|
735
|
+
if (newViewProps.nextFocusLeft.has_value()) {
|
|
736
|
+
UIView *rootView = [self containingRootView];
|
|
737
|
+
_nextFocusLeft = [rootView viewWithTag:newViewProps.nextFocusLeft.value()];
|
|
738
|
+
} else {
|
|
739
|
+
_nextFocusLeft = nil;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
// `nextFocusRight`
|
|
743
|
+
if (oldViewProps.nextFocusRight != newViewProps.nextFocusRight) {
|
|
744
|
+
if (newViewProps.nextFocusRight.has_value()) {
|
|
745
|
+
UIView *rootView = [self containingRootView];
|
|
746
|
+
_nextFocusRight = [rootView viewWithTag:newViewProps.nextFocusRight.value()];
|
|
747
|
+
} else {
|
|
748
|
+
_nextFocusRight = nil;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
#endif
|
|
752
|
+
|
|
753
|
+
|
|
362
754
|
_needsInvalidateLayer = _needsInvalidateLayer || needsInvalidateLayer;
|
|
363
755
|
|
|
364
756
|
_props = std::static_pointer_cast<ViewProps const>(props);
|
|
365
757
|
}
|
|
366
758
|
|
|
759
|
+
- (float)getFloat:(better::optional<float>)property orDefault:(float)defaultValue
|
|
760
|
+
{
|
|
761
|
+
return property.has_value() ? (float)property.value() : defaultValue;
|
|
762
|
+
}
|
|
763
|
+
|
|
367
764
|
- (void)updateEventEmitter:(EventEmitter::Shared const &)eventEmitter
|
|
368
765
|
{
|
|
369
766
|
assert(std::dynamic_pointer_cast<ViewEventEmitter const>(eventEmitter));
|
|
@@ -387,6 +784,25 @@ using namespace facebook::react;
|
|
|
387
784
|
if (_contentView) {
|
|
388
785
|
_contentView.frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame());
|
|
389
786
|
}
|
|
787
|
+
|
|
788
|
+
#if TARGET_OS_TV
|
|
789
|
+
[self.focusGuide.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES;
|
|
790
|
+
[self.focusGuide.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = YES;
|
|
791
|
+
[self.focusGuide.widthAnchor constraintEqualToAnchor:self.widthAnchor].active = YES;
|
|
792
|
+
[self.focusGuide.heightAnchor constraintEqualToAnchor:self.heightAnchor].active = YES;
|
|
793
|
+
|
|
794
|
+
if (_hasTVPreferredFocus) {
|
|
795
|
+
RCTRootComponentView *rootview = [self containingRootView];
|
|
796
|
+
if (rootview != nil && rootview.reactPreferredFocusedView != self) {
|
|
797
|
+
rootview.reactPreferredFocusedView = self;
|
|
798
|
+
[rootview setNeedsFocusUpdate];
|
|
799
|
+
[rootview updateFocusIfNeeded];
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
#endif
|
|
804
|
+
|
|
805
|
+
|
|
390
806
|
}
|
|
391
807
|
|
|
392
808
|
- (BOOL)isJSResponder
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
#import "RCTFabricComponentsPlugins.h"
|
|
31
31
|
#import "RCTImageComponentView.h"
|
|
32
32
|
#import "RCTLegacyViewManagerInteropComponentView.h"
|
|
33
|
+
#import "RCTModalHostViewComponentView.h"
|
|
33
34
|
#import "RCTMountingTransactionObserving.h"
|
|
34
35
|
#import "RCTParagraphComponentView.h"
|
|
35
36
|
#import "RCTRootComponentView.h"
|
|
@@ -73,6 +74,7 @@ static Class<RCTComponentViewProtocol> RCTComponentViewClassWithName(const char
|
|
|
73
74
|
[componentViewFactory registerComponentViewClass:[RCTViewComponentView class]];
|
|
74
75
|
[componentViewFactory registerComponentViewClass:[RCTParagraphComponentView class]];
|
|
75
76
|
[componentViewFactory registerComponentViewClass:[RCTTextInputComponentView class]];
|
|
77
|
+
[componentViewFactory registerComponentViewClass:[RCTModalHostViewComponentView class]];
|
|
76
78
|
|
|
77
79
|
Class<RCTComponentViewProtocol> imageClass = RCTComponentViewClassWithName("Image");
|
|
78
80
|
[componentViewFactory registerComponentViewClass:imageClass];
|
|
@@ -33,6 +33,9 @@ Pod::Spec.new do |s|
|
|
|
33
33
|
s.source_files = "Fabric/**/*.{c,h,m,mm,S,cpp}"
|
|
34
34
|
s.exclude_files = "**/tests/*",
|
|
35
35
|
"**/android/*",
|
|
36
|
+
s.tvos.exclude_files = "Fabric/**/RCTPullToRefreshViewComponentView*",
|
|
37
|
+
"Fabric/**/RCTSwitchComponentView*",
|
|
38
|
+
"Fabric/**/RCTSliderComponentView*"
|
|
36
39
|
s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags
|
|
37
40
|
s.header_dir = "React"
|
|
38
41
|
s.framework = "JavaScriptCore"
|
package/React/Views/RCTTVView.h
CHANGED
package/React/Views/RCTTVView.m
CHANGED
|
@@ -375,4 +375,20 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
|
|
|
375
375
|
}
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
+
- (void)addFocusGuide:(NSArray*)destinations {
|
|
379
|
+
|
|
380
|
+
UIView *origin = [self reactSuperview];
|
|
381
|
+
if (self.focusGuide == nil && origin != nil) {
|
|
382
|
+
self.focusGuide = [UIFocusGuide new];
|
|
383
|
+
[self addLayoutGuide:self.focusGuide];
|
|
384
|
+
|
|
385
|
+
[self.focusGuide.widthAnchor constraintEqualToAnchor:origin.widthAnchor].active = YES;
|
|
386
|
+
[self.focusGuide.heightAnchor constraintEqualToAnchor:origin.heightAnchor].active = YES;
|
|
387
|
+
[self.focusGuide.topAnchor constraintEqualToAnchor:origin.topAnchor].active = YES;
|
|
388
|
+
[self.focusGuide.leftAnchor constraintEqualToAnchor:origin.leftAnchor].active = YES;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
self.focusGuide.preferredFocusEnvironments = destinations;
|
|
392
|
+
}
|
|
393
|
+
|
|
378
394
|
@end
|
|
@@ -435,4 +435,21 @@ RCT_EXPORT_SHADOW_PROPERTY(onLayout, RCTDirectEventBlock)
|
|
|
435
435
|
|
|
436
436
|
RCT_EXPORT_SHADOW_PROPERTY(direction, YGDirection)
|
|
437
437
|
|
|
438
|
+
#if TARGET_OS_TV
|
|
439
|
+
RCT_EXPORT_METHOD(setDestinations : (nonnull NSNumber *)viewTag reactTags : (NSArray<NSNumber *> *)destinationTags)
|
|
440
|
+
{
|
|
441
|
+
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
|
442
|
+
RCTTVView *view = (RCTTVView *)viewRegistry[viewTag];
|
|
443
|
+
NSMutableArray* destinations = [NSMutableArray array];
|
|
444
|
+
for (NSNumber * tag in destinationTags) {
|
|
445
|
+
RCTTVView *destination = (RCTTVView*)viewRegistry[tag];
|
|
446
|
+
if (destination != nil) {
|
|
447
|
+
[destinations addObject:destination];
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
[view addFocusGuide:destinations];
|
|
451
|
+
}];
|
|
452
|
+
}
|
|
453
|
+
#endif
|
|
454
|
+
|
|
438
455
|
@end
|
|
@@ -89,4 +89,9 @@ public class ReactFeatureFlags {
|
|
|
89
89
|
public static boolean enableLockFreeEventDispatcher = false;
|
|
90
90
|
|
|
91
91
|
public static boolean enableAggressiveEventEmitterCleanup = false;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Send key down events as well as key up events
|
|
95
|
+
*/
|
|
96
|
+
public static boolean enableKeyDownEvents = false;
|
|
92
97
|
}
|
|
@@ -15,6 +15,7 @@ import com.facebook.react.bridge.WritableMap;
|
|
|
15
15
|
import com.facebook.react.bridge.WritableNativeMap;
|
|
16
16
|
import com.facebook.react.common.MapBuilder;
|
|
17
17
|
import com.facebook.react.common.SystemClock;
|
|
18
|
+
import com.facebook.react.config.ReactFeatureFlags;
|
|
18
19
|
|
|
19
20
|
import java.util.Map;
|
|
20
21
|
|
|
@@ -79,10 +80,7 @@ public class ReactAndroidHWInputDeviceHelper {
|
|
|
79
80
|
&& isSelectEvent(eventKeyCode) && mLastKeyDownTime == 0) {
|
|
80
81
|
mLastKeyDownTime = time;
|
|
81
82
|
}
|
|
82
|
-
|
|
83
|
-
// if ((eventKeyAction == KeyEvent.ACTION_UP || eventKeyAction == KeyEvent.ACTION_DOWN)
|
|
84
|
-
if ((eventKeyAction == KeyEvent.ACTION_UP)
|
|
85
|
-
&& KEY_EVENTS_ACTIONS.containsKey(eventKeyCode)) {
|
|
83
|
+
if (shouldDispatchEvent(eventKeyCode, eventKeyAction)) {
|
|
86
84
|
long delta = time - mLastKeyDownTime;
|
|
87
85
|
if (isSelectEvent(eventKeyCode) && (delta > mPressedDelta)) {
|
|
88
86
|
dispatchEvent("longSelect", mLastFocusedViewId, eventKeyAction, context);
|
|
@@ -93,6 +91,14 @@ public class ReactAndroidHWInputDeviceHelper {
|
|
|
93
91
|
}
|
|
94
92
|
}
|
|
95
93
|
|
|
94
|
+
// Android TV: Only send key up actions, unless key down events are enabled
|
|
95
|
+
private boolean shouldDispatchEvent(int eventKeyCode, int eventKeyAction) {
|
|
96
|
+
return KEY_EVENTS_ACTIONS.containsKey(eventKeyCode) && (
|
|
97
|
+
(eventKeyAction == KeyEvent.ACTION_UP) ||
|
|
98
|
+
(eventKeyAction == KeyEvent.ACTION_DOWN && ReactFeatureFlags.enableKeyDownEvents)
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
96
102
|
/** Called from {@link com.facebook.react.ReactRootView} when focused view changes. */
|
|
97
103
|
public void onFocusChanged(View newFocusedView, ReactContext context) {
|
|
98
104
|
if (mLastFocusedViewId == newFocusedView.getId()) {
|
package/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
CHANGED
|
@@ -1090,12 +1090,12 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
|
|
1090
1090
|
} else {
|
|
1091
1091
|
if (view != null) {
|
|
1092
1092
|
view.requestFocus();
|
|
1093
|
-
}
|
|
1094
1093
|
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1094
|
+
// manually triggering onFocusChanged when having only one input field on
|
|
1095
|
+
// the screen and no other focusable elements
|
|
1096
|
+
if (editText.getId() == view.getId()) {
|
|
1097
|
+
editText.onFocusChanged(false, View.FOCUSABLES_ALL, null);
|
|
1098
|
+
}
|
|
1099
1099
|
}
|
|
1100
1100
|
editText.hideKeyboard();
|
|
1101
1101
|
}
|