From e16faca18c052a1c3b7ce6fb3b8236347b222e8a Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 17 May 2024 20:38:26 -0700 Subject: [PATCH] Propagate layout direction to Android Views and Drawables Summary: Right now we use layout direction determined by I18NManager to influence the root Yoga layout direction. Individual views may have a different resolved layout direction (e.g. due to `direction` style prop), and even though we don't rely on Android layout props, Android components still need to inherit or know the right layout direction to still do correct drawing. Example of this was scrollbar showing up on the left, instead of right in RTL, as soon as ScrollView knew it was in RTL. This has potential to change a good amount of behavior, so this is under QE. Changelog: [Android][Fixed] - Propagate layout direction to Android Views and Drawables Reviewed By: joevilches Differential Revision: D57248417 fbshipit-source-id: 4bcdf2b23277ff926a796b8377df08d49c7b914c --- .../ReactAndroid/api/ReactAndroid.api | 9 ++-- .../mounting/SurfaceMountingManager.java | 16 ++++++- .../mountitems/IntBufferBatchMountItem.java | 12 +++-- .../featureflags/ReactNativeFeatureFlags.kt | 8 +++- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 ++++- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +++++- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../drawable/CSSBackgroundDrawable.java | 45 +++++++++---------- .../ReactHorizontalScrollContainerView.java | 13 +++++- .../scroll/ReactHorizontalScrollView.java | 12 ++++- .../react/views/view/ReactViewGroup.java | 20 +++------ .../react/fabric/FabricMountingManager.cpp | 23 +++++++--- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 ++++++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 ++- .../featureflags/ReactNativeFeatureFlags.cpp | 6 ++- .../featureflags/ReactNativeFeatureFlags.h | 7 ++- .../ReactNativeFeatureFlagsAccessor.cpp | 26 +++++++++-- .../ReactNativeFeatureFlagsAccessor.h | 6 ++- .../ReactNativeFeatureFlagsDefaults.h | 6 ++- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 ++- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 4 ++ .../featureflags/ReactNativeFeatureFlags.js | 7 ++- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 27 files changed, 216 insertions(+), 79 deletions(-) diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 0e01ff68d47..480748f43fc 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -2790,7 +2790,7 @@ public class com/facebook/react/fabric/mounting/SurfaceMountingManager { public fun setJSResponder (IIZ)V public fun stopSurface ()V public fun updateEventEmitter (ILcom/facebook/react/fabric/events/EventEmitterWrapper;)V - public fun updateLayout (IIIIIII)V + public fun updateLayout (IIIIIIII)V public fun updateOverflowInset (IIIII)V public fun updatePadding (IIIII)V public fun updateProps (ILcom/facebook/react/bridge/ReadableMap;)V @@ -5527,12 +5527,11 @@ public class com/facebook/react/uimanager/drawable/CSSBackgroundDrawable : andro public fun getComputedBorderRadius ()Lcom/facebook/react/uimanager/style/ComputedBorderRadius; public fun getDirectionAwareBorderInsets ()Landroid/graphics/RectF; public fun getFullBorderWidth ()F + public fun getLayoutDirection ()I public fun getOpacity ()I public fun getOutline (Landroid/graphics/Outline;)V - public fun getResolvedLayoutDirection ()I public fun hasRoundedBorders ()Z protected fun onBoundsChange (Landroid/graphics/Rect;)V - public fun onResolvedLayoutDirectionChanged (I)Z public fun paddingBoxPath ()Landroid/graphics/Path; public fun setAlpha (I)V public fun setBorderColor (IFF)V @@ -5542,9 +5541,9 @@ public class com/facebook/react/uimanager/drawable/CSSBackgroundDrawable : andro public fun setBorderWidth (IF)V public fun setColor (I)V public fun setColorFilter (Landroid/graphics/ColorFilter;)V + public fun setLayoutDirectionOverride (I)V public fun setRadius (F)V public fun setRadius (FI)V - public fun setResolvedLayoutDirection (I)Z } public abstract interface class com/facebook/react/uimanager/events/BatchEventDispatchedListener { @@ -6633,6 +6632,7 @@ public class com/facebook/react/views/scroll/OnScrollDispatchHelper { public class com/facebook/react/views/scroll/ReactHorizontalScrollContainerView : com/facebook/react/views/view/ReactViewGroup { public fun (Landroid/content/Context;)V + public fun getLayoutDirection ()I protected fun onLayout (ZIIII)V public fun setRemoveClippedSubviews (Z)V } @@ -7927,7 +7927,6 @@ public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGro public fun onInterceptTouchEvent (Landroid/view/MotionEvent;)Z protected fun onLayout (ZIIII)V protected fun onMeasure (II)V - public fun onRtlPropertiesChanged (I)V protected fun onSizeChanged (IIII)V public fun onTouchEvent (Landroid/view/MotionEvent;)Z public fun removeView (Landroid/view/View;)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java index e1de337a0de..72b640e0bbb 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java @@ -938,7 +938,14 @@ public class SurfaceMountingManager { @UiThread public void updateLayout( - int reactTag, int parentTag, int x, int y, int width, int height, int displayType) { + int reactTag, + int parentTag, + int x, + int y, + int width, + int height, + int displayType, + int layoutDirection) { if (isStopped()) { return; } @@ -954,6 +961,13 @@ public class SurfaceMountingManager { throw new IllegalStateException("Unable to find View for tag: " + reactTag); } + if (ReactNativeFeatureFlags.setAndroidLayoutDirection()) { + viewToUpdate.setLayoutDirection( + layoutDirection == 1 + ? View.LAYOUT_DIRECTION_LTR + : layoutDirection == 2 ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_INHERIT); + } + viewToUpdate.measure( View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.java index 45621a90dad..c574a2724c5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.java @@ -20,6 +20,7 @@ import com.facebook.react.bridge.ReadableMap; import com.facebook.react.fabric.events.EventEmitterWrapper; import com.facebook.react.fabric.mounting.MountingManager; import com.facebook.react.fabric.mounting.SurfaceMountingManager; +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags; import com.facebook.react.uimanager.StateWrapper; import com.facebook.systrace.Systrace; @@ -150,9 +151,14 @@ final class IntBufferBatchMountItem implements BatchMountItem { int height = mIntBuffer[i++]; int displayType = mIntBuffer[i++]; - surfaceMountingManager.updateLayout( - reactTag, parentTag, x, y, width, height, displayType); - + if (ReactNativeFeatureFlags.setAndroidLayoutDirection()) { + int layoutDirection = mIntBuffer[i++]; + surfaceMountingManager.updateLayout( + reactTag, parentTag, x, y, width, height, displayType, layoutDirection); + } else { + surfaceMountingManager.updateLayout( + reactTag, parentTag, x, y, width, height, displayType, 0); + } } else if (type == INSTRUCTION_UPDATE_PADDING) { surfaceMountingManager.updatePadding( mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++]); 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 845e58a7ae6..6710de5fbf1 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<> + * @generated SignedSource<<0ae7be647ca12c3efcc34f6098155550>> */ /** @@ -130,6 +130,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun preventDoubleTextMeasure(): Boolean = accessor.preventDoubleTextMeasure() + /** + * Propagate layout direction to Android views. + */ + @JvmStatic + public fun setAndroidLayoutDirection(): Boolean = accessor.setAndroidLayoutDirection() + /** * When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with priorities from any thread. */ 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 5dc81b6f875..49b093d4d2f 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<> + * @generated SignedSource<<5323fb8be9ec7ee6ac43d7f01bca020e>> */ /** @@ -37,6 +37,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso private var inspectorEnableModernCDPRegistryCache: Boolean? = null private var lazyAnimationCallbacksCache: Boolean? = null private var preventDoubleTextMeasureCache: Boolean? = null + private var setAndroidLayoutDirectionCache: Boolean? = null private var useModernRuntimeSchedulerCache: Boolean? = null private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null private var useStateAlignmentMechanismCache: Boolean? = null @@ -194,6 +195,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } + override fun setAndroidLayoutDirection(): Boolean { + var cached = setAndroidLayoutDirectionCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.setAndroidLayoutDirection() + setAndroidLayoutDirectionCache = cached + } + return cached + } + override fun useModernRuntimeScheduler(): Boolean { var cached = useModernRuntimeSchedulerCache 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 b9295fc96c7..de3f8cb5818 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<<77b8e93114ed0cc2b6bdd9ee46e162c7>> + * @generated SignedSource<> */ /** @@ -62,6 +62,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun preventDoubleTextMeasure(): Boolean + @DoNotStrip @JvmStatic public external fun setAndroidLayoutDirection(): Boolean + @DoNotStrip @JvmStatic public external fun useModernRuntimeScheduler(): Boolean @DoNotStrip @JvmStatic public external fun useNativeViewConfigsInBridgelessMode(): 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 2914764ebfe..149ecce3378 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<<13a5d73dea53b8d4f16946cebc49e684>> + * @generated SignedSource<<4f50dab251c41380a14506975355e49d>> */ /** @@ -57,6 +57,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun preventDoubleTextMeasure(): Boolean = false + override fun setAndroidLayoutDirection(): Boolean = false + override fun useModernRuntimeScheduler(): Boolean = false override fun useNativeViewConfigsInBridgelessMode(): 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 d2e26508e67..4ff04cdf7c1 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<<67f1828f6a4cf9b2cf808be12d3fb06b>> + * @generated SignedSource<<00a695402781a9f3c45ba0594a785b57>> */ /** @@ -41,6 +41,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private var inspectorEnableModernCDPRegistryCache: Boolean? = null private var lazyAnimationCallbacksCache: Boolean? = null private var preventDoubleTextMeasureCache: Boolean? = null + private var setAndroidLayoutDirectionCache: Boolean? = null private var useModernRuntimeSchedulerCache: Boolean? = null private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null private var useStateAlignmentMechanismCache: Boolean? = null @@ -215,6 +216,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun setAndroidLayoutDirection(): Boolean { + var cached = setAndroidLayoutDirectionCache + if (cached == null) { + cached = currentProvider.setAndroidLayoutDirection() + accessedFeatureFlags.add("setAndroidLayoutDirection") + setAndroidLayoutDirectionCache = cached + } + return cached + } + override fun useModernRuntimeScheduler(): Boolean { var cached = useModernRuntimeSchedulerCache if (cached == null) { 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 e1f4174654a..3cd8ba0e743 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<> + * @generated SignedSource<> */ /** @@ -57,6 +57,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun preventDoubleTextMeasure(): Boolean + @DoNotStrip public fun setAndroidLayoutDirection(): Boolean + @DoNotStrip public fun useModernRuntimeScheduler(): Boolean @DoNotStrip public fun useNativeViewConfigsInBridgelessMode(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java index 4e795bf6f23..39a486d26ea 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java @@ -7,6 +7,7 @@ package com.facebook.react.uimanager.drawable; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; @@ -119,7 +120,9 @@ public class CSSBackgroundDrawable extends Drawable { private BorderRadiusStyle mBorderRadius = new BorderRadiusStyle(); private ComputedBorderRadius mComputedBorderRadius = new ComputedBorderRadius(); private final Context mContext; - private int mLayoutDirection; + + // Should be removed after migrating to Android layout direction. + private int mLayoutDirectionOverride = -1; public CSSBackgroundDrawable(Context context) { mContext = context; @@ -163,6 +166,19 @@ public class CSSBackgroundDrawable extends Drawable { // do nothing } + @Deprecated + public void setLayoutDirectionOverride(int layoutDirection) { + if (mLayoutDirectionOverride != layoutDirection) { + mLayoutDirectionOverride = layoutDirection; + } + } + + @Override + @SuppressLint("WrongConstant") + public int getLayoutDirection() { + return mLayoutDirectionOverride == -1 ? super.getLayoutDirection() : mLayoutDirectionOverride; + } + @Override public int getOpacity() { return (Color.alpha(mColor) * mAlpha) >> 8; @@ -292,25 +308,6 @@ public class CSSBackgroundDrawable extends Drawable { invalidateSelf(); } - /** Similar to Drawable.getLayoutDirection, but available in APIs < 23. */ - public int getResolvedLayoutDirection() { - return mLayoutDirection; - } - - /** Similar to Drawable.setLayoutDirection, but available in APIs < 23. */ - public boolean setResolvedLayoutDirection(int layoutDirection) { - if (mLayoutDirection != layoutDirection) { - mLayoutDirection = layoutDirection; - return onResolvedLayoutDirectionChanged(layoutDirection); - } - return false; - } - - /** Similar to Drawable.onLayoutDirectionChanged, but available in APIs < 23. */ - public boolean onResolvedLayoutDirectionChanged(int layoutDirection) { - return false; - } - @VisibleForTesting public int getColor() { return mColor; @@ -392,7 +389,7 @@ public class CSSBackgroundDrawable extends Drawable { // Clip inner border canvas.clipPath(mInnerClipPathForBorderRadius, Region.Op.DIFFERENCE); - final boolean isRTL = getResolvedLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + final boolean isRTL = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; int colorStart = getBorderColor(Spacing.START); int colorEnd = getBorderColor(Spacing.END); @@ -591,7 +588,7 @@ public class CSSBackgroundDrawable extends Drawable { mComputedBorderRadius = mBorderRadius.resolve( - mLayoutDirection, + getLayoutDirection(), mContext, mOuterClipTempRectForBorderRadius.width(), mOuterClipTempRectForBorderRadius.height()); @@ -1080,7 +1077,7 @@ public class CSSBackgroundDrawable extends Drawable { colorTop = colorBlockStart; } - final boolean isRTL = getResolvedLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + final boolean isRTL = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; int colorStart = getBorderColor(Spacing.START); int colorEnd = getBorderColor(Spacing.END); @@ -1305,7 +1302,7 @@ public class CSSBackgroundDrawable extends Drawable { float borderRightWidth = getBorderWidthOrDefaultTo(borderWidth, Spacing.RIGHT); if (mBorderWidth != null) { - final boolean isRTL = getResolvedLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + final boolean isRTL = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; float borderStartWidth = mBorderWidth.getRaw(Spacing.START); float borderEndWidth = mBorderWidth.getRaw(Spacing.END); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java index 3a60050526b..fbb10ecfc40 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java @@ -10,6 +10,7 @@ package com.facebook.react.views.scroll; import android.content.Context; import androidx.core.view.ViewCompat; import com.facebook.infer.annotation.Nullsafe; +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags; import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.views.view.ReactViewGroup; @@ -27,6 +28,14 @@ public class ReactHorizontalScrollContainerView extends ReactViewGroup { : ViewCompat.LAYOUT_DIRECTION_LTR; } + @Override + public int getLayoutDirection() { + if (ReactNativeFeatureFlags.setAndroidLayoutDirection()) { + return super.getLayoutDirection(); + } + return mLayoutDirection; + } + @Override public void setRemoveClippedSubviews(boolean removeClippedSubviews) { // Clipping doesn't work well for horizontal scroll views in RTL mode - in both @@ -34,7 +43,7 @@ public class ReactHorizontalScrollContainerView extends ReactViewGroup { // is TextInputs being blurred immediately after being focused. So, for now, // it's easier to just disable this for these specific RTL views. // TODO T86027499: support `setRemoveClippedSubviews` in RTL mode - if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { + if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) { super.setRemoveClippedSubviews(false); return; } @@ -44,7 +53,7 @@ public class ReactHorizontalScrollContainerView extends ReactViewGroup { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { + if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) { // When the layout direction is RTL, we expect Yoga to give us a layout // that extends off the screen to the left so we re-center it with left=0 int newLeft = 0; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 0c1ef425494..4ffe87f9d30 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -35,6 +35,7 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.infer.annotation.Nullsafe; import com.facebook.react.common.ReactConstants; import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags; import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.uimanager.MeasureSpecAssertions; import com.facebook.react.uimanager.PointerEvents; @@ -1061,7 +1062,10 @@ public class ReactHorizontalScrollView extends HorizontalScrollView int firstOffset = 0; int lastOffset = maximumOffset; int width = getWidth() - ViewCompat.getPaddingStart(this) - ViewCompat.getPaddingEnd(this); - int layoutDirection = getReactScrollViewScrollState().getLayoutDirection(); + int layoutDirection = + ReactNativeFeatureFlags.setAndroidLayoutDirection() + ? getLayoutDirection() + : mReactScrollViewScrollState.getLayoutDirection(); // offsets are from the right edge in RTL layouts if (layoutDirection == LAYOUT_DIRECTION_RTL) { @@ -1385,7 +1389,11 @@ public class ReactHorizontalScrollView extends HorizontalScrollView // does not shift layout. If `maintainVisibleContentPosition` is enabled, we try to adjust // position so that the viewport keeps the same insets to previously visible views. TODO: MVCP // does not work in RTL. - if (mReactScrollViewScrollState.getLayoutDirection() == LAYOUT_DIRECTION_RTL) { + int layoutDirection = + ReactNativeFeatureFlags.setAndroidLayoutDirection() + ? v.getLayoutDirection() + : mReactScrollViewScrollState.getLayoutDirection(); + if (layoutDirection == LAYOUT_DIRECTION_RTL) { adjustPositionForContentChangeRTL(left, right, oldLeft, oldRight); } else if (mMaintainVisibleContentPositionHelper != null) { mMaintainVisibleContentPositionHelper.updateScrollPosition(); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java index ce066636ca8..3114e2cfdc1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java @@ -32,6 +32,7 @@ import com.facebook.react.bridge.ReactNoCrashSoftException; import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.common.annotations.VisibleForTesting; +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags; import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.touch.OnInterceptTouchEventListener; import com.facebook.react.touch.ReactHitSlopView; @@ -129,7 +130,6 @@ public class ReactViewGroup extends ViewGroup private boolean mNeedsOffscreenAlphaCompositing; private @Nullable ViewGroupDrawingOrderHelper mDrawingOrderHelper; private @Nullable Path mPath; - private int mLayoutDirection; private float mBackfaceOpacity; private String mBackfaceVisibility; @@ -159,7 +159,6 @@ public class ReactViewGroup extends ViewGroup mNeedsOffscreenAlphaCompositing = false; mDrawingOrderHelper = null; mPath = null; - mLayoutDirection = 0; // set when background is created mBackfaceOpacity = 1.f; mBackfaceVisibility = "visible"; } @@ -199,13 +198,6 @@ public class ReactViewGroup extends ViewGroup // No-op since UIManagerModule handles actually laying out children. } - @Override - public void onRtlPropertiesChanged(int layoutDirection) { - if (mCSSBackgroundDrawable != null) { - mCSSBackgroundDrawable.setResolvedLayoutDirection(mLayoutDirection); - } - } - @Override @SuppressLint("MissingSuperCall") public void requestLayout() { @@ -806,10 +798,12 @@ public class ReactViewGroup extends ViewGroup new LayerDrawable(new Drawable[] {mCSSBackgroundDrawable, backgroundDrawable}); updateBackgroundDrawable(layerDrawable); } - - mLayoutDirection = - I18nUtil.getInstance().isRTL(getContext()) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR; - mCSSBackgroundDrawable.setResolvedLayoutDirection(mLayoutDirection); + if (!ReactNativeFeatureFlags.setAndroidLayoutDirection()) { + mCSSBackgroundDrawable.setLayoutDirectionOverride( + I18nUtil.getInstance().isRTL(getContext()) + ? LAYOUT_DIRECTION_RTL + : LAYOUT_DIRECTION_LTR); + } } return mCSSBackgroundDrawable; } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp index 61ec7ceaca4..ca193764065 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp @@ -63,7 +63,9 @@ static inline int getIntBufferSizeForType(CppMountItem::Type mountItemType) { case CppMountItem::Type::UpdatePadding: return 5; // tag, top, left, bottom, right case CppMountItem::Type::UpdateLayout: - return 7; // tag, parentTag, x, y, w, h, DisplayType + return ReactNativeFeatureFlags::setAndroidLayoutDirection() + ? 8 // tag, parentTag, x, y, w, h, DisplayType, LayoutDirection + : 7; // tag, parentTag, x, y, w, h, DisplayType case CppMountItem::Type::UpdateOverflowInset: return 5; // tag, left, top, right, bottom case CppMountItem::Undefined: @@ -496,7 +498,7 @@ void FabricMountingManager::executeMount( int intBufferPosition = 0; int objBufferPosition = 0; int prevMountItemType = -1; - jint temp[7]; + jint temp[8]; for (int i = 0; i < cppCommonMountItems.size(); i++) { const auto& mountItem = cppCommonMountItems[i]; const auto& mountItemType = mountItem.type; @@ -656,7 +658,7 @@ void FabricMountingManager::executeMount( intBufferPosition); for (const auto& mountItem : cppUpdateLayoutMountItems) { - auto layoutMetrics = mountItem.newChildShadowView.layoutMetrics; + const auto& layoutMetrics = mountItem.newChildShadowView.layoutMetrics; auto pointScaleFactor = layoutMetrics.pointScaleFactor; auto frame = layoutMetrics.frame; @@ -664,8 +666,8 @@ void FabricMountingManager::executeMount( int y = round(scale(frame.origin.y, pointScaleFactor)); int w = round(scale(frame.size.width, pointScaleFactor)); int h = round(scale(frame.size.height, pointScaleFactor)); - int displayType = - toInt(mountItem.newChildShadowView.layoutMetrics.displayType); + int displayType = toInt(layoutMetrics.displayType); + int layoutDirection = toInt(layoutMetrics.layoutDirection); temp[0] = mountItem.newChildShadowView.tag; temp[1] = mountItem.parentShadowView.tag; @@ -674,8 +676,15 @@ void FabricMountingManager::executeMount( temp[4] = w; temp[5] = h; temp[6] = displayType; - env->SetIntArrayRegion(intBufferArray, intBufferPosition, 7, temp); - intBufferPosition += 7; + + if (ReactNativeFeatureFlags::setAndroidLayoutDirection()) { + temp[7] = layoutDirection; + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 8, temp); + intBufferPosition += 8; + } else { + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 7, temp); + intBufferPosition += 7; + } } } if (!cppUpdateOverflowInsetMountItems.empty()) { 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 be8c264daea..4bfccaff624 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<<5d0f0b1953af3f0dd2ee66fed2b1115a>> */ /** @@ -141,6 +141,12 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool setAndroidLayoutDirection() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("setAndroidLayoutDirection"); + return method(javaProvider_); + } + bool useModernRuntimeScheduler() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("useModernRuntimeScheduler"); @@ -248,6 +254,11 @@ bool JReactNativeFeatureFlagsCxxInterop::preventDoubleTextMeasure( return ReactNativeFeatureFlags::preventDoubleTextMeasure(); } +bool JReactNativeFeatureFlagsCxxInterop::setAndroidLayoutDirection( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::setAndroidLayoutDirection(); +} + bool JReactNativeFeatureFlagsCxxInterop::useModernRuntimeScheduler( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::useModernRuntimeScheduler(); @@ -331,6 +342,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "preventDoubleTextMeasure", JReactNativeFeatureFlagsCxxInterop::preventDoubleTextMeasure), + makeNativeMethod( + "setAndroidLayoutDirection", + JReactNativeFeatureFlagsCxxInterop::setAndroidLayoutDirection), makeNativeMethod( "useModernRuntimeScheduler", JReactNativeFeatureFlagsCxxInterop::useModernRuntimeScheduler), 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 778b3befef4..4b026033b0d 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<> + * @generated SignedSource<> */ /** @@ -81,6 +81,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool preventDoubleTextMeasure( facebook::jni::alias_ref); + static bool setAndroidLayoutDirection( + facebook::jni::alias_ref); + static bool useModernRuntimeScheduler( 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 f14991eddd7..d7df428cd27 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<<5001001b2979f9b759ddac1b035842bf>> + * @generated SignedSource<<71ea09d31186aea96ff207b1015f155e>> */ /** @@ -89,6 +89,10 @@ bool ReactNativeFeatureFlags::preventDoubleTextMeasure() { return getAccessor().preventDoubleTextMeasure(); } +bool ReactNativeFeatureFlags::setAndroidLayoutDirection() { + return getAccessor().setAndroidLayoutDirection(); +} + bool ReactNativeFeatureFlags::useModernRuntimeScheduler() { return getAccessor().useModernRuntimeScheduler(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 061dced7211..eca725c01bc 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<<4dd4bd2c61bff37de19a52b1bb60211c>> + * @generated SignedSource<> */ /** @@ -122,6 +122,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool preventDoubleTextMeasure(); + /** + * Propagate layout direction to Android views. + */ + RN_EXPORT static bool setAndroidLayoutDirection(); + /** * When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with priorities from any thread. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 21365b36ed0..6fabfbab220 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<<7766e3504d137c8dee703a23d776689c>> + * @generated SignedSource<<2e874e6368f85709ecbb4c1e086ae7a9>> */ /** @@ -335,6 +335,24 @@ bool ReactNativeFeatureFlagsAccessor::preventDoubleTextMeasure() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::setAndroidLayoutDirection() { + auto flagValue = setAndroidLayoutDirection_.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(17, "setAndroidLayoutDirection"); + + flagValue = currentProvider_->setAndroidLayoutDirection(); + setAndroidLayoutDirection_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { auto flagValue = useModernRuntimeScheduler_.load(); @@ -344,7 +362,7 @@ bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(17, "useModernRuntimeScheduler"); + markFlagAsAccessed(18, "useModernRuntimeScheduler"); flagValue = currentProvider_->useModernRuntimeScheduler(); useModernRuntimeScheduler_ = flagValue; @@ -362,7 +380,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(18, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(19, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -380,7 +398,7 @@ bool ReactNativeFeatureFlagsAccessor::useStateAlignmentMechanism() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(19, "useStateAlignmentMechanism"); + markFlagAsAccessed(20, "useStateAlignmentMechanism"); flagValue = currentProvider_->useStateAlignmentMechanism(); useStateAlignmentMechanism_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 0483526befe..3c2daeb6ad5 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<<00646bb38ef65efa782e5f601cd91716>> + * @generated SignedSource<<7d1c4c90d5e1feb0015376f20943ee51>> */ /** @@ -48,6 +48,7 @@ class ReactNativeFeatureFlagsAccessor { bool inspectorEnableModernCDPRegistry(); bool lazyAnimationCallbacks(); bool preventDoubleTextMeasure(); + bool setAndroidLayoutDirection(); bool useModernRuntimeScheduler(); bool useNativeViewConfigsInBridgelessMode(); bool useStateAlignmentMechanism(); @@ -61,7 +62,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 20> accessedFeatureFlags_; + std::array, 21> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> allowCollapsableChildren_; @@ -80,6 +81,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> inspectorEnableModernCDPRegistry_; std::atomic> lazyAnimationCallbacks_; std::atomic> preventDoubleTextMeasure_; + std::atomic> setAndroidLayoutDirection_; std::atomic> useModernRuntimeScheduler_; std::atomic> useNativeViewConfigsInBridgelessMode_; std::atomic> useStateAlignmentMechanism_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 7399078817e..c50025ebf06 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<> + * @generated SignedSource<<2bfa4670757aa340c88ae8ff57270308>> */ /** @@ -95,6 +95,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool setAndroidLayoutDirection() override { + return false; + } + bool useModernRuntimeScheduler() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 81fc8ce6c00..319de4e0794 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<> + * @generated SignedSource<> */ /** @@ -42,6 +42,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool inspectorEnableModernCDPRegistry() = 0; virtual bool lazyAnimationCallbacks() = 0; virtual bool preventDoubleTextMeasure() = 0; + virtual bool setAndroidLayoutDirection() = 0; virtual bool useModernRuntimeScheduler() = 0; virtual bool useNativeViewConfigsInBridgelessMode() = 0; virtual bool useStateAlignmentMechanism() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index b8c2fe670ef..29091f3cbde 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<<1fdca69483db9e0fc9f9ab7a2cd9ff85>> */ /** @@ -122,6 +122,11 @@ bool NativeReactNativeFeatureFlags::preventDoubleTextMeasure( return ReactNativeFeatureFlags::preventDoubleTextMeasure(); } +bool NativeReactNativeFeatureFlags::setAndroidLayoutDirection( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::setAndroidLayoutDirection(); +} + bool NativeReactNativeFeatureFlags::useModernRuntimeScheduler( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::useModernRuntimeScheduler(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 696c5591ea8..815380f8988 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<<07b3c6e705bdc70c7eba50d70b0dbf32>> + * @generated SignedSource<<9653b7699416f1702ba2f5e8b8f6da93>> */ /** @@ -69,6 +69,8 @@ class NativeReactNativeFeatureFlags bool preventDoubleTextMeasure(jsi::Runtime& runtime); + bool setAndroidLayoutDirection(jsi::Runtime& runtime); + bool useModernRuntimeScheduler(jsi::Runtime& runtime); bool useNativeViewConfigsInBridgelessMode(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index d124be2d97e..fbc772c54fe 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -118,6 +118,10 @@ const definitions: FeatureFlagDefinitions = { description: 'When enabled, ParagraphShadowNode will no longer call measure twice.', }, + setAndroidLayoutDirection: { + defaultValue: false, + description: 'Propagate layout direction to Android views.', + }, useModernRuntimeScheduler: { defaultValue: false, description: diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 8c4fa4747c0..0378f10607e 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<<7d51e120696413a287b048d6c4dd7b5c>> + * @generated SignedSource<<20c3c50d2124fbfc7bbe434c940ce3bf>> * @flow strict-local */ @@ -57,6 +57,7 @@ export type ReactNativeFeatureFlags = { inspectorEnableModernCDPRegistry: Getter, lazyAnimationCallbacks: Getter, preventDoubleTextMeasure: Getter, + setAndroidLayoutDirection: Getter, useModernRuntimeScheduler: Getter, useNativeViewConfigsInBridgelessMode: Getter, useStateAlignmentMechanism: Getter, @@ -170,6 +171,10 @@ export const lazyAnimationCallbacks: Getter = createNativeFlagGetter('l * When enabled, ParagraphShadowNode will no longer call measure twice. */ export const preventDoubleTextMeasure: Getter = createNativeFlagGetter('preventDoubleTextMeasure', false); +/** + * Propagate layout direction to Android views. + */ +export const setAndroidLayoutDirection: Getter = createNativeFlagGetter('setAndroidLayoutDirection', false); /** * When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with priorities from any thread. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index b4e60a304d5..f1b75954e54 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<> * @flow strict-local */ @@ -40,6 +40,7 @@ export interface Spec extends TurboModule { +inspectorEnableModernCDPRegistry?: () => boolean; +lazyAnimationCallbacks?: () => boolean; +preventDoubleTextMeasure?: () => boolean; + +setAndroidLayoutDirection?: () => boolean; +useModernRuntimeScheduler?: () => boolean; +useNativeViewConfigsInBridgelessMode?: () => boolean; +useStateAlignmentMechanism?: () => boolean;