diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/FabricViewStateManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/FabricViewStateManager.java deleted file mode 100644 index 2b096b1bbf0..00000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/FabricViewStateManager.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.uimanager; - -import androidx.annotation.Nullable; -import com.facebook.common.logging.FLog; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableMap; - -/** - * This is a helper base class for ViewGroups that use Fabric State. - * - *

Reason to use this: UpdateState calls from the View layer to the Fabric core can fail, and - * optionally Fabric will call a "failure callback" if that happens. This class abstracts that and - * makes it easier ensure that State in Fabric is always up-to-date. - * - *

1. Whenever ViewManager.updateState is called, call View.setStateWrapper. 2. Instead of - * calling StateWrapper.updateState directly, call View.setState and it will automatically keep - * retrying the UpdateState call until it succeeds; or you call setState again; or the View layer is - * updated with a newer StateWrapper. - */ -public class FabricViewStateManager { - private static final String TAG = "FabricViewStateManager"; - - public interface HasFabricViewStateManager { - FabricViewStateManager getFabricViewStateManager(); - } - - public interface StateUpdateCallback { - WritableMap getStateUpdate(); - } - - @Nullable private StateWrapper mStateWrapper = null; - - public void setStateWrapper(StateWrapper stateWrapper) { - mStateWrapper = stateWrapper; - } - - public boolean hasStateWrapper() { - return mStateWrapper != null; - } - - private void setState( - @Nullable final StateWrapper stateWrapper, - final StateUpdateCallback stateUpdateCallback, - final int numTries) { - // The StateWrapper will change, breaking the async loop, whenever the UpdateState MountItem - // is executed. - // The caller is responsible for detecting if data is up-to-date, and doing nothing, or - // detecting if state is stale and calling setState again. - if (stateWrapper == null) { - FLog.e(TAG, "setState called without a StateWrapper"); - return; - } - if (stateWrapper != mStateWrapper) { - return; - } - // We bail out after an arbitrary number of tries. In practice this should never go higher - // than 2 or 3, but there's nothing guaranteeing that. - if (numTries > 60) { - return; - } - - @Nullable WritableMap stateUpdate = stateUpdateCallback.getStateUpdate(); - if (stateUpdate == null) { - return; - } - - // TODO: State update cannot fail; remove `failureRunnable` and custom retrying logic. - stateWrapper.updateState(stateUpdate); - } - - public void setState(final StateUpdateCallback stateUpdateCallback) { - setState(mStateWrapper, stateUpdateCallback, 0); - } - - public @Nullable ReadableMap getStateData() { - return mStateWrapper != null ? mStateWrapper.getStateData() : null; - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java index 15ce94cb0ad..74c6b8e707c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java @@ -167,7 +167,7 @@ public class ReactModalHostManager extends ViewGroupManager @Override public Object updateState( ReactModalHostView view, ReactStylesDiffMap props, StateWrapper stateWrapper) { - view.getFabricViewStateManager().setStateWrapper(stateWrapper); + view.setStateWrapper(stateWrapper); Point modalSize = ModalHostHelper.getModalHostSize(view.getContext()); view.updateState(modalSize.x, modalSize.y); return null; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java index d6320d42b72..2b3de095d40 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java @@ -36,11 +36,11 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.config.ReactFeatureFlags; -import com.facebook.react.uimanager.FabricViewStateManager; import com.facebook.react.uimanager.JSPointerDispatcher; import com.facebook.react.uimanager.JSTouchDispatcher; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.RootView; +import com.facebook.react.uimanager.StateWrapper; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.events.EventDispatcher; @@ -63,8 +63,7 @@ import java.util.ArrayList; * around addition and removal of views to the DialogRootViewGroup. * */ -public class ReactModalHostView extends ViewGroup - implements LifecycleEventListener, FabricViewStateManager.HasFabricViewStateManager { +public class ReactModalHostView extends ViewGroup implements LifecycleEventListener { private static final String TAG = "ReactModalHost"; @@ -395,11 +394,15 @@ public class ReactModalHostView extends ViewGroup } } - @Override - public FabricViewStateManager getFabricViewStateManager() { - return mHostView.getFabricViewStateManager(); + @Nullable + public StateWrapper getStateWrapper() { + return mHostView.getStateWrapper(); } + public void setStateWrapper(StateWrapper stateWrapper) { + mHostView.setStateWrapper(stateWrapper); + }; + public void updateState(final int width, final int height) { mHostView.updateState(width, height); } @@ -415,14 +418,13 @@ public class ReactModalHostView extends ViewGroup * styleHeight on the LayoutShadowNode to be the window size. This is done through the * UIManagerModule, and will then cause the children to layout as if they can fill the window. */ - static class DialogRootViewGroup extends ReactViewGroup - implements RootView, FabricViewStateManager.HasFabricViewStateManager { + static class DialogRootViewGroup extends ReactViewGroup implements RootView { private boolean hasAdjustedSize = false; private int viewWidth; private int viewHeight; private EventDispatcher mEventDispatcher; - private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager(); + private StateWrapper mStateWrapper = null; private final JSTouchDispatcher mJSTouchDispatcher = new JSTouchDispatcher(this); @Nullable private JSPointerDispatcher mJSPointerDispatcher; @@ -450,7 +452,7 @@ public class ReactModalHostView extends ViewGroup if (getChildCount() > 0) { hasAdjustedSize = false; final int viewTag = getChildAt(0).getId(); - if (mFabricViewStateManager.hasStateWrapper()) { + if (mStateWrapper != null) { // This will only be called under Fabric updateState(viewWidth, viewHeight); } else { @@ -485,7 +487,7 @@ public class ReactModalHostView extends ViewGroup // Check incoming state values. If they're already the correct value, return early to prevent // infinite UpdateState/SetState loop. - ReadableMap currentState = getFabricViewStateManager().getStateData(); + ReadableMap currentState = mStateWrapper.getStateData(); if (currentState != null) { float delta = (float) 0.9; float stateScreenHeight = @@ -501,16 +503,12 @@ public class ReactModalHostView extends ViewGroup } } - mFabricViewStateManager.setState( - new FabricViewStateManager.StateUpdateCallback() { - @Override - public WritableMap getStateUpdate() { - WritableMap map = new WritableNativeMap(); - map.putDouble("screenWidth", realWidth); - map.putDouble("screenHeight", realHeight); - return map; - } - }); + if (mStateWrapper != null) { + WritableMap newStateData = new WritableNativeMap(); + newStateData.putDouble("screenWidth", realWidth); + newStateData.putDouble("screenHeight", realHeight); + mStateWrapper.updateState(newStateData); + } } @Override @@ -589,9 +587,13 @@ public class ReactModalHostView extends ViewGroup // even when some other view disallow that } - @Override - public FabricViewStateManager getFabricViewStateManager() { - return mFabricViewStateManager; + @Nullable + public StateWrapper getStateWrapper() { + return mStateWrapper; + } + + public void setStateWrapper(StateWrapper stateWrapper) { + mStateWrapper = stateWrapper; } } } 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 e5a9e12e4bc..483ff24e0d9 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,18 +35,19 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.react.common.ReactConstants; import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.modules.i18nmanager.I18nUtil; -import com.facebook.react.uimanager.FabricViewStateManager; import com.facebook.react.uimanager.MeasureSpecAssertions; import com.facebook.react.uimanager.PointerEvents; import com.facebook.react.uimanager.ReactClippingViewGroup; import com.facebook.react.uimanager.ReactClippingViewGroupHelper; import com.facebook.react.uimanager.ReactOverflowViewWithInset; +import com.facebook.react.uimanager.StateWrapper; import com.facebook.react.uimanager.ViewProps; import com.facebook.react.uimanager.events.NativeGestureUtil; import com.facebook.react.views.scroll.ReactScrollViewHelper.HasFlingAnimator; import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollEventThrottle; import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollState; import com.facebook.react.views.scroll.ReactScrollViewHelper.HasSmoothScroll; +import com.facebook.react.views.scroll.ReactScrollViewHelper.HasStateWrapper; import com.facebook.react.views.scroll.ReactScrollViewHelper.ReactScrollViewScrollState; import com.facebook.react.views.view.ReactViewBackgroundManager; import java.lang.reflect.Field; @@ -58,9 +59,9 @@ public class ReactHorizontalScrollView extends HorizontalScrollView implements ReactClippingViewGroup, ViewGroup.OnHierarchyChangeListener, View.OnLayoutChangeListener, - FabricViewStateManager.HasFabricViewStateManager, ReactOverflowViewWithInset, HasScrollState, + HasStateWrapper, HasFlingAnimator, HasScrollEventThrottle, HasSmoothScroll { @@ -106,7 +107,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView private boolean mPagedArrowScrolling = false; private int pendingContentOffsetX = UNSET_CONTENT_OFFSET; private int pendingContentOffsetY = UNSET_CONTENT_OFFSET; - private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager(); + private StateWrapper mStateWrapper = null; private final ReactScrollViewScrollState mReactScrollViewScrollState; private final ValueAnimator DEFAULT_FLING_ANIMATOR = ObjectAnimator.ofInt(this, "scrollX", 0, 0); private PointerEvents mPointerEvents = PointerEvents.AUTO; @@ -1355,9 +1356,13 @@ public class ReactHorizontalScrollView extends HorizontalScrollView } } - @Override - public FabricViewStateManager getFabricViewStateManager() { - return mFabricViewStateManager; + @Nullable + public StateWrapper getStateWrapper() { + return mStateWrapper; + } + + public void setStateWrapper(StateWrapper stateWrapper) { + mStateWrapper = stateWrapper; } @Override diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java index f148dcda3be..85f784833b5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java @@ -69,7 +69,7 @@ public class ReactHorizontalScrollViewManager extends ViewGroupManager sScrollListeners = Collections.newSetFromMap(new WeakHashMap()); @@ -337,10 +342,7 @@ public class ReactScrollViewHelper { * by calculate the "would be" initial velocity with internal friction to move to the point (x, * y), then apply that to the animator. */ - public static < - T extends - ViewGroup & FabricViewStateManager.HasFabricViewStateManager & HasScrollState - & HasFlingAnimator> + public static void smoothScrollTo(final T scrollView, final int x, final int y) { if (DEBUG_MODE) { FLog.i(TAG, "smoothScrollTo[%d] x %d y %d", scrollView.getId(), x, y); @@ -370,10 +372,7 @@ public class ReactScrollViewHelper { } /** Get current position or position after current animation finishes, if any. */ - public static < - T extends - ViewGroup & FabricViewStateManager.HasFabricViewStateManager & HasScrollState - & HasFlingAnimator> + public static int getNextFlingStartValue( final T scrollView, final int currentValue, @@ -394,10 +393,7 @@ public class ReactScrollViewHelper { : currentValue; } - public static < - T extends - ViewGroup & FabricViewStateManager.HasFabricViewStateManager & HasScrollState - & HasFlingAnimator> + public static boolean updateFabricScrollState(final T scrollView) { return updateFabricScrollState(scrollView, scrollView.getScrollX(), scrollView.getScrollY()); } @@ -405,10 +401,7 @@ public class ReactScrollViewHelper { /** * Called on any stabilized onScroll change to propagate content offset value to a Shadow Node. */ - public static < - T extends - ViewGroup & FabricViewStateManager.HasFabricViewStateManager & HasScrollState - & HasFlingAnimator> + public static boolean updateFabricScrollState(final T scrollView, final int scrollX, final int scrollY) { if (DEBUG_MODE) { FLog.i( @@ -434,10 +427,7 @@ public class ReactScrollViewHelper { return true; } - public static < - T extends - ViewGroup & FabricViewStateManager.HasFabricViewStateManager & HasScrollState - & HasFlingAnimator> + public static void forceUpdateState(final T scrollView) { final ReactScrollViewScrollState scrollState = scrollView.getReactScrollViewScrollState(); final int scrollAwayPaddingTop = scrollState.getScrollAwayPaddingTop(); @@ -467,26 +457,21 @@ public class ReactScrollViewHelper { fabricScrollX); } - scrollView - .getFabricViewStateManager() - .setState( - new FabricViewStateManager.StateUpdateCallback() { - @Override - public WritableMap getStateUpdate() { - WritableMap map = new WritableNativeMap(); - map.putDouble(CONTENT_OFFSET_LEFT, PixelUtil.toDIPFromPixel(scrollX)); - map.putDouble(CONTENT_OFFSET_TOP, PixelUtil.toDIPFromPixel(scrollY)); - map.putDouble( - SCROLL_AWAY_PADDING_TOP, PixelUtil.toDIPFromPixel(scrollAwayPaddingTop)); - return map; - } - }); + StateWrapper stateWrapper = scrollView.getStateWrapper(); + if (stateWrapper != null) { + WritableMap newStateData = new WritableNativeMap(); + newStateData.putDouble(CONTENT_OFFSET_LEFT, PixelUtil.toDIPFromPixel(scrollX)); + newStateData.putDouble(CONTENT_OFFSET_TOP, PixelUtil.toDIPFromPixel(scrollY)); + newStateData.putDouble( + SCROLL_AWAY_PADDING_TOP, PixelUtil.toDIPFromPixel(scrollAwayPaddingTop)); + stateWrapper.updateState(newStateData); + } } public static < T extends - ViewGroup & FabricViewStateManager.HasFabricViewStateManager & HasScrollState - & HasFlingAnimator & HasScrollEventThrottle> + ViewGroup & HasStateWrapper & HasScrollState & HasFlingAnimator + & HasScrollEventThrottle> void updateStateOnScrollChanged( final T scrollView, final float xVelocity, final float yVelocity) { // Race an UpdateState with every onScroll. This makes it more likely that, in Fabric, @@ -497,10 +482,7 @@ public class ReactScrollViewHelper { emitScrollEvent(scrollView, xVelocity, yVelocity); } - public static < - T extends - ViewGroup & FabricViewStateManager.HasFabricViewStateManager & HasScrollState - & HasFlingAnimator> + public static void registerFlingAnimator(final T scrollView) { scrollView .getFlingAnimator() @@ -530,10 +512,7 @@ public class ReactScrollViewHelper { }); } - public static < - T extends - ViewGroup & FabricViewStateManager.HasFabricViewStateManager & HasScrollState - & HasFlingAnimator> + public static Point predictFinalScrollPosition( final T scrollView, final int velocityX, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java index 33658e78788..af022790a81 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java @@ -337,7 +337,7 @@ public class ReactScrollViewManager extends ViewGroupManager @Override public Object updateState( ReactScrollView view, ReactStylesDiffMap props, StateWrapper stateWrapper) { - view.getFabricViewStateManager().setStateWrapper(stateWrapper); + view.setStateWrapper(stateWrapper); return null; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java index c0f793a8d7d..2a77990f9be 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java @@ -8,7 +8,6 @@ package com.facebook.react.views.textinput; import static com.facebook.react.uimanager.UIManagerHelper.getReactContext; -import static com.facebook.react.views.text.TextAttributeProps.UNSET; import android.content.ClipData; import android.content.ClipboardManager; @@ -50,8 +49,8 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.common.build.ReactBuildConfig; -import com.facebook.react.uimanager.FabricViewStateManager; import com.facebook.react.uimanager.ReactAccessibilityDelegate; +import com.facebook.react.uimanager.StateWrapper; import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.views.text.CustomLetterSpacingSpan; @@ -84,8 +83,7 @@ import java.util.Objects; * called this explicitly. This is the default behavior on other platforms as well. * VisibleForTesting from {@link TextInputEventsTestCase}. */ -public class ReactEditText extends AppCompatEditText - implements FabricViewStateManager.HasFabricViewStateManager { +public class ReactEditText extends AppCompatEditText { private final InputMethodManager mInputMethodManager; private final String TAG = ReactEditText.class.getSimpleName(); public static final boolean DEBUG_MODE = ReactBuildConfig.DEBUG && false; @@ -126,7 +124,7 @@ public class ReactEditText extends AppCompatEditText private ReactViewBackgroundManager mReactBackgroundManager; - private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager(); + private StateWrapper mStateWrapper = null; protected boolean mDisableTextDiffing = false; protected boolean mIsSettingTextFromState = false; @@ -926,9 +924,7 @@ public class ReactEditText extends AppCompatEditText // view, we don't need to construct one or apply it at all - it provides no use in Fabric. ReactContext reactContext = getReactContext(this); - if (mFabricViewStateManager != null - && !mFabricViewStateManager.hasStateWrapper() - && !reactContext.isBridgeless()) { + if (mStateWrapper != null && !reactContext.isBridgeless()) { final ReactTextInputLocalData localData = new ReactTextInputLocalData(this); UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class); @@ -1157,9 +1153,13 @@ public class ReactEditText extends AppCompatEditText } } - @Override - public FabricViewStateManager getFabricViewStateManager() { - return mFabricViewStateManager; + @Nullable + public StateWrapper getStateWrapper() { + return mStateWrapper; + } + + public void setStateWrapper(StateWrapper stateWrapper) { + mStateWrapper = stateWrapper; } /** @@ -1170,7 +1170,7 @@ public class ReactEditText extends AppCompatEditText */ private void updateCachedSpannable() { // Noops in non-Fabric - if (mFabricViewStateManager == null || !mFabricViewStateManager.hasStateWrapper()) { + if (mStateWrapper == null) { return; } // If this view doesn't have an ID yet, we don't have a cache key, so bail here diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index b27ace40cc9..8496a7d059e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -49,7 +49,6 @@ import com.facebook.react.common.ReactConstants; import com.facebook.react.common.mapbuffer.MapBuffer; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.BaseViewManager; -import com.facebook.react.uimanager.FabricViewStateManager; import com.facebook.react.uimanager.LayoutShadowNode; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactStylesDiffMap; @@ -1053,23 +1052,13 @@ public class ReactTextInputManager extends BaseViewManager