diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java index aa3cb0f6fe2190..ccce30a7d60e36 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java @@ -31,6 +31,10 @@ public static JavaOnlyMap of(Object... keysAndValues) { return new JavaOnlyMap(keysAndValues); } + public static JavaOnlyMap from(Map map) { + return new JavaOnlyMap(map); + } + public static JavaOnlyMap deepClone(ReadableMap map) { JavaOnlyMap res = new JavaOnlyMap(); ReadableMapKeySetIterator iter = map.keySetIterator(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.java b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.java index b757de4fc3880c..4115ccc8923f33 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.java +++ b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.java @@ -301,6 +301,36 @@ public boolean equals(@Nullable Object obj) { return thisByteBuffer.equals(otherByteBuffer); } + @Override + public String toString() { + StringBuilder builder = new StringBuilder("{"); + for (MapBufferEntry entry : this) { + int key = entry.getKey(); + builder.append(key); + builder.append('='); + switch (entry.getType()) { + case BOOL: + builder.append(entry.getBoolean()); + break; + case INT: + builder.append(entry.getInt()); + break; + case DOUBLE: + builder.append(entry.getDouble()); + break; + case STRING: + builder.append(entry.getString()); + break; + case MAP: + builder.append(entry.getReadableMapBuffer().toString()); + break; + } + builder.append(','); + } + builder.append('}'); + return builder.toString(); + } + /** @return an {@link Iterator} for the entries of this MapBuffer. */ @Override public Iterator iterator() { @@ -372,7 +402,7 @@ public boolean getBoolean() { } /** @return the String value that is stored in this {@link MapBufferEntry}. */ - public @Nullable String getString() { + public String getString() { assertType(DataType.STRING); return readStringValue(mBucketOffset + VALUE_OFFSET); } @@ -380,7 +410,7 @@ public boolean getBoolean() { /** * @return the {@link ReadableMapBuffer} value that is stored in this {@link MapBufferEntry}. */ - public @Nullable ReadableMapBuffer getReadableMapBuffer() { + public ReadableMapBuffer getReadableMapBuffer() { assertType(DataType.MAP); return readMapBufferValue(mBucketOffset + VALUE_OFFSET); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index daa9a16d700d16..defe20a78c7519 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -704,7 +704,7 @@ private void preallocateView( int rootTag, int reactTag, final String componentName, - @Nullable ReadableMap props, + @Nullable Object props, @Nullable Object stateWrapper, @Nullable Object eventEmitterWrapper, boolean isLayoutable) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.cpp b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.cpp index 2849c112145b3a..eadb1fb1560b33 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.cpp +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.cpp @@ -28,8 +28,11 @@ CppMountItem CppMountItem::RemoveMountItem( int index) { return {CppMountItem::Type::Remove, parentView, shadowView, {}, index}; } -CppMountItem CppMountItem::UpdatePropsMountItem(ShadowView const &shadowView) { - return {CppMountItem::Type::UpdateProps, {}, {}, shadowView, -1}; +CppMountItem CppMountItem::UpdatePropsMountItem( + ShadowView const &oldShadowView, + ShadowView const &newShadowView) { + return { + CppMountItem::Type::UpdateProps, {}, oldShadowView, newShadowView, -1}; } CppMountItem CppMountItem::UpdateStateMountItem(ShadowView const &shadowView) { return {CppMountItem::Type::UpdateState, {}, {}, shadowView, -1}; diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.h b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.h index 4764fafa1ca582..6117ea25a2e072 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.h +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.h @@ -35,7 +35,9 @@ struct CppMountItem final { ShadowView const &shadowView, int index); - static CppMountItem UpdatePropsMountItem(ShadowView const &shadowView); + static CppMountItem UpdatePropsMountItem( + ShadowView const &oldShadowView, + ShadowView const &newShadowView); static CppMountItem UpdateStateMountItem(ShadowView const &shadowView); diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.cpp b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.cpp index 5121593409f4af..f8a5920e48105a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.cpp +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.cpp @@ -8,6 +8,7 @@ #include "FabricMountingManager.h" #include "EventEmitterWrapper.h" #include "StateWrapperImpl.h" +#include "viewPropConversions.h" #include #include @@ -177,11 +178,6 @@ static inline void writeIntBufferTypePreamble( } } -inline local_ref castReadableMap( - local_ref const &nativeMap) { - return make_local(reinterpret_cast(nativeMap.get())); -} - inline local_ref castReadableArray( local_ref const &nativeArray) { return make_local( @@ -222,6 +218,22 @@ static inline float scale(Float value, Float pointScaleFactor) { return result; } +local_ref FabricMountingManager::getProps( + ShadowView const &oldShadowView, + ShadowView const &newShadowView) { + if (useMapBufferForViewProps_ && + newShadowView.traits.check(ShadowNodeTraits::Trait::View)) { + auto oldProps = oldShadowView.props != nullptr + ? static_cast(*oldShadowView.props) + : ViewProps{}; + auto newProps = static_cast(*newShadowView.props); + return ReadableMapBuffer::createWithContents( + viewPropsDiff(oldProps, newProps)); + } else { + return ReadableNativeMap::newObjectCxxArgs(newShadowView.props->rawProps); + } +} + void FabricMountingManager::executeMount( MountingCoordinator::Shared const &mountingCoordinator) { std::lock_guard lock(commitMutex_); @@ -308,7 +320,8 @@ void FabricMountingManager::executeMount( if (!isVirtual) { if (oldChildShadowView.props != newChildShadowView.props) { cppUpdatePropsMountItems.push_back( - CppMountItem::UpdatePropsMountItem(newChildShadowView)); + CppMountItem::UpdatePropsMountItem( + oldChildShadowView, newChildShadowView)); } if (oldChildShadowView.state != newChildShadowView.state) { cppUpdateStateMountItems.push_back( @@ -367,7 +380,7 @@ void FabricMountingManager::executeMount( shouldRememberAllocatedViews_ ? allocationCheck : revisionCheck; if (shouldCreateView) { cppUpdatePropsMountItems.push_back( - CppMountItem::UpdatePropsMountItem(newChildShadowView)); + CppMountItem::UpdatePropsMountItem({}, newChildShadowView)); } // State @@ -484,7 +497,6 @@ void FabricMountingManager::executeMount( // Allocate the intBuffer and object array, now that we know exact sizes // necessary - // TODO: don't allocate at all if size is zero jintArray intBufferArray = env->NewIntArray(batchMountItemIntsSize); local_ref> objBufferArray = JArrayClass::newArray(batchMountItemObjectsSize); @@ -525,10 +537,8 @@ void FabricMountingManager::executeMount( int isLayoutable = mountItem.newChildShadowView.layoutMetrics != EmptyLayoutMetrics ? 1 : 0; - - local_ref props = - castReadableMap(ReadableNativeMap::newObjectCxxArgs( - mountItem.newChildShadowView.props->rawProps)); + local_ref props = + getProps(mountItem.oldChildShadowView, mountItem.newChildShadowView); // Do not hold onto Java object from C // We DO want to hold onto C object from Java, since we don't know the @@ -584,11 +594,8 @@ void FabricMountingManager::executeMount( temp[0] = mountItem.newChildShadowView.tag; env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp); intBufferPosition += 1; - - auto newProps = mountItem.newChildShadowView.props->rawProps; - local_ref newPropsReadableMap = - castReadableMap(ReadableNativeMap::newObjectCxxArgs(newProps)); - (*objBufferArray)[objBufferPosition++] = newPropsReadableMap.get(); + (*objBufferArray)[objBufferPosition++] = + getProps(mountItem.oldChildShadowView, mountItem.newChildShadowView); } } if (!cppUpdateStateMountItems.empty()) { @@ -795,15 +802,11 @@ void FabricMountingManager::preallocateShadowView( bool isLayoutableShadowNode = shadowView.layoutMetrics != EmptyLayoutMetrics; - static auto preallocateView = jni::findClassStatic(UIManagerJavaDescriptor) - ->getMethod("preallocateView"); + static auto preallocateView = + jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod( + "preallocateView"); // Do not hold onto Java object from C // We DO want to hold onto C object from Java, since we don't know the @@ -826,8 +829,8 @@ void FabricMountingManager::preallocateShadowView( } } - local_ref props = castReadableMap( - ReadableNativeMap::newObjectCxxArgs(shadowView.props->rawProps)); + local_ref props = getProps({}, shadowView); + auto component = getPlatformComponentName(shadowView); preallocateView( @@ -942,6 +945,8 @@ FabricMountingManager::FabricMountingManager( useOverflowInset_ = doesUseOverflowInset(); shouldRememberAllocatedViews_ = config->getBool( "react_native_new_architecture:remember_views_on_mount_android"); + useMapBufferForViewProps_ = config->getBool( + "react_native_new_architecture:use_mapbuffer_for_viewprops"); } } // namespace react diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.h b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.h index 618d24154b7113..3fd1606a0bb9b3 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.h +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.h @@ -76,6 +76,11 @@ class FabricMountingManager { bool disableRevisionCheckForPreallocation_{false}; bool useOverflowInset_{false}; bool shouldRememberAllocatedViews_{false}; + bool useMapBufferForViewProps_{false}; + + jni::local_ref getProps( + ShadowView const &oldShadowView, + ShadowView const &newShadowView); }; } // namespace react diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java index 3c7311af3f868d..159a27e597532f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java @@ -26,6 +26,7 @@ import com.facebook.react.bridge.SoftAssertions; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.common.mapbuffer.ReadableMapBuffer; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.fabric.events.EventEmitterWrapper; import com.facebook.react.fabric.mounting.MountingManager.MountItemExecutor; @@ -42,6 +43,8 @@ import com.facebook.react.uimanager.ViewGroupManager; import com.facebook.react.uimanager.ViewManager; import com.facebook.react.uimanager.ViewManagerRegistry; +import com.facebook.react.views.view.ReactMapBufferViewManager; +import com.facebook.react.views.view.ReactViewManagerWrapper; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -163,7 +166,13 @@ private void addRootView(@NonNull final View rootView) { return; } - mTagToViewState.put(mSurfaceId, new ViewState(mSurfaceId, rootView, mRootViewManager, true)); + mTagToViewState.put( + mSurfaceId, + new ViewState( + mSurfaceId, + rootView, + new ReactViewManagerWrapper.DefaultViewManager((ViewManager) mRootViewManager), + true)); Runnable runnable = new Runnable() { @@ -534,7 +543,7 @@ public void run() { public void createView( @NonNull String componentName, int reactTag, - @Nullable ReadableMap props, + @Nullable Object props, @Nullable StateWrapper stateWrapper, @Nullable EventEmitterWrapper eventEmitterWrapper, boolean isLayoutable) { @@ -572,41 +581,48 @@ public void createView( public void createViewUnsafe( @NonNull String componentName, int reactTag, - @Nullable ReadableMap props, + @Nullable Object props, @Nullable StateWrapper stateWrapper, @Nullable EventEmitterWrapper eventEmitterWrapper, boolean isLayoutable) { View view = null; - ViewManager viewManager = null; + ReactViewManagerWrapper viewManager = null; - ReactStylesDiffMap propsDiffMap = null; - if (props != null) { - propsDiffMap = new ReactStylesDiffMap(props); + Object propMap; + if (props instanceof ReadableMap) { + propMap = new ReactStylesDiffMap((ReadableMap) props); + } else { + propMap = props; } if (isLayoutable) { - viewManager = mViewManagerRegistry.get(componentName); + viewManager = + props instanceof ReadableMapBuffer + ? ReactMapBufferViewManager.INSTANCE + : new ReactViewManagerWrapper.DefaultViewManager( + mViewManagerRegistry.get(componentName)); // View Managers are responsible for dealing with initial state and props. view = viewManager.createView( - reactTag, mThemedReactContext, propsDiffMap, stateWrapper, mJSResponderHandler); + reactTag, mThemedReactContext, propMap, stateWrapper, mJSResponderHandler); } ViewState viewState = new ViewState(reactTag, view, viewManager); - viewState.mCurrentProps = propsDiffMap; + viewState.mCurrentProps = propMap; viewState.mStateWrapper = stateWrapper; viewState.mEventEmitter = eventEmitterWrapper; mTagToViewState.put(reactTag, viewState); } - public void updateProps(int reactTag, ReadableMap props) { + public void updateProps(int reactTag, Object props) { if (isStopped()) { return; } ViewState viewState = getViewState(reactTag); - viewState.mCurrentProps = new ReactStylesDiffMap(props); + viewState.mCurrentProps = + props instanceof ReadableMap ? new ReactStylesDiffMap((ReadableMap) props) : props; View view = viewState.mView; if (view == null) { @@ -751,7 +767,7 @@ public void updatePadding(int reactTag, int left, int top, int right, int bottom throw new IllegalStateException("Unable to find View for tag: " + reactTag); } - ViewManager viewManager = viewState.mViewManager; + ReactViewManagerWrapper viewManager = viewState.mViewManager; if (viewManager == null) { throw new IllegalStateException("Unable to find ViewManager for view: " + viewState); } @@ -801,7 +817,7 @@ public void updateState(final int reactTag, @Nullable StateWrapper stateWrapper) StateWrapper prevStateWrapper = viewState.mStateWrapper; viewState.mStateWrapper = stateWrapper; - ViewManager viewManager = viewState.mViewManager; + ReactViewManagerWrapper viewManager = viewState.mViewManager; if (viewManager == null) { throw new IllegalStateException("Unable to find ViewManager for tag: " + reactTag); @@ -891,7 +907,7 @@ private void onViewStateDeleted(ViewState viewState) { } // For non-root views we notify viewmanager with {@link ViewManager#onDropInstance} - ViewManager viewManager = viewState.mViewManager; + ReactViewManagerWrapper viewManager = viewState.mViewManager; if (!viewState.mIsRoot && viewManager != null) { viewManager.onDropViewInstance(viewState.mView); } @@ -926,7 +942,7 @@ public void deleteView(int reactTag) { public void preallocateView( String componentName, int reactTag, - @Nullable ReadableMap props, + @Nullable Object props, @Nullable StateWrapper stateWrapper, @Nullable EventEmitterWrapper eventEmitterWrapper, boolean isLayoutable) { @@ -984,7 +1000,7 @@ public View getView(int reactTag) { if (viewState.mViewManager == null) { throw new IllegalStateException("Unable to find ViewManager for view: " + viewState); } - return (ViewGroupManager) viewState.mViewManager; + return (ViewGroupManager) viewState.mViewManager.getViewGroupManager(); } public void printSurfaceState() { @@ -1014,17 +1030,22 @@ private static class ViewState { @Nullable final View mView; final int mReactTag; final boolean mIsRoot; - @Nullable final ViewManager mViewManager; - @Nullable public ReactStylesDiffMap mCurrentProps = null; + @Nullable final ReactViewManagerWrapper mViewManager; + @Nullable public Object mCurrentProps = null; @Nullable public ReadableMap mCurrentLocalData = null; @Nullable public StateWrapper mStateWrapper = null; @Nullable public EventEmitterWrapper mEventEmitter = null; - private ViewState(int reactTag, @Nullable View view, @Nullable ViewManager viewManager) { + private ViewState( + int reactTag, @Nullable View view, @Nullable ReactViewManagerWrapper viewManager) { this(reactTag, view, viewManager, false); } - private ViewState(int reactTag, @Nullable View view, ViewManager viewManager, boolean isRoot) { + private ViewState( + int reactTag, + @Nullable View view, + @Nullable ReactViewManagerWrapper viewManager, + boolean isRoot) { mReactTag = reactTag; mView = view; mIsRoot = isRoot; diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.java index a8fae7d252dfe7..76154a085787bd 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.java @@ -16,7 +16,6 @@ import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.ReactMarker; import com.facebook.react.bridge.ReactMarkerConstants; -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; @@ -102,10 +101,6 @@ private static StateWrapper castToState(Object obj) { return obj != null ? (StateWrapper) obj : null; } - private static ReadableMap castToProps(Object obj) { - return obj != null ? (ReadableMap) obj : null; - } - private static EventEmitterWrapper castToEventEmitter(Object obj) { return obj != null ? (EventEmitterWrapper) obj : null; } @@ -141,7 +136,7 @@ public void execute(@NonNull MountingManager mountingManager) { surfaceMountingManager.createView( componentName, mIntBuffer[i++], - castToProps(mObjBuffer[j++]), + mObjBuffer[j++], castToState(mObjBuffer[j++]), castToEventEmitter(mObjBuffer[j++]), mIntBuffer[i++] == 1); @@ -154,7 +149,7 @@ public void execute(@NonNull MountingManager mountingManager) { } else if (type == INSTRUCTION_REMOVE) { surfaceMountingManager.removeViewAt(mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++]); } else if (type == INSTRUCTION_UPDATE_PROPS) { - surfaceMountingManager.updateProps(mIntBuffer[i++], castToProps(mObjBuffer[j++])); + surfaceMountingManager.updateProps(mIntBuffer[i++], mObjBuffer[j++]); } else if (type == INSTRUCTION_UPDATE_STATE) { surfaceMountingManager.updateState(mIntBuffer[i++], castToState(mObjBuffer[j++])); } else if (type == INSTRUCTION_UPDATE_LAYOUT) { @@ -234,10 +229,10 @@ public String toString() { String.format( "REMOVE [%d]->[%d] @%d\n", mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++])); } else if (type == INSTRUCTION_UPDATE_PROPS) { - ReadableMap props = castToProps(mObjBuffer[j++]); + Object props = mObjBuffer[j++]; String propsString = IS_DEVELOPMENT_ENVIRONMENT - ? (props != null ? props.toHashMap().toString() : "") + ? (props != null ? props.toString() : "") : ""; s.append(String.format("UPDATE PROPS [%d]: %s\n", mIntBuffer[i++], propsString)); } else if (type == INSTRUCTION_UPDATE_STATE) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PreAllocateViewMountItem.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PreAllocateViewMountItem.java index babc0142bdba1a..0cd7b721ce9278 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PreAllocateViewMountItem.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PreAllocateViewMountItem.java @@ -13,7 +13,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; -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; @@ -25,7 +24,7 @@ public class PreAllocateViewMountItem implements MountItem { @NonNull private final String mComponent; private final int mSurfaceId; private final int mReactTag; - private final @Nullable ReadableMap mProps; + private final @Nullable Object mProps; private final @Nullable StateWrapper mStateWrapper; private final @Nullable EventEmitterWrapper mEventEmitterWrapper; private final boolean mIsLayoutable; @@ -34,7 +33,7 @@ public PreAllocateViewMountItem( int surfaceId, int reactTag, @NonNull String component, - @Nullable ReadableMap props, + @Nullable Object props, @NonNull StateWrapper stateWrapper, @Nullable EventEmitterWrapper eventEmitterWrapper, boolean isLayoutable) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/view/BUCK index 8dc27557662720..b8cf424594bf96 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/BUCK @@ -2,16 +2,21 @@ load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "r rn_android_library( name = "view", - srcs = glob(["*.java"]), + srcs = glob([ + "*.java", + "*.kt", + ]), autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], + language = "KOTLIN", provided_deps = [ react_native_dep("third-party/android/androidx:core"), react_native_dep("third-party/android/androidx:fragment"), react_native_dep("third-party/android/androidx:legacy-support-core-ui"), react_native_dep("third-party/android/androidx:legacy-support-core-utils"), ], + pure_kotlin = False, visibility = [ "PUBLIC", ], @@ -23,6 +28,7 @@ rn_android_library( react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), + react_native_target("java/com/facebook/react/common/mapbuffer:mapbuffer"), react_native_target("java/com/facebook/react/config:config"), react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/touch:touch"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactMapBufferPropSetter.kt b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactMapBufferPropSetter.kt new file mode 100644 index 00000000000000..11a281778c80b4 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactMapBufferPropSetter.kt @@ -0,0 +1,474 @@ +/* + * 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.view + +import android.graphics.Color +import android.graphics.Rect +import androidx.core.view.ViewCompat +import com.facebook.react.bridge.DynamicFromObject +import com.facebook.react.bridge.JavaOnlyArray +import com.facebook.react.bridge.JavaOnlyMap +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.common.mapbuffer.ReadableMapBuffer +import com.facebook.react.uimanager.PixelUtil +import com.facebook.react.uimanager.PointerEvents + +object ReactMapBufferPropSetter { + // ViewProps values + private const val VP_ACCESSIBILITY_ACTIONS = 0 + private const val VP_ACCESSIBILITY_HINT = 1 + private const val VP_ACCESSIBILITY_LABEL = 2 + private const val VP_ACCESSIBILITY_LABELLED_BY = 3 + private const val VP_ACCESSIBILITY_LIVE_REGION = 4 + private const val VP_ACCESSIBILITY_ROLE = 5 + private const val VP_ACCESSIBILITY_STATE = 6 + private const val VP_ACCESSIBILITY_VALUE = 7 + private const val VP_ACCESSIBLE = 8 + private const val VP_BACKFACE_VISIBILITY = 9 + private const val VP_BG_COLOR = 10 + private const val VP_BORDER_COLOR = 11 + private const val VP_BORDER_RADII = 12 + private const val VP_BORDER_STYLE = 13 + private const val VP_COLLAPSABLE = 14 + private const val VP_ELEVATION = 15 + private const val VP_FOCUSABLE = 16 + private const val VP_HAS_TV_FOCUS = 17 + private const val VP_HIT_SLOP = 18 + private const val VP_IMPORTANT_FOR_ACCESSIBILITY = 19 + private const val VP_NATIVE_BACKGROUND = 20 + private const val VP_NATIVE_FOREGROUND = 21 + private const val VP_NATIVE_ID = 22 + private const val VP_OFFSCREEN_ALPHA_COMPOSITING = 23 + private const val VP_OPACITY = 24 + private const val VP_POINTER_EVENTS = 25 + private const val VP_POINTER_ENTER = 26 + private const val VP_POINTER_LEAVE = 27 + private const val VP_POINTER_MOVE = 28 + private const val VP_REMOVE_CLIPPED_SUBVIEW = 29 + private const val VP_RENDER_TO_HARDWARE_TEXTURE = 30 + private const val VP_SHADOW_COLOR = 31 + private const val VP_TEST_ID = 32 + private const val VP_TRANSFORM = 33 + private const val VP_ZINDEX = 34 + + // Yoga values + private const val YG_BORDER_WIDTH = 100 + private const val YG_OVERFLOW = 101 + + // AccessibilityAction values + private const val ACCESSIBILITY_ACTION_NAME = 0 + private const val ACCESSIBILITY_ACTION_LABEL = 1 + + // AccessibilityState values + private const val ACCESSIBILITY_STATE_BUSY = 0 + private const val ACCESSIBILITY_STATE_DISABLED = 1 + private const val ACCESSIBILITY_STATE_EXPANDED = 2 + private const val ACCESSIBILITY_STATE_SELECTED = 3 + private const val ACCESSIBILITY_STATE_CHECKED = 4 + + private const val EDGE_TOP = 0 + private const val EDGE_LEFT = 1 + private const val EDGE_RIGHT = 2 + private const val EDGE_BOTTOM = 3 + private const val EDGE_START = 4 + private const val EDGE_END = 5 + private const val EDGE_ALL = 6 + + private const val CORNER_TOP_LEFT = 0 + private const val CORNER_TOP_RIGHT = 1 + private const val CORNER_BOTTOM_RIGHT = 2 + private const val CORNER_BOTTOM_LEFT = 3 + private const val CORNER_TOP_START = 4 + private const val CORNER_TOP_END = 5 + private const val CORNER_BOTTOM_END = 6 + private const val CORNER_BOTTOM_START = 7 + private const val CORNER_ALL = 8 + + private const val NATIVE_DRAWABLE_KIND = 0 + private const val NATIVE_DRAWABLE_ATTRIBUTE = 1 + private const val NATIVE_DRAWABLE_COLOR = 2 + private const val NATIVE_DRAWABLE_BORDERLESS = 3 + private const val NATIVE_DRAWABLE_RIPPLE_RADIUS = 4 + + private const val UNDEF_COLOR = Int.MAX_VALUE + + fun setProps(view: ReactViewGroup, viewManager: ReactViewManager, props: ReadableMapBuffer) { + for (entry in props) { + when (entry.key) { + VP_ACCESSIBILITY_ACTIONS -> { + viewManager.accessibilityActions(view, entry.readableMapBuffer) + } + VP_ACCESSIBILITY_HINT -> { + viewManager.setAccessibilityHint(view, entry.string.takeIf { it.isNotEmpty() }) + } + VP_ACCESSIBILITY_LABEL -> { + viewManager.setAccessibilityLabel(view, entry.string.takeIf { it.isNotEmpty() }) + } + VP_ACCESSIBILITY_LABELLED_BY -> { + viewManager.accessibilityLabelledBy(view, entry.readableMapBuffer) + } + VP_ACCESSIBILITY_LIVE_REGION -> { + view.accessibilityLiveRegion(entry.int) + } + VP_ACCESSIBILITY_ROLE -> { + viewManager.setAccessibilityRole(view, entry.string.takeIf { it.isNotEmpty() }) + } + VP_ACCESSIBILITY_STATE -> { + viewManager.accessibilityState(view, entry.readableMapBuffer) + } + VP_ACCESSIBILITY_VALUE -> { + viewManager.accessibilityValue(view, entry.string) + } + VP_ACCESSIBLE -> { + viewManager.setAccessible(view, entry.boolean) + } + VP_BACKFACE_VISIBILITY -> { + viewManager.backfaceVisibility(view, entry.int) + } + VP_BG_COLOR -> { + // TODO: color for some reason can be object in Java but not in C++ + viewManager.backgroundColor(view, entry.int) + } + VP_BORDER_COLOR -> { + viewManager.borderColor(view, entry.readableMapBuffer) + } + VP_BORDER_RADII -> { + viewManager.borderRadius(view, entry.readableMapBuffer) + } + VP_BORDER_STYLE -> { + viewManager.borderStyle(view, entry.int) + } + VP_ELEVATION -> { + viewManager.setElevation(view, entry.double.toFloat()) + } + VP_FOCUSABLE -> { + viewManager.setFocusable(view, entry.boolean) + } + VP_HAS_TV_FOCUS -> { + viewManager.setTVPreferredFocus(view, entry.boolean) + } + VP_HIT_SLOP -> { + view.hitSlop(entry.readableMapBuffer) + } + VP_IMPORTANT_FOR_ACCESSIBILITY -> { + view.importantForAccessibility(entry.int) + } + VP_NATIVE_BACKGROUND -> { + viewManager.nativeBackground(view, entry.readableMapBuffer) + } + VP_NATIVE_FOREGROUND -> { + viewManager.nativeForeground(view, entry.readableMapBuffer) + } + VP_NATIVE_ID -> { + viewManager.setNativeId(view, entry.string.takeIf { it.isNotEmpty() }) + } + VP_OFFSCREEN_ALPHA_COMPOSITING -> { + viewManager.setNeedsOffscreenAlphaCompositing(view, entry.boolean) + } + VP_OPACITY -> { + viewManager.setOpacity(view, entry.double.toFloat()) + } + VP_POINTER_EVENTS -> { + view.pointerEvents(entry.int) + } + VP_POINTER_ENTER -> { + viewManager.setPointerEnter(view, entry.boolean) + } + VP_POINTER_LEAVE -> { + viewManager.setPointerLeave(view, entry.boolean) + } + VP_POINTER_MOVE -> { + viewManager.setPointerMove(view, entry.boolean) + } + VP_REMOVE_CLIPPED_SUBVIEW -> { + viewManager.setRemoveClippedSubviews(view, entry.boolean) + } + VP_RENDER_TO_HARDWARE_TEXTURE -> { + viewManager.setRenderToHardwareTexture(view, entry.boolean) + } + VP_SHADOW_COLOR -> { + // TODO: color for some reason can be object in Java but not in C++ + viewManager.shadowColor(view, entry.int) + } + VP_TEST_ID -> { + viewManager.setTestId(view, entry.string.takeIf { it.isNotEmpty() }) + } + VP_TRANSFORM -> { + viewManager.transform(view, entry.readableMapBuffer) + } + VP_ZINDEX -> { + viewManager.setZIndex(view, entry.int.toFloat()) + } + YG_BORDER_WIDTH -> { + viewManager.borderWidth(view, entry.readableMapBuffer) + } + YG_OVERFLOW -> { + viewManager.overflow(view, entry.int) + } + } + } + } + + private fun ReactViewManager.accessibilityActions( + view: ReactViewGroup, + mapBuffer: ReadableMapBuffer + ) { + val actions = mutableListOf() + for (entry in mapBuffer) { + val map = JavaOnlyMap() + val action = entry.readableMapBuffer + if (action != null) { + map.putString("name", action.getString(ACCESSIBILITY_ACTION_NAME)) + if (action.hasKey(ACCESSIBILITY_ACTION_LABEL)) { + map.putString("label", action.getString(ACCESSIBILITY_ACTION_LABEL)) + } + } + actions.add(map) + } + + setAccessibilityActions(view, JavaOnlyArray.from(actions)) + } + + private fun ReactViewGroup.accessibilityLiveRegion(value: Int) { + val mode = + when (value) { + 0 -> ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE + 1 -> ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE + 2 -> ViewCompat.ACCESSIBILITY_LIVE_REGION_ASSERTIVE + else -> ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE + } + ViewCompat.setAccessibilityLiveRegion(this, mode) + } + + private fun ReactViewManager.accessibilityState(view: ReactViewGroup, value: ReadableMapBuffer) { + val accessibilityState = JavaOnlyMap() + accessibilityState.putBoolean("selected", value.getBoolean(ACCESSIBILITY_STATE_SELECTED)) + accessibilityState.putBoolean("busy", value.getBoolean(ACCESSIBILITY_STATE_BUSY)) + accessibilityState.putBoolean("expanded", value.getBoolean(ACCESSIBILITY_STATE_EXPANDED)) + accessibilityState.putBoolean("disabled", value.getBoolean(ACCESSIBILITY_STATE_DISABLED)) + + when (value.getInt(ACCESSIBILITY_STATE_CHECKED)) { + // Unchecked + 0 -> accessibilityState.putBoolean("checked", false) + // Checked + 1 -> accessibilityState.putBoolean("checked", true) + // Mixed + 2 -> accessibilityState.putString("checked", "mixed") + // 3 -> None + } + + setViewState(view, accessibilityState) + } + + private fun ReactViewManager.accessibilityValue(view: ReactViewGroup, value: String) { + val map = JavaOnlyMap() + if (value.isNotEmpty()) { + map.putString("text", value) + } + setAccessibilityValue(view, map) + } + + private fun ReactViewManager.accessibilityLabelledBy( + view: ReactViewGroup, + value: ReadableMapBuffer + ) { + val converted = + if (value.count == 0) { + DynamicFromObject(null) + } else { + val array = JavaOnlyArray() + for (label in value) { + array.pushString(label.string) + } + DynamicFromObject(array) + } + + setAccessibilityLabelledBy(view, converted) + } + + private fun ReactViewManager.backfaceVisibility(view: ReactViewGroup, value: Int) { + val stringName = + when (value) { + 1 -> "visible" + 2 -> "hidden" + else -> "auto" + } + setBackfaceVisibility(view, stringName) + } + + private fun ReactViewManager.backgroundColor(view: ReactViewGroup, value: Int) { + val color = value.takeIf { it != UNDEF_COLOR } ?: Color.TRANSPARENT + setBackgroundColor(view, color) + } + + private fun ReactViewManager.borderColor(view: ReactViewGroup, value: ReadableMapBuffer) { + for (entry in value) { + val index = + when (val key = entry.key) { + EDGE_ALL -> 0 + EDGE_LEFT -> 1 + EDGE_RIGHT -> 2 + EDGE_TOP -> 3 + EDGE_BOTTOM -> 4 + EDGE_START -> 5 + EDGE_END -> 6 + else -> throw IllegalArgumentException("Unknown key for border color: $key") + } + val colorValue = entry.int + setBorderColor(view, index, colorValue.takeIf { it != -1 }) + } + } + + private fun ReactViewManager.borderRadius(view: ReactViewGroup, value: ReadableMapBuffer) { + for (entry in value) { + val index = + when (val key = entry.key) { + CORNER_ALL -> 0 + CORNER_TOP_LEFT -> 1 + CORNER_TOP_RIGHT -> 2 + CORNER_BOTTOM_RIGHT -> 3 + CORNER_BOTTOM_LEFT -> 4 + CORNER_TOP_START -> 5 + CORNER_TOP_END -> 6 + CORNER_BOTTOM_START -> 7 + CORNER_BOTTOM_END -> 8 + else -> throw IllegalArgumentException("Unknown key for border style: $key") + } + val borderRadius = entry.double + if (!borderRadius.isNaN()) { + setBorderRadius(view, index, borderRadius.toFloat()) + } + } + } + + private fun ReactViewManager.borderStyle(view: ReactViewGroup, value: Int) { + val stringValue = + when (value) { + 0 -> "solid" + 1 -> "dotted" + 2 -> "dashed" + else -> null + } + setBorderStyle(view, stringValue) + } + + private fun ReactViewGroup.hitSlop(value: ReadableMapBuffer) { + val rect = + Rect( + PixelUtil.toPixelFromDIP(value.getDouble(EDGE_LEFT)).toInt(), + PixelUtil.toPixelFromDIP(value.getDouble(EDGE_TOP)).toInt(), + PixelUtil.toPixelFromDIP(value.getDouble(EDGE_RIGHT)).toInt(), + PixelUtil.toPixelFromDIP(value.getDouble(EDGE_BOTTOM)).toInt(), + ) + hitSlopRect = rect + } + + private fun ReactViewGroup.importantForAccessibility(value: Int) { + val mode = + when (value) { + 0 -> ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO + 1 -> ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES + 2 -> ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO + 3 -> ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS + else -> ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO + } + ViewCompat.setImportantForAccessibility(this, mode) + } + + private fun ReactViewGroup.pointerEvents(value: Int) { + val pointerEvents = + when (value) { + 0 -> PointerEvents.AUTO + 1 -> PointerEvents.NONE + 2 -> PointerEvents.BOX_NONE + 3 -> PointerEvents.BOX_ONLY + else -> throw IllegalArgumentException("Unknown value for pointer events: $value") + } + setPointerEvents(pointerEvents) + } + + private fun ReactViewManager.transform(view: ReactViewGroup, value: ReadableMapBuffer) { + val list = JavaOnlyArray() + for (entry in value) { + list.pushDouble(entry.double) + } + setTransform(view, list) + } + + private fun ReactViewManager.borderWidth(view: ReactViewGroup, value: ReadableMapBuffer) { + for (entry in value) { + val index = + when (val key = entry.key) { + EDGE_ALL -> 0 + EDGE_LEFT -> 1 + EDGE_RIGHT -> 2 + EDGE_TOP -> 3 + EDGE_BOTTOM -> 4 + EDGE_START -> 5 + EDGE_END -> 6 + else -> throw IllegalArgumentException("Unknown key for border width: $key") + } + val borderWidth = entry.double + if (!borderWidth.isNaN()) { + setBorderWidth(view, index, borderWidth.toFloat()) + } + } + } + + private fun ReactViewManager.overflow(view: ReactViewGroup, value: Int) { + val stringValue = + when (value) { + 0 -> "visible" + 1 -> "hidden" + 2 -> "scroll" + else -> throw IllegalArgumentException("Unknown overflow value: $value") + } + + setOverflow(view, stringValue) + } + + private fun ReactViewManager.shadowColor(view: ReactViewGroup, value: Int) { + val color = value.takeIf { it != UNDEF_COLOR } ?: Color.BLACK + setShadowColor(view, color) + } + + private fun ReactViewManager.nativeBackground(view: ReactViewGroup, value: ReadableMapBuffer) { + setNativeBackground(view, value.toJsDrawableDescription()) + } + + private fun ReactViewManager.nativeForeground(view: ReactViewGroup, value: ReadableMapBuffer) { + setNativeForeground(view, value.toJsDrawableDescription()) + } + + private fun ReadableMapBuffer.toJsDrawableDescription(): ReadableMap? { + if (count == 0) { + return null + } + + val kind = getInt(NATIVE_DRAWABLE_KIND) + val result = JavaOnlyMap() + when (kind) { + 0 -> { + result.putString("type", "ThemeAttrAndroid") + result.putString("attribute", getString(NATIVE_DRAWABLE_ATTRIBUTE)) + } + 1 -> { + result.putString("type", "RippleAndroid") + if (hasKey(NATIVE_DRAWABLE_COLOR)) { + result.putInt("color", getInt(NATIVE_DRAWABLE_COLOR)) + } + result.putBoolean("borderless", getBoolean(NATIVE_DRAWABLE_BORDERLESS)) + if (hasKey(NATIVE_DRAWABLE_RIPPLE_RADIUS)) { + result.putDouble("rippleRadius", getDouble(NATIVE_DRAWABLE_RIPPLE_RADIUS)) + } + } + else -> throw IllegalArgumentException("Unknown native drawable: $kind") + } + return result + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactMapBufferViewManager.kt b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactMapBufferViewManager.kt new file mode 100644 index 00000000000000..fe7aa854808a84 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactMapBufferViewManager.kt @@ -0,0 +1,75 @@ +/* + * 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.view + +import android.view.View +import com.facebook.react.bridge.ReadableArray +import com.facebook.react.common.mapbuffer.ReadableMapBuffer +import com.facebook.react.touch.JSResponderHandler +import com.facebook.react.uimanager.ReactStylesDiffMap +import com.facebook.react.uimanager.StateWrapper +import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.ViewGroupManager + +object ReactMapBufferViewManager : ReactViewManagerWrapper { + private val viewManager = ReactViewManager() + + override fun createView( + reactTag: Int, + reactContext: ThemedReactContext, + props: Any?, + stateWrapper: StateWrapper?, + jsResponderHandler: JSResponderHandler + ): View = + viewManager.createView( + reactTag, + reactContext, + props as? ReactStylesDiffMap, + stateWrapper, + jsResponderHandler) + .also { view -> + if (props is ReadableMapBuffer) { + updateProperties(view, props) + } + } + + override fun updateProperties(viewToUpdate: View, props: Any?) { + if (props !is ReadableMapBuffer) { + viewManager.updateProperties(viewToUpdate as ReactViewGroup, props as? ReactStylesDiffMap) + } else { + ReactMapBufferPropSetter.setProps(viewToUpdate as ReactViewGroup, viewManager, props) + } + } + + override fun receiveCommand(root: View, commandId: String, args: ReadableArray?) { + viewManager.receiveCommand(root as ReactViewGroup, commandId, args) + } + + override fun receiveCommand(root: View, commandId: Int, args: ReadableArray?) { + viewManager.receiveCommand(root as ReactViewGroup, commandId, args) + } + + override fun setPadding(view: View, left: Int, top: Int, right: Int, bottom: Int) { + viewManager.setPadding(view as ReactViewGroup, left, top, right, bottom) + } + + override fun updateState(view: View, props: Any?, stateWrapper: StateWrapper?): Any? = null + + override fun updateExtraData(root: View, extraData: Any?) { + viewManager.updateExtraData(root as ReactViewGroup, extraData) + } + + override fun onDropViewInstance(view: View) { + viewManager.onDropViewInstance(view as ReactViewGroup) + } + + override fun getName(): String = viewManager.name + + override val viewGroupManager: ViewGroupManager<*> + get() = viewManager +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManagerWrapper.kt b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManagerWrapper.kt new file mode 100644 index 00000000000000..ac9327e190a165 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManagerWrapper.kt @@ -0,0 +1,90 @@ +/* + * 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.view + +import android.view.View +import com.facebook.react.bridge.ReadableArray +import com.facebook.react.touch.JSResponderHandler +import com.facebook.react.uimanager.ReactStylesDiffMap +import com.facebook.react.uimanager.StateWrapper +import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.ViewGroupManager +import com.facebook.react.uimanager.ViewManager + +interface ReactViewManagerWrapper { + fun createView( + reactTag: Int, + reactContext: ThemedReactContext, + props: Any?, + stateWrapper: StateWrapper?, + jsResponderHandler: JSResponderHandler + ): View + + fun updateProperties(viewToUpdate: View, props: Any?) + + fun receiveCommand(root: View, commandId: String, args: ReadableArray?) + + fun receiveCommand(root: View, commandId: Int, args: ReadableArray?) + + fun setPadding(view: View, left: Int, top: Int, right: Int, bottom: Int) + + fun updateState(view: View, props: Any?, stateWrapper: StateWrapper?): Any? + + fun updateExtraData(root: View, extraData: Any?) + + fun onDropViewInstance(view: View) + + fun getName(): String + + val viewGroupManager: ViewGroupManager<*> + + class DefaultViewManager(private val viewManager: ViewManager) : + ReactViewManagerWrapper { + override fun createView( + reactTag: Int, + reactContext: ThemedReactContext, + props: Any?, + stateWrapper: StateWrapper?, + jsResponderHandler: JSResponderHandler + ): View = + viewManager.createView( + reactTag, reactContext, props as? ReactStylesDiffMap, stateWrapper, jsResponderHandler) + + override fun updateProperties(viewToUpdate: View, props: Any?) { + viewManager.updateProperties(viewToUpdate, props as? ReactStylesDiffMap) + } + + override fun receiveCommand(root: View, commandId: String, args: ReadableArray?) { + viewManager.receiveCommand(root, commandId, args) + } + + override fun receiveCommand(root: View, commandId: Int, args: ReadableArray?) { + viewManager.receiveCommand(root, commandId, args) + } + + override fun setPadding(view: View, left: Int, top: Int, right: Int, bottom: Int) { + viewManager.setPadding(view, left, top, right, bottom) + } + + override fun updateState(view: View, props: Any?, stateWrapper: StateWrapper?): Any? = + viewManager.updateState(view, props as? ReactStylesDiffMap, stateWrapper) + + override fun updateExtraData(root: View, extraData: Any?) { + viewManager.updateExtraData(root, extraData) + } + + override fun onDropViewInstance(view: View) { + viewManager.onDropViewInstance(view) + } + + override fun getName(): String = viewManager.name + + override val viewGroupManager: ViewGroupManager<*> + get() = viewManager as ViewGroupManager<*> + } +}