react-native-tvos 0.85.0-0rc5 → 0.85.2-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 (54) 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/Components/TextInput/AndroidTextInputNativeComponent.js +5 -1
  7. package/Libraries/Components/TextInput/TextInput.flow.js +1 -0
  8. package/Libraries/Core/ReactNativeVersion.js +2 -2
  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/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.h +1 -0
  13. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm +78 -0
  14. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +32 -6
  15. package/React/Views/ScrollView/RCTScrollViewManager.m +1 -0
  16. package/ReactAndroid/build.gradle.kts +2 -0
  17. package/ReactAndroid/gradle.properties +1 -1
  18. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +2 -2
  19. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt +5 -1
  20. package/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkEventUtil.kt +8 -0
  21. package/ReactAndroid/src/main/java/com/facebook/react/modules/network/RequestBodyUtil.kt +2 -0
  22. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +2 -2
  23. package/ReactAndroid/src/main/java/com/facebook/react/views/common/UiModeUtils.kt +20 -0
  24. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +57 -8
  25. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.kt +5 -0
  26. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +48 -2
  27. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.kt +5 -0
  28. package/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java +28 -3
  29. package/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.kt +5 -0
  30. package/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputSubmitEditingEvent.kt +6 -1
  31. package/ReactCommon/React-Fabric.podspec +1 -0
  32. package/ReactCommon/cxxreact/ReactNativeVersion.h +3 -3
  33. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +2 -2
  34. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h +9 -1
  35. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm +3 -1
  36. package/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +4 -1
  37. package/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +58 -25
  38. package/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +9 -0
  39. package/ReactCommon/react/renderer/animationbackend/AnimationChoreographer.h +5 -0
  40. package/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.cpp +10 -0
  41. package/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.h +1 -0
  42. package/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h +1 -0
  43. package/package.json +9 -9
  44. package/scripts/codegen/generate-artifacts-executor/generateAppDependencyProvider.js +4 -4
  45. package/scripts/codegen/generate-artifacts-executor/generateCustomURLHandlers.js +3 -3
  46. package/scripts/codegen/generate-artifacts-executor/generateNativeCode.js +2 -3
  47. package/scripts/codegen/generate-artifacts-executor/generatePackageSwift.js +2 -2
  48. package/scripts/codegen/generate-artifacts-executor/generateRCTModuleProviders.js +3 -2
  49. package/scripts/codegen/generate-artifacts-executor/generateRCTThirdPartyComponents.js +3 -2
  50. package/scripts/codegen/generate-artifacts-executor/generateReactCodegenPodspec.js +2 -2
  51. package/scripts/codegen/generate-artifacts-executor/generateUnstableModulesRequiringMainQueueSetupProvider.js +3 -3
  52. package/scripts/codegen/generate-artifacts-executor/utils.js +48 -0
  53. package/src/private/featureflags/ReactNativeFeatureFlags.js +2 -2
  54. package/types/public/ReactNativeTVTypes.d.ts +8 -0
@@ -54,6 +54,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = {
54
54
  borderTopRightRadius: true,
55
55
  borderLeftColor: colorAttribute,
56
56
  snapToItemPadding: true,
57
+ scrollAnimationEnabled: true,
57
58
  pointerEvents: true,
58
59
  scrollsChildToFocus: true,
59
60
  },
@@ -754,6 +754,13 @@ export interface ScrollViewProps
754
754
  */
755
755
  removeClippedSubviews?: boolean | undefined;
756
756
 
757
+ /**
758
+ * (TV only)
759
+ * When false, the scroll view will jump to the correct offset without animation
760
+ * when focus changes. Defaults to true.
761
+ */
762
+ scrollAnimationEnabled?: boolean | undefined;
763
+
757
764
  /**
758
765
  * When true, shows a horizontal scroll indicator.
759
766
  */
@@ -670,6 +670,12 @@ type ScrollViewBaseProps = Readonly<{
670
670
  * true.
671
671
  */
672
672
  removeClippedSubviews?: ?boolean,
673
+ /**
674
+ * (TV only)
675
+ * When false, the scroll view will jump to the correct offset without animation
676
+ * when focus changes. Defaults to true.
677
+ */
678
+ scrollAnimationEnabled?: ?boolean,
673
679
  /**
674
680
  * A RefreshControl component, used to provide pull-to-refresh
675
681
  * functionality for the ScrollView. Only works for vertical ScrollViews
@@ -76,6 +76,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig =
76
76
  borderTopRightRadius: true,
77
77
  borderLeftColor: colorAttribute,
78
78
  snapToItemPadding: true,
79
+ scrollAnimationEnabled: true,
79
80
  pointerEvents: true,
80
81
  isInvertedVirtualizedList: true,
81
82
  scrollsChildToFocus: true,
@@ -142,6 +143,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig =
142
143
  scrollsToTop: true,
143
144
  showsHorizontalScrollIndicator: true,
144
145
  showsVerticalScrollIndicator: true,
146
+ scrollAnimationEnabled: true,
145
147
  showsScrollIndex: true,
146
148
  snapToItemPadding: true,
147
149
  snapToAlignment: true,
@@ -73,6 +73,7 @@ export type ScrollViewNativeProps = Readonly<{
73
73
  snapToItemPadding?: ?number,
74
74
  sendMomentumEvents?: ?boolean,
75
75
  showsHorizontalScrollIndicator?: ?boolean,
76
+ scrollAnimationEnabled?: ?boolean,
76
77
  showsScrollIndex?: ?boolean,
77
78
  showsVerticalScrollIndicator?: ?boolean,
78
79
  snapToAlignment?: ?('start' | 'center' | 'end' | 'item'),
@@ -402,7 +402,11 @@ export type AndroidTextInputNativeProps = Readonly<{
402
402
  * Invalid if `multiline={true}` is specified.
403
403
  */
404
404
  onSubmitEditing?: ?BubblingEventHandler<
405
- Readonly<{target: Int32, text: string}>,
405
+ Readonly<{
406
+ target: Int32,
407
+ text: string,
408
+ action?: 'submit' | 'next' | 'previous',
409
+ }>,
406
410
  >,
407
411
 
408
412
  /**
@@ -127,6 +127,7 @@ type TextInputSubmitEditingEventData = Readonly<{
127
127
  ...TargetEvent,
128
128
  eventCount: number,
129
129
  text: string,
130
+ action?: 'submit' | 'next' | 'previous',
130
131
  ...
131
132
  }>;
132
133
 
@@ -28,8 +28,8 @@
28
28
  export default class ReactNativeVersion {
29
29
  static major: number = 0;
30
30
  static minor: number = 85;
31
- static patch: number = 0;
32
- static prerelease: string | null = '0rc5';
31
+ static patch: number = 2;
32
+ static prerelease: string | null = '0';
33
33
 
34
34
  static getVersionString(): string {
35
35
  return `${this.major}.${this.minor}.${this.patch}${this.prerelease != null ? `-${this.prerelease}` : ''}`;
package/README.md CHANGED
@@ -262,6 +262,58 @@ class Game2048 extends React.Component {
262
262
 
263
263
  - _TVTextScrollView_: On Apple TV, a ScrollView will not scroll unless there are focusable items inside it or above/below it. This component works on both Apple TV and Android TV, using native code to allow scrolling using swipe gestures from the remote control.
264
264
 
265
+ - _ScrollView snap alignment_: The existing `snapToAlignment` prop has been extended with a new `'item'` value that enables per-item snap alignment on TV. Instead of snapping all items uniformly to `'start'`, `'center'`, or `'end'`, each child can specify its own alignment via the `scrollSnapAlign` View prop. Optional padding can be applied with `snapToItemPadding` on the ScrollView.
266
+
267
+ | Prop (ScrollView) | Value | Description |
268
+ |---|---|---|
269
+ | snapToAlignment | `'item'` | Enables per-item snap alignment (TV only). Requires `snapToInterval` to be set. |
270
+ | snapToItemPadding | number? | Extra padding (in dp/pt) applied around items when snapping. Only used with `snapToAlignment="item"`. |
271
+
272
+ | Prop (View) | Value | Description |
273
+ |---|---|---|
274
+ | scrollSnapAlign | `'start'` \| `'center'` \| `'end'` | Controls where this item snaps inside its parent ScrollView. Only used when the parent has `snapToAlignment="item"`. |
275
+
276
+ ```jsx
277
+ <ScrollView
278
+ horizontal
279
+ snapToInterval={300}
280
+ snapToAlignment="item"
281
+ snapToItemPadding={20}>
282
+ <Pressable scrollSnapAlign="start" style={{width: 300}}>
283
+ <Text>Snaps to start</Text>
284
+ </Pressable>
285
+ <Pressable scrollSnapAlign="center" style={{width: 200}}>
286
+ <Text>Snaps to center</Text>
287
+ </Pressable>
288
+ <Pressable scrollSnapAlign="end" style={{width: 300}}>
289
+ <Text>Snaps to end</Text>
290
+ </Pressable>
291
+ </ScrollView>
292
+ ```
293
+
294
+ - _ScrollView animation control_: The `scrollAnimationEnabled` prop allows disabling scroll animations when focus changes on TV. When set to `false`, the scroll view jumps instantly to the focused item instead of animating. This only affects TV platforms and has no effect on mobile.
295
+
296
+ | Prop (ScrollView) | Value | Description |
297
+ |---|---|---|
298
+ | scrollAnimationEnabled | boolean? | When `false`, disables animated scrolling on focus change. Defaults to `true`. TV only. |
299
+
300
+ ```jsx
301
+ <ScrollView horizontal scrollAnimationEnabled={false}>
302
+ <Pressable style={{width: 300}}><Text>Item 1</Text></Pressable>
303
+ <Pressable style={{width: 300}}><Text>Item 2</Text></Pressable>
304
+ <Pressable style={{width: 300}}><Text>Item 3</Text></Pressable>
305
+ </ScrollView>
306
+ ```
307
+
308
+ - _Interaction with existing ScrollView props_: The TV scroll props build on top of existing React Native ScrollView behavior. Here is how they interact:
309
+
310
+ - `snapToAlignment="item"` does not require `snapToInterval` to be set. It works as a standalone snapping mode where each child's `scrollSnapAlign` determines the snap position. If `snapToInterval` is also set, the interval is applied as an additional constraint after the item offset is computed.
311
+ - `snapToAlignment="item"` should not be combined with `pagingEnabled`. Both attempt to control scroll positioning independently, which leads to unpredictable behavior. Use one or the other.
312
+ - `snapToStart` and `snapToEnd` work independently of `snapToAlignment="item"`. They control edge behavior during swipe/drag momentum (whether the scroll view snaps to the first or last position), but do not affect focus driven item snapping.
313
+ - `scrollAnimationEnabled={false}` disables all scroll animations, including programmatic `scrollTo({animated: true})` calls. When disabled, all scrolling is instant.
314
+ - `decelerationRate` has no effect when `scrollAnimationEnabled={false}`, since there is no animation to decelerate.
315
+ - `scrollAnimationEnabled` and `snapToAlignment="item"` work well together. Snapping still occurs, but instantly instead of animated.
316
+
265
317
  - _VirtualizedList_: We extend `VirtualizedList` to make virtualization work well with focus management in mind. All of the improvements that we made are automatically available to all the VirtualizedList based components such as `FlatList`.
266
318
  - Defaults: VirtualizeList contents are automatically wrapped with a `TVFocusGuideView` with `trapFocus*` properties enabled depending on the orientation of the list. This default makes sure that focus doesn't leave the list accidentally due to a virtualization issue etc. until reaching the beginning or the end of the list.
267
319
  - New Props:
@@ -23,8 +23,8 @@ NSDictionary* RCTGetReactNativeVersion(void)
23
23
  __rnVersion = @{
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(85),
26
- RCTVersionPatch: @(0),
27
- RCTVersionPrerelease: @"0rc5",
26
+ RCTVersionPatch: @(2),
27
+ RCTVersionPrerelease: @"0",
28
28
  };
29
29
  });
30
30
  return __rnVersion;
@@ -51,10 +51,27 @@ RCT_EXPORT_MODULE()
51
51
  selector:@selector(hide)
52
52
  name:RCTJavaScriptDidFailToLoadNotification
53
53
  object:nil];
54
+ [[NSNotificationCenter defaultCenter] addObserver:self
55
+ selector:@selector(hide)
56
+ name:@"RCTInstanceDidLoadBundle"
57
+ object:nil];
54
58
  }
55
59
  return self;
56
60
  }
57
61
 
62
+ - (void)dealloc
63
+ {
64
+ [self clearInitialMessageDelay];
65
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
66
+ UIWindow *window = _window;
67
+ _window = nil;
68
+ if (window) {
69
+ RCTExecuteOnMainQueue(^{
70
+ window.hidden = YES;
71
+ });
72
+ }
73
+ }
74
+
58
75
  + (void)setEnabled:(BOOL)enabled
59
76
  {
60
77
  RCTDevLoadingViewSetEnabled(enabled);
@@ -52,6 +52,7 @@ NS_ASSUME_NONNULL_BEGIN
52
52
  @property (nonatomic, assign) BOOL snapToEnd;
53
53
  @property (nonatomic, copy) NSArray<NSNumber *> *snapToOffsets;
54
54
  @property (nonatomic, assign) BOOL scrollSnapEnabled;
55
+ @property (nonatomic, assign) BOOL scrollAnimationEnabled;
55
56
 
56
57
  /*
57
58
  * Makes `setContentOffset:` method no-op when given `block` is executed.
@@ -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];
@@ -1252,7 +1255,7 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1252
1255
  ? CGPointMake(targetOffset, scrollView.contentOffset.y)
1253
1256
  : CGPointMake(scrollView.contentOffset.x, targetOffset);
1254
1257
  self.preferredContentOffset = targetContentOffset;
1255
- [_scrollView setContentOffset:targetContentOffset animated:YES];
1258
+ [_scrollView setContentOffset:targetContentOffset animated:scrollProps.scrollAnimationEnabled];
1256
1259
  }
1257
1260
 
1258
1261
  - (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context
@@ -1261,7 +1264,18 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1261
1264
  self.preferredContentOffset = NO_PREFERRED_CONTENT_OFFSET;
1262
1265
  const auto &scrollProps = static_cast<const ScrollViewProps &>(*_props);
1263
1266
  BOOL hasItemSnapAlignment = scrollProps.snapToAlignment == ScrollViewSnapToAlignment::Item;
1264
- if (context.previouslyFocusedView == context.nextFocusedView || (!_props->isTVSelectable && !hasItemSnapAlignment)) {
1267
+ if (context.previouslyFocusedView == context.nextFocusedView) {
1268
+ return;
1269
+ }
1270
+ if (!_props->isTVSelectable && !hasItemSnapAlignment) {
1271
+ if (!scrollProps.scrollAnimationEnabled && [context.nextFocusedView isDescendantOfView:_scrollView]) {
1272
+ // When animations are disabled and there's no snap alignment, manually scroll
1273
+ // the focused view into view instantly. We can't let UIScrollView's default
1274
+ // focus handling do this because it uses deceleration (timer-based) that can't
1275
+ // be blocked by the animation-blocking layer.
1276
+ CGRect targetRect = [context.nextFocusedView convertRect:context.nextFocusedView.bounds toView:_scrollView];
1277
+ [_scrollView scrollRectToVisible:targetRect animated:NO];
1278
+ }
1265
1279
  return;
1266
1280
  }
1267
1281
  if (_props->isTVSelectable && context.nextFocusedView == self) {
@@ -1429,10 +1443,16 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1429
1443
  limitedOffset = MAX(limitedOffset, 0.0);
1430
1444
  limitedOffset = MIN(limitedOffset, maxOffset);
1431
1445
 
1432
- [UIView animateWithDuration:[self swipeDuration] animations:^{
1446
+ const auto &scrollProps = static_cast<const ScrollViewProps &>(*self->_props);
1447
+ if (scrollProps.scrollAnimationEnabled) {
1448
+ [UIView animateWithDuration:[self swipeDuration] animations:^{
1449
+ self.scrollView.contentOffset =
1450
+ CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
1451
+ }];
1452
+ } else {
1433
1453
  self.scrollView.contentOffset =
1434
1454
  CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
1435
- }];
1455
+ }
1436
1456
  });
1437
1457
  }
1438
1458
 
@@ -1453,10 +1473,16 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1453
1473
  limitedOffset = MAX(limitedOffset, 0.0);
1454
1474
  limitedOffset = MIN(limitedOffset, maxOffset);
1455
1475
 
1456
- [UIView animateWithDuration:[self swipeDuration] animations:^{
1476
+ const auto &scrollProps = static_cast<const ScrollViewProps &>(*self->_props);
1477
+ if (scrollProps.scrollAnimationEnabled) {
1478
+ [UIView animateWithDuration:[self swipeDuration] animations:^{
1479
+ self.scrollView.contentOffset =
1480
+ CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
1481
+ }];
1482
+ } else {
1457
1483
  self.scrollView.contentOffset =
1458
1484
  CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
1459
- }];
1485
+ }
1460
1486
  });
1461
1487
  }
1462
1488
 
@@ -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)
@@ -102,6 +102,8 @@ val preparePrefab by
102
102
  Pair("../ReactCommon/hermes/inspector-modern/", "hermes/inspector-modern/"),
103
103
  // fabricjni
104
104
  Pair("src/main/jni/react/fabric", "react/fabric/"),
105
+ // uimanagerjni
106
+ Pair("src/main/jni/react/uimanager", "react/uimanager/"),
105
107
  // glog
106
108
  Pair(File(buildDir, "third-party-ndk/glog/exported/").absolutePath, ""),
107
109
  // jsiinpsector
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.85.0-0rc5
1
+ VERSION_NAME=0.85.2-0
2
2
  react.internal.publishingGroup=io.github.react-native-tvos
3
3
  react.internal.hermesPublishingGroup=com.facebook.hermes
4
4
 
@@ -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<<89c61520177334f93c65ff92c2fc74a6>>
7
+ * @generated SignedSource<<404cef70d58d40565506ab37a4697da8>>
8
8
  */
9
9
 
10
10
  /**
@@ -57,7 +57,7 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
57
57
 
58
58
  override fun enableCppPropsIteratorSetter(): Boolean = false
59
59
 
60
- override fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean = true
60
+ override fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean = false
61
61
 
62
62
  override fun enableDestroyShadowTreeRevisionAsync(): Boolean = false
63
63
 
@@ -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<<8531ce29d0e5362517d35559ebda623b>>
7
+ * @generated SignedSource<<2452c003ffcba8e20b7cd40c68e05e3d>>
8
8
  */
9
9
 
10
10
  /**
@@ -23,6 +23,8 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android :
23
23
  // We could use JNI to get the defaults from C++,
24
24
  // but that is more expensive than just duplicating the defaults here.
25
25
 
26
+ override fun cxxNativeAnimatedEnabled(): Boolean = true
27
+
26
28
  override fun enableAccessibilityOrder(): Boolean = true
27
29
 
28
30
  override fun enableSwiftUIBasedFilters(): Boolean = true
@@ -30,4 +32,6 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android :
30
32
  override fun fixTextClippingAndroid15useBoundsForWidth(): Boolean = true
31
33
 
32
34
  override fun preventShadowTreeCommitExhaustion(): Boolean = true
35
+
36
+ override fun useSharedAnimatedBackend(): Boolean = true
33
37
  }
@@ -19,6 +19,7 @@ import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
19
19
  import java.io.IOException
20
20
  import java.net.SocketTimeoutException
21
21
  import okhttp3.Headers
22
+ import okhttp3.MultipartBody
22
23
  import okhttp3.RequestBody
23
24
  import okio.Buffer
24
25
 
@@ -263,6 +264,13 @@ internal object NetworkEventUtil {
263
264
  return "[Preview unavailable]"
264
265
  }
265
266
 
267
+ // MultipartBody does not propagate isOneShot() from its parts, so check each
268
+ // part explicitly. Reading a one-shot part here would drain the underlying
269
+ // stream and cause the real request to fail.
270
+ if (body is MultipartBody && body.parts().any { it.body().isOneShot() }) {
271
+ return "[Preview unavailable]"
272
+ }
273
+
266
274
  return try {
267
275
  val buffer = Buffer()
268
276
  body.writeTo(buffer)
@@ -146,6 +146,8 @@ internal object RequestBodyUtil {
146
146
  }
147
147
  }
148
148
 
149
+ override fun isOneShot(): Boolean = true
150
+
149
151
  @Throws(IOException::class)
150
152
  override fun writeTo(sink: BufferedSink) {
151
153
  var source: Source? = null
@@ -14,7 +14,7 @@ public object ReactNativeVersion {
14
14
  public val VERSION: Map<String, Any?> = mapOf(
15
15
  "major" to 0,
16
16
  "minor" to 85,
17
- "patch" to 0,
18
- "prerelease" to "0rc5"
17
+ "patch" to 2,
18
+ "prerelease" to "0"
19
19
  )
20
20
  }
@@ -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
  *