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
@@ -12,7 +12,9 @@ import com.facebook.proguard.annotations.DoNotStripAny
12
12
  import com.facebook.react.bridge.UiThreadUtil
13
13
  import com.facebook.react.common.annotations.FrameworkAPI
14
14
  import com.facebook.react.common.annotations.UnstableReactNativeAPI
15
- import com.facebook.react.devsupport.interfaces.TracingState
15
+ import com.facebook.react.devsupport.inspector.FrameTimingSequence
16
+ import com.facebook.react.devsupport.inspector.TracingState
17
+ import com.facebook.react.devsupport.inspector.TracingStateListener
16
18
  import com.facebook.react.devsupport.perfmonitor.PerfMonitorInspectorTarget
17
19
  import com.facebook.react.devsupport.perfmonitor.PerfMonitorUpdateListener
18
20
  import com.facebook.soloader.SoLoader
@@ -41,11 +43,13 @@ internal class ReactHostInspectorTarget(reactHostImpl: ReactHostImpl) :
41
43
 
42
44
  external fun stopAndDiscardBackgroundTrace()
43
45
 
44
- external fun tracingStateAsInt(): Int
46
+ external override fun getTracingState(): TracingState
45
47
 
46
- override fun getTracingState(): TracingState {
47
- return TracingState.entries[tracingStateAsInt()]
48
- }
48
+ external fun registerTracingStateListener(listener: TracingStateListener): Long
49
+
50
+ external fun unregisterTracingStateListener(subscriptionId: Long)
51
+
52
+ external fun recordFrameTimings(frameTimingSequence: FrameTimingSequence)
49
53
 
50
54
  override fun addPerfMonitorListener(listener: PerfMonitorUpdateListener) {
51
55
  perfMonitorListeners.add(listener)
@@ -63,7 +67,7 @@ internal class ReactHostInspectorTarget(reactHostImpl: ReactHostImpl) :
63
67
  override fun resumeBackgroundTrace() {
64
68
  startBackgroundTrace()
65
69
  perfMonitorListeners.forEach { listener ->
66
- listener.onRecordingStateChanged(TracingState.ENABLEDINBACKGROUNDMODE)
70
+ listener.onRecordingStateChanged(TracingState.ENABLED_IN_BACKGROUND_MODE)
67
71
  }
68
72
  }
69
73
 
@@ -7,12 +7,32 @@
7
7
 
8
8
  package com.facebook.react.views.common
9
9
 
10
+ import android.app.UiModeManager
10
11
  import android.content.Context
11
12
  import android.content.res.Configuration
12
13
 
13
14
  /** Utility object providing static methods for working with UI mode properties from Context. */
14
15
  internal object UiModeUtils {
15
16
 
17
+ private var isTVDeviceCached: Boolean? = null
18
+
19
+ /**
20
+ * Determines whether the device is a TV (Android TV / Fire TV).
21
+ * The result is cached after the first call.
22
+ *
23
+ * @param context The context to check the UI mode from
24
+ * @return true if the device is running in television mode, false otherwise
25
+ */
26
+ @JvmStatic
27
+ fun isTVDevice(context: Context): Boolean {
28
+ return isTVDeviceCached ?: run {
29
+ val uiModeManager = context.getSystemService(Context.UI_MODE_SERVICE) as? UiModeManager
30
+ val result = uiModeManager?.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION
31
+ isTVDeviceCached = result
32
+ result
33
+ }
34
+ }
35
+
16
36
  /**
17
37
  * Determines whether the current UI mode is dark mode
18
38
  *
@@ -29,6 +29,7 @@ import android.view.KeyEvent;
29
29
  import android.view.MotionEvent;
30
30
  import android.view.View;
31
31
  import android.view.ViewGroup;
32
+ import android.view.ViewParent;
32
33
  import android.view.accessibility.AccessibilityNodeInfo;
33
34
  import android.widget.HorizontalScrollView;
34
35
  import android.widget.OverScroller;
@@ -38,6 +39,7 @@ import androidx.core.view.ViewCompat.FocusDirection;
38
39
  import com.facebook.common.logging.FLog;
39
40
  import com.facebook.infer.annotation.Assertions;
40
41
  import com.facebook.infer.annotation.Nullsafe;
42
+ import com.facebook.react.views.common.UiModeUtils;
41
43
  import com.facebook.react.R;
42
44
  import com.facebook.react.common.ReactConstants;
43
45
  import com.facebook.react.common.build.ReactBuildConfig;
@@ -136,6 +138,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
136
138
  private int mFadingEdgeLengthStart = 0;
137
139
  private int mFadingEdgeLengthEnd = 0;
138
140
  private int mSnapToItemPadding;
141
+ private boolean mScrollAnimationEnabled = true;
142
+ private boolean mBlockScrollDelta = false;
139
143
 
140
144
  public ReactHorizontalScrollView(Context context) {
141
145
  this(context, null);
@@ -247,9 +251,23 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
247
251
  return mScrollEnabled && super.canScrollHorizontally(direction);
248
252
  }
249
253
 
254
+ @Override
255
+ public void computeScroll() {
256
+ if (UiModeUtils.isTVDevice(getContext())
257
+ && !mScrollAnimationEnabled
258
+ && mScroller != null
259
+ && !mScroller.isFinished()) {
260
+ // Jump instantly to the target position instead of animating.
261
+ scrollTo(mScroller.getFinalX(), mScroller.getFinalY());
262
+ mScroller.forceFinished(true);
263
+ return;
264
+ }
265
+ super.computeScroll();
266
+ }
267
+
250
268
  @Override
251
269
  protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
252
- if (!mScrollEnabled) {
270
+ if (!mScrollEnabled || mBlockScrollDelta) {
253
271
  return 0;
254
272
  }
255
273
  return super.computeScrollDeltaToGetChildRectOnScreen(rect);
@@ -388,6 +406,10 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
388
406
  mSnapToItemPadding = snapToItemPadding;
389
407
  }
390
408
 
409
+ public void setScrollAnimationEnabled(boolean scrollAnimationEnabled) {
410
+ mScrollAnimationEnabled = scrollAnimationEnabled;
411
+ }
412
+
391
413
  @Override
392
414
  protected float getLeftFadingEdgeStrength() {
393
415
  float max = Math.max(mFadingEdgeLengthStart, mFadingEdgeLengthEnd);
@@ -603,7 +625,11 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
603
625
  * unblocks such customization.
604
626
  */
605
627
  protected void requestChildFocusWithoutScroll(View child, View focused) {
628
+ // Temporarily block HorizontalScrollView's internal scrollToChild from running
629
+ // during super.requestChildFocus — we've already handled scrolling in requestChildFocus.
630
+ mBlockScrollDelta = true;
606
631
  super.requestChildFocus(child, focused);
632
+ mBlockScrollDelta = false;
607
633
  }
608
634
 
609
635
  @Override
@@ -654,6 +680,18 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
654
680
  return scrollDelta != 0 && Math.abs(scrollDelta) < (mTempRect.width() / 2);
655
681
  }
656
682
 
683
+ /** Returns whether the given view is a descendant of the given ancestor */
684
+ private static boolean isDescendantOf(View view, View ancestor) {
685
+ ViewParent parent = view.getParent();
686
+ while (parent != null) {
687
+ if (parent == ancestor) {
688
+ return true;
689
+ }
690
+ parent = parent.getParent();
691
+ }
692
+ return false;
693
+ }
694
+
657
695
  private void scrollToChild(View child) {
658
696
  int scrollDelta = getScrollDelta(child);
659
697
 
@@ -796,21 +834,41 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
796
834
  public boolean arrowScroll(int direction) {
797
835
  boolean handled = false;
798
836
 
799
- if (mPagingEnabled) {
837
+ if (mSnapToAlignment == SNAP_ALIGNMENT_ITEM) {
838
+ // When snapToAlignment is "item", find the next focusable and request focus directly.
839
+ // This avoids super.arrowScroll() which starts its own scroll animation that conflicts
840
+ // with tryScrollSnapToChild's snap scrolling.
841
+ // requestChildFocus → tryScrollSnapToChild handles scrolling.
842
+ View currentFocused = findFocus();
843
+ View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
844
+ if (nextFocused != null && nextFocused != currentFocused && nextFocused != this) {
845
+ nextFocused.requestFocus(direction);
846
+ handled = true;
847
+ }
848
+ } else if (mPagingEnabled) {
800
849
  mPagedArrowScrolling = true;
801
850
 
802
851
  if (getChildCount() > 0) {
803
852
  View currentFocused = findFocus();
804
853
  View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
805
854
  View rootChild = getContentView();
806
- if (rootChild != null && nextFocused != null && nextFocused.getParent() == rootChild) {
807
- if (!isScrolledInView(nextFocused) && !isMostlyScrolledInView(nextFocused)) {
808
- smoothScrollToNextPage(direction);
855
+ if (rootChild != null && nextFocused != null && isDescendantOf(nextFocused, rootChild)) {
856
+ if (mSnapToAlignment == SNAP_ALIGNMENT_ITEM) {
857
+ // When snapToAlignment is "item", don't use smoothScrollToNextPage (which scrolls
858
+ // by full page width and ignores snapToItemPadding). Instead just request focus —
859
+ // requestChildFocus → tryScrollSnapToChild handles scrolling with correct padding.
860
+ nextFocused.requestFocus();
861
+ } else {
862
+ if (!isScrolledInView(nextFocused) && !isMostlyScrolledInView(nextFocused)) {
863
+ smoothScrollToNextPage(direction);
864
+ }
865
+ nextFocused.requestFocus();
809
866
  }
810
- nextFocused.requestFocus();
811
867
  handled = true;
812
868
  } else {
813
- smoothScrollToNextPage(direction);
869
+ if (mSnapToAlignment != SNAP_ALIGNMENT_ITEM) {
870
+ smoothScrollToNextPage(direction);
871
+ }
814
872
  handled = true;
815
873
  }
816
874
  }
@@ -1595,7 +1653,11 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
1595
1653
  * scroll view and state. Calling raw `smoothScrollTo` doesn't update state.
1596
1654
  */
1597
1655
  public void reactSmoothScrollTo(int x, int y) {
1598
- ReactScrollViewHelper.smoothScrollTo(this, x, y);
1656
+ if (mScrollAnimationEnabled || !UiModeUtils.isTVDevice(getContext())) {
1657
+ ReactScrollViewHelper.smoothScrollTo(this, x, y);
1658
+ } else {
1659
+ scrollTo(x, y);
1660
+ }
1599
1661
  setPendingContentOffsets(x, y);
1600
1662
  }
1601
1663
 
@@ -164,6 +164,11 @@ constructor(private val fpsListener: FpsListener? = null) :
164
164
  view.setSnapToItemPadding(px)
165
165
  }
166
166
 
167
+ @ReactProp(name = "scrollAnimationEnabled", defaultBoolean = true)
168
+ public fun setScrollAnimationEnabled(view: ReactHorizontalScrollView, value: Boolean) {
169
+ view.setScrollAnimationEnabled(value)
170
+ }
171
+
167
172
  @ReactProp(name = ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS)
168
173
  public fun setRemoveClippedSubviews(
169
174
  view: ReactHorizontalScrollView,
@@ -24,6 +24,7 @@ import android.graphics.Rect;
24
24
  import android.graphics.drawable.ColorDrawable;
25
25
  import android.graphics.drawable.Drawable;
26
26
  import android.os.Build;
27
+ import android.view.FocusFinder;
27
28
  import android.view.KeyEvent;
28
29
  import android.view.MotionEvent;
29
30
  import android.view.View;
@@ -38,6 +39,7 @@ import androidx.core.view.ViewCompat.FocusDirection;
38
39
  import com.facebook.common.logging.FLog;
39
40
  import com.facebook.infer.annotation.Assertions;
40
41
  import com.facebook.infer.annotation.Nullsafe;
42
+ import com.facebook.react.views.common.UiModeUtils;
41
43
  import com.facebook.react.R;
42
44
  import com.facebook.react.bridge.ReadableMap;
43
45
  import com.facebook.react.common.ReactConstants;
@@ -134,6 +136,8 @@ public class ReactScrollView extends ScrollView
134
136
  private int mFadingEdgeLengthStart;
135
137
  private int mFadingEdgeLengthEnd;
136
138
  private int mSnapToItemPadding;
139
+ private boolean mScrollAnimationEnabled = true;
140
+ private boolean mBlockScrollDelta = false;
137
141
 
138
142
  public ReactScrollView(Context context) {
139
143
  this(context, null);
@@ -358,6 +362,10 @@ public class ReactScrollView extends ScrollView
358
362
  mSnapToItemPadding = snapToItemPadding;
359
363
  }
360
364
 
365
+ public void setScrollAnimationEnabled(boolean scrollAnimationEnabled) {
366
+ mScrollAnimationEnabled = scrollAnimationEnabled;
367
+ }
368
+
361
369
  @Override
362
370
  protected float getTopFadingEdgeStrength() {
363
371
  float max = Math.max(mFadingEdgeLengthStart, mFadingEdgeLengthEnd);
@@ -539,6 +547,24 @@ public class ReactScrollView extends ScrollView
539
547
  return true;
540
548
  }
541
549
 
550
+ @Override
551
+ public boolean arrowScroll(int direction) {
552
+ if (mSnapToAlignment == SNAP_ALIGNMENT_ITEM) {
553
+ // When snapToAlignment is "item", find the next focusable and request focus directly.
554
+ // This avoids super.arrowScroll() which starts its own scroll animation that conflicts
555
+ // with tryScrollSnapToChild's snap scrolling.
556
+ // requestChildFocus → tryScrollSnapToChild handles scrolling.
557
+ View currentFocused = findFocus();
558
+ View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
559
+ if (nextFocused != null && nextFocused != currentFocused && nextFocused != this) {
560
+ nextFocused.requestFocus(direction);
561
+ return true;
562
+ }
563
+ return false;
564
+ }
565
+ return super.arrowScroll(direction);
566
+ }
567
+
542
568
  /**
543
569
  * Since ReactScrollView handles layout changes on JS side, it does not call super.onlayout due to
544
570
  * which mIsLayoutDirty flag in ScrollView remains true and prevents scrolling to child when
@@ -562,7 +588,9 @@ public class ReactScrollView extends ScrollView
562
588
  * unblocks such customization.
563
589
  */
564
590
  protected void requestChildFocusWithoutScroll(View child, View focused) {
591
+ mBlockScrollDelta = true;
565
592
  super.requestChildFocus(child, focused);
593
+ mBlockScrollDelta = false;
566
594
  }
567
595
 
568
596
  private int getScrollDelta(View descendent) {
@@ -606,9 +634,23 @@ public class ReactScrollView extends ScrollView
606
634
  }
607
635
  }
608
636
 
637
+ @Override
638
+ public void computeScroll() {
639
+ if (UiModeUtils.isTVDevice(getContext())
640
+ && !mScrollAnimationEnabled
641
+ && mScroller != null
642
+ && !mScroller.isFinished()) {
643
+ // Jump instantly to the target position instead of animating.
644
+ scrollTo(mScroller.getFinalX(), mScroller.getFinalY());
645
+ mScroller.forceFinished(true);
646
+ return;
647
+ }
648
+ super.computeScroll();
649
+ }
650
+
609
651
  @Override
610
652
  protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
611
- if (!mScrollEnabled) {
653
+ if (!mScrollEnabled || mBlockScrollDelta) {
612
654
  return 0;
613
655
  }
614
656
  return super.computeScrollDeltaToGetChildRectOnScreen(rect);
@@ -1362,7 +1404,11 @@ public class ReactScrollView extends ScrollView
1362
1404
  * scroll view and state. Calling raw `smoothScrollTo` doesn't update state.
1363
1405
  */
1364
1406
  public void reactSmoothScrollTo(int x, int y) {
1365
- ReactScrollViewHelper.smoothScrollTo(this, x, y);
1407
+ if (mScrollAnimationEnabled || !UiModeUtils.isTVDevice(getContext())) {
1408
+ ReactScrollViewHelper.smoothScrollTo(this, x, y);
1409
+ } else {
1410
+ scrollTo(x, y);
1411
+ }
1366
1412
  setPendingContentOffsets(x, y);
1367
1413
  }
1368
1414
 
@@ -148,6 +148,11 @@ constructor(private val fpsListener: FpsListener? = null) :
148
148
  view.setSnapToItemPadding(px)
149
149
  }
150
150
 
151
+ @ReactProp(name = "scrollAnimationEnabled", defaultBoolean = true)
152
+ public fun setScrollAnimationEnabled(view: ReactScrollView, value: Boolean) {
153
+ view.setScrollAnimationEnabled(value)
154
+ }
155
+
151
156
  @ReactProp(name = ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS)
152
157
  public fun setRemoveClippedSubviews(view: ReactScrollView, removeClippedSubviews: Boolean) {
153
158
  view.removeClippedSubviews = removeClippedSubviews
@@ -30,6 +30,7 @@ import androidx.appcompat.widget.TintContextWrapper;
30
30
  import androidx.core.view.AccessibilityDelegateCompat;
31
31
  import androidx.core.view.ViewCompat;
32
32
  import androidx.customview.widget.ExploreByTouchHelper;
33
+ import com.facebook.react.views.common.UiModeUtils;
33
34
  import com.facebook.common.logging.FLog;
34
35
  import com.facebook.infer.annotation.Assertions;
35
36
  import com.facebook.infer.annotation.Nullsafe;
@@ -151,14 +152,38 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
151
152
  // mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED |
152
153
  // LAYOUT_DIRECTION_INHERIT;
153
154
  setEnabled(true);
154
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
155
- setFocusable(View.FOCUSABLE_AUTO);
156
- }
155
+ // Changed from FOCUSABLE_AUTO to NOT_FOCUSABLE to prevent Android TV's
156
+ // focus engine from landing on text views when no other views are available
157
+ setFocusable(View.NOT_FOCUSABLE);
157
158
 
158
159
  setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
159
160
  updateView(); // call after changing ellipsizeLocation in particular
160
161
  }
161
162
 
163
+ // On Android TV, prevent text views from becoming focusable.
164
+ // ReactTextAnchorViewManager.setAccessible() ties isFocusable to the
165
+ // "accessible" prop, which causes Android TV's D-pad focus engine to land
166
+ // on text views inside recycled list items (FlashList), triggering
167
+ // requestChildFocus -> scrollToChild scroll jumps.
168
+ // On non-TV devices, focusability is left unchanged for accessibility.
169
+ @Override
170
+ public void setFocusable(boolean focusable) {
171
+ if (UiModeUtils.isTVDevice(getContext())) {
172
+ super.setFocusable(false);
173
+ } else {
174
+ super.setFocusable(focusable);
175
+ }
176
+ }
177
+
178
+ @Override
179
+ public void setFocusable(int focusable) {
180
+ if (UiModeUtils.isTVDevice(getContext())) {
181
+ super.setFocusable(View.NOT_FOCUSABLE);
182
+ } else {
183
+ super.setFocusable(focusable);
184
+ }
185
+ }
186
+
162
187
  private static WritableMap inlineViewJson(
163
188
  int visibility, int index, int left, int top, int right, int bottom) {
164
189
  WritableMap json = Arguments.createMap();
@@ -11,6 +11,12 @@
11
11
 
12
12
  namespace facebook::react::jsinspector_modern {
13
13
 
14
+ bool JInspectorFlags::getScreenshotCaptureEnabled(
15
+ jni::alias_ref<jclass> /*unused*/) {
16
+ auto& inspectorFlags = InspectorFlags::getInstance();
17
+ return inspectorFlags.getScreenshotCaptureEnabled();
18
+ }
19
+
14
20
  bool JInspectorFlags::getFuseboxEnabled(jni::alias_ref<jclass> /*unused*/) {
15
21
  auto& inspectorFlags = InspectorFlags::getInstance();
16
22
  return inspectorFlags.getFuseboxEnabled();
@@ -21,7 +27,18 @@ bool JInspectorFlags::getIsProfilingBuild(jni::alias_ref<jclass> /*unused*/) {
21
27
  return inspectorFlags.getIsProfilingBuild();
22
28
  }
23
29
 
30
+ bool JInspectorFlags::getFrameRecordingEnabled(
31
+ jni::alias_ref<jclass> /*unused*/) {
32
+ auto& inspectorFlags = InspectorFlags::getInstance();
33
+ return inspectorFlags.getFrameRecordingEnabled();
34
+ }
35
+
24
36
  void JInspectorFlags::registerNatives() {
37
+ javaClassLocal()->registerNatives({
38
+ makeNativeMethod(
39
+ "getScreenshotCaptureEnabled",
40
+ JInspectorFlags::getScreenshotCaptureEnabled),
41
+ });
25
42
  javaClassLocal()->registerNatives({
26
43
  makeNativeMethod("getFuseboxEnabled", JInspectorFlags::getFuseboxEnabled),
27
44
  });
@@ -29,6 +46,11 @@ void JInspectorFlags::registerNatives() {
29
46
  makeNativeMethod(
30
47
  "getIsProfilingBuild", JInspectorFlags::getIsProfilingBuild),
31
48
  });
49
+ javaClassLocal()->registerNatives({
50
+ makeNativeMethod(
51
+ "getFrameRecordingEnabled",
52
+ JInspectorFlags::getFrameRecordingEnabled),
53
+ });
32
54
  }
33
55
 
34
56
  } // namespace facebook::react::jsinspector_modern
@@ -18,8 +18,10 @@ class JInspectorFlags : public jni::JavaClass<JInspectorFlags> {
18
18
  public:
19
19
  static constexpr auto kJavaDescriptor = "Lcom/facebook/react/devsupport/InspectorFlags;";
20
20
 
21
+ static bool getScreenshotCaptureEnabled(jni::alias_ref<jclass> /*unused*/);
21
22
  static bool getFuseboxEnabled(jni::alias_ref<jclass> /*unused*/);
22
23
  static bool getIsProfilingBuild(jni::alias_ref<jclass> /*unused*/);
24
+ static bool getFrameRecordingEnabled(jni::alias_ref<jclass> /*unused*/);
23
25
 
24
26
  static void registerNatives();
25
27
 
@@ -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<<adc73df33ed2112584feaf83fb8d5e17>>
7
+ * @generated SignedSource<<d4aa5fd7828c31ac309551066662eeaa>>
8
8
  */
9
9
 
10
10
  /**
@@ -381,12 +381,24 @@ class ReactNativeFeatureFlagsJavaProvider
381
381
  return method(javaProvider_);
382
382
  }
383
383
 
384
+ bool fuseboxFrameRecordingEnabled() override {
385
+ static const auto method =
386
+ getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fuseboxFrameRecordingEnabled");
387
+ return method(javaProvider_);
388
+ }
389
+
384
390
  bool fuseboxNetworkInspectionEnabled() override {
385
391
  static const auto method =
386
392
  getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fuseboxNetworkInspectionEnabled");
387
393
  return method(javaProvider_);
388
394
  }
389
395
 
396
+ bool fuseboxScreenshotCaptureEnabled() override {
397
+ static const auto method =
398
+ getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fuseboxScreenshotCaptureEnabled");
399
+ return method(javaProvider_);
400
+ }
401
+
390
402
  bool hideOffscreenVirtualViewsOnIOS() override {
391
403
  static const auto method =
392
404
  getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("hideOffscreenVirtualViewsOnIOS");
@@ -838,11 +850,21 @@ bool JReactNativeFeatureFlagsCxxInterop::fuseboxEnabledRelease(
838
850
  return ReactNativeFeatureFlags::fuseboxEnabledRelease();
839
851
  }
840
852
 
853
+ bool JReactNativeFeatureFlagsCxxInterop::fuseboxFrameRecordingEnabled(
854
+ facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
855
+ return ReactNativeFeatureFlags::fuseboxFrameRecordingEnabled();
856
+ }
857
+
841
858
  bool JReactNativeFeatureFlagsCxxInterop::fuseboxNetworkInspectionEnabled(
842
859
  facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
843
860
  return ReactNativeFeatureFlags::fuseboxNetworkInspectionEnabled();
844
861
  }
845
862
 
863
+ bool JReactNativeFeatureFlagsCxxInterop::fuseboxScreenshotCaptureEnabled(
864
+ facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
865
+ return ReactNativeFeatureFlags::fuseboxScreenshotCaptureEnabled();
866
+ }
867
+
846
868
  bool JReactNativeFeatureFlagsCxxInterop::hideOffscreenVirtualViewsOnIOS(
847
869
  facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
848
870
  return ReactNativeFeatureFlags::hideOffscreenVirtualViewsOnIOS();
@@ -1180,9 +1202,15 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
1180
1202
  makeNativeMethod(
1181
1203
  "fuseboxEnabledRelease",
1182
1204
  JReactNativeFeatureFlagsCxxInterop::fuseboxEnabledRelease),
1205
+ makeNativeMethod(
1206
+ "fuseboxFrameRecordingEnabled",
1207
+ JReactNativeFeatureFlagsCxxInterop::fuseboxFrameRecordingEnabled),
1183
1208
  makeNativeMethod(
1184
1209
  "fuseboxNetworkInspectionEnabled",
1185
1210
  JReactNativeFeatureFlagsCxxInterop::fuseboxNetworkInspectionEnabled),
1211
+ makeNativeMethod(
1212
+ "fuseboxScreenshotCaptureEnabled",
1213
+ JReactNativeFeatureFlagsCxxInterop::fuseboxScreenshotCaptureEnabled),
1186
1214
  makeNativeMethod(
1187
1215
  "hideOffscreenVirtualViewsOnIOS",
1188
1216
  JReactNativeFeatureFlagsCxxInterop::hideOffscreenVirtualViewsOnIOS),
@@ -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<<9853cf162b179249b2c6af120a662cf3>>
7
+ * @generated SignedSource<<554bd02cd328ba462d4e703b653d6cdd>>
8
8
  */
9
9
 
10
10
  /**
@@ -201,9 +201,15 @@ class JReactNativeFeatureFlagsCxxInterop
201
201
  static bool fuseboxEnabledRelease(
202
202
  facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
203
203
 
204
+ static bool fuseboxFrameRecordingEnabled(
205
+ facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
206
+
204
207
  static bool fuseboxNetworkInspectionEnabled(
205
208
  facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
206
209
 
210
+ static bool fuseboxScreenshotCaptureEnabled(
211
+ facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
212
+
207
213
  static bool hideOffscreenVirtualViewsOnIOS(
208
214
  facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
209
215