react-native 0.84.0-nightly-20251204-5bb3a6d68 → 0.84.0-nightly-20251206-63b0aef13

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 (75) hide show
  1. package/Libraries/Animated/nodes/AnimatedProps.js +29 -3
  2. package/Libraries/Core/ReactNativeVersion.js +1 -1
  3. package/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm +7 -0
  4. package/Libraries/NativeAnimation/React-RCTAnimation.podspec +1 -0
  5. package/React/Base/RCTVersion.m +1 -1
  6. package/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm +7 -0
  7. package/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h +2 -0
  8. package/React/FBReactNativeSpec/FBReactNativeSpecJSI.h +18 -0
  9. package/React/Fabric/RCTSurfaceTouchHandler.mm +1 -1
  10. package/ReactAndroid/api/ReactAndroid.api +0 -2
  11. package/ReactAndroid/gradle.properties +1 -1
  12. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/FrameTimingsObserver.kt +85 -11
  13. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +7 -1
  14. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +11 -1
  15. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +3 -1
  16. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +3 -1
  17. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +12 -1
  18. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +3 -1
  19. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
  20. package/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt +7 -3
  21. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyOptimizer.java +9 -91
  22. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java +0 -2
  23. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java +4 -23
  24. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java +1 -3
  25. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +1 -10
  26. package/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewAccessibilityDelegate.kt +1 -0
  27. package/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt +42 -28
  28. package/ReactAndroid/src/main/jni/CMakeLists.txt +7 -0
  29. package/ReactAndroid/src/main/jni/react/devsupport/CMakeLists.txt +7 -0
  30. package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +15 -1
  31. package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +4 -1
  32. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  33. package/ReactCommon/jsi/jsi/jsi.cpp +2 -1
  34. package/ReactCommon/jsinspector-modern/HostAgent.cpp +2 -2
  35. package/ReactCommon/jsinspector-modern/InspectorInterfaces.cpp +10 -5
  36. package/ReactCommon/jsinspector-modern/InspectorInterfaces.h +2 -2
  37. package/ReactCommon/jsinspector-modern/TracingAgent.cpp +1 -1
  38. package/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.cpp +4 -4
  39. package/ReactCommon/jsinspector-modern/tracing/TracingCategory.h +11 -6
  40. package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +5 -1
  41. package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +6 -1
  42. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +51 -33
  43. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +4 -2
  44. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +5 -1
  45. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +10 -1
  46. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +2 -1
  47. package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +6 -1
  48. package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +3 -1
  49. package/ReactCommon/react/renderer/animated/AnimatedModule.cpp +19 -1
  50. package/ReactCommon/react/renderer/animated/AnimatedModule.h +8 -0
  51. package/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +83 -9
  52. package/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h +6 -0
  53. package/ReactCommon/react/renderer/animated/drivers/FrameAnimationDriver.cpp +1 -0
  54. package/ReactCommon/react/renderer/animated/nodes/ColorAnimatedNode.cpp +0 -1
  55. package/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.h +1 -0
  56. package/ReactCommon/react/renderer/animated/nodes/RoundAnimatedNode.cpp +1 -1
  57. package/ReactCommon/react/renderer/animated/nodes/ValueAnimatedNode.cpp +1 -1
  58. package/ReactCommon/react/renderer/animated/tests/AnimatedNodeTests.cpp +67 -0
  59. package/ReactCommon/react/renderer/animated/tests/AnimationDriverTests.cpp +59 -0
  60. package/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp +81 -0
  61. package/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h +83 -0
  62. package/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +26 -15
  63. package/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +7 -4
  64. package/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp +69 -0
  65. package/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.h +32 -0
  66. package/ReactCommon/react/renderer/uimanager/UIManager.cpp +6 -0
  67. package/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h +1 -0
  68. package/package.json +9 -9
  69. package/scripts/cocoapods/utils.rb +2 -0
  70. package/sdks/hermes-engine/version.properties +1 -1
  71. package/src/private/animated/NativeAnimatedHelper.js +29 -1
  72. package/src/private/featureflags/ReactNativeFeatureFlags.js +6 -1
  73. package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +2 -1
  74. package/src/private/specs_DEPRECATED/modules/NativeAnimatedModule.js +4 -0
  75. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeKind.kt +0 -32
@@ -14,7 +14,9 @@ import type {AnimatedNodeConfig} from './AnimatedNode';
14
14
  import type {AnimatedStyleAllowlist} from './AnimatedStyle';
15
15
 
16
16
  import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
17
+ import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
17
18
  import {findNodeHandle} from '../../ReactNative/RendererProxy';
19
+ import {getNodeFromPublicInstance} from '../../ReactPrivate/ReactNativePrivateInterface';
18
20
  import flattenStyle from '../../StyleSheet/flattenStyle';
19
21
  import {AnimatedEvent} from '../AnimatedEvent';
20
22
  import AnimatedNode from './AnimatedNode';
@@ -251,7 +253,9 @@ export default class AnimatedProps extends AnimatedNode {
251
253
  super.__setPlatformConfig(platformConfig);
252
254
 
253
255
  if (this._target != null) {
254
- this.#connectAnimatedView(this._target);
256
+ const target = this._target;
257
+ this.#connectAnimatedView(target);
258
+ this.#connectShadowNode(target);
255
259
  }
256
260
  }
257
261
  }
@@ -260,9 +264,10 @@ export default class AnimatedProps extends AnimatedNode {
260
264
  if (this._target?.instance === instance) {
261
265
  return;
262
266
  }
263
- this._target = {instance, connectedViewTag: null};
267
+ const target = (this._target = {instance, connectedViewTag: null});
264
268
  if (this.__isNative) {
265
- this.#connectAnimatedView(this._target);
269
+ this.#connectAnimatedView(target);
270
+ this.#connectShadowNode(target);
266
271
  }
267
272
  }
268
273
 
@@ -283,6 +288,27 @@ export default class AnimatedProps extends AnimatedNode {
283
288
  target.connectedViewTag = viewTag;
284
289
  }
285
290
 
291
+ #connectShadowNode(target: TargetView): void {
292
+ if (
293
+ !ReactNativeFeatureFlags.cxxNativeAnimatedEnabled() ||
294
+ //eslint-disable-next-line
295
+ !ReactNativeFeatureFlags.useSharedAnimatedBackend()
296
+ ) {
297
+ return;
298
+ }
299
+
300
+ invariant(this.__isNative, 'Expected node to be marked as "native"');
301
+ // $FlowExpectedError[incompatible-type] - target.instance may be an HTMLElement but we need ReactNativeElement for Fabric
302
+ const shadowNode = getNodeFromPublicInstance(target.instance);
303
+ if (shadowNode == null) {
304
+ return;
305
+ }
306
+ NativeAnimatedHelper.API.connectAnimatedNodeToShadowNodeFamily(
307
+ this.__getNativeTag(),
308
+ shadowNode,
309
+ );
310
+ }
311
+
286
312
  #disconnectAnimatedView(target: TargetView): void {
287
313
  invariant(this.__isNative, 'Expected node to be marked as "native"');
288
314
  const viewTag = target.connectedViewTag;
@@ -29,7 +29,7 @@ export default class ReactNativeVersion {
29
29
  static major: number = 0;
30
30
  static minor: number = 84;
31
31
  static patch: number = 0;
32
- static prerelease: string | null = 'nightly-20251204-5bb3a6d68';
32
+ static prerelease: string | null = 'nightly-20251206-63b0aef13';
33
33
 
34
34
  static getVersionString(): string {
35
35
  return `${this.major}.${this.minor}.${this.patch}${this.prerelease != null ? `-${this.prerelease}` : ''}`;
@@ -10,6 +10,7 @@
10
10
  #import <React/RCTInitializing.h>
11
11
  #import <React/RCTNativeAnimatedNodesManager.h>
12
12
  #import <React/RCTNativeAnimatedTurboModule.h>
13
+ #import <react/debug/react_native_assert.h>
13
14
  #import <react/featureflags/ReactNativeFeatureFlags.h>
14
15
 
15
16
  #import "RCTAnimationPlugins.h"
@@ -165,6 +166,12 @@ RCT_EXPORT_METHOD(connectAnimatedNodeToView : (double)nodeTag viewTag : (double)
165
166
  }];
166
167
  }
167
168
 
169
+ RCT_EXPORT_METHOD(connectAnimatedNodeToShadowNodeFamily : (double)nodeTag shadowNode : (NSDictionary *)shadowNode)
170
+ {
171
+ // This method should only be called when using CxxNativeAnimated
172
+ react_native_assert(false);
173
+ }
174
+
168
175
  RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView : (double)nodeTag viewTag : (double)viewTag)
169
176
  {
170
177
  [self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
@@ -48,6 +48,7 @@ Pod::Spec.new do |s|
48
48
  add_dependency(s, "ReactCommon", :subspec => "turbomodule/core", :additional_framework_paths => ["react/nativemodule/core"])
49
49
  add_dependency(s, "React-NativeModulesApple")
50
50
  add_dependency(s, "React-featureflags")
51
+ add_dependency(s, "React-debug")
51
52
 
52
53
  add_rn_third_party_dependencies(s)
53
54
  add_rncore_dependency(s)
@@ -24,7 +24,7 @@ NSDictionary* RCTGetReactNativeVersion(void)
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(84),
26
26
  RCTVersionPatch: @(0),
27
- RCTVersionPrerelease: @"nightly-20251204-5bb3a6d68",
27
+ RCTVersionPrerelease: @"nightly-20251206-63b0aef13",
28
28
  };
29
29
  });
30
30
  return __rnVersion;
@@ -365,6 +365,10 @@ namespace facebook::react {
365
365
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "connectAnimatedNodeToView", @selector(connectAnimatedNodeToView:viewTag:), args, count);
366
366
  }
367
367
 
368
+ static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_connectAnimatedNodeToShadowNodeFamily(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
369
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "connectAnimatedNodeToShadowNodeFamily", @selector(connectAnimatedNodeToShadowNodeFamily:shadowNode:), args, count);
370
+ }
371
+
368
372
  static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_disconnectAnimatedNodeFromView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
369
373
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "disconnectAnimatedNodeFromView", @selector(disconnectAnimatedNodeFromView:viewTag:), args, count);
370
374
  }
@@ -448,6 +452,9 @@ namespace facebook::react {
448
452
  methodMap_["connectAnimatedNodeToView"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_connectAnimatedNodeToView};
449
453
 
450
454
 
455
+ methodMap_["connectAnimatedNodeToShadowNodeFamily"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_connectAnimatedNodeToShadowNodeFamily};
456
+
457
+
451
458
  methodMap_["disconnectAnimatedNodeFromView"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_disconnectAnimatedNodeFromView};
452
459
 
453
460
 
@@ -323,6 +323,8 @@ saveValueCallback:(RCTResponseSenderBlock)saveValueCallback;
323
323
  - (void)extractAnimatedNodeOffset:(double)nodeTag;
324
324
  - (void)connectAnimatedNodeToView:(double)nodeTag
325
325
  viewTag:(double)viewTag;
326
+ - (void)connectAnimatedNodeToShadowNodeFamily:(double)nodeTag
327
+ shadowNode:(NSDictionary *)shadowNode;
326
328
  - (void)disconnectAnimatedNodeFromView:(double)nodeTag
327
329
  viewTag:(double)viewTag;
328
330
  - (void)restoreDefaultValues:(double)nodeTag;
@@ -281,6 +281,7 @@ protected:
281
281
  methodMap_["enableVirtualViewWindowFocusDetection"] = MethodMetadata {.argCount = 0, .invoker = __enableVirtualViewWindowFocusDetection};
282
282
  methodMap_["enableWebPerformanceAPIsByDefault"] = MethodMetadata {.argCount = 0, .invoker = __enableWebPerformanceAPIsByDefault};
283
283
  methodMap_["fixMappingOfEventPrioritiesBetweenFabricAndReact"] = MethodMetadata {.argCount = 0, .invoker = __fixMappingOfEventPrioritiesBetweenFabricAndReact};
284
+ methodMap_["fixTextClippingAndroid15useBoundsForWidth"] = MethodMetadata {.argCount = 0, .invoker = __fixTextClippingAndroid15useBoundsForWidth};
284
285
  methodMap_["fuseboxAssertSingleHostState"] = MethodMetadata {.argCount = 0, .invoker = __fuseboxAssertSingleHostState};
285
286
  methodMap_["fuseboxEnabledRelease"] = MethodMetadata {.argCount = 0, .invoker = __fuseboxEnabledRelease};
286
287
  methodMap_["fuseboxNetworkInspectionEnabled"] = MethodMetadata {.argCount = 0, .invoker = __fuseboxNetworkInspectionEnabled};
@@ -722,6 +723,13 @@ private:
722
723
  return bridging::callFromJs<bool>(rt, &T::fixMappingOfEventPrioritiesBetweenFabricAndReact, static_cast<NativeReactNativeFeatureFlagsCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
723
724
  }
724
725
 
726
+ static jsi::Value __fixTextClippingAndroid15useBoundsForWidth(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
727
+ static_assert(
728
+ bridging::getParameterCount(&T::fixTextClippingAndroid15useBoundsForWidth) == 1,
729
+ "Expected fixTextClippingAndroid15useBoundsForWidth(...) to have 1 parameters");
730
+ return bridging::callFromJs<bool>(rt, &T::fixTextClippingAndroid15useBoundsForWidth, static_cast<NativeReactNativeFeatureFlagsCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
731
+ }
732
+
725
733
  static jsi::Value __fuseboxAssertSingleHostState(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
726
734
  static_assert(
727
735
  bridging::getParameterCount(&T::fuseboxAssertSingleHostState) == 1,
@@ -1482,6 +1490,7 @@ protected:
1482
1490
  methodMap_["flattenAnimatedNodeOffset"] = MethodMetadata {.argCount = 1, .invoker = __flattenAnimatedNodeOffset};
1483
1491
  methodMap_["extractAnimatedNodeOffset"] = MethodMetadata {.argCount = 1, .invoker = __extractAnimatedNodeOffset};
1484
1492
  methodMap_["connectAnimatedNodeToView"] = MethodMetadata {.argCount = 2, .invoker = __connectAnimatedNodeToView};
1493
+ methodMap_["connectAnimatedNodeToShadowNodeFamily"] = MethodMetadata {.argCount = 2, .invoker = __connectAnimatedNodeToShadowNodeFamily};
1485
1494
  methodMap_["disconnectAnimatedNodeFromView"] = MethodMetadata {.argCount = 2, .invoker = __disconnectAnimatedNodeFromView};
1486
1495
  methodMap_["restoreDefaultValues"] = MethodMetadata {.argCount = 1, .invoker = __restoreDefaultValues};
1487
1496
  methodMap_["dropAnimatedNode"] = MethodMetadata {.argCount = 1, .invoker = __dropAnimatedNode};
@@ -1630,6 +1639,15 @@ private:
1630
1639
  count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asNumber());return jsi::Value::undefined();
1631
1640
  }
1632
1641
 
1642
+ static jsi::Value __connectAnimatedNodeToShadowNodeFamily(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
1643
+ static_assert(
1644
+ bridging::getParameterCount(&T::connectAnimatedNodeToShadowNodeFamily) == 3,
1645
+ "Expected connectAnimatedNodeToShadowNodeFamily(...) to have 3 parameters");
1646
+ bridging::callFromJs<void>(rt, &T::connectAnimatedNodeToShadowNodeFamily, static_cast<NativeAnimatedModuleCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
1647
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber(),
1648
+ count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asObject(rt));return jsi::Value::undefined();
1649
+ }
1650
+
1633
1651
  static jsi::Value __disconnectAnimatedNodeFromView(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
1634
1652
  static_assert(
1635
1653
  bridging::getParameterCount(&T::disconnectAnimatedNodeFromView) == 3,
@@ -399,7 +399,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)act
399
399
  shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
400
400
  {
401
401
  BOOL canBePrevented = [self canBePreventedByGestureRecognizer:otherGestureRecognizer];
402
- if (canBePrevented) {
402
+ if (canBePrevented && otherGestureRecognizer.cancelsTouchesInView) {
403
403
  [self _cancelTouches];
404
404
  }
405
405
  return NO;
@@ -3842,7 +3842,6 @@ public abstract interface class com/facebook/react/uimanager/ReactShadowNode {
3842
3842
  public abstract fun getLayoutX ()F
3843
3843
  public abstract fun getLayoutY ()F
3844
3844
  public abstract fun getNativeChildCount ()I
3845
- public abstract fun getNativeKind ()Lcom/facebook/react/uimanager/NativeKind;
3846
3845
  public abstract fun getNativeOffsetForChild (Lcom/facebook/react/uimanager/ReactShadowNode;)I
3847
3846
  public abstract fun getNativeParent ()Lcom/facebook/react/uimanager/ReactShadowNode;
3848
3847
  public abstract fun getPadding (I)F
@@ -3971,7 +3970,6 @@ public class com/facebook/react/uimanager/ReactShadowNodeImpl : com/facebook/rea
3971
3970
  public final fun getLayoutX ()F
3972
3971
  public final fun getLayoutY ()F
3973
3972
  public final fun getNativeChildCount ()I
3974
- public fun getNativeKind ()Lcom/facebook/react/uimanager/NativeKind;
3975
3973
  public synthetic fun getNativeOffsetForChild (Lcom/facebook/react/uimanager/ReactShadowNode;)I
3976
3974
  public final fun getNativeOffsetForChild (Lcom/facebook/react/uimanager/ReactShadowNodeImpl;)I
3977
3975
  public synthetic fun getNativeParent ()Lcom/facebook/react/uimanager/ReactShadowNode;
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.84.0-nightly-20251204-5bb3a6d68
1
+ VERSION_NAME=0.84.0-nightly-20251206-63b0aef13
2
2
  react.internal.publishingGroup=com.facebook.react
3
3
  react.internal.hermesPublishingGroup=com.facebook.hermes
4
4
 
@@ -7,25 +7,36 @@
7
7
 
8
8
  package com.facebook.react.devsupport.inspector
9
9
 
10
+ import android.graphics.Bitmap
10
11
  import android.os.Build
11
12
  import android.os.Handler
12
13
  import android.os.Looper
13
14
  import android.os.Process
15
+ import android.util.Base64
14
16
  import android.view.FrameMetrics
17
+ import android.view.PixelCopy
15
18
  import android.view.Window
16
19
  import com.facebook.proguard.annotations.DoNotStripAny
20
+ import java.io.ByteArrayOutputStream
21
+ import kotlin.coroutines.resume
22
+ import kotlin.coroutines.suspendCoroutine
23
+ import kotlinx.coroutines.CoroutineScope
24
+ import kotlinx.coroutines.Dispatchers
25
+ import kotlinx.coroutines.launch
17
26
 
18
27
  @DoNotStripAny
19
28
  internal class FrameTimingsObserver(
20
29
  private val window: Window,
21
- onFrameTimingSequence: (sequence: FrameTimingSequence) -> Unit,
30
+ private val screenshotsEnabled: Boolean,
31
+ private val onFrameTimingSequence: (sequence: FrameTimingSequence) -> Unit,
22
32
  ) {
23
33
  private val handler = Handler(Looper.getMainLooper())
24
34
  private var frameCounter: Int = 0
35
+ private var bitmapBuffer: Bitmap? = null
25
36
 
26
37
  private val frameMetricsListener =
27
38
  Window.OnFrameMetricsAvailableListener { _, frameMetrics, _dropCount ->
28
- val beginDrawingTimestamp = frameMetrics.getMetric(FrameMetrics.INTENDED_VSYNC_TIMESTAMP)
39
+ val beginDrawingTimestamp = frameMetrics.getMetric(FrameMetrics.VSYNC_TIMESTAMP)
29
40
  val commitTimestamp =
30
41
  beginDrawingTimestamp + frameMetrics.getMetric(FrameMetrics.INPUT_HANDLING_DURATION)
31
42
  +frameMetrics.getMetric(FrameMetrics.ANIMATION_DURATION)
@@ -35,17 +46,77 @@ internal class FrameTimingsObserver(
35
46
  val endDrawingTimestamp =
36
47
  beginDrawingTimestamp + frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION)
37
48
 
38
- onFrameTimingSequence(
39
- FrameTimingSequence(
40
- frameCounter++,
41
- Process.myTid(),
42
- beginDrawingTimestamp,
43
- commitTimestamp,
44
- endDrawingTimestamp,
45
- )
46
- )
49
+ val frameId = frameCounter++
50
+ val threadId = Process.myTid()
51
+
52
+ CoroutineScope(Dispatchers.Default).launch {
53
+ val screenshot = if (screenshotsEnabled) captureScreenshot() else null
54
+
55
+ onFrameTimingSequence(
56
+ FrameTimingSequence(
57
+ frameId,
58
+ threadId,
59
+ beginDrawingTimestamp,
60
+ commitTimestamp,
61
+ endDrawingTimestamp,
62
+ screenshot,
63
+ )
64
+ )
65
+ }
47
66
  }
48
67
 
68
+ private suspend fun captureScreenshot(): String? = suspendCoroutine { continuation ->
69
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
70
+ continuation.resume(null)
71
+ return@suspendCoroutine
72
+ }
73
+
74
+ val decorView = window.decorView
75
+ val width = decorView.width
76
+ val height = decorView.height
77
+
78
+ // Reuse bitmap if dimensions haven't changed
79
+ val bitmap =
80
+ bitmapBuffer?.let {
81
+ if (it.width == width && it.height == height) {
82
+ it
83
+ } else {
84
+ null
85
+ }
86
+ } ?: Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { bitmapBuffer = it }
87
+
88
+ PixelCopy.request(
89
+ window,
90
+ bitmap,
91
+ { copyResult ->
92
+ if (copyResult == PixelCopy.SUCCESS) {
93
+ CoroutineScope(Dispatchers.Default).launch {
94
+ try {
95
+ val scaleFactor = 0.5f
96
+ val scaledWidth = (width * scaleFactor).toInt()
97
+ val scaledHeight = (height * scaleFactor).toInt()
98
+ val scaledBitmap =
99
+ Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true)
100
+
101
+ val outputStream = ByteArrayOutputStream()
102
+ scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream)
103
+ val jpegBytes = outputStream.toByteArray()
104
+ val jpegBase64 = Base64.encodeToString(jpegBytes, Base64.NO_WRAP)
105
+ continuation.resume(jpegBase64)
106
+
107
+ scaledBitmap.recycle()
108
+ } catch (e: Exception) {
109
+ continuation.resume(null)
110
+ }
111
+ }
112
+ } else {
113
+ continuation.resume(null)
114
+ }
115
+ },
116
+ handler,
117
+ )
118
+ }
119
+
49
120
  fun start() {
50
121
  frameCounter = 0
51
122
  if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
@@ -62,5 +133,8 @@ internal class FrameTimingsObserver(
62
133
 
63
134
  window.removeOnFrameMetricsAvailableListener(frameMetricsListener)
64
135
  handler.removeCallbacksAndMessages(null)
136
+
137
+ bitmapBuffer?.recycle()
138
+ bitmapBuffer = null
65
139
  }
66
140
  }
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @generated SignedSource<<5685f79f9b08afe94f5b3e8ec8ff6748>>
7
+ * @generated SignedSource<<adc6b24ae04487d2b82a2bac35f027a7>>
8
8
  */
9
9
 
10
10
  /**
@@ -372,6 +372,12 @@ public object ReactNativeFeatureFlags {
372
372
  @JvmStatic
373
373
  public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = accessor.fixMappingOfEventPrioritiesBetweenFabricAndReact()
374
374
 
375
+ /**
376
+ * Fix text clipping starting in Android 15 due to usage of useBoundsForWidth
377
+ */
378
+ @JvmStatic
379
+ public fun fixTextClippingAndroid15useBoundsForWidth(): Boolean = accessor.fixTextClippingAndroid15useBoundsForWidth()
380
+
375
381
  /**
376
382
  * Enable system assertion validating that Fusebox is configured with a single host. When set, the CDP backend will dynamically disable features (Perf and Network) in the event that multiple hosts are registered (undefined behaviour), and broadcast this over `ReactNativeApplication.systemStateChanged`.
377
383
  */
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @generated SignedSource<<6645323047d21e58469de0d385954eab>>
7
+ * @generated SignedSource<<3ebb3c265193b4f61e5bf43082c5be54>>
8
8
  */
9
9
 
10
10
  /**
@@ -77,6 +77,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
77
77
  private var enableVirtualViewWindowFocusDetectionCache: Boolean? = null
78
78
  private var enableWebPerformanceAPIsByDefaultCache: Boolean? = null
79
79
  private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
80
+ private var fixTextClippingAndroid15useBoundsForWidthCache: Boolean? = null
80
81
  private var fuseboxAssertSingleHostStateCache: Boolean? = null
81
82
  private var fuseboxEnabledReleaseCache: Boolean? = null
82
83
  private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
@@ -623,6 +624,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
623
624
  return cached
624
625
  }
625
626
 
627
+ override fun fixTextClippingAndroid15useBoundsForWidth(): Boolean {
628
+ var cached = fixTextClippingAndroid15useBoundsForWidthCache
629
+ if (cached == null) {
630
+ cached = ReactNativeFeatureFlagsCxxInterop.fixTextClippingAndroid15useBoundsForWidth()
631
+ fixTextClippingAndroid15useBoundsForWidthCache = cached
632
+ }
633
+ return cached
634
+ }
635
+
626
636
  override fun fuseboxAssertSingleHostState(): Boolean {
627
637
  var cached = fuseboxAssertSingleHostStateCache
628
638
  if (cached == null) {
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @generated SignedSource<<3da42d9ffdbba52e9e1d11a4e4dac8c4>>
7
+ * @generated SignedSource<<eb11cf643c4e94eddc5d7985a8a7a03a>>
8
8
  */
9
9
 
10
10
  /**
@@ -142,6 +142,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
142
142
 
143
143
  @DoNotStrip @JvmStatic public external fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean
144
144
 
145
+ @DoNotStrip @JvmStatic public external fun fixTextClippingAndroid15useBoundsForWidth(): Boolean
146
+
145
147
  @DoNotStrip @JvmStatic public external fun fuseboxAssertSingleHostState(): Boolean
146
148
 
147
149
  @DoNotStrip @JvmStatic public external fun fuseboxEnabledRelease(): Boolean
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @generated SignedSource<<46e1f708d91d5ec45c866b8192192e72>>
7
+ * @generated SignedSource<<58cb08dbd188ec64d3738f548567bcde>>
8
8
  */
9
9
 
10
10
  /**
@@ -137,6 +137,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
137
137
 
138
138
  override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = false
139
139
 
140
+ override fun fixTextClippingAndroid15useBoundsForWidth(): Boolean = false
141
+
140
142
  override fun fuseboxAssertSingleHostState(): Boolean = true
141
143
 
142
144
  override fun fuseboxEnabledRelease(): Boolean = false
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @generated SignedSource<<c19ea5e9ad6435b1f8587287cacf020b>>
7
+ * @generated SignedSource<<f9ee0d1f23024b026d065ffc3d48cfcf>>
8
8
  */
9
9
 
10
10
  /**
@@ -81,6 +81,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
81
81
  private var enableVirtualViewWindowFocusDetectionCache: Boolean? = null
82
82
  private var enableWebPerformanceAPIsByDefaultCache: Boolean? = null
83
83
  private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
84
+ private var fixTextClippingAndroid15useBoundsForWidthCache: Boolean? = null
84
85
  private var fuseboxAssertSingleHostStateCache: Boolean? = null
85
86
  private var fuseboxEnabledReleaseCache: Boolean? = null
86
87
  private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
@@ -684,6 +685,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
684
685
  return cached
685
686
  }
686
687
 
688
+ override fun fixTextClippingAndroid15useBoundsForWidth(): Boolean {
689
+ var cached = fixTextClippingAndroid15useBoundsForWidthCache
690
+ if (cached == null) {
691
+ cached = currentProvider.fixTextClippingAndroid15useBoundsForWidth()
692
+ accessedFeatureFlags.add("fixTextClippingAndroid15useBoundsForWidth")
693
+ fixTextClippingAndroid15useBoundsForWidthCache = cached
694
+ }
695
+ return cached
696
+ }
697
+
687
698
  override fun fuseboxAssertSingleHostState(): Boolean {
688
699
  var cached = fuseboxAssertSingleHostStateCache
689
700
  if (cached == null) {
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @generated SignedSource<<17400be6ee20befd9eafb094455dfb63>>
7
+ * @generated SignedSource<<09cef8cedd515f477a32c2ed77d86fc4>>
8
8
  */
9
9
 
10
10
  /**
@@ -137,6 +137,8 @@ public interface ReactNativeFeatureFlagsProvider {
137
137
 
138
138
  @DoNotStrip public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean
139
139
 
140
+ @DoNotStrip public fun fixTextClippingAndroid15useBoundsForWidth(): Boolean
141
+
140
142
  @DoNotStrip public fun fuseboxAssertSingleHostState(): Boolean
141
143
 
142
144
  @DoNotStrip public fun fuseboxEnabledRelease(): Boolean
@@ -15,6 +15,6 @@ public object ReactNativeVersion {
15
15
  "major" to 0,
16
16
  "minor" to 84,
17
17
  "patch" to 0,
18
- "prerelease" to "nightly-20251204-5bb3a6d68"
18
+ "prerelease" to "nightly-20251206-63b0aef13"
19
19
  )
20
20
  }
@@ -1565,9 +1565,13 @@ public class ReactHostImpl(
1565
1565
  TracingState.ENABLED_IN_CDP_MODE -> {
1566
1566
  currentActivity?.window?.let { window ->
1567
1567
  val observer =
1568
- FrameTimingsObserver(window) { frameTimingsSequence ->
1569
- inspectorTarget.recordFrameTimings(frameTimingsSequence)
1570
- }
1568
+ FrameTimingsObserver(
1569
+ window,
1570
+ _screenshotsEnabled,
1571
+ { frameTimingsSequence ->
1572
+ inspectorTarget.recordFrameTimings(frameTimingsSequence)
1573
+ },
1574
+ )
1571
1575
  observer.start()
1572
1576
  frameTimingsObserver = observer
1573
1577
  }