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 81852d502dd464..05ef66be7bebec 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<<132ff30c4a5ecf6b38dd0d6cc47d3abc>> + * @generated SignedSource<<2ac9938108dfe555fc7e7d875cc21987>> */ /** @@ -312,6 +312,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableViewRecyclingForView(): Boolean = accessor.enableViewRecyclingForView() + /** + * Enables the experimental version of `VirtualViewContainerState`. + */ + @JvmStatic + public fun enableVirtualViewContainerStateExperimental(): Boolean = accessor.enableVirtualViewContainerStateExperimental() + /** * Enables VirtualView debug features such as logging and overlays. */ 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 69780c1932b5ad..a1f1c9868c1adb 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<<38838d89c61124afce1f13045593aeb4>> + * @generated SignedSource<<3cb451816d08f2f3cefb757f1e2ce72a>> */ /** @@ -67,6 +67,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableViewRecyclingForScrollViewCache: Boolean? = null private var enableViewRecyclingForTextCache: Boolean? = null private var enableViewRecyclingForViewCache: Boolean? = null + private var enableVirtualViewContainerStateExperimentalCache: Boolean? = null private var enableVirtualViewDebugFeaturesCache: Boolean? = null private var enableVirtualViewRenderStateCache: Boolean? = null private var enableVirtualViewWindowFocusDetectionCache: Boolean? = null @@ -524,6 +525,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableVirtualViewContainerStateExperimental(): Boolean { + var cached = enableVirtualViewContainerStateExperimentalCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableVirtualViewContainerStateExperimental() + enableVirtualViewContainerStateExperimentalCache = cached + } + return cached + } + override fun enableVirtualViewDebugFeatures(): Boolean { var cached = enableVirtualViewDebugFeaturesCache 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 715c857c064f20..d61d85596d659b 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<> */ /** @@ -122,6 +122,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableViewRecyclingForView(): Boolean + @DoNotStrip @JvmStatic public external fun enableVirtualViewContainerStateExperimental(): Boolean + @DoNotStrip @JvmStatic public external fun enableVirtualViewDebugFeatures(): Boolean @DoNotStrip @JvmStatic public external fun enableVirtualViewRenderState(): 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 ad3f72f5451ea1..1a8cc5d40b8cc4 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<> + * @generated SignedSource<<674f7910fe5dcd750ba2661122c82670>> */ /** @@ -117,6 +117,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableViewRecyclingForView(): Boolean = true + override fun enableVirtualViewContainerStateExperimental(): Boolean = false + override fun enableVirtualViewDebugFeatures(): Boolean = false override fun enableVirtualViewRenderState(): Boolean = true 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 a40d37694d0dca..818587a09b5f57 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<<2fa1e7cd2e1d4009dfa09a5fd27a872a>> + * @generated SignedSource<<5facf1328467d62b41dd9f82c5111882>> */ /** @@ -71,6 +71,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableViewRecyclingForScrollViewCache: Boolean? = null private var enableViewRecyclingForTextCache: Boolean? = null private var enableViewRecyclingForViewCache: Boolean? = null + private var enableVirtualViewContainerStateExperimentalCache: Boolean? = null private var enableVirtualViewDebugFeaturesCache: Boolean? = null private var enableVirtualViewRenderStateCache: Boolean? = null private var enableVirtualViewWindowFocusDetectionCache: Boolean? = null @@ -575,6 +576,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableVirtualViewContainerStateExperimental(): Boolean { + var cached = enableVirtualViewContainerStateExperimentalCache + if (cached == null) { + cached = currentProvider.enableVirtualViewContainerStateExperimental() + accessedFeatureFlags.add("enableVirtualViewContainerStateExperimental") + enableVirtualViewContainerStateExperimentalCache = cached + } + return cached + } + override fun enableVirtualViewDebugFeatures(): Boolean { var cached = enableVirtualViewDebugFeaturesCache 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 468a619ec5df0e..753fbd5601120d 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<<901e5678bff081bcb6b8e2d46364b977>> + * @generated SignedSource<<5a9a51638a72bda657f312260bb1297a>> */ /** @@ -117,6 +117,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableViewRecyclingForView(): Boolean + @DoNotStrip public fun enableVirtualViewContainerStateExperimental(): Boolean + @DoNotStrip public fun enableVirtualViewDebugFeatures(): Boolean @DoNotStrip public fun enableVirtualViewRenderState(): Boolean 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 948285d831acb6..7303a6e8ca388f 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 @@ -216,7 +216,7 @@ private void updateView() {} @Override public VirtualViewContainerState getVirtualViewContainerState() { if (mVirtualViewContainerState == null) { - mVirtualViewContainerState = new VirtualViewContainerState(this); + mVirtualViewContainerState = VirtualViewContainerState.create(this); } return mVirtualViewContainerState; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index f36a586d051921..8197e749d5d581 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -213,7 +213,7 @@ private void updateView() {} @Override public VirtualViewContainerState getVirtualViewContainerState() { if (mVirtualViewContainerState == null) { - mVirtualViewContainerState = new VirtualViewContainerState(this); + mVirtualViewContainerState = VirtualViewContainerState.create(this); } return mVirtualViewContainerState; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainer.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainer.kt index 251aeda09fcbc0..ac9a3bcbe71842 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainer.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainer.kt @@ -22,7 +22,6 @@ internal interface VirtualViewContainer { public interface VirtualView { public val virtualViewID: String - public val containerRelativeRect: Rect public fun onModeChange(newMode: VirtualViewMode, thresholdRect: Rect): Unit @@ -35,7 +34,7 @@ public interface VirtualView { * considered to overlap with another Rect if the line or point is within the rect bounds. However, * two Rects are not considered to overlap if they only share a boundary. */ -private fun rectsOverlap(rect1: Rect, rect2: Rect): Boolean { +internal fun rectsOverlap(rect1: Rect, rect2: Rect): Boolean { if (rect1.top >= rect2.bottom || rect2.top >= rect1.bottom) { // No overlap on the y-axis. return false @@ -47,17 +46,15 @@ private fun rectsOverlap(rect1: Rect, rect2: Rect): Boolean { return true } -internal class VirtualViewContainerState { - - private val prerenderRatio: Double = ReactNativeFeatureFlags.virtualViewPrerenderRatio() - private val hysteresisRatio: Double = ReactNativeFeatureFlags.virtualViewHysteresisRatio() - - private val virtualViews: MutableSet = mutableSetOf() - private val emptyRect: Rect = Rect() - private val visibleRect: Rect = Rect() - private val prerenderRect: Rect = Rect() - private val hysteresisRect: Rect = Rect() - private val onWindowFocusChangeListener = +internal abstract class VirtualViewContainerState { + protected val prerenderRatio: Double = ReactNativeFeatureFlags.virtualViewPrerenderRatio() + protected val hysteresisRatio: Double = ReactNativeFeatureFlags.virtualViewHysteresisRatio() + protected abstract val virtualViews: MutableCollection + protected val emptyRect: Rect = Rect() + protected val visibleRect: Rect = Rect() + protected val prerenderRect: Rect = Rect() + protected val hysteresisRect: Rect = Rect() + protected val onWindowFocusChangeListener = if (ReactNativeFeatureFlags.enableVirtualViewWindowFocusDetection()) { ViewTreeObserver.OnWindowFocusChangeListener { debugLog("onWindowFocusChanged") @@ -66,8 +63,18 @@ internal class VirtualViewContainerState { } else { null } + protected val scrollView: ViewGroup - private val scrollView: ViewGroup + companion object { + @JvmStatic + fun create(scrollView: ViewGroup): VirtualViewContainerState { + return if (ReactNativeFeatureFlags.enableVirtualViewContainerStateExperimental()) { + VirtualViewContainerStateExperimental(scrollView) + } else { + VirtualViewContainerStateClassic(scrollView) + } + } + } constructor(scrollView: ViewGroup) { this.scrollView = scrollView @@ -76,13 +83,13 @@ internal class VirtualViewContainerState { } } - public fun cleanup() { + fun cleanup() { if (onWindowFocusChangeListener != null) { scrollView.viewTreeObserver.removeOnWindowFocusChangeListener(onWindowFocusChangeListener) } } - public fun onChange(virtualView: VirtualView) { + open fun onChange(virtualView: VirtualView) { if (virtualViews.add(virtualView)) { debugLog("add", { "virtualViewID=${virtualView.virtualViewID}" }) } else { @@ -91,7 +98,7 @@ internal class VirtualViewContainerState { updateModes(virtualView) } - public fun remove(virtualView: VirtualView) { + open fun remove(virtualView: VirtualView) { assert(virtualViews.remove(virtualView)) { "Attempting to remove non-existent VirtualView: ${virtualView.virtualViewID}" } @@ -99,83 +106,19 @@ internal class VirtualViewContainerState { } // Called on ScrollView onLayout or onScroll - public fun updateState() { + fun updateState() { debugLog("updateState") updateModes() } - private fun updateModes(virtualView: VirtualView? = null) { - scrollView.getDrawingRect(visibleRect) - - // This happens because ScrollView content isn't ready yet. The danger here is if ScrollView - // intentionally goes but curently ScrollView and v1 Fling use this check to determine if - // "content ready" - if (visibleRect.isEmpty()) { - debugLog("updateModes", { "scrollView visibleRect is empty" }) - return - } - - prerenderRect.set(visibleRect) - prerenderRect.inset( - (-prerenderRect.width() * prerenderRatio).toInt(), - (-prerenderRect.height() * prerenderRatio).toInt(), - ) - - if (hysteresisRatio > 0.0) { - hysteresisRect.set(prerenderRect) - hysteresisRect.inset( - (-visibleRect.width() * hysteresisRatio).toInt(), - (-visibleRect.height() * hysteresisRatio).toInt(), - ) - } - - val virtualViewsIt = - if (virtualView != null) listOf(virtualView) else virtualViews.toMutableSet() - virtualViewsIt.forEach { vv -> - val rect = vv.containerRelativeRect - - var mode: VirtualViewMode? = VirtualViewMode.Hidden - var thresholdRect = emptyRect - when { - rectsOverlap(rect, visibleRect) -> { - thresholdRect = visibleRect - if (onWindowFocusChangeListener != null) { - if (scrollView.hasWindowFocus()) { - mode = VirtualViewMode.Visible - } else { - mode = VirtualViewMode.Prerender - } - } else { - mode = VirtualViewMode.Visible - } - } - rectsOverlap(rect, prerenderRect) -> { - mode = VirtualViewMode.Prerender - thresholdRect = prerenderRect - } - (hysteresisRatio > 0.0 && rectsOverlap(rect, hysteresisRect)) -> { - mode = null - } - } - - if (mode != null) { - vv.onModeChange(mode, thresholdRect) - debugLog( - "updateModes", - { - "virtualView=${vv.virtualViewID} mode=$mode rect=$rect thresholdRect=$thresholdRect" - }, - ) - } - } - } + protected abstract fun updateModes(virtualView: VirtualView? = null) } private const val DEBUG_TAG: String = "VirtualViewContainerState" -private val IS_DEBUG_BUILD = +internal val IS_DEBUG_BUILD = ReactBuildConfig.DEBUG || ReactBuildConfig.IS_INTERNAL_BUILD || ReactBuildConfig.ENABLE_PERFETTO -internal inline fun debugLog(subtag: String, block: () -> String = { "" }) { +private inline fun debugLog(subtag: String, block: () -> String = { "" }) { if (IS_DEBUG_BUILD && ReactNativeFeatureFlags.enableVirtualViewDebugFeatures()) { FLog.d("$DEBUG_TAG:$subtag", block()) } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainerStateClassic.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainerStateClassic.kt new file mode 100644 index 00000000000000..b616cca1c77a41 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainerStateClassic.kt @@ -0,0 +1,92 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.views.scroll + +import android.view.ViewGroup +import com.facebook.common.logging.FLog +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags +import com.facebook.react.views.virtual.VirtualViewMode + +internal class VirtualViewContainerStateClassic(scrollView: ViewGroup) : + VirtualViewContainerState(scrollView) { + + // Provide the concrete implementation for the abstract virtualViews property + override val virtualViews: MutableCollection = mutableSetOf() + + // Implement the abstract updateModes method + override fun updateModes(virtualView: VirtualView?) { + scrollView.getDrawingRect(visibleRect) + + if (visibleRect.isEmpty()) { + debugLog("updateModes", { "scrollView visibleRect is empty" }) + return + } + + prerenderRect.set(visibleRect) + prerenderRect.inset( + (-prerenderRect.width() * prerenderRatio).toInt(), + (-prerenderRect.height() * prerenderRatio).toInt(), + ) + + if (hysteresisRatio > 0.0) { + hysteresisRect.set(prerenderRect) + hysteresisRect.inset( + (-visibleRect.width() * hysteresisRatio).toInt(), + (-visibleRect.height() * hysteresisRatio).toInt(), + ) + } + + val virtualViewsIt = + if (virtualView != null) listOf(virtualView) else virtualViews.toMutableSet() + virtualViewsIt.forEach { vv -> + val rect = vv.containerRelativeRect + + var mode: VirtualViewMode? = VirtualViewMode.Hidden + var thresholdRect = emptyRect + when { + rectsOverlap(rect, visibleRect) -> { + thresholdRect = visibleRect + if (onWindowFocusChangeListener != null) { + if (scrollView.hasWindowFocus()) { + mode = VirtualViewMode.Visible + } else { + mode = VirtualViewMode.Prerender + } + } else { + mode = VirtualViewMode.Visible + } + } + rectsOverlap(rect, prerenderRect) -> { + mode = VirtualViewMode.Prerender + thresholdRect = prerenderRect + } + (hysteresisRatio > 0.0 && rectsOverlap(rect, hysteresisRect)) -> { + mode = null + } + } + + if (mode != null) { + vv.onModeChange(mode, thresholdRect) + debugLog( + "updateModes", + { + "virtualView=${vv.virtualViewID} mode=$mode rect=$rect thresholdRect=$thresholdRect" + }, + ) + } + } + } +} + +private const val DEBUG_TAG: String = "VirtualViewContainerStateClassic" + +private inline fun debugLog(subtag: String, block: () -> String = { "" }) { + if (IS_DEBUG_BUILD && ReactNativeFeatureFlags.enableVirtualViewDebugFeatures()) { + FLog.d("$DEBUG_TAG:$subtag", block()) + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainerStateExperimental.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainerStateExperimental.kt new file mode 100644 index 00000000000000..193d184c9e9c52 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainerStateExperimental.kt @@ -0,0 +1,97 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.views.scroll + +import android.view.ViewGroup +import com.facebook.common.logging.FLog +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags +import com.facebook.react.views.virtual.VirtualViewMode + +/** + * A class that implements VirtualViewContainerState with a more efficient updateModes() algorithm. + * This has been filled out with the Classic implementation for now to support the new Factory + * creation method. + */ +internal class VirtualViewContainerStateExperimental(scrollView: ViewGroup) : + VirtualViewContainerState(scrollView) { + + // Provide the concrete implementation for the abstract virtualViews property + override val virtualViews: MutableCollection = mutableSetOf() + + // Implement the abstract updateModes method + override fun updateModes(virtualView: VirtualView?) { + scrollView.getDrawingRect(visibleRect) + + if (visibleRect.isEmpty()) { + debugLog("updateModes", { "scrollView visibleRect is empty" }) + return + } + + prerenderRect.set(visibleRect) + prerenderRect.inset( + (-prerenderRect.width() * prerenderRatio).toInt(), + (-prerenderRect.height() * prerenderRatio).toInt(), + ) + + if (hysteresisRatio > 0.0) { + hysteresisRect.set(prerenderRect) + hysteresisRect.inset( + (-visibleRect.width() * hysteresisRatio).toInt(), + (-visibleRect.height() * hysteresisRatio).toInt(), + ) + } + + val virtualViewsIt = + if (virtualView != null) listOf(virtualView) else virtualViews.toMutableSet() + virtualViewsIt.forEach { vv -> + val rect = vv.containerRelativeRect + + var mode: VirtualViewMode? = VirtualViewMode.Hidden + var thresholdRect = emptyRect + when { + rectsOverlap(rect, visibleRect) -> { + thresholdRect = visibleRect + if (onWindowFocusChangeListener != null) { + if (scrollView.hasWindowFocus()) { + mode = VirtualViewMode.Visible + } else { + mode = VirtualViewMode.Prerender + } + } else { + mode = VirtualViewMode.Visible + } + } + rectsOverlap(rect, prerenderRect) -> { + mode = VirtualViewMode.Prerender + thresholdRect = prerenderRect + } + (hysteresisRatio > 0.0 && rectsOverlap(rect, hysteresisRect)) -> { + mode = null + } + } + + if (mode != null) { + vv.onModeChange(mode, thresholdRect) + debugLog( + "updateModes", + { + "virtualView=${vv.virtualViewID} mode=$mode rect=$rect thresholdRect=$thresholdRect" + }, + ) + } + } + } +} + +private const val DEBUG_TAG: String = "VirtualViewContainerStateExperimental" + +private inline fun debugLog(subtag: String, block: () -> String = { "" }) { + if (IS_DEBUG_BUILD && ReactNativeFeatureFlags.enableVirtualViewDebugFeatures()) { + FLog.d("$DEBUG_TAG:$subtag", block()) + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental.kt index d5ced13f205ef9..cda516002e2890 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental.kt @@ -20,7 +20,6 @@ import com.facebook.react.uimanager.ReactClippingViewGroup import com.facebook.react.uimanager.ReactRoot import com.facebook.react.views.scroll.VirtualView import com.facebook.react.views.scroll.VirtualViewContainer -import com.facebook.react.views.scroll.debugLog import com.facebook.react.views.view.ReactViewGroup import com.facebook.react.views.virtual.VirtualViewMode import com.facebook.react.views.virtual.VirtualViewModeChangeEmitter @@ -293,7 +292,7 @@ public class ReactVirtualViewExperimental(context: Context) : internal inline fun debugLog(subtag: String, block: () -> String = { "" }) { if (IS_DEBUG_BUILD && ReactNativeFeatureFlags.enableVirtualViewDebugFeatures()) { - FLog.d("$DEBUG_TAG:[$virtualViewID]:$subtag", "${block()}") + FLog.d("$DEBUG_TAG:[$virtualViewID]:$subtag", block()) } } } 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 65cd08f0044f23..ab104ee60faf19 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<<285ec8cc3b3e5f55c5c31106b6df8717>> + * @generated SignedSource<> */ /** @@ -321,6 +321,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableVirtualViewContainerStateExperimental() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableVirtualViewContainerStateExperimental"); + return method(javaProvider_); + } + bool enableVirtualViewDebugFeatures() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableVirtualViewDebugFeatures"); @@ -758,6 +764,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableViewRecyclingForView( return ReactNativeFeatureFlags::enableViewRecyclingForView(); } +bool JReactNativeFeatureFlagsCxxInterop::enableVirtualViewContainerStateExperimental( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableVirtualViewContainerStateExperimental(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableVirtualViewDebugFeatures( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableVirtualViewDebugFeatures(); @@ -1095,6 +1106,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableViewRecyclingForView", JReactNativeFeatureFlagsCxxInterop::enableViewRecyclingForView), + makeNativeMethod( + "enableVirtualViewContainerStateExperimental", + JReactNativeFeatureFlagsCxxInterop::enableVirtualViewContainerStateExperimental), makeNativeMethod( "enableVirtualViewDebugFeatures", JReactNativeFeatureFlagsCxxInterop::enableVirtualViewDebugFeatures), 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 32a967b440b76d..07fcd182139e10 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<<34ad93365b68934d051b0de522a36014>> + * @generated SignedSource<<5789c040408e16ad3e5f79b169084c15>> */ /** @@ -171,6 +171,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableViewRecyclingForView( facebook::jni::alias_ref); + static bool enableVirtualViewContainerStateExperimental( + facebook::jni::alias_ref); + static bool enableVirtualViewDebugFeatures( 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 eed12781ff94ec..1ec39885116869 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<<5fe8801a343267a840956183ca93c757>> + * @generated SignedSource<> */ /** @@ -214,6 +214,10 @@ bool ReactNativeFeatureFlags::enableViewRecyclingForView() { return getAccessor().enableViewRecyclingForView(); } +bool ReactNativeFeatureFlags::enableVirtualViewContainerStateExperimental() { + return getAccessor().enableVirtualViewContainerStateExperimental(); +} + bool ReactNativeFeatureFlags::enableVirtualViewDebugFeatures() { return getAccessor().enableVirtualViewDebugFeatures(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index b6b174c8e26d51..a6c6ac8c15ae70 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<<67cb9ad627e865b24b380be1ef6e0db7>> + * @generated SignedSource<<1067eff3cc68c8aa3177974dd13b7845>> */ /** @@ -274,6 +274,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableViewRecyclingForView(); + /** + * Enables the experimental version of `VirtualViewContainerState`. + */ + RN_EXPORT static bool enableVirtualViewContainerStateExperimental(); + /** * Enables VirtualView debug features such as logging and overlays. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 5d7d8a5b3c2b98..35c9ced61de23e 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<<74fe84795fd187061bb01fca048fbd13>> */ /** @@ -875,6 +875,24 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimental() { + auto flagValue = enableVirtualViewContainerStateExperimental_.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(47, "enableVirtualViewContainerStateExperimental"); + + flagValue = currentProvider_->enableVirtualViewContainerStateExperimental(); + enableVirtualViewContainerStateExperimental_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableVirtualViewDebugFeatures() { auto flagValue = enableVirtualViewDebugFeatures_.load(); @@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewDebugFeatures() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(47, "enableVirtualViewDebugFeatures"); + markFlagAsAccessed(48, "enableVirtualViewDebugFeatures"); flagValue = currentProvider_->enableVirtualViewDebugFeatures(); enableVirtualViewDebugFeatures_ = flagValue; @@ -902,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewRenderState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(48, "enableVirtualViewRenderState"); + markFlagAsAccessed(49, "enableVirtualViewRenderState"); flagValue = currentProvider_->enableVirtualViewRenderState(); enableVirtualViewRenderState_ = flagValue; @@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewWindowFocusDetection() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(49, "enableVirtualViewWindowFocusDetection"); + markFlagAsAccessed(50, "enableVirtualViewWindowFocusDetection"); flagValue = currentProvider_->enableVirtualViewWindowFocusDetection(); enableVirtualViewWindowFocusDetection_ = flagValue; @@ -938,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::enableWebPerformanceAPIsByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(50, "enableWebPerformanceAPIsByDefault"); + markFlagAsAccessed(51, "enableWebPerformanceAPIsByDefault"); flagValue = currentProvider_->enableWebPerformanceAPIsByDefault(); enableWebPerformanceAPIsByDefault_ = flagValue; @@ -956,7 +974,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(51, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(52, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -974,7 +992,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(52, "fuseboxEnabledRelease"); + markFlagAsAccessed(53, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(53, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(54, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(54, "hideOffscreenVirtualViewsOnIOS"); + markFlagAsAccessed(55, "hideOffscreenVirtualViewsOnIOS"); flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS(); hideOffscreenVirtualViewsOnIOS_ = flagValue; @@ -1028,7 +1046,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(55, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(56, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1046,7 +1064,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(56, "perfMonitorV2Enabled"); + markFlagAsAccessed(57, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1064,7 +1082,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(57, "preparedTextCacheSize"); + markFlagAsAccessed(58, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1082,7 +1100,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(58, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(59, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1100,7 +1118,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(59, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(60, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1118,7 +1136,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(60, "shouldTriggerResponderTransferOnScrollAndroid"); + markFlagAsAccessed(61, "shouldTriggerResponderTransferOnScrollAndroid"); flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid(); shouldTriggerResponderTransferOnScrollAndroid_ = flagValue; @@ -1136,7 +1154,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(61, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(62, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::sweepActiveTouchOnChildNativeGesturesAndro // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "sweepActiveTouchOnChildNativeGesturesAndroid"); + markFlagAsAccessed(63, "sweepActiveTouchOnChildNativeGesturesAndroid"); flagValue = currentProvider_->sweepActiveTouchOnChildNativeGesturesAndroid(); sweepActiveTouchOnChildNativeGesturesAndroid_ = flagValue; @@ -1172,7 +1190,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(63, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(64, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1190,7 +1208,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(64, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(65, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1208,7 +1226,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(65, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(66, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1226,7 +1244,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(66, "useFabricInterop"); + markFlagAsAccessed(67, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1244,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroi // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "useNativeEqualsInNativeReadableArrayAndroid"); + markFlagAsAccessed(68, "useNativeEqualsInNativeReadableArrayAndroid"); flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid(); useNativeEqualsInNativeReadableArrayAndroid_ = flagValue; @@ -1262,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "useNativeTransformHelperAndroid"); + markFlagAsAccessed(69, "useNativeTransformHelperAndroid"); flagValue = currentProvider_->useNativeTransformHelperAndroid(); useNativeTransformHelperAndroid_ = flagValue; @@ -1280,7 +1298,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(69, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(70, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1298,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(71, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "useRawPropsJsiValue"); + markFlagAsAccessed(72, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -1334,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "useShadowNodeStateOnClone"); + markFlagAsAccessed(73, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = flagValue; @@ -1352,7 +1370,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(73, "useSharedAnimatedBackend"); + markFlagAsAccessed(74, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1370,7 +1388,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(74, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(75, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1388,7 +1406,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(75, "useTurboModuleInterop"); + markFlagAsAccessed(76, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1406,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(76, "useTurboModules"); + markFlagAsAccessed(77, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1424,7 +1442,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(77, "viewCullingOutsetRatio"); + markFlagAsAccessed(78, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1442,7 +1460,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewHysteresisRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "virtualViewHysteresisRatio"); + markFlagAsAccessed(79, "virtualViewHysteresisRatio"); flagValue = currentProvider_->virtualViewHysteresisRatio(); virtualViewHysteresisRatio_ = flagValue; @@ -1460,7 +1478,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(79, "virtualViewPrerenderRatio"); + markFlagAsAccessed(80, "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 0a555b5b3263dc..1e20fe0f3090ed 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<<1c59c66275538ea4cd0bc47f1e038924>> + * @generated SignedSource<> */ /** @@ -79,6 +79,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableViewRecyclingForScrollView(); bool enableViewRecyclingForText(); bool enableViewRecyclingForView(); + bool enableVirtualViewContainerStateExperimental(); bool enableVirtualViewDebugFeatures(); bool enableVirtualViewRenderState(); bool enableVirtualViewWindowFocusDetection(); @@ -123,7 +124,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 80> accessedFeatureFlags_; + std::array, 81> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -172,6 +173,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableViewRecyclingForScrollView_; std::atomic> enableViewRecyclingForText_; std::atomic> enableViewRecyclingForView_; + std::atomic> enableVirtualViewContainerStateExperimental_; std::atomic> enableVirtualViewDebugFeatures_; std::atomic> enableVirtualViewRenderState_; std::atomic> enableVirtualViewWindowFocusDetection_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index b463244c948d14..30257b4d49297d 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<<5c430e6e6ac1a7272eae8f89f4052193>> + * @generated SignedSource<<0b0473f9301b18ed030398828d4cccc3>> */ /** @@ -215,6 +215,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return true; } + bool enableVirtualViewContainerStateExperimental() override { + return false; + } + bool enableVirtualViewDebugFeatures() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index ef67b0b2c806bd..852d6c37e3ba5f 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<<52167b9f49cce6af156a38967019baff>> + * @generated SignedSource<<64312c960a4285e84f549f866231cfa5>> */ /** @@ -468,6 +468,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableViewRecyclingForView(); } + bool enableVirtualViewContainerStateExperimental() override { + auto value = values_["enableVirtualViewContainerStateExperimental"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableVirtualViewContainerStateExperimental(); + } + bool enableVirtualViewDebugFeatures() override { auto value = values_["enableVirtualViewDebugFeatures"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index b3182788822312..ced491b5234cc8 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<<473f0fb45b0a0dc7ee86c7415daae7e0>> */ /** @@ -72,6 +72,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableViewRecyclingForScrollView() = 0; virtual bool enableViewRecyclingForText() = 0; virtual bool enableViewRecyclingForView() = 0; + virtual bool enableVirtualViewContainerStateExperimental() = 0; virtual bool enableVirtualViewDebugFeatures() = 0; virtual bool enableVirtualViewRenderState() = 0; virtual bool enableVirtualViewWindowFocusDetection() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 9d4cb62f058a21..b8695655aeda8e 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<<8d6ba2c295ca32ced7acd3a0679a3cab>> + * @generated SignedSource<<1ade18325eca7a59607494a15f2efda8>> */ /** @@ -279,6 +279,11 @@ bool NativeReactNativeFeatureFlags::enableViewRecyclingForView( return ReactNativeFeatureFlags::enableViewRecyclingForView(); } +bool NativeReactNativeFeatureFlags::enableVirtualViewContainerStateExperimental( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableVirtualViewContainerStateExperimental(); +} + bool NativeReactNativeFeatureFlags::enableVirtualViewDebugFeatures( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableVirtualViewDebugFeatures(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 16d470427e0b95..02cb690acf2f4d 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<<1c0cdfdfd5d77e602e4b44bb13e7be4b>> + * @generated SignedSource<<5bbc0358328af095c15f0b51c9e97547>> */ /** @@ -130,6 +130,8 @@ class NativeReactNativeFeatureFlags bool enableViewRecyclingForView(jsi::Runtime& runtime); + bool enableVirtualViewContainerStateExperimental(jsi::Runtime& runtime); + bool enableVirtualViewDebugFeatures(jsi::Runtime& runtime); bool enableVirtualViewRenderState(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index f2b8083a880f06..50b0bf6453c4f5 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -549,6 +549,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableVirtualViewContainerStateExperimental: { + defaultValue: false, + metadata: { + dateAdded: '2025-10-09', + description: + 'Enables the experimental version of `VirtualViewContainerState`.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, enableVirtualViewDebugFeatures: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 8961f79ea24740..000bc5d0a2340f 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<<42fe3f524ad5d1e8a565ada8f9eebd24>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -97,6 +97,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableViewRecyclingForScrollView: Getter, enableViewRecyclingForText: Getter, enableViewRecyclingForView: Getter, + enableVirtualViewContainerStateExperimental: Getter, enableVirtualViewDebugFeatures: Getter, enableVirtualViewRenderState: Getter, enableVirtualViewWindowFocusDetection: Getter, @@ -399,6 +400,10 @@ export const enableViewRecyclingForText: Getter = createNativeFlagGette * Enables View Recycling for via ReactViewGroup/ReactViewManager. */ export const enableViewRecyclingForView: Getter = createNativeFlagGetter('enableViewRecyclingForView', true); +/** + * Enables the experimental version of `VirtualViewContainerState`. + */ +export const enableVirtualViewContainerStateExperimental: Getter = createNativeFlagGetter('enableVirtualViewContainerStateExperimental', false); /** * Enables VirtualView debug features such as logging and overlays. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 115f2d00d058ce..1b34aace95563d 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<<5a4824857285ef91a4e2f4aeb589cc5d>> + * @generated SignedSource<<97c319551b68819a08ccd44c49b21978>> * @flow strict * @noformat */ @@ -72,6 +72,7 @@ export interface Spec extends TurboModule { +enableViewRecyclingForScrollView?: () => boolean; +enableViewRecyclingForText?: () => boolean; +enableViewRecyclingForView?: () => boolean; + +enableVirtualViewContainerStateExperimental?: () => boolean; +enableVirtualViewDebugFeatures?: () => boolean; +enableVirtualViewRenderState?: () => boolean; +enableVirtualViewWindowFocusDetection?: () => boolean;