react-native-tvos 0.83.4-1 → 0.83.6-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.
Files changed (133) hide show
  1. package/Libraries/Components/ScrollView/AndroidHorizontalScrollViewNativeComponent.js +1 -0
  2. package/Libraries/Components/ScrollView/ScrollView.d.ts +7 -0
  3. package/Libraries/Components/ScrollView/ScrollView.js +6 -0
  4. package/Libraries/Components/ScrollView/ScrollViewNativeComponent.js +2 -0
  5. package/Libraries/Components/ScrollView/ScrollViewNativeComponentType.js +1 -0
  6. package/Libraries/Core/ReactNativeVersion.js +2 -2
  7. package/Libraries/Utilities/Appearance.js +6 -1
  8. package/Libraries/Utilities/HMRClient.js +28 -1
  9. package/README.md +52 -0
  10. package/React/Base/RCTVersion.m +2 -2
  11. package/React/CoreModules/RCTDevLoadingView.mm +17 -0
  12. package/React/DevSupport/RCTFrameTimingsObserver.h +24 -0
  13. package/React/DevSupport/RCTFrameTimingsObserver.mm +298 -0
  14. package/React/FBReactNativeSpec/FBReactNativeSpecJSI.h +16 -0
  15. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.h +1 -0
  16. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm +78 -0
  17. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +32 -6
  18. package/React/Views/ScrollView/RCTScrollViewManager.m +1 -0
  19. package/ReactAndroid/api/ReactAndroid.api +0 -9
  20. package/ReactAndroid/gradle.properties +1 -1
  21. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/BridgelessDevSupportManager.kt +2 -2
  22. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.kt +7 -7
  23. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorFlags.kt +4 -0
  24. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/FrameTimingSequence.kt +16 -0
  25. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/FrameTimingsObserver.kt +275 -0
  26. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/TracingState.kt +17 -0
  27. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/TracingStateListener.kt +15 -0
  28. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/{interfaces → inspector}/TracingStateProvider.kt +1 -1
  29. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/perfmonitor/PerfMonitorInspectorTargetBinding.kt +1 -1
  30. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/perfmonitor/PerfMonitorOverlayManager.kt +4 -4
  31. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/perfmonitor/PerfMonitorOverlayView.kt +3 -3
  32. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/perfmonitor/PerfMonitorUpdateListener.kt +1 -1
  33. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +13 -1
  34. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +21 -1
  35. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +5 -1
  36. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +6 -2
  37. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +23 -1
  38. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +5 -1
  39. package/ReactAndroid/src/main/java/com/facebook/react/internal/tracing/PerformanceTracer.kt +39 -0
  40. package/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BlobModule.kt +1 -1
  41. package/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkEventUtil.kt +20 -19
  42. package/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.kt +6 -12
  43. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +2 -2
  44. package/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt +86 -4
  45. package/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImplDevHelper.kt +3 -3
  46. package/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostInspectorTarget.kt +10 -6
  47. package/ReactAndroid/src/main/java/com/facebook/react/views/common/UiModeUtils.kt +20 -0
  48. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +70 -8
  49. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.kt +5 -0
  50. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +48 -2
  51. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.kt +5 -0
  52. package/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java +28 -3
  53. package/ReactAndroid/src/main/jni/react/devsupport/JInspectorFlags.cpp +22 -0
  54. package/ReactAndroid/src/main/jni/react/devsupport/JInspectorFlags.h +2 -0
  55. package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +29 -1
  56. package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +7 -1
  57. package/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.cpp +196 -17
  58. package/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.h +168 -18
  59. package/ReactAndroid/src/main/jni/third-party/folly/CMakeLists.txt +1 -0
  60. package/ReactCommon/cxxreact/ReactNativeVersion.h +3 -3
  61. package/ReactCommon/hermes/inspector-modern/chrome/Registration.cpp +44 -2
  62. package/ReactCommon/jsinspector-modern/HostAgent.cpp +45 -10
  63. package/ReactCommon/jsinspector-modern/HostAgent.h +2 -2
  64. package/ReactCommon/jsinspector-modern/HostTarget.cpp +14 -7
  65. package/ReactCommon/jsinspector-modern/HostTarget.h +101 -14
  66. package/ReactCommon/jsinspector-modern/HostTargetTraceRecording.cpp +39 -8
  67. package/ReactCommon/jsinspector-modern/HostTargetTraceRecording.h +42 -5
  68. package/ReactCommon/jsinspector-modern/HostTargetTracing.cpp +54 -21
  69. package/ReactCommon/jsinspector-modern/HostTargetTracing.h +89 -0
  70. package/ReactCommon/jsinspector-modern/InspectorFlags.cpp +12 -0
  71. package/ReactCommon/jsinspector-modern/InspectorFlags.h +12 -0
  72. package/ReactCommon/jsinspector-modern/InspectorInterfaces.cpp +3 -7
  73. package/ReactCommon/jsinspector-modern/InstanceAgent.cpp +2 -11
  74. package/ReactCommon/jsinspector-modern/NetworkIOAgent.cpp +1 -1
  75. package/ReactCommon/jsinspector-modern/RuntimeAgent.cpp +19 -0
  76. package/ReactCommon/jsinspector-modern/RuntimeAgent.h +7 -0
  77. package/ReactCommon/jsinspector-modern/RuntimeTarget.cpp +33 -0
  78. package/ReactCommon/jsinspector-modern/RuntimeTarget.h +6 -0
  79. package/ReactCommon/jsinspector-modern/TracingAgent.cpp +29 -13
  80. package/ReactCommon/jsinspector-modern/TracingAgent.h +5 -4
  81. package/ReactCommon/jsinspector-modern/tests/HostTargetTest.cpp +65 -0
  82. package/ReactCommon/jsinspector-modern/tests/InspectorMocks.h +23 -2
  83. package/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp +1 -0
  84. package/ReactCommon/jsinspector-modern/tests/NetworkReporterTest.cpp +1 -0
  85. package/ReactCommon/jsinspector-modern/tests/TracingTest.cpp +335 -0
  86. package/ReactCommon/jsinspector-modern/tests/TracingTest.h +95 -0
  87. package/ReactCommon/jsinspector-modern/tests/utils/InspectorFlagOverridesGuard.cpp +10 -0
  88. package/ReactCommon/jsinspector-modern/tests/utils/InspectorFlagOverridesGuard.h +3 -1
  89. package/ReactCommon/jsinspector-modern/tracing/CMakeLists.txt +1 -0
  90. package/ReactCommon/jsinspector-modern/tracing/FrameTimingSequence.h +61 -0
  91. package/ReactCommon/jsinspector-modern/tracing/HostTracingProfile.h +43 -0
  92. package/ReactCommon/jsinspector-modern/tracing/HostTracingProfileSerializer.cpp +165 -0
  93. package/ReactCommon/jsinspector-modern/tracing/HostTracingProfileSerializer.h +50 -0
  94. package/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp +16 -14
  95. package/ReactCommon/jsinspector-modern/tracing/PerformanceTracerSection.h +113 -0
  96. package/ReactCommon/jsinspector-modern/tracing/React-jsinspectortracing.podspec +1 -0
  97. package/ReactCommon/jsinspector-modern/tracing/TimeWindowedBuffer.h +158 -0
  98. package/ReactCommon/jsinspector-modern/tracing/TraceEvent.h +2 -1
  99. package/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.cpp +100 -0
  100. package/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.h +60 -0
  101. package/ReactCommon/jsinspector-modern/tracing/TraceEventSerializer.cpp +44 -1
  102. package/ReactCommon/jsinspector-modern/tracing/TraceEventSerializer.h +7 -0
  103. package/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h +18 -7
  104. package/ReactCommon/jsinspector-modern/tracing/TracingCategory.h +136 -0
  105. package/ReactCommon/jsinspector-modern/tracing/tests/TimeWindowedBufferTest.cpp +352 -0
  106. package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +9 -1
  107. package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +11 -1
  108. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +65 -29
  109. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +6 -2
  110. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +10 -2
  111. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +19 -1
  112. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +3 -1
  113. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm +3 -1
  114. package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +11 -1
  115. package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +5 -1
  116. package/ReactCommon/react/performance/timeline/PerformanceObserver.cpp +18 -6
  117. package/ReactCommon/react/performance/timeline/PerformanceObserver.h +2 -0
  118. package/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.cpp +10 -0
  119. package/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.h +1 -0
  120. package/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm +115 -0
  121. package/ReactCommon/{jsinspector-modern → react/utils}/Base64.h +2 -2
  122. package/gradle/libs.versions.toml +1 -1
  123. package/package.json +10 -10
  124. package/scripts/cocoapods/utils.rb +1 -0
  125. package/src/private/featureflags/ReactNativeFeatureFlags.js +12 -2
  126. package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +3 -1
  127. package/third-party-podspecs/RCT-Folly.podspec +1 -1
  128. package/third-party-podspecs/fmt.podspec +2 -2
  129. package/types/public/ReactNativeTVTypes.d.ts +8 -0
  130. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/TracingState.kt +0 -19
  131. package/ReactCommon/jsinspector-modern/tracing/TraceRecordingStateSerializer.cpp +0 -68
  132. package/ReactCommon/jsinspector-modern/tracing/TraceRecordingStateSerializer.h +0 -42
  133. package/ReactCommon/jsinspector-modern/tracing/TracingState.h +0 -24
@@ -9,6 +9,28 @@
9
9
  #import <React/RCTUtils.h>
10
10
  #import <react/utils/FloatComparison.h>
11
11
 
12
+ #if TARGET_OS_TV
13
+ // Layer subclass that can block all animations when scrollAnimationEnabled is NO.
14
+ // This intercepts animations at the point they're added to the layer, which catches
15
+ // all animation sources: UIView animations, focus coordinator animations, and
16
+ // Core Animation implicit/explicit animations.
17
+ @interface RCTScrollViewLayer : CALayer
18
+ @property (nonatomic, assign) BOOL blockAnimations;
19
+ @end
20
+
21
+ @implementation RCTScrollViewLayer
22
+
23
+ - (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key
24
+ {
25
+ if (_blockAnimations) {
26
+ return;
27
+ }
28
+ [super addAnimation:anim forKey:key];
29
+ }
30
+
31
+ @end
32
+ #endif
33
+
12
34
  @interface RCTEnhancedScrollView () <UIScrollViewDelegate>
13
35
  @end
14
36
 
@@ -17,6 +39,13 @@
17
39
  BOOL _isSetContentOffsetDisabled;
18
40
  }
19
41
 
42
+ #if TARGET_OS_TV
43
+ + (Class)layerClass
44
+ {
45
+ return [RCTScrollViewLayer class];
46
+ }
47
+ #endif
48
+
20
49
  + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
21
50
  {
22
51
  if ([key isEqualToString:@"delegate"]) {
@@ -28,9 +57,18 @@
28
57
  return [super automaticallyNotifiesObserversForKey:key];
29
58
  }
30
59
 
60
+ - (void)setScrollAnimationEnabled:(BOOL)scrollAnimationEnabled
61
+ {
62
+ _scrollAnimationEnabled = scrollAnimationEnabled;
63
+ #if TARGET_OS_TV
64
+ ((RCTScrollViewLayer *)self.layer).blockAnimations = !scrollAnimationEnabled;
65
+ #endif
66
+ }
67
+
31
68
  - (instancetype)initWithFrame:(CGRect)frame
32
69
  {
33
70
  if (self = [super initWithFrame:frame]) {
71
+ _scrollAnimationEnabled = YES;
34
72
  // We set the default behavior to "never" so that iOS
35
73
  // doesn't do weird things to UIScrollView insets automatically
36
74
  // and keeps it as an opt-in behavior.
@@ -102,6 +140,46 @@
102
140
  RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y"));
103
141
  }
104
142
 
143
+ - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
144
+ {
145
+ if (_isSetContentOffsetDisabled) {
146
+ return;
147
+ }
148
+ #if TARGET_OS_TV
149
+ animated = animated && _scrollAnimationEnabled;
150
+ #endif
151
+ [super setContentOffset:CGPointMake(
152
+ RCTSanitizeNaNValue(contentOffset.x, @"scrollView.contentOffset.x"),
153
+ RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y"))
154
+ animated:animated];
155
+ }
156
+
157
+ - (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated
158
+ {
159
+ #if TARGET_OS_TV
160
+ animated = animated && _scrollAnimationEnabled;
161
+ #endif
162
+ [super scrollRectToVisible:rect animated:animated];
163
+ }
164
+
165
+ #if TARGET_OS_TV
166
+ - (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context
167
+ withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
168
+ {
169
+ if (_scrollAnimationEnabled) {
170
+ [super didUpdateFocusInContext:context withAnimationCoordinator:coordinator];
171
+ return;
172
+ }
173
+
174
+ // When scrollAnimationEnabled is NO, don't call super and don't scroll here.
175
+ // RCTScrollViewComponentView.didUpdateFocusInContext handles all scrolling
176
+ // (both snap and non-snap cases) when animation is disabled. Scrolling here
177
+ // as well would cause conflicts — e.g. at the end of a snapToInterval list,
178
+ // this method computes a "just make visible" position while the component view
179
+ // computes the correct snapped position, causing a bounce effect.
180
+ }
181
+ #endif
182
+
105
183
  - (void)setFrame:(CGRect)frame
106
184
  {
107
185
  [super setFrame:frame];
@@ -434,6 +434,9 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
434
434
 
435
435
  scrollView.snapToStart = newScrollViewProps.snapToStart;
436
436
  scrollView.snapToEnd = newScrollViewProps.snapToEnd;
437
+ #if TARGET_OS_TV
438
+ scrollView.scrollAnimationEnabled = newScrollViewProps.scrollAnimationEnabled;
439
+ #endif
437
440
 
438
441
  if (oldScrollViewProps.snapToOffsets != newScrollViewProps.snapToOffsets) {
439
442
  NSMutableArray<NSNumber *> *snapToOffsets = [NSMutableArray array];
@@ -1235,7 +1238,7 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1235
1238
  ? CGPointMake(targetOffset, scrollView.contentOffset.y)
1236
1239
  : CGPointMake(scrollView.contentOffset.x, targetOffset);
1237
1240
  self.preferredContentOffset = targetContentOffset;
1238
- [_scrollView setContentOffset:targetContentOffset animated:YES];
1241
+ [_scrollView setContentOffset:targetContentOffset animated:scrollProps.scrollAnimationEnabled];
1239
1242
  }
1240
1243
 
1241
1244
  - (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context
@@ -1244,7 +1247,18 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1244
1247
  self.preferredContentOffset = NO_PREFERRED_CONTENT_OFFSET;
1245
1248
  const auto &scrollProps = static_cast<const ScrollViewProps &>(*_props);
1246
1249
  BOOL hasItemSnapAlignment = scrollProps.snapToAlignment == ScrollViewSnapToAlignment::Item;
1247
- if (context.previouslyFocusedView == context.nextFocusedView || (!_props->isTVSelectable && !hasItemSnapAlignment)) {
1250
+ if (context.previouslyFocusedView == context.nextFocusedView) {
1251
+ return;
1252
+ }
1253
+ if (!_props->isTVSelectable && !hasItemSnapAlignment) {
1254
+ if (!scrollProps.scrollAnimationEnabled && [context.nextFocusedView isDescendantOfView:_scrollView]) {
1255
+ // When animations are disabled and there's no snap alignment, manually scroll
1256
+ // the focused view into view instantly. We can't let UIScrollView's default
1257
+ // focus handling do this because it uses deceleration (timer-based) that can't
1258
+ // be blocked by the animation-blocking layer.
1259
+ CGRect targetRect = [context.nextFocusedView convertRect:context.nextFocusedView.bounds toView:_scrollView];
1260
+ [_scrollView scrollRectToVisible:targetRect animated:NO];
1261
+ }
1248
1262
  return;
1249
1263
  }
1250
1264
  if (_props->isTVSelectable && context.nextFocusedView == self) {
@@ -1410,10 +1424,16 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1410
1424
  limitedOffset = MAX(limitedOffset, 0.0);
1411
1425
  limitedOffset = MIN(limitedOffset, maxOffset);
1412
1426
 
1413
- [UIView animateWithDuration:[self swipeDuration] animations:^{
1427
+ const auto &scrollProps = static_cast<const ScrollViewProps &>(*self->_props);
1428
+ if (scrollProps.scrollAnimationEnabled) {
1429
+ [UIView animateWithDuration:[self swipeDuration] animations:^{
1430
+ self.scrollView.contentOffset =
1431
+ CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
1432
+ }];
1433
+ } else {
1414
1434
  self.scrollView.contentOffset =
1415
1435
  CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
1416
- }];
1436
+ }
1417
1437
  });
1418
1438
  }
1419
1439
 
@@ -1434,10 +1454,16 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1434
1454
  limitedOffset = MAX(limitedOffset, 0.0);
1435
1455
  limitedOffset = MIN(limitedOffset, maxOffset);
1436
1456
 
1437
- [UIView animateWithDuration:[self swipeDuration] animations:^{
1457
+ const auto &scrollProps = static_cast<const ScrollViewProps &>(*self->_props);
1458
+ if (scrollProps.scrollAnimationEnabled) {
1459
+ [UIView animateWithDuration:[self swipeDuration] animations:^{
1460
+ self.scrollView.contentOffset =
1461
+ CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
1462
+ }];
1463
+ } else {
1438
1464
  self.scrollView.contentOffset =
1439
1465
  CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
1440
- }];
1466
+ }
1441
1467
  });
1442
1468
  }
1443
1469
 
@@ -106,6 +106,7 @@ RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollEnd, RCTDirectEventBlock)
106
106
  RCT_EXPORT_VIEW_PROPERTY(inverted, BOOL)
107
107
  #if TARGET_OS_TV
108
108
  RCT_EXPORT_VIEW_PROPERTY(showsScrollIndex, BOOL)
109
+ RCT_EXPORT_VIEW_PROPERTY(scrollAnimationEnabled, BOOL)
109
110
  #endif
110
111
  RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustsScrollIndicatorInsets, BOOL)
111
112
  RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior)
@@ -2257,15 +2257,6 @@ public abstract interface class com/facebook/react/devsupport/interfaces/StackFr
2257
2257
  public abstract fun toJSON ()Lorg/json/JSONObject;
2258
2258
  }
2259
2259
 
2260
- public final class com/facebook/react/devsupport/interfaces/TracingState : java/lang/Enum {
2261
- public static final field DISABLED Lcom/facebook/react/devsupport/interfaces/TracingState;
2262
- public static final field ENABLEDINBACKGROUNDMODE Lcom/facebook/react/devsupport/interfaces/TracingState;
2263
- public static final field ENABLEDINCDPMODE Lcom/facebook/react/devsupport/interfaces/TracingState;
2264
- public static fun getEntries ()Lkotlin/enums/EnumEntries;
2265
- public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/devsupport/interfaces/TracingState;
2266
- public static fun values ()[Lcom/facebook/react/devsupport/interfaces/TracingState;
2267
- }
2268
-
2269
2260
  public final class com/facebook/react/fabric/ComponentFactory {
2270
2261
  public fun <init> ()V
2271
2262
  }
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.83.4-1
1
+ VERSION_NAME=0.83.6-0
2
2
  react.internal.publishingGroup=io.github.react-native-tvos
3
3
  react.internal.hermesPublishingGroup=com.facebook.hermes
4
4
 
@@ -10,12 +10,12 @@ package com.facebook.react.devsupport
10
10
  import android.content.Context
11
11
  import com.facebook.react.bridge.UiThreadUtil
12
12
  import com.facebook.react.common.SurfaceDelegateFactory
13
+ import com.facebook.react.devsupport.inspector.TracingState
13
14
  import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener
14
15
  import com.facebook.react.devsupport.interfaces.DevLoadingViewManager
15
16
  import com.facebook.react.devsupport.interfaces.DevSupportManager
16
17
  import com.facebook.react.devsupport.interfaces.PausedInDebuggerOverlayManager
17
18
  import com.facebook.react.devsupport.interfaces.RedBoxHandler
18
- import com.facebook.react.devsupport.interfaces.TracingState
19
19
  import com.facebook.react.packagerconnection.RequestHandler
20
20
 
21
21
  /**
@@ -83,6 +83,6 @@ internal class BridgelessDevSupportManager(
83
83
  }
84
84
 
85
85
  fun tracingState(): TracingState {
86
- return TracingState.ENABLEDINCDPMODE
86
+ return TracingState.ENABLED_IN_CDP_MODE
87
87
  }
88
88
  }
@@ -52,6 +52,8 @@ import com.facebook.react.devsupport.DevServerHelper.PackagerCommandListener
52
52
  import com.facebook.react.devsupport.InspectorFlags.getFuseboxEnabled
53
53
  import com.facebook.react.devsupport.StackTraceHelper.convertJavaStackTrace
54
54
  import com.facebook.react.devsupport.StackTraceHelper.convertJsStackTrace
55
+ import com.facebook.react.devsupport.inspector.TracingState
56
+ import com.facebook.react.devsupport.inspector.TracingStateProvider
55
57
  import com.facebook.react.devsupport.interfaces.BundleLoadCallback
56
58
  import com.facebook.react.devsupport.interfaces.DebuggerFrontendPanelName
57
59
  import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener
@@ -66,8 +68,6 @@ import com.facebook.react.devsupport.interfaces.PackagerStatusCallback
66
68
  import com.facebook.react.devsupport.interfaces.PausedInDebuggerOverlayManager
67
69
  import com.facebook.react.devsupport.interfaces.RedBoxHandler
68
70
  import com.facebook.react.devsupport.interfaces.StackFrame
69
- import com.facebook.react.devsupport.interfaces.TracingState
70
- import com.facebook.react.devsupport.interfaces.TracingStateProvider
71
71
  import com.facebook.react.devsupport.perfmonitor.PerfMonitorDevHelper
72
72
  import com.facebook.react.devsupport.perfmonitor.PerfMonitorOverlayManager
73
73
  import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
@@ -396,21 +396,21 @@ public abstract class DevSupportManagerBase(
396
396
 
397
397
  val analyzePerformanceItemString =
398
398
  when (tracingState) {
399
- TracingState.ENABLEDINBACKGROUNDMODE ->
399
+ TracingState.ENABLED_IN_BACKGROUND_MODE ->
400
400
  applicationContext.getString(R.string.catalyst_performance_background)
401
- TracingState.ENABLEDINCDPMODE ->
401
+ TracingState.ENABLED_IN_CDP_MODE ->
402
402
  applicationContext.getString(R.string.catalyst_performance_cdp)
403
403
  TracingState.DISABLED ->
404
404
  applicationContext.getString(R.string.catalyst_performance_disabled)
405
405
  }
406
406
 
407
- if (!isConnected || tracingState == TracingState.ENABLEDINCDPMODE) {
407
+ if (!isConnected || tracingState == TracingState.ENABLED_IN_CDP_MODE) {
408
408
  disabledItemKeys.add(analyzePerformanceItemString)
409
409
  }
410
410
 
411
411
  options[analyzePerformanceItemString] =
412
412
  when (tracingState) {
413
- TracingState.ENABLEDINBACKGROUNDMODE ->
413
+ TracingState.ENABLED_IN_BACKGROUND_MODE ->
414
414
  DevOptionHandler {
415
415
  UiThreadUtil.runOnUiThread {
416
416
  if (reactInstanceDevHelper is PerfMonitorDevHelper) {
@@ -427,7 +427,7 @@ public abstract class DevSupportManagerBase(
427
427
  if (reactInstanceDevHelper is PerfMonitorDevHelper)
428
428
  reactInstanceDevHelper.inspectorTarget?.resumeBackgroundTrace()
429
429
  }
430
- TracingState.ENABLEDINCDPMODE -> DevOptionHandler {}
430
+ TracingState.ENABLED_IN_CDP_MODE -> DevOptionHandler {}
431
431
  }
432
432
  }
433
433
 
@@ -17,7 +17,11 @@ internal object InspectorFlags {
17
17
  SoLoader.loadLibrary("react_devsupportjni")
18
18
  }
19
19
 
20
+ @DoNotStrip @JvmStatic external fun getScreenshotCaptureEnabled(): Boolean
21
+
20
22
  @DoNotStrip @JvmStatic external fun getFuseboxEnabled(): Boolean
21
23
 
22
24
  @DoNotStrip @JvmStatic external fun getIsProfilingBuild(): Boolean
25
+
26
+ @DoNotStrip @JvmStatic external fun getFrameRecordingEnabled(): Boolean
23
27
  }
@@ -0,0 +1,16 @@
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
+ package com.facebook.react.devsupport.inspector
9
+
10
+ internal data class FrameTimingSequence(
11
+ val id: Int,
12
+ val threadId: Int,
13
+ val beginTimestamp: Long,
14
+ val endTimestamp: Long,
15
+ val screenshot: ByteArray? = null,
16
+ )
@@ -0,0 +1,275 @@
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
+ package com.facebook.react.devsupport.inspector
9
+
10
+ import android.graphics.Bitmap
11
+ import android.os.Build
12
+ import android.os.Handler
13
+ import android.os.Looper
14
+ import android.os.Process
15
+ import android.view.FrameMetrics
16
+ import android.view.PixelCopy
17
+ import android.view.Window
18
+ import com.facebook.proguard.annotations.DoNotStripAny
19
+ import java.io.ByteArrayOutputStream
20
+ import java.util.concurrent.Executors
21
+ import java.util.concurrent.atomic.AtomicBoolean
22
+ import java.util.concurrent.atomic.AtomicReference
23
+ import kotlinx.coroutines.CoroutineDispatcher
24
+ import kotlinx.coroutines.CoroutineScope
25
+ import kotlinx.coroutines.Dispatchers
26
+ import kotlinx.coroutines.asCoroutineDispatcher
27
+ import kotlinx.coroutines.launch
28
+
29
+ @DoNotStripAny
30
+ internal class FrameTimingsObserver(
31
+ private val screenshotsEnabled: Boolean,
32
+ private val onFrameTimingSequence: (sequence: FrameTimingSequence) -> Unit,
33
+ ) {
34
+ private val isSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
35
+ private val mainHandler = Handler(Looper.getMainLooper())
36
+
37
+ // Serial dispatcher for encoding work (single background thread). We limit to 1 thread to
38
+ // minimize the performance impact of screenshot recording.
39
+ private val encodingDispatcher: CoroutineDispatcher =
40
+ Executors.newSingleThreadExecutor().asCoroutineDispatcher()
41
+
42
+ // Stores the most recently captured frame to opportunistically encode after the current frame.
43
+ // Replaced frames are emitted as timings without screenshots.
44
+ private val lastFrameBuffer = AtomicReference<FrameData?>(null)
45
+
46
+ private var frameCounter: Int = 0
47
+ private val encodingInProgress = AtomicBoolean(false)
48
+ @Volatile private var isTracing: Boolean = false
49
+ @Volatile private var currentWindow: Window? = null
50
+
51
+ private data class FrameData(
52
+ val bitmap: Bitmap,
53
+ val frameId: Int,
54
+ val threadId: Int,
55
+ val beginTimestamp: Long,
56
+ val endTimestamp: Long,
57
+ )
58
+
59
+ fun start() {
60
+ if (!isSupported) {
61
+ return
62
+ }
63
+
64
+ frameCounter = 0
65
+ encodingInProgress.set(false)
66
+ lastFrameBuffer.set(null)
67
+ isTracing = true
68
+
69
+ // Emit initial frame event
70
+ val timestamp = System.nanoTime()
71
+ emitFrameTiming(timestamp, timestamp)
72
+
73
+ currentWindow?.addOnFrameMetricsAvailableListener(frameMetricsListener, mainHandler)
74
+ }
75
+
76
+ fun stop() {
77
+ if (!isSupported) {
78
+ return
79
+ }
80
+
81
+ isTracing = false
82
+
83
+ currentWindow?.removeOnFrameMetricsAvailableListener(frameMetricsListener)
84
+ mainHandler.removeCallbacksAndMessages(null)
85
+ lastFrameBuffer.getAndSet(null)?.bitmap?.recycle()
86
+ }
87
+
88
+ fun setCurrentWindow(window: Window?) {
89
+ if (!isSupported || currentWindow === window) {
90
+ return
91
+ }
92
+
93
+ currentWindow?.removeOnFrameMetricsAvailableListener(frameMetricsListener)
94
+ currentWindow = window
95
+ if (isTracing) {
96
+ currentWindow?.addOnFrameMetricsAvailableListener(frameMetricsListener, mainHandler)
97
+ }
98
+ }
99
+
100
+ private val frameMetricsListener =
101
+ Window.OnFrameMetricsAvailableListener { _, frameMetrics, _ ->
102
+ // Guard against calls after stop()
103
+ if (!isTracing) {
104
+ return@OnFrameMetricsAvailableListener
105
+ }
106
+ val beginTimestamp = frameMetrics.getMetric(FrameMetrics.VSYNC_TIMESTAMP)
107
+ val endTimestamp = beginTimestamp + frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION)
108
+ emitFrameTiming(beginTimestamp, endTimestamp)
109
+ }
110
+
111
+ private fun emitFrameTiming(beginTimestamp: Long, endTimestamp: Long) {
112
+ val frameId = frameCounter++
113
+ val threadId = Process.myTid()
114
+
115
+ if (!screenshotsEnabled) {
116
+ // Screenshots disabled - emit without screenshot
117
+ emitFrameEvent(frameId, threadId, beginTimestamp, endTimestamp, null)
118
+ return
119
+ }
120
+
121
+ captureScreenshot(frameId, threadId, beginTimestamp, endTimestamp) { frameData ->
122
+ if (frameData != null) {
123
+ if (encodingInProgress.compareAndSet(false, true)) {
124
+ // Not encoding - encode this frame immediately
125
+ encodeFrame(frameData)
126
+ } else {
127
+ // Encoding thread busy - store current screenshot in buffer for tail-capture
128
+ val oldFrameData = lastFrameBuffer.getAndSet(frameData)
129
+ if (oldFrameData != null) {
130
+ // Skipped frame - emit event without screenshot
131
+ emitFrameEvent(
132
+ oldFrameData.frameId,
133
+ oldFrameData.threadId,
134
+ oldFrameData.beginTimestamp,
135
+ oldFrameData.endTimestamp,
136
+ null,
137
+ )
138
+ oldFrameData.bitmap.recycle()
139
+ }
140
+ }
141
+ } else {
142
+ // Failed to capture (e.g. timeout) - emit without screenshot
143
+ emitFrameEvent(frameId, threadId, beginTimestamp, endTimestamp, null)
144
+ }
145
+ }
146
+ }
147
+
148
+ private fun emitFrameEvent(
149
+ frameId: Int,
150
+ threadId: Int,
151
+ beginTimestamp: Long,
152
+ endTimestamp: Long,
153
+ screenshot: ByteArray?,
154
+ ) {
155
+ CoroutineScope(Dispatchers.Default).launch {
156
+ onFrameTimingSequence(
157
+ FrameTimingSequence(frameId, threadId, beginTimestamp, endTimestamp, screenshot)
158
+ )
159
+ }
160
+ }
161
+
162
+ private fun encodeFrame(frameData: FrameData) {
163
+ CoroutineScope(encodingDispatcher).launch {
164
+ try {
165
+ val screenshot = encodeScreenshot(frameData.bitmap)
166
+ emitFrameEvent(
167
+ frameData.frameId,
168
+ frameData.threadId,
169
+ frameData.beginTimestamp,
170
+ frameData.endTimestamp,
171
+ screenshot,
172
+ )
173
+ } finally {
174
+ frameData.bitmap.recycle()
175
+ }
176
+
177
+ // Clear encoding flag early, allowing new frames to start fresh encoding sessions
178
+ encodingInProgress.set(false)
179
+
180
+ // Opportunistically encode tail frame (if present) without blocking new frames
181
+ val tailFrame = lastFrameBuffer.getAndSet(null)
182
+ if (tailFrame != null) {
183
+ try {
184
+ val screenshot = encodeScreenshot(tailFrame.bitmap)
185
+ emitFrameEvent(
186
+ tailFrame.frameId,
187
+ tailFrame.threadId,
188
+ tailFrame.beginTimestamp,
189
+ tailFrame.endTimestamp,
190
+ screenshot,
191
+ )
192
+ } finally {
193
+ tailFrame.bitmap.recycle()
194
+ }
195
+ }
196
+ }
197
+ }
198
+
199
+ // Must be called from the main thread so that PixelCopy captures the current frame.
200
+ private fun captureScreenshot(
201
+ frameId: Int,
202
+ threadId: Int,
203
+ beginTimestamp: Long,
204
+ endTimestamp: Long,
205
+ callback: (FrameData?) -> Unit,
206
+ ) {
207
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
208
+ // PixelCopy not available
209
+ callback(null)
210
+ return
211
+ }
212
+
213
+ val window = currentWindow
214
+ if (window == null) {
215
+ // No window
216
+ callback(null)
217
+ return
218
+ }
219
+
220
+ val decorView = window.decorView
221
+ val width = decorView.width
222
+ val height = decorView.height
223
+ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
224
+
225
+ PixelCopy.request(
226
+ window,
227
+ bitmap,
228
+ { copyResult ->
229
+ if (copyResult == PixelCopy.SUCCESS) {
230
+ callback(FrameData(bitmap, frameId, threadId, beginTimestamp, endTimestamp))
231
+ } else {
232
+ bitmap.recycle()
233
+ callback(null)
234
+ }
235
+ },
236
+ mainHandler,
237
+ )
238
+ }
239
+
240
+ private fun encodeScreenshot(bitmap: Bitmap): ByteArray? {
241
+ var scaledBitmap: Bitmap? = null
242
+ return try {
243
+ val window = currentWindow ?: return null
244
+ val width = bitmap.width
245
+ val height = bitmap.height
246
+ val density = window.context.resources.displayMetrics.density
247
+ val scaledWidth = (width / density * SCREENSHOT_SCALE_FACTOR).toInt()
248
+ val scaledHeight = (height / density * SCREENSHOT_SCALE_FACTOR).toInt()
249
+ scaledBitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true)
250
+
251
+ val compressFormat =
252
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) Bitmap.CompressFormat.WEBP_LOSSY
253
+ else Bitmap.CompressFormat.JPEG
254
+
255
+ ByteArrayOutputStream(SCREENSHOT_OUTPUT_SIZE_HINT).use { outputStream ->
256
+ scaledBitmap.compress(compressFormat, SCREENSHOT_QUALITY, outputStream)
257
+ outputStream.toByteArray()
258
+ }
259
+ } catch (e: Exception) {
260
+ null
261
+ } finally {
262
+ scaledBitmap?.recycle()
263
+ }
264
+ }
265
+
266
+ companion object {
267
+ private const val SCREENSHOT_SCALE_FACTOR = 1.0f
268
+ private const val SCREENSHOT_QUALITY = 80
269
+
270
+ // Capacity hint for the ByteArrayOutputStream used during bitmap
271
+ // compression. Sized slightly above typical compressed output to minimise
272
+ // internal buffer resizing.
273
+ private const val SCREENSHOT_OUTPUT_SIZE_HINT = 65536 // 64 KB
274
+ }
275
+ }
@@ -0,0 +1,17 @@
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
+ package com.facebook.react.devsupport.inspector
9
+
10
+ import com.facebook.proguard.annotations.DoNotStripAny
11
+
12
+ @DoNotStripAny
13
+ internal enum class TracingState {
14
+ DISABLED, // There is no active trace
15
+ ENABLED_IN_BACKGROUND_MODE, // Trace is currently running in background mode
16
+ ENABLED_IN_CDP_MODE, // Trace is currently running in CDP mode
17
+ }
@@ -0,0 +1,15 @@
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
+ package com.facebook.react.devsupport.inspector
9
+
10
+ import com.facebook.proguard.annotations.DoNotStripAny
11
+
12
+ @DoNotStripAny
13
+ internal fun interface TracingStateListener {
14
+ public fun onStateChanged(state: TracingState, screenshotsEnabled: Boolean)
15
+ }
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- package com.facebook.react.devsupport.interfaces
8
+ package com.facebook.react.devsupport.inspector
9
9
 
10
10
  internal interface TracingStateProvider {
11
11
  fun getTracingState(): TracingState
@@ -7,7 +7,7 @@
7
7
 
8
8
  package com.facebook.react.devsupport.perfmonitor
9
9
 
10
- import com.facebook.react.devsupport.interfaces.TracingState
10
+ import com.facebook.react.devsupport.inspector.TracingState
11
11
 
12
12
  /**
13
13
  * [Experimental] Interface implemented by [com.facebook.react.runtime.ReactHostInspectorTarget]