mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Use mapbuffer for ReactViewGroup
Summary: Provides an alternative way of diffing props on Android side. Instead of passing dynamic JSON values from JS, the new approach diff C++ props and serializes difference into a `MapBuffer`. Current implementation does this only for `RCTView` component to test performance, correctness and memory impact (as MapBuffers are supposed to be more compact and performant). This way of passing props allows for additional alignment between platforms, as C++ props are now source of truth for the view state, similar to iOS. At the same time, changing serialization method requires reimplementing `ViewManager` codegen (done manually for now) to account for `MapBuffer`s. Changelog: [Added][Android] - Added an experimental prop serialization path based on MapBuffer Reviewed By: mdvacca Differential Revision: D33735245 fbshipit-source-id: 1515b5fe92f6557ae55abe215ce48277c83974a6
This commit is contained in:
committed by
Facebook GitHub Bot
parent
b1a779392d
commit
cbcdaae2b5
@@ -31,6 +31,10 @@ public class JavaOnlyMap implements ReadableMap, WritableMap {
|
||||
return new JavaOnlyMap(keysAndValues);
|
||||
}
|
||||
|
||||
public static JavaOnlyMap from(Map<String, Object> map) {
|
||||
return new JavaOnlyMap(map);
|
||||
}
|
||||
|
||||
public static JavaOnlyMap deepClone(ReadableMap map) {
|
||||
JavaOnlyMap res = new JavaOnlyMap();
|
||||
ReadableMapKeySetIterator iter = map.keySetIterator();
|
||||
|
||||
+32
-2
@@ -301,6 +301,36 @@ public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
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<MapBufferEntry>} for the entries of this MapBuffer. */
|
||||
@Override
|
||||
public Iterator<MapBufferEntry> iterator() {
|
||||
@@ -372,7 +402,7 @@ public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
}
|
||||
|
||||
/** @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 class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
@@ -704,7 +704,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
int rootTag,
|
||||
int reactTag,
|
||||
final String componentName,
|
||||
@Nullable ReadableMap props,
|
||||
@Nullable Object props,
|
||||
@Nullable Object stateWrapper,
|
||||
@Nullable Object eventEmitterWrapper,
|
||||
boolean isLayoutable) {
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+33
-28
@@ -8,6 +8,7 @@
|
||||
#include "FabricMountingManager.h"
|
||||
#include "EventEmitterWrapper.h"
|
||||
#include "StateWrapperImpl.h"
|
||||
#include "viewPropConversions.h"
|
||||
|
||||
#include <react/jni/ReadableNativeMap.h>
|
||||
#include <react/renderer/components/scrollview/ScrollViewProps.h>
|
||||
@@ -177,11 +178,6 @@ static inline void writeIntBufferTypePreamble(
|
||||
}
|
||||
}
|
||||
|
||||
inline local_ref<ReadableMap::javaobject> castReadableMap(
|
||||
local_ref<ReadableNativeMap::javaobject> const &nativeMap) {
|
||||
return make_local(reinterpret_cast<ReadableMap::javaobject>(nativeMap.get()));
|
||||
}
|
||||
|
||||
inline local_ref<ReadableArray::javaobject> castReadableArray(
|
||||
local_ref<ReadableNativeArray::javaobject> const &nativeArray) {
|
||||
return make_local(
|
||||
@@ -222,6 +218,22 @@ static inline float scale(Float value, Float pointScaleFactor) {
|
||||
return result;
|
||||
}
|
||||
|
||||
local_ref<jobject> FabricMountingManager::getProps(
|
||||
ShadowView const &oldShadowView,
|
||||
ShadowView const &newShadowView) {
|
||||
if (useMapBufferForViewProps_ &&
|
||||
newShadowView.traits.check(ShadowNodeTraits::Trait::View)) {
|
||||
auto oldProps = oldShadowView.props != nullptr
|
||||
? static_cast<ViewProps const &>(*oldShadowView.props)
|
||||
: ViewProps{};
|
||||
auto newProps = static_cast<ViewProps const &>(*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<std::recursive_mutex> 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<JArrayClass<jobject>> objBufferArray =
|
||||
JArrayClass<jobject>::newArray(batchMountItemObjectsSize);
|
||||
@@ -525,10 +537,8 @@ void FabricMountingManager::executeMount(
|
||||
int isLayoutable =
|
||||
mountItem.newChildShadowView.layoutMetrics != EmptyLayoutMetrics ? 1
|
||||
: 0;
|
||||
|
||||
local_ref<ReadableMap::javaobject> props =
|
||||
castReadableMap(ReadableNativeMap::newObjectCxxArgs(
|
||||
mountItem.newChildShadowView.props->rawProps));
|
||||
local_ref<JObject> 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<ReadableMap::javaobject> 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<void(
|
||||
jint,
|
||||
jint,
|
||||
jstring,
|
||||
ReadableMap::javaobject,
|
||||
jobject,
|
||||
jobject,
|
||||
jboolean)>("preallocateView");
|
||||
static auto preallocateView =
|
||||
jni::findClassStatic(UIManagerJavaDescriptor)
|
||||
->getMethod<void(
|
||||
jint, jint, jstring, jobject, jobject, jobject, jboolean)>(
|
||||
"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<ReadableMap::javaobject> props = castReadableMap(
|
||||
ReadableNativeMap::newObjectCxxArgs(shadowView.props->rawProps));
|
||||
local_ref<JObject> 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
|
||||
|
||||
@@ -76,6 +76,11 @@ class FabricMountingManager {
|
||||
bool disableRevisionCheckForPreallocation_{false};
|
||||
bool useOverflowInset_{false};
|
||||
bool shouldRememberAllocatedViews_{false};
|
||||
bool useMapBufferForViewProps_{false};
|
||||
|
||||
jni::local_ref<jobject> getProps(
|
||||
ShadowView const &oldShadowView,
|
||||
ShadowView const &newShadowView);
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
|
||||
+42
-21
@@ -26,6 +26,7 @@ import com.facebook.react.bridge.RetryableMountingLayerException;
|
||||
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.ThemedReactContext;
|
||||
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 @@ public class SurfaceMountingManager {
|
||||
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 class SurfaceMountingManager {
|
||||
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 class SurfaceMountingManager {
|
||||
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 class SurfaceMountingManager {
|
||||
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 class SurfaceMountingManager {
|
||||
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 @@ public class SurfaceMountingManager {
|
||||
}
|
||||
|
||||
// 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 class SurfaceMountingManager {
|
||||
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 class SurfaceMountingManager {
|
||||
if (viewState.mViewManager == null) {
|
||||
throw new IllegalStateException("Unable to find ViewManager for view: " + viewState);
|
||||
}
|
||||
return (ViewGroupManager<ViewGroup>) viewState.mViewManager;
|
||||
return (ViewGroupManager<ViewGroup>) viewState.mViewManager.getViewGroupManager();
|
||||
}
|
||||
|
||||
public void printSurfaceState() {
|
||||
@@ -1014,17 +1030,22 @@ public class SurfaceMountingManager {
|
||||
@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;
|
||||
|
||||
+4
-9
@@ -16,7 +16,6 @@ import com.facebook.common.logging.FLog;
|
||||
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 @@ public class IntBufferBatchMountItem implements MountItem {
|
||||
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 class IntBufferBatchMountItem implements MountItem {
|
||||
surfaceMountingManager.createView(
|
||||
componentName,
|
||||
mIntBuffer[i++],
|
||||
castToProps(mObjBuffer[j++]),
|
||||
mObjBuffer[j++],
|
||||
castToState(mObjBuffer[j++]),
|
||||
castToEventEmitter(mObjBuffer[j++]),
|
||||
mIntBuffer[i++] == 1);
|
||||
@@ -154,7 +149,7 @@ public class IntBufferBatchMountItem implements MountItem {
|
||||
} 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 class IntBufferBatchMountItem implements MountItem {
|
||||
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() : "<null>")
|
||||
? (props != null ? props.toString() : "<null>")
|
||||
: "<hidden>";
|
||||
s.append(String.format("UPDATE PROPS [%d]: %s\n", mIntBuffer[i++], propsString));
|
||||
} else if (type == INSTRUCTION_UPDATE_STATE) {
|
||||
|
||||
+2
-3
@@ -13,7 +13,6 @@ import static com.facebook.react.fabric.FabricUIManager.TAG;
|
||||
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 class PreAllocateViewMountItem implements MountItem {
|
||||
int surfaceId,
|
||||
int reactTag,
|
||||
@NonNull String component,
|
||||
@Nullable ReadableMap props,
|
||||
@Nullable Object props,
|
||||
@NonNull StateWrapper stateWrapper,
|
||||
@Nullable EventEmitterWrapper eventEmitterWrapper,
|
||||
boolean isLayoutable) {
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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<ReadableMap>()
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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<View, *>) :
|
||||
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<*>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user