From 479266c6bc4ea23623e56c6f270549088409259b Mon Sep 17 00:00:00 2001 From: Jason Zheng Date: Wed, 7 Oct 2026 15:35:22 -0700 Subject: [PATCH] Fix borderless ripple effect on Android (#58902) Summary: Pull Request resolved: https://github.com/react/react-native/pull/58902 Fixes the issue described in [#48552](https://github.com/react/react-native/issues/48552), where a Pressable component can lose both its background color and ripple effect when borderless is true. ### Root Cause Android sets `isProjected() = true` for a borderless RippleDrawable so the ripple can extend beyond the view's bounds. React Native adds the background and ripple as layers in a CompositeBackgroundDrawable, where `isProjected() = true` if any layer is projected. With projection, both the background and ripple are drawn on the nearest native ancestor with a background. Given "root view -> parent view -> Pressable", the ripple and background should now be drawn on the parent. However, since the parent doesn't form a stacking context, Fabric flattens its children. The Pressable and parent become siblings, and the Pressable's background and ripple are now drawn on a further ancestor and consequently become hidden. Workarounds like opacity, `overflow: hidden`, and `collapsable={false}` work by establishing a stacking context so the parent remains the Pressable's ancestor. ### This PR Modify CompositeBackgroundDrawable to wrap `feedbackUnderlay` in a DrawableWrapper where `isProjected() = false`. The background and ripple are drawn in-place, so they remain visible without a stacking context. The ripple still extends beyond the view's bounds since `clipChildren = false` in React Native. This is gated behind the `fixBorderlessRippleAndroid` flag which defaults to false. ### Limitations With projection, the background and borderless ripple are drawn underneath all siblings. Without projection, they are now drawn over previous siblings and underneath following siblings. This matches behavior already shown by React Native foreground ripples, and Jetpack Compose also disables projection in `RippleHostView.UnprojectedRipple` and exhibits the same behavior. Disabling ripple projection also causes some behavioral changes with clipping. The ripple is no longer clipped to the outline of the ancestor it was projected on, and it can now be clipped by `overflow: hidden` or ScrollView ancestors it used to draw beyond. These changes also align with foreground ripples and Jetpack Compose. One alternative which more closely preserves projected behavior is to modify Fabric's core renderer logic to maintain a parent stacking context if a borderless ripple is present. However, this can potentially change stacking behavior in non-obvious ways depending on where a ripple is defined, where the nearest ancestor with a background exists, and siblings in between. This impacts zIndex behavior and makes Android's stacking diverge from iOS, where ripples don't exist. Changelog: [Android][Fixed] - Fix Pressable and TouchableNativeFeedback losing their background color and ripple when using a borderless ripple Reviewed By: cortinico Differential Revision: D123471013 --- .../featureflags/ReactNativeFeatureFlags.kt | 8 +- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 ++- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 ++- ...tureFlagsOverrides_RNOSS_Canary_Android.kt | 4 +- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../drawable/CompositeBackgroundDrawable.kt | 27 +++++- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 +++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 86 +++++++++++-------- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 ++- ...eactNativeFeatureFlagsOverridesOSSCanary.h | 6 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 11 +++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 23 files changed, 204 insertions(+), 56 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index fe85f503ae4c..141b2b056fbe 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<81b1343c027b377be3bdea9167e16a5d>> + * @generated SignedSource<<8ea15f9bb12ee59c1ed54881b7ab2799>> */ /** @@ -372,6 +372,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableVirtualViewContainerStateExperimental(): Boolean = accessor.enableVirtualViewContainerStateExperimental() + /** + * Prevents a borderless ripple feedback underlay from projecting the entire view background on Android, which could hide the background and ripple. + */ + @JvmStatic + public fun fixBorderlessRippleAndroid(): Boolean = accessor.fixBorderlessRippleAndroid() + /** * Uses the default event priority instead of the discreet event priority by default when dispatching events from Fabric to React. */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index 535c60b7e069..5bb8f5e93033 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<3e939eebe846d12cab15905db8efba18>> + * @generated SignedSource<<423f851be2b501d3fb7211859c731e74>> */ /** @@ -77,6 +77,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableViewRecyclingForTextCache: Boolean? = null private var enableViewRecyclingForViewCache: Boolean? = null private var enableVirtualViewContainerStateExperimentalCache: Boolean? = null + private var fixBorderlessRippleAndroidCache: Boolean? = null private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null private var fixYogaFlexBasisFitContentInMainAxisCache: Boolean? = null private var fuseboxAssertSingleHostStateCache: Boolean? = null @@ -624,6 +625,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun fixBorderlessRippleAndroid(): Boolean { + var cached = fixBorderlessRippleAndroidCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.fixBorderlessRippleAndroid() + fixBorderlessRippleAndroidCache = cached + } + return cached + } + override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean { var cached = fixMappingOfEventPrioritiesBetweenFabricAndReactCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index 586fec5afb58..069a4b5be7c6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<15959c4c8592c5132ebcf822f776f2e8>> */ /** @@ -142,6 +142,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableVirtualViewContainerStateExperimental(): Boolean + @DoNotStrip @JvmStatic public external fun fixBorderlessRippleAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean @DoNotStrip @JvmStatic public external fun fixYogaFlexBasisFitContentInMainAxis(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index 067d6fe4f126..8b6fb2429bba 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<0f4c074c11c564f05950eab73a8fd452>> + * @generated SignedSource<<29635538bf32dd2455977f40eb9b1ee4>> */ /** @@ -137,6 +137,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableVirtualViewContainerStateExperimental(): Boolean = false + override fun fixBorderlessRippleAndroid(): Boolean = false + override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = false override fun fixYogaFlexBasisFitContentInMainAxis(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index 0d473e108d0b..473faecb815c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<013790bd2c3fa8b9ff03e8c5e924bf91>> + * @generated SignedSource<<2730b5fcf6901e962f6e2efb9ebe30da>> */ /** @@ -81,6 +81,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableViewRecyclingForTextCache: Boolean? = null private var enableViewRecyclingForViewCache: Boolean? = null private var enableVirtualViewContainerStateExperimentalCache: Boolean? = null + private var fixBorderlessRippleAndroidCache: Boolean? = null private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null private var fixYogaFlexBasisFitContentInMainAxisCache: Boolean? = null private var fuseboxAssertSingleHostStateCache: Boolean? = null @@ -685,6 +686,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun fixBorderlessRippleAndroid(): Boolean { + var cached = fixBorderlessRippleAndroidCache + if (cached == null) { + cached = currentProvider.fixBorderlessRippleAndroid() + accessedFeatureFlags.add("fixBorderlessRippleAndroid") + fixBorderlessRippleAndroidCache = cached + } + return cached + } + override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean { var cached = fixMappingOfEventPrioritiesBetweenFabricAndReactCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Canary_Android.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Canary_Android.kt index 7d945676704d..91f5e33fdd1b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Canary_Android.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Canary_Android.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<843b2fa7e358dd73c9db083e42b93be6>> + * @generated SignedSource<> */ /** @@ -33,6 +33,8 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Canary_Android : ReactN override fun enableSwiftUIBasedFilters(): Boolean = true + override fun fixBorderlessRippleAndroid(): Boolean = true + override fun fuseboxFrameRecordingEnabled(): Boolean = true override fun fuseboxWebSocketEventsEnabled(): Boolean = true diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index cdf1a8048184..6f678717cfbd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<6607c8678df2693c361f6d8091483662>> + * @generated SignedSource<<361dd8199f01e13cca4b5508371fcc76>> */ /** @@ -137,6 +137,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableVirtualViewContainerStateExperimental(): Boolean + @DoNotStrip public fun fixBorderlessRippleAndroid(): Boolean + @DoNotStrip public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean @DoNotStrip public fun fixYogaFlexBasisFitContentInMainAxis(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CompositeBackgroundDrawable.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CompositeBackgroundDrawable.kt index c790234868db..335ea16bdd63 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CompositeBackgroundDrawable.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CompositeBackgroundDrawable.kt @@ -10,11 +10,14 @@ package com.facebook.react.uimanager.drawable import android.content.Context import android.graphics.Outline import android.graphics.Path +import android.graphics.Rect import android.graphics.RectF import android.graphics.drawable.Drawable +import android.graphics.drawable.DrawableWrapper import android.graphics.drawable.LayerDrawable import android.os.Build import com.facebook.react.common.annotations.UnstableReactNativeAPI +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags import com.facebook.react.uimanager.PixelUtil.dpToPx import com.facebook.react.uimanager.style.BorderInsets import com.facebook.react.uimanager.style.BorderRadiusStyle @@ -240,10 +243,32 @@ internal class CompositeBackgroundDrawable( background?.let { layers.add(it) } backgroundImage?.let { layers.add(it) } border?.let { layers.add(it) } - feedbackUnderlay?.let { layers.add(it) } + feedbackUnderlay?.let { + layers.add( + if (ReactNativeFeatureFlags.fixBorderlessRippleAndroid()) { + UnprojectedRippleWrapper(it) + } else { + it + } + ) + } layers.addAll(innerShadows.asReversed()) outline?.let { layers.add(it) } return layers.toTypedArray() } } } + +/** + * Wraps the feedback underlay so it is never reported as projected. LayerDrawable reports itself as + * projected if any layer is, so a borderless ripple in the feedback underlay would project the + * entire background, including the background color and borders, onto the nearest native ancestor's + * background. With view flattening, that ancestor may be painted under the view's flattened React + * parent, hiding the background and ripple. Ignore the underlay's projection; borderless ripples + * can still draw outside the view's bounds since React Native views don't clip their children. + */ +private class UnprojectedRippleWrapper(drawable: Drawable) : DrawableWrapper(drawable) { + override fun isProjected(): Boolean = false + + override fun getDirtyBounds(): Rect = drawable?.dirtyBounds ?: super.getDirtyBounds() +} diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index e1824f6ca0b1..f6dcfbe7da9f 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<6238811c274064fbbcd2839230f00b93>> */ /** @@ -381,6 +381,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool fixBorderlessRippleAndroid() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("fixBorderlessRippleAndroid"); + return method(javaProvider_); + } + bool fixMappingOfEventPrioritiesBetweenFabricAndReact() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("fixMappingOfEventPrioritiesBetweenFabricAndReact"); @@ -868,6 +874,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableVirtualViewContainerStateExperime return ReactNativeFeatureFlags::enableVirtualViewContainerStateExperimental(); } +bool JReactNativeFeatureFlagsCxxInterop::fixBorderlessRippleAndroid( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::fixBorderlessRippleAndroid(); +} + bool JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabricAndReact( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact(); @@ -1235,6 +1246,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableVirtualViewContainerStateExperimental", JReactNativeFeatureFlagsCxxInterop::enableVirtualViewContainerStateExperimental), + makeNativeMethod( + "fixBorderlessRippleAndroid", + JReactNativeFeatureFlagsCxxInterop::fixBorderlessRippleAndroid), makeNativeMethod( "fixMappingOfEventPrioritiesBetweenFabricAndReact", JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabricAndReact), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index 1b6f4ee66460..f0df514b5166 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<7a9a9c1938c3e01f589e05fffe349ad3>> + * @generated SignedSource<> */ /** @@ -201,6 +201,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableVirtualViewContainerStateExperimental( facebook::jni::alias_ref); + static bool fixBorderlessRippleAndroid( + facebook::jni::alias_ref); + static bool fixMappingOfEventPrioritiesBetweenFabricAndReact( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index fdc1b47a2d6f..dfc498391315 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<8b23b6ea5f2d377f8126adffd753dae7>> + * @generated SignedSource<<18217c67d74c25b53dbf51c57225eab6>> */ /** @@ -254,6 +254,10 @@ bool ReactNativeFeatureFlags::enableVirtualViewContainerStateExperimental() { return getAccessor().enableVirtualViewContainerStateExperimental(); } +bool ReactNativeFeatureFlags::fixBorderlessRippleAndroid() { + return getAccessor().fixBorderlessRippleAndroid(); +} + bool ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact() { return getAccessor().fixMappingOfEventPrioritiesBetweenFabricAndReact(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 6cfd3491e98c..015793c70a05 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<862975d58a367fc28cd240e991ee5ff5>> */ /** @@ -326,6 +326,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableVirtualViewContainerStateExperimental(); + /** + * Prevents a borderless ripple feedback underlay from projecting the entire view background on Android, which could hide the background and ripple. + */ + RN_EXPORT static bool fixBorderlessRippleAndroid(); + /** * Uses the default event priority instead of the discreet event priority by default when dispatching events from Fabric to React. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index ab9754f2662e..df3467589e5b 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<439c4243406fb3bc639ce9d686c415af>> */ /** @@ -1055,6 +1055,24 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimenta return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::fixBorderlessRippleAndroid() { + auto flagValue = fixBorderlessRippleAndroid_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(57, "fixBorderlessRippleAndroid"); + + flagValue = currentProvider_->fixBorderlessRippleAndroid(); + fixBorderlessRippleAndroid_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAndReact() { auto flagValue = fixMappingOfEventPrioritiesBetweenFabricAndReact_.load(); @@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(57, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(58, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::fixYogaFlexBasisFitContentInMainAxis() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "fixYogaFlexBasisFitContentInMainAxis"); + markFlagAsAccessed(59, "fixYogaFlexBasisFitContentInMainAxis"); flagValue = currentProvider_->fixYogaFlexBasisFitContentInMainAxis(); fixYogaFlexBasisFitContentInMainAxis_ = flagValue; @@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxAssertSingleHostState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "fuseboxAssertSingleHostState"); + markFlagAsAccessed(60, "fuseboxAssertSingleHostState"); flagValue = currentProvider_->fuseboxAssertSingleHostState(); fuseboxAssertSingleHostState_ = flagValue; @@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "fuseboxEnabledRelease"); + markFlagAsAccessed(61, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxFrameRecordingEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "fuseboxFrameRecordingEnabled"); + markFlagAsAccessed(62, "fuseboxFrameRecordingEnabled"); flagValue = currentProvider_->fuseboxFrameRecordingEnabled(); fuseboxFrameRecordingEnabled_ = flagValue; @@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxScreenshotCaptureEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "fuseboxScreenshotCaptureEnabled"); + markFlagAsAccessed(63, "fuseboxScreenshotCaptureEnabled"); flagValue = currentProvider_->fuseboxScreenshotCaptureEnabled(); fuseboxScreenshotCaptureEnabled_ = flagValue; @@ -1172,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxWebSocketEventsEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(63, "fuseboxWebSocketEventsEnabled"); + markFlagAsAccessed(64, "fuseboxWebSocketEventsEnabled"); flagValue = currentProvider_->fuseboxWebSocketEventsEnabled(); fuseboxWebSocketEventsEnabled_ = flagValue; @@ -1190,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::optimizedAnimatedPropUpdates() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "optimizedAnimatedPropUpdates"); + markFlagAsAccessed(65, "optimizedAnimatedPropUpdates"); flagValue = currentProvider_->optimizedAnimatedPropUpdates(); optimizedAnimatedPropUpdates_ = flagValue; @@ -1208,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(66, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1226,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "perfIssuesEnabled"); + markFlagAsAccessed(67, "perfIssuesEnabled"); flagValue = currentProvider_->perfIssuesEnabled(); perfIssuesEnabled_ = flagValue; @@ -1244,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "perfMonitorV2Enabled"); + markFlagAsAccessed(68, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1262,7 +1280,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "preparedTextCacheSize"); + markFlagAsAccessed(69, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1280,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(70, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1298,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2Android() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "redBoxV2Android"); + markFlagAsAccessed(71, "redBoxV2Android"); flagValue = currentProvider_->redBoxV2Android(); redBoxV2Android_ = flagValue; @@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2IOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "redBoxV2IOS"); + markFlagAsAccessed(72, "redBoxV2IOS"); flagValue = currentProvider_->redBoxV2IOS(); redBoxV2IOS_ = flagValue; @@ -1334,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(73, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1352,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(73, "shouldTriggerResponderTransferOnScrollAndroid"); + markFlagAsAccessed(74, "shouldTriggerResponderTransferOnScrollAndroid"); flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid(); shouldTriggerResponderTransferOnScrollAndroid_ = flagValue; @@ -1370,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(74, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(75, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1388,7 +1406,7 @@ bool ReactNativeFeatureFlagsAccessor::syncAndroidClipBoundsWithOverflow() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(75, "syncAndroidClipBoundsWithOverflow"); + markFlagAsAccessed(76, "syncAndroidClipBoundsWithOverflow"); flagValue = currentProvider_->syncAndroidClipBoundsWithOverflow(); syncAndroidClipBoundsWithOverflow_ = flagValue; @@ -1406,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(76, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(77, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1424,7 +1442,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(77, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(78, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1442,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommitT // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "updateRuntimeShadowNodeReferencesOnCommitThread"); + markFlagAsAccessed(79, "updateRuntimeShadowNodeReferencesOnCommitThread"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommitThread(); updateRuntimeShadowNodeReferencesOnCommitThread_ = flagValue; @@ -1460,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(80, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1478,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(80, "useFabricInterop"); + markFlagAsAccessed(81, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1496,7 +1514,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(82, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1514,7 +1532,7 @@ bool ReactNativeFeatureFlagsAccessor::useNestedScrollViewAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "useNestedScrollViewAndroid"); + markFlagAsAccessed(83, "useNestedScrollViewAndroid"); flagValue = currentProvider_->useNestedScrollViewAndroid(); useNestedScrollViewAndroid_ = flagValue; @@ -1532,7 +1550,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(83, "useSharedAnimatedBackend"); + markFlagAsAccessed(84, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1550,7 +1568,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(84, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(85, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1568,7 +1586,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(85, "useTurboModuleInterop"); + markFlagAsAccessed(86, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1586,7 +1604,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(86, "viewCullingOutsetRatio"); + markFlagAsAccessed(87, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1604,7 +1622,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(87, "viewTransitionEnabled"); + markFlagAsAccessed(88, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1622,7 +1640,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionUseHardwareBitmapAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(88, "viewTransitionUseHardwareBitmapAndroid"); + markFlagAsAccessed(89, "viewTransitionUseHardwareBitmapAndroid"); flagValue = currentProvider_->viewTransitionUseHardwareBitmapAndroid(); viewTransitionUseHardwareBitmapAndroid_ = flagValue; @@ -1640,7 +1658,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(89, "virtualViewPrerenderRatio"); + markFlagAsAccessed(90, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 6cbf6d88937f..86b68e7e0f43 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<390d809fda507cdfb5d6f3a166ca9a38>> */ /** @@ -91,6 +91,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableViewRecyclingForText(); bool enableViewRecyclingForView(); bool enableVirtualViewContainerStateExperimental(); + bool fixBorderlessRippleAndroid(); bool fixMappingOfEventPrioritiesBetweenFabricAndReact(); bool fixYogaFlexBasisFitContentInMainAxis(); bool fuseboxAssertSingleHostState(); @@ -135,7 +136,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 90> accessedFeatureFlags_; + std::array, 91> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -194,6 +195,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableViewRecyclingForText_; std::atomic> enableViewRecyclingForView_; std::atomic> enableVirtualViewContainerStateExperimental_; + std::atomic> fixBorderlessRippleAndroid_; std::atomic> fixMappingOfEventPrioritiesBetweenFabricAndReact_; std::atomic> fixYogaFlexBasisFitContentInMainAxis_; std::atomic> fuseboxAssertSingleHostState_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index e5ea88af00f8..918fef273700 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<6abef654d3dd784b7211ac8874069020>> + * @generated SignedSource<<8600358e2b37f974188be1cb376d8b78>> */ /** @@ -257,6 +257,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool fixBorderlessRippleAndroid() override { + return false; + } + bool fixMappingOfEventPrioritiesBetweenFabricAndReact() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index fc4a49a0eda6..39be8788ba24 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<625c7c4f1d55e7ca5c226793fe982c7b>> + * @generated SignedSource<> */ /** @@ -560,6 +560,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableVirtualViewContainerStateExperimental(); } + bool fixBorderlessRippleAndroid() override { + auto value = values_["fixBorderlessRippleAndroid"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::fixBorderlessRippleAndroid(); + } + bool fixMappingOfEventPrioritiesBetweenFabricAndReact() override { auto value = values_["fixMappingOfEventPrioritiesBetweenFabricAndReact"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSCanary.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSCanary.h index 4b4a74abe343..4bf6a6363c3b 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSCanary.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSCanary.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<94c1abd04adec2f81634da6786979552>> + * @generated SignedSource<<4b66991416806affd46aab79e36c4a45>> */ /** @@ -49,6 +49,10 @@ class ReactNativeFeatureFlagsOverridesOSSCanary : public ReactNativeFeatureFlags return true; } + bool fixBorderlessRippleAndroid() override { + return true; + } + bool fuseboxFrameRecordingEnabled() override { return true; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 958088683100..cd5937530ce7 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<428c475acbb4baed380207f0b08b4e26>> + * @generated SignedSource<> */ /** @@ -84,6 +84,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableViewRecyclingForText() = 0; virtual bool enableViewRecyclingForView() = 0; virtual bool enableVirtualViewContainerStateExperimental() = 0; + virtual bool fixBorderlessRippleAndroid() = 0; virtual bool fixMappingOfEventPrioritiesBetweenFabricAndReact() = 0; virtual bool fixYogaFlexBasisFitContentInMainAxis() = 0; virtual bool fuseboxAssertSingleHostState() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index bb691d5b3351..25d8cd42fed7 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<98e46e2b6c10f8080df75eccfa9082a0>> */ /** @@ -329,6 +329,11 @@ bool NativeReactNativeFeatureFlags::enableVirtualViewContainerStateExperimental( return ReactNativeFeatureFlags::enableVirtualViewContainerStateExperimental(); } +bool NativeReactNativeFeatureFlags::fixBorderlessRippleAndroid( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::fixBorderlessRippleAndroid(); +} + bool NativeReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 16c7dec0a06a..8c63f99fa854 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -152,6 +152,8 @@ class NativeReactNativeFeatureFlags bool enableVirtualViewContainerStateExperimental(jsi::Runtime& runtime); + bool fixBorderlessRippleAndroid(jsi::Runtime& runtime); + bool fixMappingOfEventPrioritiesBetweenFabricAndReact(jsi::Runtime& runtime); bool fixYogaFlexBasisFitContentInMainAxis(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 0d65492be708..579d6953505f 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -661,6 +661,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + fixBorderlessRippleAndroid: { + defaultValue: false, + metadata: { + dateAdded: '2026-10-06', + description: + 'Prevents a borderless ripple feedback underlay from projecting the entire view background on Android, which could hide the background and ripple.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'canary', + }, fixMappingOfEventPrioritiesBetweenFabricAndReact: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 816d971c6b79..1610359f48e4 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<6b32a5764e7c6c1016c3e99511dce903>> * @flow strict * @noformat */ @@ -106,6 +106,7 @@ export type ReactNativeFeatureFlags = Readonly<{ enableViewRecyclingForText: Getter, enableViewRecyclingForView: Getter, enableVirtualViewContainerStateExperimental: Getter, + fixBorderlessRippleAndroid: Getter, fixMappingOfEventPrioritiesBetweenFabricAndReact: Getter, fixYogaFlexBasisFitContentInMainAxis: Getter, fuseboxAssertSingleHostState: Getter, @@ -443,6 +444,10 @@ export const enableViewRecyclingForView: Getter = createNativeFlagGette * Enables the experimental version of `VirtualViewContainerState`. */ export const enableVirtualViewContainerStateExperimental: Getter = createNativeFlagGetter('enableVirtualViewContainerStateExperimental', false); +/** + * Prevents a borderless ripple feedback underlay from projecting the entire view background on Android, which could hide the background and ripple. + */ +export const fixBorderlessRippleAndroid: Getter = createNativeFlagGetter('fixBorderlessRippleAndroid', false); /** * Uses the default event priority instead of the discreet event priority by default when dispatching events from Fabric to React. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 591e4ef1ae15..dc621a7907b3 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<5748d89f596f74a59c5500cc77ad40e7>> * @flow strict * @noformat */ @@ -82,6 +82,7 @@ export interface Spec extends TurboModule { readonly enableViewRecyclingForText?: () => boolean; readonly enableViewRecyclingForView?: () => boolean; readonly enableVirtualViewContainerStateExperimental?: () => boolean; + readonly fixBorderlessRippleAndroid?: () => boolean; readonly fixMappingOfEventPrioritiesBetweenFabricAndReact?: () => boolean; readonly fixYogaFlexBasisFitContentInMainAxis?: () => boolean; readonly fuseboxAssertSingleHostState?: () => boolean;