react-native-tvos 0.83.2-0 → 0.83.4-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/Components/ScrollView/AndroidHorizontalScrollViewNativeComponent.js +1 -0
- package/Libraries/Components/ScrollView/ScrollView.d.ts +2 -1
- package/Libraries/Components/ScrollView/ScrollView.js +8 -1
- package/Libraries/Components/ScrollView/ScrollViewNativeComponent.js +2 -0
- package/Libraries/Components/ScrollView/ScrollViewNativeComponentType.js +2 -1
- package/Libraries/Components/TV/TVViewPropTypes.js +7 -0
- package/Libraries/Components/View/View.js +6 -0
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/Core/setUpReactDevTools.js +23 -6
- package/Libraries/NativeComponent/TVViewConfig.js +1 -0
- package/Libraries/Network/RCTHTTPRequestHandler.h +9 -0
- package/Libraries/Network/RCTHTTPRequestHandler.mm +15 -1
- package/Libraries/Pressability/Pressability.js +7 -0
- package/Libraries/WebSocket/RCTReconnectingWebSocket.m +4 -1
- package/React/Base/RCTBundleURLProvider.mm +5 -3
- package/React/Base/RCTDevSupportHttpHeaders.h +24 -0
- package/React/Base/RCTDevSupportHttpHeaders.m +65 -0
- package/React/Base/RCTMultipartDataTask.h +9 -0
- package/React/Base/RCTMultipartDataTask.m +16 -1
- package/React/Base/RCTVersion.m +1 -1
- package/React/CoreModules/RCTWebSocketModule.h +6 -0
- package/React/CoreModules/RCTWebSocketModule.mm +14 -1
- package/React/DevSupport/RCTInspectorDevServerHelper.mm +33 -22
- package/React/DevSupport/RCTInspectorNetworkHelper.mm +2 -0
- package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.h +1 -0
- package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm +3 -0
- package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +110 -4
- package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h +2 -0
- package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +31 -4
- package/React/Inspector/RCTCxxInspectorWebSocketAdapter.mm +5 -1
- package/ReactAndroid/api/ReactAndroid.api +1 -0
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/CxxInspectorPackagerConnection.kt +2 -8
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.kt +17 -13
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/PackagerStatusCheck.kt +10 -19
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxContentView.kt +2 -2
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt +49 -0
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/InspectorNetworkHelper.kt +1 -12
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.kt +3 -4
- package/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.kt +2 -1
- package/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.kt +2 -8
- package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +49 -5
- package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.kt +7 -0
- package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +46 -4
- package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.kt +69 -0
- package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.kt +7 -0
- package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt +1 -0
- package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.kt +4 -0
- package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
- package/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.cpp +10 -0
- package/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.h +1 -0
- package/ReactCommon/react/renderer/components/scrollview/conversions.h +6 -0
- package/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.cpp +18 -1
- package/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.h +2 -0
- package/ReactCommon/react/renderer/components/scrollview/primitives.h +1 -1
- package/ReactCommon/react/renderer/components/view/BaseViewProps.cpp +11 -1
- package/ReactCommon/react/renderer/components/view/BaseViewProps.h +4 -0
- package/ReactCommon/react/renderer/components/view/platform/android/react/renderer/components/view/HostPlatformViewProps.cpp +18 -0
- package/ReactCommon/react/renderer/components/view/platform/android/react/renderer/components/view/HostPlatformViewProps.h +1 -0
- package/package.json +8 -8
- package/scripts/replace-rncore-version.js +62 -8
- package/settings.gradle.kts +21 -1
- package/types/public/ReactNativeTVTypes.d.ts +19 -2
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
#import <React/RCTTVRemoteHandler.h>
|
|
31
31
|
#import <React/RCTTVNavigationEventNotification.h>
|
|
32
32
|
#import "React/RCTI18nUtil.h"
|
|
33
|
+
#import "RCTViewComponentView.h"
|
|
33
34
|
#endif
|
|
34
35
|
|
|
35
36
|
using namespace facebook::react;
|
|
@@ -40,6 +41,7 @@ static NSString *kOnScrollEndEvent = @"onScrollEnded";
|
|
|
40
41
|
|
|
41
42
|
static const CGFloat kClippingLeeway = 44.0;
|
|
42
43
|
static const float TV_DEFAULT_SWIPE_DURATION = 0.3;
|
|
44
|
+
static const CGPoint NO_PREFERRED_CONTENT_OFFSET = CGPointMake(CGFLOAT_MIN, CGFLOAT_MIN);
|
|
43
45
|
|
|
44
46
|
static UIScrollViewKeyboardDismissMode RCTUIKeyboardDismissModeFromProps(const ScrollViewProps &props)
|
|
45
47
|
{
|
|
@@ -97,6 +99,8 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
|
|
|
97
99
|
RCTScrollableProtocol,
|
|
98
100
|
RCTEnhancedScrollViewOverridingDelegate>
|
|
99
101
|
|
|
102
|
+
@property (nonatomic, assign) CGPoint preferredContentOffset;
|
|
103
|
+
|
|
100
104
|
@end
|
|
101
105
|
|
|
102
106
|
@implementation RCTScrollViewComponentView {
|
|
@@ -172,6 +176,7 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
|
|
|
172
176
|
_endDraggingSensitivityMultiplier = 1;
|
|
173
177
|
|
|
174
178
|
_tvRemoteGestureRecognizers = [NSMutableDictionary new];
|
|
179
|
+
_preferredContentOffset = NO_PREFERRED_CONTENT_OFFSET;
|
|
175
180
|
}
|
|
176
181
|
|
|
177
182
|
return self;
|
|
@@ -465,6 +470,12 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
|
|
|
465
470
|
scrollView.keyboardDismissMode = RCTUIKeyboardDismissModeFromProps(newScrollViewProps);
|
|
466
471
|
}
|
|
467
472
|
|
|
473
|
+
#if TARGET_OS_TV
|
|
474
|
+
if (oldScrollViewProps.snapToAlignment != newScrollViewProps.snapToAlignment) {
|
|
475
|
+
scrollView.scrollSnapEnabled = newScrollViewProps.snapToAlignment == ScrollViewSnapToAlignment::Item;
|
|
476
|
+
}
|
|
477
|
+
#endif
|
|
478
|
+
|
|
468
479
|
[super updateProps:props oldProps:oldProps];
|
|
469
480
|
}
|
|
470
481
|
|
|
@@ -722,7 +733,10 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
|
|
|
722
733
|
withVelocity:(CGPoint)velocity
|
|
723
734
|
targetContentOffset:(inout CGPoint *)targetContentOffset
|
|
724
735
|
{
|
|
725
|
-
|
|
736
|
+
if (!CGPointEqualToPoint(self.preferredContentOffset, NO_PREFERRED_CONTENT_OFFSET))
|
|
737
|
+
{
|
|
738
|
+
*targetContentOffset = self.preferredContentOffset;
|
|
739
|
+
} else if (fabs(_endDraggingSensitivityMultiplier - 1) > 0.0001f) {
|
|
726
740
|
if (targetContentOffset->y > 0) {
|
|
727
741
|
const CGFloat travel = targetContentOffset->y - scrollView.contentOffset.y;
|
|
728
742
|
targetContentOffset->y = scrollView.contentOffset.y + travel * _endDraggingSensitivityMultiplier;
|
|
@@ -1137,19 +1151,109 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
|
|
|
1137
1151
|
#pragma mark Apple TV swipe and focus handling
|
|
1138
1152
|
|
|
1139
1153
|
#if TARGET_OS_TV
|
|
1154
|
+
// Focus marker helper: traverses view hierarchy to find scrollSnapAlign prop
|
|
1155
|
+
// Returns the view that has the property via the output parameter
|
|
1156
|
+
- (NSString *)findScrollSnapAlignInView:(UIView *)view foundView:(UIView **)outView
|
|
1157
|
+
{
|
|
1158
|
+
UIView *testView = view;
|
|
1159
|
+
UIView *snapTarget;
|
|
1160
|
+
NSString *marker;
|
|
1161
|
+
|
|
1162
|
+
while (testView && testView != self) {
|
|
1163
|
+
if (![testView isKindOfClass:RCTViewComponentView.class])
|
|
1164
|
+
{
|
|
1165
|
+
testView = [testView superview];
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
RCTViewComponentView *componentView = (RCTViewComponentView *)testView;
|
|
1169
|
+
|
|
1170
|
+
const auto &viewProps = static_cast<const facebook::react::BaseViewProps &>(*componentView.props);
|
|
1171
|
+
if (viewProps.scrollSnapAlign.has_value() && !viewProps.scrollSnapAlign.value().empty()) {
|
|
1172
|
+
marker = [NSString stringWithUTF8String:viewProps.scrollSnapAlign.value().c_str()];
|
|
1173
|
+
snapTarget = componentView;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
testView = [testView superview];
|
|
1177
|
+
}
|
|
1178
|
+
*outView = snapTarget;
|
|
1179
|
+
return marker;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
- (void)_handleScrollSnapForFocusedView:(UIView *)focusedView
|
|
1183
|
+
{
|
|
1184
|
+
const auto &scrollProps = static_cast<const ScrollViewProps &>(*_props);
|
|
1185
|
+
UIView *snapAlignView = nil;
|
|
1186
|
+
NSString *scrollSnapAlign = [self findScrollSnapAlignInView:focusedView foundView:&snapAlignView];
|
|
1187
|
+
if (scrollSnapAlign == nil || snapAlignView == nil) {
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
RCTEnhancedScrollView *scrollView = (RCTEnhancedScrollView *)_scrollView;
|
|
1192
|
+
CGRect focusedFrame = [snapAlignView convertRect:snapAlignView.bounds toView:_scrollView];
|
|
1193
|
+
CGFloat targetOffset;
|
|
1194
|
+
CGFloat snapToItemPadding = scrollProps.snapToItemPadding;
|
|
1195
|
+
|
|
1196
|
+
BOOL isHorizontalSnap = _scrollView.contentSize.width > self.frame.size.width;
|
|
1197
|
+
// Determine axis-specific properties
|
|
1198
|
+
CGFloat viewportSize, focusedOrigin, focusedSize, currentOffset, maxContentSize;
|
|
1199
|
+
if (isHorizontalSnap) {
|
|
1200
|
+
viewportSize = scrollView.bounds.size.width;
|
|
1201
|
+
focusedOrigin = focusedFrame.origin.x;
|
|
1202
|
+
focusedSize = focusedFrame.size.width;
|
|
1203
|
+
currentOffset = scrollView.contentOffset.x;
|
|
1204
|
+
maxContentSize = scrollView.contentSize.width;
|
|
1205
|
+
} else {
|
|
1206
|
+
viewportSize = scrollView.bounds.size.height;
|
|
1207
|
+
focusedOrigin = focusedFrame.origin.y;
|
|
1208
|
+
focusedSize = focusedFrame.size.height;
|
|
1209
|
+
currentOffset = scrollView.contentOffset.y;
|
|
1210
|
+
maxContentSize = scrollView.contentSize.height;
|
|
1211
|
+
}
|
|
1212
|
+
// Calculate target offset based on scrollSnapAlign (unified for both axes)
|
|
1213
|
+
if ([scrollSnapAlign isEqualToString:@"start"]) {
|
|
1214
|
+
targetOffset = focusedOrigin - snapToItemPadding;
|
|
1215
|
+
} else if ([scrollSnapAlign isEqualToString:@"center"]) {
|
|
1216
|
+
CGFloat viewportCenter = viewportSize / 2;
|
|
1217
|
+
CGFloat focusedCenter = focusedOrigin + (focusedSize / 2);
|
|
1218
|
+
targetOffset = focusedCenter - viewportCenter + (snapToItemPadding / 2);
|
|
1219
|
+
} else if ([scrollSnapAlign isEqualToString:@"end"]) {
|
|
1220
|
+
targetOffset = (focusedOrigin + focusedSize) - viewportSize + snapToItemPadding;
|
|
1221
|
+
} else {
|
|
1222
|
+
targetOffset = currentOffset;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// Apply snap-to-interval if configured
|
|
1226
|
+
if (scrollView.snapToInterval > 0) {
|
|
1227
|
+
CGFloat interval = scrollView.snapToInterval;
|
|
1228
|
+
targetOffset = floor(targetOffset / interval) * interval;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
// Clamp to valid range
|
|
1232
|
+
CGFloat maxOffset = MAX(maxContentSize - viewportSize, 0);
|
|
1233
|
+
targetOffset = MAX(0, MIN(targetOffset, maxOffset));
|
|
1234
|
+
CGPoint targetContentOffset = isHorizontalSnap
|
|
1235
|
+
? CGPointMake(targetOffset, scrollView.contentOffset.y)
|
|
1236
|
+
: CGPointMake(scrollView.contentOffset.x, targetOffset);
|
|
1237
|
+
self.preferredContentOffset = targetContentOffset;
|
|
1238
|
+
[_scrollView setContentOffset:targetContentOffset animated:YES];
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1140
1241
|
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context
|
|
1141
1242
|
withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
|
|
1142
1243
|
{
|
|
1143
|
-
|
|
1244
|
+
self.preferredContentOffset = NO_PREFERRED_CONTENT_OFFSET;
|
|
1245
|
+
const auto &scrollProps = static_cast<const ScrollViewProps &>(*_props);
|
|
1246
|
+
BOOL hasItemSnapAlignment = scrollProps.snapToAlignment == ScrollViewSnapToAlignment::Item;
|
|
1247
|
+
if (context.previouslyFocusedView == context.nextFocusedView || (!_props->isTVSelectable && !hasItemSnapAlignment)) {
|
|
1144
1248
|
return;
|
|
1145
1249
|
}
|
|
1146
|
-
if (context.nextFocusedView == self) {
|
|
1250
|
+
if (_props->isTVSelectable && context.nextFocusedView == self) {
|
|
1147
1251
|
[self becomeFirstResponder];
|
|
1148
1252
|
[self addSwipeGestureRecognizers];
|
|
1149
1253
|
// if we enter the scroll view from different view then block first touch event since it is the event that triggered the focus
|
|
1150
1254
|
_blockFirstTouch = (unsigned long)context.focusHeading != 0;
|
|
1151
1255
|
[self addArrowsListeners];
|
|
1152
|
-
} else if (context.previouslyFocusedView == self) {
|
|
1256
|
+
} else if (_props->isTVSelectable && context.previouslyFocusedView == self) {
|
|
1153
1257
|
[self removeArrowsListeners];
|
|
1154
1258
|
[self removeSwipeGestureRecognizers];
|
|
1155
1259
|
[self resignFirstResponder];
|
|
@@ -1171,6 +1275,8 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
|
|
|
1171
1275
|
[self scrollToHorizontalOffset:scrollView.contentSize.width];
|
|
1172
1276
|
}
|
|
1173
1277
|
}
|
|
1278
|
+
} else if ([context.nextFocusedView isDescendantOfView:_scrollView]) {
|
|
1279
|
+
[self _handleScrollSnapForFocusedView:context.nextFocusedView];
|
|
1174
1280
|
}
|
|
1175
1281
|
}
|
|
1176
1282
|
|
|
@@ -80,6 +80,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
80
80
|
@property(nonatomic, nullable) UIFocusGuide *focusGuideLeft;
|
|
81
81
|
@property(nonatomic, nullable) UIFocusGuide *focusGuideRight;
|
|
82
82
|
@property(nonatomic, nullable, strong) RCTTVRemoteSelectHandler *tvRemoteSelectHandler;
|
|
83
|
+
|
|
84
|
+
- (NSString *)scrollSnapAlign;
|
|
83
85
|
#endif
|
|
84
86
|
|
|
85
87
|
/**
|
|
@@ -87,7 +87,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
|
|
|
87
87
|
BOOL _trapFocusRight;
|
|
88
88
|
NSArray* _focusDestinations;
|
|
89
89
|
id<UIFocusItem> _previouslyFocusedItem;
|
|
90
|
+
NSString *_scrollSnapAlign;
|
|
90
91
|
RCTSwiftUIContainerViewWrapper *_swiftUIWrapper;
|
|
92
|
+
BOOL _shouldFocusOnMount;
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
#ifdef RCT_DYNAMIC_FRAMEWORKS
|
|
@@ -113,6 +115,8 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
|
|
|
113
115
|
_tvParallaxProperties.pressMagnification = 1.0f;
|
|
114
116
|
_tvParallaxProperties.pressDuration = 0.3f;
|
|
115
117
|
_tvParallaxProperties.pressDelay = 0.0f;
|
|
118
|
+
|
|
119
|
+
_shouldFocusOnMount = false;
|
|
116
120
|
#else
|
|
117
121
|
self.multipleTouchEnabled = YES;
|
|
118
122
|
#endif
|
|
@@ -170,6 +174,15 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
|
|
|
170
174
|
|
|
171
175
|
#pragma mark - Apple TV methods
|
|
172
176
|
|
|
177
|
+
- (void)didMoveToWindow {
|
|
178
|
+
[super didMoveToWindow];
|
|
179
|
+
|
|
180
|
+
if (self->_shouldFocusOnMount) {
|
|
181
|
+
self->_shouldFocusOnMount = false;
|
|
182
|
+
[self focusSelf];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
173
186
|
- (RCTSurfaceHostingProxyRootView *)containingRootView
|
|
174
187
|
{
|
|
175
188
|
UIView *rootview = self;
|
|
@@ -205,10 +218,8 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
|
|
|
205
218
|
if (!focusedSync) {
|
|
206
219
|
// `focusSelf` function relies on `rootView` which may not be present on the first render.
|
|
207
220
|
// `focusSelf` fails and returns `false` in that case. We try re-executing the same action
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
[self focusSelf];
|
|
211
|
-
});
|
|
221
|
+
// when the view has moved to the window. This is tracked by the `_shouldFocusOnMount` flag.
|
|
222
|
+
self->_shouldFocusOnMount = true;
|
|
212
223
|
}
|
|
213
224
|
}
|
|
214
225
|
|
|
@@ -431,6 +442,13 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
|
|
|
431
442
|
_motionEffectsAdded = NO;
|
|
432
443
|
}
|
|
433
444
|
|
|
445
|
+
- (NSString *)scrollSnapAlign
|
|
446
|
+
{
|
|
447
|
+
#if TARGET_OS_TV
|
|
448
|
+
return _scrollSnapAlign;
|
|
449
|
+
#endif
|
|
450
|
+
return nil;
|
|
451
|
+
}
|
|
434
452
|
|
|
435
453
|
- (BOOL)isTVFocusGuide
|
|
436
454
|
{
|
|
@@ -1160,6 +1178,15 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
|
|
|
1160
1178
|
_trapFocusDown = newViewProps.trapFocusDown;
|
|
1161
1179
|
_trapFocusLeft = newViewProps.trapFocusLeft;
|
|
1162
1180
|
_trapFocusRight = newViewProps.trapFocusRight;
|
|
1181
|
+
|
|
1182
|
+
// `scrollSnapAlign`
|
|
1183
|
+
if (oldViewProps.scrollSnapAlign != newViewProps.scrollSnapAlign) {
|
|
1184
|
+
if (newViewProps.scrollSnapAlign.has_value()) {
|
|
1185
|
+
_scrollSnapAlign = [[NSString alloc] initWithUTF8String:newViewProps.scrollSnapAlign.value().c_str()];
|
|
1186
|
+
} else {
|
|
1187
|
+
_scrollSnapAlign = nil;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1163
1190
|
#endif
|
|
1164
1191
|
|
|
1165
1192
|
_needsInvalidateLayer = _needsInvalidateLayer || needsInvalidateLayer;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#if RCT_DEV || RCT_REMOTE_PROFILE
|
|
11
11
|
|
|
12
|
+
#import <React/RCTDevSupportHttpHeaders.h>
|
|
12
13
|
#import <React/RCTInspector.h>
|
|
13
14
|
#import <React/RCTInspectorPackagerConnection.h>
|
|
14
15
|
#import <React/RCTLog.h>
|
|
@@ -36,7 +37,10 @@ NSString *NSStringFromUTF8StringView(std::string_view view)
|
|
|
36
37
|
{
|
|
37
38
|
if ((self = [super init]) != nullptr) {
|
|
38
39
|
_delegate = delegate;
|
|
39
|
-
|
|
40
|
+
NSURL *requestURL = [NSURL URLWithString:NSStringFromUTF8StringView(url)];
|
|
41
|
+
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL];
|
|
42
|
+
[[RCTDevSupportHttpHeaders sharedInstance] applyHeadersToRequest:request];
|
|
43
|
+
_webSocket = [[SRWebSocket alloc] initWithURLRequest:request];
|
|
40
44
|
_webSocket.delegate = self;
|
|
41
45
|
[_webSocket open];
|
|
42
46
|
}
|
|
@@ -2129,6 +2129,7 @@ public final class com/facebook/react/devsupport/StackTraceHelper$StackFrameImpl
|
|
|
2129
2129
|
public fun toJSON ()Lorg/json/JSONObject;
|
|
2130
2130
|
}
|
|
2131
2131
|
|
|
2132
|
+
|
|
2132
2133
|
public abstract interface class com/facebook/react/devsupport/interfaces/BundleLoadCallback {
|
|
2133
2134
|
public fun onError (Ljava/lang/Exception;)V
|
|
2134
2135
|
public abstract fun onSuccess ()V
|
package/ReactAndroid/src/main/java/com/facebook/react/devsupport/CxxInspectorPackagerConnection.kt
CHANGED
|
@@ -12,10 +12,9 @@ import android.os.Looper
|
|
|
12
12
|
import com.facebook.jni.HybridData
|
|
13
13
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
14
14
|
import com.facebook.proguard.annotations.DoNotStripAny
|
|
15
|
+
import com.facebook.react.devsupport.inspector.DevSupportHttpClient
|
|
15
16
|
import com.facebook.soloader.SoLoader
|
|
16
17
|
import java.io.Closeable
|
|
17
|
-
import java.util.concurrent.TimeUnit
|
|
18
|
-
import okhttp3.OkHttpClient
|
|
19
18
|
import okhttp3.Request
|
|
20
19
|
import okhttp3.Response
|
|
21
20
|
import okhttp3.WebSocket
|
|
@@ -78,12 +77,7 @@ internal class CxxInspectorPackagerConnection(
|
|
|
78
77
|
|
|
79
78
|
/** Java implementation of the C++ InspectorPackagerConnectionDelegate interface. */
|
|
80
79
|
private class DelegateImpl {
|
|
81
|
-
private val httpClient =
|
|
82
|
-
OkHttpClient.Builder()
|
|
83
|
-
.connectTimeout(10, TimeUnit.SECONDS)
|
|
84
|
-
.writeTimeout(10, TimeUnit.SECONDS)
|
|
85
|
-
.readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read
|
|
86
|
-
.build()
|
|
80
|
+
private val httpClient = DevSupportHttpClient.websocketClient
|
|
87
81
|
|
|
88
82
|
private val handler = Handler(Looper.getMainLooper())
|
|
89
83
|
|
|
@@ -21,6 +21,7 @@ import com.facebook.react.bridge.ReactContext
|
|
|
21
21
|
import com.facebook.react.common.ReactConstants
|
|
22
22
|
import com.facebook.react.devsupport.InspectorFlags.getFuseboxEnabled
|
|
23
23
|
import com.facebook.react.devsupport.InspectorFlags.getIsProfilingBuild
|
|
24
|
+
import com.facebook.react.devsupport.inspector.DevSupportHttpClient
|
|
24
25
|
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener
|
|
25
26
|
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback
|
|
26
27
|
import com.facebook.react.modules.debug.interfaces.DeveloperSettings
|
|
@@ -39,7 +40,6 @@ import java.io.UnsupportedEncodingException
|
|
|
39
40
|
import java.security.MessageDigest
|
|
40
41
|
import java.security.NoSuchAlgorithmException
|
|
41
42
|
import java.util.Locale
|
|
42
|
-
import java.util.concurrent.TimeUnit
|
|
43
43
|
import okhttp3.Call
|
|
44
44
|
import okhttp3.Callback
|
|
45
45
|
import okhttp3.OkHttpClient
|
|
@@ -79,19 +79,15 @@ public open class DevServerHelper(
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
public val websocketProxyURL: String
|
|
82
|
-
get() =
|
|
82
|
+
get() =
|
|
83
|
+
"${DevSupportHttpClient.wsScheme(packagerConnectionSettings.debugServerHost)}://${packagerConnectionSettings.debugServerHost}/debugger-proxy?role=client"
|
|
83
84
|
|
|
84
85
|
private enum class BundleType(val typeID: String) {
|
|
85
86
|
BUNDLE("bundle"),
|
|
86
87
|
MAP("map"),
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
private val client: OkHttpClient =
|
|
90
|
-
OkHttpClient.Builder()
|
|
91
|
-
.connectTimeout(HTTP_CONNECT_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
|
|
92
|
-
.readTimeout(0, TimeUnit.MILLISECONDS)
|
|
93
|
-
.writeTimeout(0, TimeUnit.MILLISECONDS)
|
|
94
|
-
.build()
|
|
90
|
+
private val client: OkHttpClient = DevSupportHttpClient.httpClient
|
|
95
91
|
private val bundleDownloader: BundleDownloader = BundleDownloader(client)
|
|
96
92
|
private val packagerStatusCheck: PackagerStatusCheck = PackagerStatusCheck(client)
|
|
97
93
|
private val packageName: String = applicationContext.packageName
|
|
@@ -129,7 +125,8 @@ public open class DevServerHelper(
|
|
|
129
125
|
get() =
|
|
130
126
|
String.format(
|
|
131
127
|
Locale.US,
|
|
132
|
-
"
|
|
128
|
+
"%s://%s/inspector/device?name=%s&app=%s&device=%s&profiling=%b",
|
|
129
|
+
DevSupportHttpClient.httpScheme(packagerConnectionSettings.debugServerHost),
|
|
133
130
|
packagerConnectionSettings.debugServerHost,
|
|
134
131
|
Uri.encode(getFriendlyDeviceName()),
|
|
135
132
|
Uri.encode(packageName),
|
|
@@ -292,7 +289,8 @@ public open class DevServerHelper(
|
|
|
292
289
|
}
|
|
293
290
|
return (String.format(
|
|
294
291
|
Locale.US,
|
|
295
|
-
"
|
|
292
|
+
"%s://%s/%s.%s?platform=android&dev=%s&lazy=%s&minify=%s&app=%s&modulesOnly=%s&runModule=%s",
|
|
293
|
+
DevSupportHttpClient.httpScheme(host),
|
|
296
294
|
host,
|
|
297
295
|
mainModuleID,
|
|
298
296
|
type.typeID,
|
|
@@ -367,7 +365,8 @@ public open class DevServerHelper(
|
|
|
367
365
|
requestUrlBuilder.append(
|
|
368
366
|
String.format(
|
|
369
367
|
Locale.US,
|
|
370
|
-
"
|
|
368
|
+
"%s://%s/open-debugger?device=%s",
|
|
369
|
+
DevSupportHttpClient.httpScheme(packagerConnectionSettings.debugServerHost),
|
|
371
370
|
packagerConnectionSettings.debugServerHost,
|
|
372
371
|
Uri.encode(inspectorDeviceId),
|
|
373
372
|
)
|
|
@@ -397,7 +396,6 @@ public open class DevServerHelper(
|
|
|
397
396
|
}
|
|
398
397
|
|
|
399
398
|
private companion object {
|
|
400
|
-
private const val HTTP_CONNECT_TIMEOUT_MS = 5000
|
|
401
399
|
private const val DEBUGGER_MSG_DISABLE = "{ \"id\":1,\"method\":\"Debugger.disable\" }"
|
|
402
400
|
|
|
403
401
|
private fun getSHA256(string: String): String {
|
|
@@ -446,7 +444,13 @@ public open class DevServerHelper(
|
|
|
446
444
|
FLog.w(ReactConstants.TAG, "Resource path should not begin with `/`, removing it.")
|
|
447
445
|
resourcePath = resourcePath.substring(1)
|
|
448
446
|
}
|
|
449
|
-
return String.format(
|
|
447
|
+
return String.format(
|
|
448
|
+
Locale.US,
|
|
449
|
+
"%s://%s/%s",
|
|
450
|
+
DevSupportHttpClient.httpScheme(host),
|
|
451
|
+
host,
|
|
452
|
+
resourcePath,
|
|
453
|
+
)
|
|
450
454
|
}
|
|
451
455
|
}
|
|
452
456
|
}
|
|
@@ -11,10 +11,10 @@ package com.facebook.react.devsupport
|
|
|
11
11
|
|
|
12
12
|
import com.facebook.common.logging.FLog
|
|
13
13
|
import com.facebook.react.common.ReactConstants
|
|
14
|
+
import com.facebook.react.devsupport.inspector.DevSupportHttpClient
|
|
14
15
|
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback
|
|
15
16
|
import java.io.IOException
|
|
16
17
|
import java.util.Locale
|
|
17
|
-
import java.util.concurrent.TimeUnit
|
|
18
18
|
import okhttp3.Call
|
|
19
19
|
import okhttp3.Callback
|
|
20
20
|
import okhttp3.OkHttpClient
|
|
@@ -22,22 +22,9 @@ import okhttp3.Request
|
|
|
22
22
|
import okhttp3.Response
|
|
23
23
|
|
|
24
24
|
/** Use this class to check if the JavaScript packager is running on the provided host. */
|
|
25
|
-
internal class PackagerStatusCheck {
|
|
25
|
+
internal class PackagerStatusCheck(private val client: OkHttpClient) {
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
constructor() {
|
|
30
|
-
client =
|
|
31
|
-
OkHttpClient.Builder()
|
|
32
|
-
.connectTimeout(HTTP_CONNECT_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
|
|
33
|
-
.readTimeout(0, TimeUnit.MILLISECONDS)
|
|
34
|
-
.writeTimeout(0, TimeUnit.MILLISECONDS)
|
|
35
|
-
.build()
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
constructor(client: OkHttpClient) {
|
|
39
|
-
this.client = client
|
|
40
|
-
}
|
|
27
|
+
constructor() : this(DevSupportHttpClient.httpClient)
|
|
41
28
|
|
|
42
29
|
fun run(host: String, callback: PackagerStatusCallback) {
|
|
43
30
|
val statusURL = createPackagerStatusURL(host)
|
|
@@ -92,10 +79,14 @@ internal class PackagerStatusCheck {
|
|
|
92
79
|
|
|
93
80
|
private companion object {
|
|
94
81
|
private const val PACKAGER_OK_STATUS = "packager-status:running"
|
|
95
|
-
private const val
|
|
96
|
-
private const val PACKAGER_STATUS_URL_TEMPLATE = "http://%s/status"
|
|
82
|
+
private const val PACKAGER_STATUS_URL_TEMPLATE = "%s://%s/status"
|
|
97
83
|
|
|
98
84
|
private fun createPackagerStatusURL(host: String): String =
|
|
99
|
-
String.format(
|
|
85
|
+
String.format(
|
|
86
|
+
Locale.US,
|
|
87
|
+
PACKAGER_STATUS_URL_TEMPLATE,
|
|
88
|
+
DevSupportHttpClient.httpScheme(host),
|
|
89
|
+
host,
|
|
90
|
+
)
|
|
100
91
|
}
|
|
101
92
|
}
|
|
@@ -33,12 +33,12 @@ import android.widget.TextView
|
|
|
33
33
|
import com.facebook.common.logging.FLog
|
|
34
34
|
import com.facebook.react.R
|
|
35
35
|
import com.facebook.react.common.ReactConstants
|
|
36
|
+
import com.facebook.react.devsupport.inspector.DevSupportHttpClient
|
|
36
37
|
import com.facebook.react.devsupport.interfaces.DevSupportManager
|
|
37
38
|
import com.facebook.react.devsupport.interfaces.ErrorType
|
|
38
39
|
import com.facebook.react.devsupport.interfaces.RedBoxHandler
|
|
39
40
|
import com.facebook.react.devsupport.interfaces.StackFrame
|
|
40
41
|
import okhttp3.MediaType
|
|
41
|
-
import okhttp3.OkHttpClient
|
|
42
42
|
import okhttp3.Request
|
|
43
43
|
import okhttp3.RequestBody
|
|
44
44
|
import org.json.JSONObject
|
|
@@ -165,7 +165,7 @@ internal class RedBoxContentView(
|
|
|
165
165
|
.query(null)
|
|
166
166
|
.build()
|
|
167
167
|
.toString()
|
|
168
|
-
val client =
|
|
168
|
+
val client = DevSupportHttpClient.httpClient
|
|
169
169
|
for (frame in stackFrames) {
|
|
170
170
|
val payload = stackFrameToJson(checkNotNull(frame)).toString()
|
|
171
171
|
val body: RequestBody = RequestBody.create(JSON, payload)
|
package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and 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
|
+
|
|
8
|
+
@file:Suppress("DEPRECATION_ERROR") // Conflicting okhttp versions
|
|
9
|
+
|
|
10
|
+
package com.facebook.react.devsupport.inspector
|
|
11
|
+
|
|
12
|
+
import com.facebook.react.modules.network.OkHttpClientProvider
|
|
13
|
+
import java.util.concurrent.TimeUnit
|
|
14
|
+
import okhttp3.OkHttpClient
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Shared [OkHttpClient] instances for devsupport networking. Uses a single connection pool and
|
|
18
|
+
* dispatcher across all dev support HTTP and WebSocket usage.
|
|
19
|
+
*/
|
|
20
|
+
internal object DevSupportHttpClient {
|
|
21
|
+
/** Client for HTTP requests: connect=5s, write=disabled, read=disabled. */
|
|
22
|
+
internal val httpClient: OkHttpClient =
|
|
23
|
+
OkHttpClientProvider.getOkHttpClient()
|
|
24
|
+
.newBuilder()
|
|
25
|
+
.connectTimeout(5, TimeUnit.SECONDS)
|
|
26
|
+
.writeTimeout(0, TimeUnit.MILLISECONDS)
|
|
27
|
+
.readTimeout(0, TimeUnit.MINUTES)
|
|
28
|
+
.build()
|
|
29
|
+
|
|
30
|
+
/** Client for WebSocket connections: connect=10s, write=10s, read=disabled. */
|
|
31
|
+
internal val websocketClient: OkHttpClient =
|
|
32
|
+
httpClient
|
|
33
|
+
.newBuilder()
|
|
34
|
+
.connectTimeout(10, TimeUnit.SECONDS)
|
|
35
|
+
.writeTimeout(10, TimeUnit.SECONDS)
|
|
36
|
+
.build()
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Returns the appropriate HTTP scheme ("http" or "https") for the given host. Uses "https" when
|
|
40
|
+
* the host specifies port 443 explicitly (e.g. "example.com:443").
|
|
41
|
+
*/
|
|
42
|
+
internal fun httpScheme(host: String): String = if (host.endsWith(":443")) "https" else "http"
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns the appropriate WebSocket scheme ("ws" or "wss") for the given host. Uses "wss" when
|
|
46
|
+
* the host specifies port 443 explicitly (e.g. "example.com:443").
|
|
47
|
+
*/
|
|
48
|
+
internal fun wsScheme(host: String): String = if (host.endsWith(":443")) "wss" else "ws"
|
|
49
|
+
}
|
package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/InspectorNetworkHelper.kt
CHANGED
|
@@ -10,26 +10,15 @@
|
|
|
10
10
|
package com.facebook.react.devsupport.inspector
|
|
11
11
|
|
|
12
12
|
import java.io.IOException
|
|
13
|
-
import java.util.concurrent.TimeUnit
|
|
14
13
|
import okhttp3.Call
|
|
15
14
|
import okhttp3.Callback
|
|
16
|
-
import okhttp3.OkHttpClient
|
|
17
15
|
import okhttp3.Request
|
|
18
16
|
import okhttp3.Response
|
|
19
17
|
|
|
20
18
|
internal object InspectorNetworkHelper {
|
|
21
|
-
private lateinit var client: OkHttpClient
|
|
22
19
|
|
|
23
20
|
@JvmStatic
|
|
24
21
|
fun loadNetworkResource(url: String, listener: InspectorNetworkRequestListener) {
|
|
25
|
-
if (!::client.isInitialized) {
|
|
26
|
-
client =
|
|
27
|
-
OkHttpClient.Builder()
|
|
28
|
-
.connectTimeout(10, TimeUnit.SECONDS)
|
|
29
|
-
.writeTimeout(10, TimeUnit.SECONDS)
|
|
30
|
-
.readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read
|
|
31
|
-
.build()
|
|
32
|
-
}
|
|
33
22
|
|
|
34
23
|
val request =
|
|
35
24
|
try {
|
|
@@ -40,7 +29,7 @@ internal object InspectorNetworkHelper {
|
|
|
40
29
|
}
|
|
41
30
|
|
|
42
31
|
// TODO(T196951523): Assign cancel function to listener
|
|
43
|
-
val call =
|
|
32
|
+
val call = DevSupportHttpClient.httpClient.newCall(request)
|
|
44
33
|
|
|
45
34
|
call.enqueue(
|
|
46
35
|
object : Callback {
|
|
@@ -22,6 +22,7 @@ import com.facebook.react.common.ReactConstants
|
|
|
22
22
|
import com.facebook.react.module.annotations.ReactModule
|
|
23
23
|
import com.facebook.react.modules.network.CustomClientBuilder
|
|
24
24
|
import com.facebook.react.modules.network.ForwardingCookieHandler
|
|
25
|
+
import com.facebook.react.modules.network.OkHttpClientProvider
|
|
25
26
|
import java.io.IOException
|
|
26
27
|
import java.net.URI
|
|
27
28
|
import java.net.URISyntaxException
|
|
@@ -80,7 +81,8 @@ public class WebSocketModule(context: ReactApplicationContext) :
|
|
|
80
81
|
) {
|
|
81
82
|
val id = socketID.toInt()
|
|
82
83
|
val okHttpBuilder =
|
|
83
|
-
|
|
84
|
+
OkHttpClientProvider.getOkHttpClient()
|
|
85
|
+
.newBuilder()
|
|
84
86
|
.connectTimeout(10, TimeUnit.SECONDS)
|
|
85
87
|
.writeTimeout(10, TimeUnit.SECONDS)
|
|
86
88
|
.readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read
|
|
@@ -198,9 +200,6 @@ public class WebSocketModule(context: ReactApplicationContext) :
|
|
|
198
200
|
}
|
|
199
201
|
},
|
|
200
202
|
)
|
|
201
|
-
|
|
202
|
-
// Trigger shutdown of the dispatcher's executor so this process can exit cleanly
|
|
203
|
-
client.dispatcher().executorService().shutdown()
|
|
204
203
|
}
|
|
205
204
|
|
|
206
205
|
override fun close(code: Double, reason: String?, socketID: Double) {
|
package/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.kt
CHANGED
|
@@ -9,6 +9,7 @@ package com.facebook.react.packagerconnection
|
|
|
9
9
|
|
|
10
10
|
import android.net.Uri
|
|
11
11
|
import com.facebook.common.logging.FLog
|
|
12
|
+
import com.facebook.react.devsupport.inspector.DevSupportHttpClient
|
|
12
13
|
import com.facebook.react.modules.systeminfo.AndroidInfoHelpers.getFriendlyDeviceName
|
|
13
14
|
import com.facebook.react.packagerconnection.ReconnectingWebSocket.MessageCallback
|
|
14
15
|
import okio.ByteString
|
|
@@ -28,7 +29,7 @@ public constructor(
|
|
|
28
29
|
init {
|
|
29
30
|
val url =
|
|
30
31
|
Uri.Builder()
|
|
31
|
-
.scheme(
|
|
32
|
+
.scheme(DevSupportHttpClient.wsScheme(settings.debugServerHost))
|
|
32
33
|
.encodedAuthority(settings.debugServerHost)
|
|
33
34
|
.appendPath("message")
|
|
34
35
|
.appendQueryParameter("device", getFriendlyDeviceName())
|
package/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.kt
CHANGED
|
@@ -10,10 +10,9 @@ package com.facebook.react.packagerconnection
|
|
|
10
10
|
import android.os.Handler
|
|
11
11
|
import android.os.Looper
|
|
12
12
|
import com.facebook.common.logging.FLog
|
|
13
|
+
import com.facebook.react.devsupport.inspector.DevSupportHttpClient
|
|
13
14
|
import java.io.IOException
|
|
14
15
|
import java.nio.channels.ClosedChannelException
|
|
15
|
-
import java.util.concurrent.TimeUnit
|
|
16
|
-
import okhttp3.OkHttpClient
|
|
17
16
|
import okhttp3.Request
|
|
18
17
|
import okhttp3.Response
|
|
19
18
|
import okhttp3.WebSocket
|
|
@@ -40,12 +39,7 @@ public class ReconnectingWebSocket(
|
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
private val handler = Handler(Looper.getMainLooper())
|
|
43
|
-
private val okHttpClient
|
|
44
|
-
OkHttpClient.Builder()
|
|
45
|
-
.connectTimeout(10, TimeUnit.SECONDS)
|
|
46
|
-
.writeTimeout(10, TimeUnit.SECONDS)
|
|
47
|
-
.readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read
|
|
48
|
-
.build()
|
|
42
|
+
private val okHttpClient = DevSupportHttpClient.websocketClient
|
|
49
43
|
private var closed = false
|
|
50
44
|
private var suppressConnectionErrors = false
|
|
51
45
|
private var webSocket: WebSocket? = null
|