react-native 0.86.0-rc.0 → 0.86.0-rc.2

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 (26) hide show
  1. package/Libraries/Components/Pressable/Pressable.d.ts +1 -0
  2. package/Libraries/Components/Pressable/useAndroidRippleForView.js +7 -8
  3. package/Libraries/Components/Touchable/TouchableNativeFeedback.js +6 -10
  4. package/Libraries/Components/View/ViewPropTypes.js +3 -1
  5. package/Libraries/Core/ReactNativeVersion.js +1 -1
  6. package/React/Base/RCTVersion.m +1 -1
  7. package/ReactAndroid/api/ReactAndroid.api +1 -0
  8. package/ReactAndroid/gradle.properties +1 -1
  9. package/ReactAndroid/src/main/java/com/facebook/react/modules/image/ImageLoaderModule.kt +59 -62
  10. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
  11. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.kt +59 -20
  12. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt +30 -0
  13. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.kt +2 -7
  14. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  15. package/ReactCommon/react/renderer/components/view/platform/android/react/renderer/components/view/HostPlatformViewProps.cpp +19 -2
  16. package/ReactCommon/react/renderer/components/view/platform/android/react/renderer/components/view/NativeDrawable.h +47 -16
  17. package/package.json +9 -9
  18. package/scripts/cocoapods/rncore.rb +65 -11
  19. package/scripts/cocoapods/rndependencies.rb +65 -11
  20. package/scripts/cocoapods/utils.rb +52 -0
  21. package/scripts/codegen/generate-artifacts-executor/generateReactCodegenPodspec.js +8 -3
  22. package/scripts/react_native_pods.rb +15 -2
  23. package/sdks/hermes-engine/hermes-utils.rb +92 -5
  24. package/types_generated/Libraries/Components/Pressable/useAndroidRippleForView.d.ts +5 -2
  25. package/types_generated/Libraries/Components/Touchable/TouchableNativeFeedback.d.ts +8 -6
  26. package/types_generated/Libraries/Components/View/ViewPropTypes.d.ts +4 -2
@@ -30,6 +30,7 @@ export interface PressableAndroidRippleConfig {
30
30
  borderless?: null | boolean | undefined;
31
31
  radius?: null | number | undefined;
32
32
  foreground?: null | boolean | undefined;
33
+ alpha?: null | number | undefined;
33
34
  }
34
35
 
35
36
  export interface PressableProps
@@ -8,6 +8,7 @@
8
8
  * @format
9
9
  */
10
10
 
11
+ import type {ProcessedColorValue} from '../../StyleSheet/processColor';
11
12
  import type {ColorValue} from '../../StyleSheet/StyleSheet';
12
13
  import type {GestureResponderEvent} from '../../Types/CoreEventTypes';
13
14
 
@@ -15,15 +16,15 @@ import processColor from '../../StyleSheet/processColor';
15
16
  import Platform from '../../Utilities/Platform';
16
17
  import View from '../View/View';
17
18
  import {Commands} from '../View/ViewNativeComponent';
18
- import invariant from 'invariant';
19
19
  import * as React from 'react';
20
20
  import {useMemo} from 'react';
21
21
 
22
22
  type NativeBackgroundProp = Readonly<{
23
23
  type: 'RippleAndroid',
24
- color: ?number,
24
+ color: ?ProcessedColorValue,
25
25
  borderless: boolean,
26
26
  rippleRadius: ?number,
27
+ alpha: ?number,
27
28
  }>;
28
29
 
29
30
  export type PressableAndroidRippleConfig = {
@@ -31,6 +32,7 @@ export type PressableAndroidRippleConfig = {
31
32
  borderless?: boolean,
32
33
  radius?: number,
33
34
  foreground?: boolean,
35
+ alpha?: number,
34
36
  };
35
37
 
36
38
  /**
@@ -48,7 +50,7 @@ export default function useAndroidRippleForView(
48
50
  | Readonly<{nativeBackgroundAndroid: NativeBackgroundProp}>
49
51
  | Readonly<{nativeForegroundAndroid: NativeBackgroundProp}>,
50
52
  }> {
51
- const {color, borderless, radius, foreground} = rippleConfig ?? {};
53
+ const {color, borderless, radius, foreground, alpha} = rippleConfig ?? {};
52
54
 
53
55
  return useMemo(() => {
54
56
  if (
@@ -56,16 +58,13 @@ export default function useAndroidRippleForView(
56
58
  (color != null || borderless != null || radius != null)
57
59
  ) {
58
60
  const processedColor = processColor(color);
59
- invariant(
60
- processedColor == null || typeof processedColor === 'number',
61
- 'Unexpected color given for Ripple color',
62
- );
63
61
 
64
62
  const nativeRippleValue = {
65
63
  type: 'RippleAndroid',
66
64
  color: processedColor,
67
65
  borderless: borderless === true,
68
66
  rippleRadius: radius,
67
+ alpha: alpha ?? null,
69
68
  };
70
69
 
71
70
  return {
@@ -105,5 +104,5 @@ export default function useAndroidRippleForView(
105
104
  };
106
105
  }
107
106
  return null;
108
- }, [borderless, color, foreground, radius, viewRef]);
107
+ }, [alpha, borderless, color, foreground, radius, viewRef]);
109
108
  }
@@ -8,6 +8,8 @@
8
8
  * @format
9
9
  */
10
10
 
11
+ import type {ProcessedColorValue} from '../../StyleSheet/processColor';
12
+ import type {ColorValue} from '../../StyleSheet/StyleSheet';
11
13
  import type {GestureResponderEvent} from '../../Types/CoreEventTypes';
12
14
  import type {TouchableWithoutFeedbackProps} from './TouchableWithoutFeedback';
13
15
 
@@ -20,7 +22,6 @@ import {findHostInstance_DEPRECATED} from '../../ReactNative/RendererProxy';
20
22
  import processColor from '../../StyleSheet/processColor';
21
23
  import Platform from '../../Utilities/Platform';
22
24
  import {Commands} from '../View/ViewNativeComponent';
23
- import invariant from 'invariant';
24
25
  import * as React from 'react';
25
26
  import {cloneElement} from 'react';
26
27
 
@@ -96,7 +97,7 @@ export type TouchableNativeFeedbackProps = Readonly<{
96
97
  }>
97
98
  | Readonly<{
98
99
  type: 'RippleAndroid',
99
- color: ?number,
100
+ color: ?ProcessedColorValue,
100
101
  borderless: boolean,
101
102
  rippleRadius: ?number,
102
103
  }>
@@ -177,23 +178,18 @@ class TouchableNativeFeedback extends React.Component<
177
178
  * @param rippleRadius The radius of ripple effect
178
179
  */
179
180
  static Ripple: (
180
- color: string,
181
+ color: ColorValue,
181
182
  borderless: boolean,
182
183
  rippleRadius?: ?number,
183
184
  ) => Readonly<{
184
185
  borderless: boolean,
185
- color: ?number,
186
+ color: ?ProcessedColorValue,
186
187
  rippleRadius: ?number,
187
188
  type: 'RippleAndroid',
188
- }> = (color: string, borderless: boolean, rippleRadius?: ?number) => {
189
+ }> = (color: ColorValue, borderless: boolean, rippleRadius?: ?number) => {
189
190
  const processedColor = processColor(color);
190
- invariant(
191
- processedColor == null || typeof processedColor === 'number',
192
- 'Unexpected color given for Ripple color',
193
- );
194
191
  return {
195
192
  type: 'RippleAndroid',
196
- // $FlowFixMe[incompatible-type]
197
193
  color: processedColor,
198
194
  borderless,
199
195
  rippleRadius,
@@ -11,6 +11,7 @@
11
11
  'use strict';
12
12
 
13
13
  import type {EdgeInsetsOrSizeProp} from '../../StyleSheet/EdgeInsetsPropType';
14
+ import type {ProcessedColorValue} from '../../StyleSheet/processColor';
14
15
  import type {ViewStyleProp} from '../../StyleSheet/StyleSheet';
15
16
  import type {
16
17
  BlurEvent,
@@ -264,9 +265,10 @@ type AndroidDrawableThemeAttr = Readonly<{
264
265
 
265
266
  type AndroidDrawableRipple = Readonly<{
266
267
  type: 'RippleAndroid',
267
- color?: ?number,
268
+ color?: ?ProcessedColorValue,
268
269
  borderless?: ?boolean,
269
270
  rippleRadius?: ?number,
271
+ alpha?: ?number,
270
272
  }>;
271
273
 
272
274
  type AndroidDrawable = AndroidDrawableThemeAttr | AndroidDrawableRipple;
@@ -29,7 +29,7 @@ export default class ReactNativeVersion {
29
29
  static major: number = 0;
30
30
  static minor: number = 86;
31
31
  static patch: number = 0;
32
- static prerelease: string | null = 'rc.0';
32
+ static prerelease: string | null = 'rc.2';
33
33
 
34
34
  static getVersionString(): string {
35
35
  return `${this.major}.${this.minor}.${this.patch}${this.prerelease != null ? `-${this.prerelease}` : ''}`;
@@ -24,7 +24,7 @@ NSDictionary* RCTGetReactNativeVersion(void)
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(86),
26
26
  RCTVersionPatch: @(0),
27
- RCTVersionPrerelease: @"rc.0",
27
+ RCTVersionPrerelease: @"rc.2",
28
28
  };
29
29
  });
30
30
  return __rnVersion;
@@ -6468,6 +6468,7 @@ public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGro
6468
6468
  public fun getZIndexMappedChildIndex (I)I
6469
6469
  public fun hasOverlappingRendering ()Z
6470
6470
  protected fun onAttachedToWindow ()V
6471
+ protected fun onConfigurationChanged (Landroid/content/res/Configuration;)V
6471
6472
  public fun onHoverEvent (Landroid/view/MotionEvent;)Z
6472
6473
  public fun onInterceptTouchEvent (Landroid/view/MotionEvent;)Z
6473
6474
  protected fun onLayout (ZIIII)V
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.86.0-rc.0
1
+ VERSION_NAME=0.86.0-rc.2
2
2
  react.internal.publishingGroup=com.facebook.react
3
3
  react.internal.hermesPublishingGroup=com.facebook.hermes
4
4
 
@@ -7,17 +7,20 @@
7
7
 
8
8
  package com.facebook.react.modules.image
9
9
 
10
+ import android.media.ExifInterface
10
11
  import android.net.Uri
11
12
  import android.util.SparseArray
12
13
  import com.facebook.common.executors.CallerThreadExecutor
14
+ import com.facebook.common.memory.PooledByteBuffer
13
15
  import com.facebook.common.references.CloseableReference
14
16
  import com.facebook.datasource.BaseDataSubscriber
15
17
  import com.facebook.datasource.DataSource
16
18
  import com.facebook.datasource.DataSubscriber
17
19
  import com.facebook.drawee.backends.pipeline.Fresco
18
20
  import com.facebook.fbreact.specs.NativeImageLoaderAndroidSpec
21
+ import com.facebook.imagepipeline.common.RotationOptions
19
22
  import com.facebook.imagepipeline.core.ImagePipeline
20
- import com.facebook.imagepipeline.image.CloseableImage
23
+ import com.facebook.imagepipeline.image.EncodedImage
21
24
  import com.facebook.imagepipeline.request.ImageRequest
22
25
  import com.facebook.imagepipeline.request.ImageRequestBuilder
23
26
  import com.facebook.react.bridge.GuardedAsyncTask
@@ -82,39 +85,13 @@ internal class ImageLoaderModule : NativeImageLoaderAndroidSpec, LifecycleEventL
82
85
  return
83
86
  }
84
87
  val source = ImageSource(reactApplicationContext, uriString)
85
- val request: ImageRequest = ImageRequestBuilder.newBuilderWithSource(source.uri).build()
86
- val dataSource: DataSource<CloseableReference<CloseableImage>> =
87
- this.imagePipeline.fetchDecodedImage(request, this.callerContext)
88
- val dataSubscriber: DataSubscriber<CloseableReference<CloseableImage>> =
89
- object : BaseDataSubscriber<CloseableReference<CloseableImage>>() {
90
- override fun onNewResultImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
91
- if (!dataSource.isFinished) {
92
- return
93
- }
94
- val ref = dataSource.result
95
- if (ref != null) {
96
- try {
97
- val image: CloseableImage = ref.get()
98
- val sizes = buildReadableMap {
99
- put("width", image.width)
100
- put("height", image.height)
101
- }
102
- promise.resolve(sizes)
103
- } catch (e: Exception) {
104
- promise.reject(ERROR_GET_SIZE_FAILURE, e)
105
- } finally {
106
- CloseableReference.closeSafely(ref)
107
- }
108
- } else {
109
- promise.reject(ERROR_GET_SIZE_FAILURE, "Failed to get the size of the image")
110
- }
111
- }
112
-
113
- override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
114
- promise.reject(ERROR_GET_SIZE_FAILURE, dataSource.failureCause)
115
- }
116
- }
117
- dataSource.subscribe(dataSubscriber, CallerThreadExecutor.getInstance())
88
+ val request: ImageRequest =
89
+ ImageRequestBuilder.newBuilderWithSource(source.uri)
90
+ .setRotationOptions(RotationOptions.disableRotation())
91
+ .build()
92
+ val dataSource: DataSource<CloseableReference<PooledByteBuffer>> =
93
+ this.imagePipeline.fetchEncodedImage(request, this.callerContext)
94
+ dataSource.subscribe(createSizeSubscriber(promise), CallerThreadExecutor.getInstance())
118
95
  }
119
96
 
120
97
  /**
@@ -134,41 +111,61 @@ internal class ImageLoaderModule : NativeImageLoaderAndroidSpec, LifecycleEventL
134
111
  val source = ImageSource(reactApplicationContext, uriString)
135
112
  val imageRequestBuilder: ImageRequestBuilder =
136
113
  ImageRequestBuilder.newBuilderWithSource(source.uri)
114
+ .setRotationOptions(RotationOptions.disableRotation())
137
115
  val request: ImageRequest =
138
116
  ReactNetworkImageRequest.fromBuilderWithHeaders(imageRequestBuilder, headers)
139
- val dataSource: DataSource<CloseableReference<CloseableImage>> =
140
- this.imagePipeline.fetchDecodedImage(request, this.callerContext)
141
- val dataSubscriber: DataSubscriber<CloseableReference<CloseableImage>> =
142
- object : BaseDataSubscriber<CloseableReference<CloseableImage>>() {
143
- override fun onNewResultImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
144
- if (!dataSource.isFinished) {
145
- return
146
- }
147
- val ref = dataSource.result
148
- if (ref != null) {
149
- try {
150
- val image: CloseableImage = ref.get()
151
- val sizes = buildReadableMap {
152
- put("width", image.width)
153
- put("height", image.height)
154
- }
155
- promise.resolve(sizes)
156
- } catch (e: Exception) {
157
- promise.reject(ERROR_GET_SIZE_FAILURE, e)
158
- } finally {
159
- CloseableReference.closeSafely(ref)
117
+ val dataSource: DataSource<CloseableReference<PooledByteBuffer>> =
118
+ this.imagePipeline.fetchEncodedImage(request, this.callerContext)
119
+ dataSource.subscribe(createSizeSubscriber(promise), CallerThreadExecutor.getInstance())
120
+ }
121
+
122
+ private fun createSizeSubscriber(
123
+ promise: Promise
124
+ ): DataSubscriber<CloseableReference<PooledByteBuffer>> =
125
+ object : BaseDataSubscriber<CloseableReference<PooledByteBuffer>>() {
126
+ override fun onNewResultImpl(dataSource: DataSource<CloseableReference<PooledByteBuffer>>) {
127
+ if (!dataSource.isFinished) {
128
+ return
129
+ }
130
+ val ref = dataSource.result
131
+ if (ref != null) {
132
+ var encodedImage: EncodedImage? = null
133
+ try {
134
+ encodedImage = EncodedImage(ref)
135
+ // Swap width and height when the image's EXIF orientation swaps the X/Y axes
136
+ // (90°/270° rotations, or transpose/transverse), so the values reflect the
137
+ // visible dimensions, matching iOS behavior.
138
+ val rotated =
139
+ encodedImage.rotationAngle == 90 ||
140
+ encodedImage.rotationAngle == 270 ||
141
+ encodedImage.exifOrientation == ExifInterface.ORIENTATION_TRANSPOSE ||
142
+ encodedImage.exifOrientation == ExifInterface.ORIENTATION_TRANSVERSE
143
+ val width = if (rotated) encodedImage.height else encodedImage.width
144
+ val height = if (rotated) encodedImage.width else encodedImage.height
145
+ if (width < 0 || height < 0) {
146
+ promise.reject(ERROR_GET_SIZE_FAILURE, "Failed to get the size of the image")
147
+ return
148
+ }
149
+ val sizes = buildReadableMap {
150
+ put("width", width)
151
+ put("height", height)
160
152
  }
161
- } else {
162
- promise.reject(ERROR_GET_SIZE_FAILURE, "Failed to get the size of the image")
153
+ promise.resolve(sizes)
154
+ } catch (e: Exception) {
155
+ promise.reject(ERROR_GET_SIZE_FAILURE, e)
156
+ } finally {
157
+ encodedImage?.close()
158
+ CloseableReference.closeSafely(ref)
163
159
  }
160
+ } else {
161
+ promise.reject(ERROR_GET_SIZE_FAILURE, "Failed to get the size of the image")
164
162
  }
163
+ }
165
164
 
166
- override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
167
- promise.reject(ERROR_GET_SIZE_FAILURE, dataSource.failureCause)
168
- }
165
+ override fun onFailureImpl(dataSource: DataSource<CloseableReference<PooledByteBuffer>>) {
166
+ promise.reject(ERROR_GET_SIZE_FAILURE, dataSource.failureCause)
169
167
  }
170
- dataSource.subscribe(dataSubscriber, CallerThreadExecutor.getInstance())
171
- }
168
+ }
172
169
 
173
170
  /**
174
171
  * Prefetches the given image to the Fresco image disk cache.
@@ -15,6 +15,6 @@ public object ReactNativeVersion {
15
15
  "major" to 0,
16
16
  "minor" to 86,
17
17
  "patch" to 0,
18
- "prerelease" to "rc.0"
18
+ "prerelease" to "rc.2"
19
19
  )
20
20
  }
@@ -15,10 +15,16 @@ import android.graphics.drawable.ColorDrawable
15
15
  import android.graphics.drawable.Drawable
16
16
  import android.graphics.drawable.RippleDrawable
17
17
  import android.util.TypedValue
18
+ import com.facebook.common.logging.FLog
19
+ import com.facebook.react.bridge.ColorPropConverter
20
+ import com.facebook.react.bridge.JSApplicationCausedNativeException
18
21
  import com.facebook.react.bridge.JSApplicationIllegalArgumentException
19
22
  import com.facebook.react.bridge.ReadableMap
23
+ import com.facebook.react.bridge.ReadableType
24
+ import com.facebook.react.common.ReactConstants
20
25
  import com.facebook.react.uimanager.PixelUtil
21
26
  import com.facebook.react.uimanager.ViewProps
27
+ import kotlin.math.roundToInt
22
28
 
23
29
  /**
24
30
  * Utility class that helps with converting android drawable description used in JS to an actual
@@ -73,11 +79,21 @@ public object ReactDrawableHelper {
73
79
  context: Context,
74
80
  drawableDescriptionDict: ReadableMap,
75
81
  ): RippleDrawable {
76
- val color = getColor(context, drawableDescriptionDict)
77
- val mask = getMask(drawableDescriptionDict)
78
- val colorStateList = ColorStateList(arrayOf(intArrayOf()), intArrayOf(color))
82
+ val resolvedColor = getColor(context, drawableDescriptionDict)
83
+ var color = resolvedColor ?: getFallbackColor(context)
84
+
85
+ if (
86
+ resolvedColor != null &&
87
+ drawableDescriptionDict.hasKey("alpha") &&
88
+ !drawableDescriptionDict.isNull("alpha")
89
+ ) {
90
+ val alphaFactor = drawableDescriptionDict.getDouble("alpha").coerceIn(0.0, 1.0)
91
+ val newAlpha = (Color.alpha(color) * alphaFactor).roundToInt()
92
+ color = Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color))
93
+ }
79
94
 
80
- return RippleDrawable(colorStateList, null, mask)
95
+ val mask = getMask(drawableDescriptionDict)
96
+ return RippleDrawable(ColorStateList(arrayOf(intArrayOf()), intArrayOf(color)), null, mask)
81
97
  }
82
98
 
83
99
  private fun setRadius(drawableDescriptionDict: ReadableMap, drawable: Drawable?): Drawable? {
@@ -88,26 +104,49 @@ public object ReactDrawableHelper {
88
104
  return drawable
89
105
  }
90
106
 
91
- private fun getColor(context: Context, drawableDescriptionDict: ReadableMap): Int =
92
- if (
93
- drawableDescriptionDict.hasKey(ViewProps.COLOR) &&
94
- !drawableDescriptionDict.isNull(ViewProps.COLOR)
95
- ) {
96
- drawableDescriptionDict.getInt(ViewProps.COLOR)
97
- } else {
107
+ /**
108
+ * Returns the resolved ripple color, or null if none was provided or the PlatformColor resource
109
+ * couldn't be found.
110
+ */
111
+ private fun getColor(context: Context, drawableDescriptionDict: ReadableMap): Int? {
112
+ val rawColor: Any? =
98
113
  if (
99
- context.theme.resolveAttribute(
100
- android.R.attr.colorControlHighlight,
101
- resolveOutValue,
102
- true,
103
- )
114
+ drawableDescriptionDict.hasKey(ViewProps.COLOR) &&
115
+ !drawableDescriptionDict.isNull(ViewProps.COLOR)
104
116
  ) {
105
- context.resources.getColor(resolveOutValue.resourceId, context.theme)
117
+ when (drawableDescriptionDict.getType(ViewProps.COLOR)) {
118
+ ReadableType.Number -> drawableDescriptionDict.getDouble(ViewProps.COLOR)
119
+ ReadableType.Map -> drawableDescriptionDict.getMap(ViewProps.COLOR)
120
+ else -> null
121
+ }
106
122
  } else {
107
- throw JSApplicationIllegalArgumentException(
108
- "Attribute colorControlHighlight couldn't be resolved into a drawable"
109
- )
123
+ null
110
124
  }
125
+ return try {
126
+ ColorPropConverter.getColor(rawColor, context)
127
+ } catch (e: JSApplicationCausedNativeException) {
128
+ FLog.w(
129
+ ReactConstants.TAG,
130
+ e,
131
+ "android_ripple: color resource not found, using colorControlHighlight",
132
+ )
133
+ null
134
+ }
135
+ }
136
+
137
+ private fun getFallbackColor(context: Context): Int =
138
+ if (
139
+ context.theme.resolveAttribute(
140
+ android.R.attr.colorControlHighlight,
141
+ resolveOutValue,
142
+ true,
143
+ )
144
+ ) {
145
+ context.resources.getColor(resolveOutValue.resourceId, context.theme)
146
+ } else {
147
+ throw JSApplicationIllegalArgumentException(
148
+ "Attribute colorControlHighlight couldn't be resolved into a drawable"
149
+ )
111
150
  }
112
151
 
113
152
  private fun getMask(drawableDescriptionDict: ReadableMap): Drawable? {
@@ -12,6 +12,7 @@ package com.facebook.react.views.view
12
12
  import android.annotation.SuppressLint
13
13
  import android.annotation.TargetApi
14
14
  import android.content.Context
15
+ import android.content.res.Configuration
15
16
  import android.graphics.BlendMode
16
17
  import android.graphics.Canvas
17
18
  import android.graphics.Paint
@@ -28,6 +29,7 @@ import com.facebook.react.R
28
29
  import com.facebook.react.bridge.ReactNoCrashSoftException
29
30
  import com.facebook.react.bridge.ReactSoftExceptionLogger
30
31
  import com.facebook.react.bridge.ReactSoftExceptionLogger.logSoftException
32
+ import com.facebook.react.bridge.ReadableMap
31
33
  import com.facebook.react.bridge.UiThreadUtil.assertOnUiThread
32
34
  import com.facebook.react.bridge.UiThreadUtil.runOnUiThread
33
35
  import com.facebook.react.common.ReactConstants.TAG
@@ -154,6 +156,9 @@ public open class ReactViewGroup public constructor(context: Context?) :
154
156
  null
155
157
  private var focusOnAttach = false
156
158
 
159
+ internal var nativeBackgroundMap: ReadableMap? = null
160
+ internal var nativeForegroundMap: ReadableMap? = null
161
+
157
162
  init {
158
163
  initView()
159
164
  }
@@ -181,6 +186,8 @@ public open class ReactViewGroup public constructor(context: Context?) :
181
186
  backfaceOpacity = 1f
182
187
  backfaceVisible = true
183
188
  childrenRemovedWhileTransitioning = null
189
+ nativeBackgroundMap = null
190
+ nativeForegroundMap = null
184
191
  }
185
192
 
186
193
  internal open fun recycleView() {
@@ -587,6 +594,29 @@ public open class ReactViewGroup public constructor(context: Context?) :
587
594
  }
588
595
  }
589
596
 
597
+ override fun onConfigurationChanged(newConfig: Configuration) {
598
+ super.onConfigurationChanged(newConfig)
599
+ if (nativeBackgroundMap != null) {
600
+ applyNativeBackground(nativeBackgroundMap)
601
+ }
602
+ if (nativeForegroundMap != null) {
603
+ applyNativeForeground(nativeForegroundMap)
604
+ }
605
+ }
606
+
607
+ internal fun applyNativeBackground(map: ReadableMap?) {
608
+ nativeBackgroundMap = map
609
+ setFeedbackUnderlay(
610
+ this,
611
+ map?.let { ReactDrawableHelper.createDrawableFromJSDescription(context, it) },
612
+ )
613
+ }
614
+
615
+ internal fun applyNativeForeground(map: ReadableMap?) {
616
+ nativeForegroundMap = map
617
+ foreground = map?.let { ReactDrawableHelper.createDrawableFromJSDescription(context, it) }
618
+ }
619
+
590
620
  override fun onViewAdded(child: View) {
591
621
  assertOnUiThread()
592
622
  checkViewClippingTag(child, false)
@@ -305,17 +305,12 @@ public open class ReactViewManager : ReactClippingViewManager<ReactViewGroup>()
305
305
 
306
306
  @ReactProp(name = "nativeBackgroundAndroid")
307
307
  public open fun setNativeBackground(view: ReactViewGroup, background: ReadableMap?) {
308
- val bg = background?.let {
309
- ReactDrawableHelper.createDrawableFromJSDescription(view.context, it)
310
- }
311
- BackgroundStyleApplicator.setFeedbackUnderlay(view, bg)
308
+ view.applyNativeBackground(background)
312
309
  }
313
310
 
314
311
  @ReactProp(name = "nativeForegroundAndroid")
315
312
  public open fun setNativeForeground(view: ReactViewGroup, foreground: ReadableMap?) {
316
- view.foreground = foreground?.let {
317
- ReactDrawableHelper.createDrawableFromJSDescription(view.context, it)
318
- }
313
+ view.applyNativeForeground(foreground)
319
314
  }
320
315
 
321
316
  @ReactProp(name = ViewProps.NEEDS_OFFSCREEN_ALPHA_COMPOSITING)
@@ -22,7 +22,7 @@ struct ReactNativeVersionType {
22
22
  int32_t Major = 0;
23
23
  int32_t Minor = 86;
24
24
  int32_t Patch = 0;
25
- std::string_view Prerelease = "rc.0";
25
+ std::string_view Prerelease = "rc.2";
26
26
  };
27
27
 
28
28
  constexpr ReactNativeVersionType ReactNativeVersion;
@@ -450,8 +450,25 @@ inline static void updateNativeDrawableProp(
450
450
  nativeDrawableResult["rippleRadius"] =
451
451
  nativeDrawableValue.ripple.rippleRadius.value();
452
452
  }
453
- if (nativeDrawableValue.ripple.color.has_value()) {
454
- nativeDrawableResult["color"] = nativeDrawableValue.ripple.color.value();
453
+ if (nativeDrawableValue.ripple.color.has_value() ||
454
+ nativeDrawableValue.ripple.colorResourcePaths.has_value()) {
455
+ if (nativeDrawableValue.ripple.colorResourcePaths.has_value()) {
456
+ folly::dynamic resourcePaths = folly::dynamic::array();
457
+ for (const auto& path :
458
+ nativeDrawableValue.ripple.colorResourcePaths.value()) {
459
+ resourcePaths.push_back(path);
460
+ }
461
+ folly::dynamic platformColorMap = folly::dynamic::object();
462
+ platformColorMap["resource_paths"] = resourcePaths;
463
+ nativeDrawableResult["color"] = platformColorMap;
464
+ } else {
465
+ nativeDrawableResult["color"] =
466
+ toAndroidRepr(nativeDrawableValue.ripple.color.value());
467
+ }
468
+ if (nativeDrawableValue.ripple.alpha.has_value()) {
469
+ nativeDrawableResult["alpha"] =
470
+ nativeDrawableValue.ripple.alpha.value();
471
+ }
455
472
  }
456
473
  nativeDrawableResult["borderless"] = nativeDrawableValue.ripple.borderless;
457
474
  } else {