>("ReactNativeConfig");
+ if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:scrollview_on_demand_mounting_ios")) {
+ RCTSetEnableOnDemandViewMounting(YES);
+ }
+
auto componentRegistryFactory =
[factory = wrapManagedObject(_mountingManager.componentViewRegistry.componentViewFactory)](
EventDispatcher::Weak const &eventDispatcher, ContextContainer::Shared const &contextContainer) {
@@ -361,6 +367,7 @@ - (void)_stopSurface:(RCTFabricSurface *)surface scheduler:(RCTScheduler *)sched
RCTMountingManager *mountingManager = _mountingManager;
RCTExecuteOnMainQueue(^{
+ surface.view.rootView = nil;
RCTComponentViewDescriptor rootViewDescriptor =
[mountingManager.componentViewRegistry componentViewDescriptorWithTag:surface.rootTag];
[mountingManager.componentViewRegistry enqueueComponentViewWithComponentHandle:RootShadowNode::Handle()
diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeCameraRollManagerSpec.java b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeCameraRollManagerSpec.java
deleted file mode 100644
index 3dd691799c9ebd..00000000000000
--- a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeCameraRollManagerSpec.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the LICENSE file in the root
- * directory of this source tree.
- *
- *
Generated by an internal genrule from Flow types.
- *
- * @generated
- * @nolint
- */
-
-package com.facebook.fbreact.specs;
-
-import com.facebook.react.bridge.Promise;
-import com.facebook.react.bridge.ReactApplicationContext;
-import com.facebook.react.bridge.ReactContextBaseJavaModule;
-import com.facebook.react.bridge.ReactMethod;
-import com.facebook.react.bridge.ReactModuleWithSpec;
-import com.facebook.react.bridge.ReadableArray;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.turbomodule.core.interfaces.TurboModule;
-
-public abstract class NativeCameraRollManagerSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule {
- public NativeCameraRollManagerSpec(ReactApplicationContext reactContext) {
- super(reactContext);
- }
-
- @ReactMethod
- public abstract void getPhotos(ReadableMap params, Promise promise);
-
- @ReactMethod
- public abstract void deletePhotos(ReadableArray assets, Promise promise);
-
- @ReactMethod
- public abstract void saveToCameraRoll(String uri, String type, Promise promise);
-}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/DebugCorePackage.java b/ReactAndroid/src/main/java/com/facebook/react/DebugCorePackage.java
index fb72f43732cc52..740182fbc0f7f1 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/DebugCorePackage.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/DebugCorePackage.java
@@ -37,7 +37,7 @@ public NativeModule getModule(String name, ReactApplicationContext reactContext)
return new JSCHeapCapture(reactContext);
default:
throw new IllegalArgumentException(
- "In CoreModulesPackage, could not find Native module for " + name);
+ "In DebugCorePackage, could not find Native module for " + name);
}
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java
index 46ce038b4052c1..7807b98c9091ee 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java
@@ -152,7 +152,7 @@ public interface ReactInstanceEventListener {
private final JavaScriptExecutorFactory mJavaScriptExecutorFactory;
private final @Nullable JSBundleLoader mBundleLoader;
- private final @Nullable String mJSMainModulePath; /* path to JS bundle root on packager server */
+ private final @Nullable String mJSMainModulePath; /* path to JS bundle root on Metro */
private final List mPackages;
private final DevSupportManager mDevSupportManager;
private final boolean mUseDeveloperSupport;
@@ -706,6 +706,7 @@ public void destroy() {
synchronized (mHasStartedDestroying) {
mHasStartedDestroying.notifyAll();
}
+ FLog.d(ReactConstants.TAG, "ReactInstanceManager has been destroyed");
}
private synchronized void moveToResumedLifecycleState(boolean force) {
diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java
index e2809cd6580c68..a3b33f6cfa3b71 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java
@@ -118,9 +118,9 @@ public ReactInstanceManagerBuilder setJSBundleLoader(JSBundleLoader jsBundleLoad
}
/**
- * Path to your app's main module on the packager server. This is used when reloading JS during
- * development. All paths are relative to the root folder the packager is serving files from.
- * Examples: {@code "index.android"} or {@code "subdirectory/index.android"}
+ * Path to your app's main module on Metro. This is used when reloading JS during development. All
+ * paths are relative to the root folder the packager is serving files from. Examples: {@code
+ * "index.android"} or {@code "subdirectory/index.android"}
*/
public ReactInstanceManagerBuilder setJSMainModulePath(String jsMainModulePath) {
mJSMainModulePath = jsMainModulePath;
diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java b/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java
index 96dd42ba9ae953..188ae05f361ee1 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java
@@ -118,9 +118,9 @@ protected UIImplementationProvider getUIImplementationProvider() {
}
/**
- * Returns the name of the main module. Determines the URL used to fetch the JS bundle from the
- * packager server. It is only used when dev support is enabled. This is the first file to be
- * executed once the {@link ReactInstanceManager} is created. e.g. "index.android"
+ * Returns the name of the main module. Determines the URL used to fetch the JS bundle from Metro.
+ * It is only used when dev support is enabled. This is the first file to be executed once the
+ * {@link ReactInstanceManager} is created. e.g. "index.android"
*/
protected String getJSMainModuleName() {
return "index.android";
@@ -138,7 +138,7 @@ protected String getJSMainModuleName() {
/**
* Returns the name of the bundle in assets. If this is null, and no file path is specified for
* the bundle, the app will only work with {@code getUseDeveloperSupport} enabled and will always
- * try to load the JS bundle from the packager server. e.g. "index.android.bundle"
+ * try to load the JS bundle from Metro. e.g. "index.android.bundle"
*/
protected @Nullable String getBundleAssetName() {
return "index.android.bundle";
diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
index f85a092952b16f..249bf3775899c1 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
@@ -32,7 +32,6 @@
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.common.UIManagerType;
import com.facebook.react.uimanager.common.ViewUtil;
-import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -87,23 +86,37 @@ public class NativeAnimatedModule extends NativeAnimatedModuleSpec
public static final String NAME = "NativeAnimatedModule";
public static final boolean ANIMATED_MODULE_DEBUG = false;
- private interface UIThreadOperation {
- void execute(NativeAnimatedNodesManager animatedNodesManager);
+ private abstract class UIThreadOperation {
+ abstract void execute(NativeAnimatedNodesManager animatedNodesManager);
+
+ long mFrameNumber = -1;
+
+ public void setFrameNumber(long frameNumber) {
+ mFrameNumber = frameNumber;
+ }
+
+ public long getFrameNumber() {
+ return mFrameNumber;
+ }
}
@NonNull private final GuardedFrameCallback mAnimatedFrameCallback;
private final ReactChoreographer mReactChoreographer;
@NonNull
- private ConcurrentLinkedQueue mOperations = new ConcurrentLinkedQueue<>();
+ private final ConcurrentLinkedQueue mOperations =
+ new ConcurrentLinkedQueue<>();
@NonNull
- private ConcurrentLinkedQueue mPreOperations = new ConcurrentLinkedQueue<>();
+ private final ConcurrentLinkedQueue mPreOperations =
+ new ConcurrentLinkedQueue<>();
private @Nullable NativeAnimatedNodesManager mNodesManager;
- private volatile boolean mFabricBatchCompleted = false;
+ private volatile long mFrameNumber = 0;
+ private long mDispatchedFrameNumber = 0;
private boolean mInitializedForFabric = false;
+ private boolean mInitializedForNonFabric = false;
private @UIManagerType int mUIManagerType = UIManagerType.DEFAULT;
private int mNumFabricAnimations = 0;
private int mNumNonFabricAnimations = 0;
@@ -145,12 +158,8 @@ protected void doFrameGuarded(final long frameTimeNanos) {
public void initialize() {
ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn();
- // TODO T59412313 Implement this API on FabricUIManager to use in bridgeless mode
- if (reactApplicationContext != null && !reactApplicationContext.isBridgeless()) {
+ if (reactApplicationContext != null) {
reactApplicationContext.addLifecycleEventListener(this);
- UIManagerModule uiManager =
- Assertions.assertNotNull(reactApplicationContext.getNativeModule(UIManagerModule.class));
- uiManager.addUIManagerEventListener(this);
}
}
@@ -159,6 +168,16 @@ public void onHostResume() {
enqueueFrameCallback();
}
+ private void addOperation(UIThreadOperation operation) {
+ operation.setFrameNumber(mFrameNumber);
+ mOperations.add(operation);
+ }
+
+ private void addPreOperation(UIThreadOperation operation) {
+ operation.setFrameNumber(mFrameNumber);
+ mPreOperations.add(operation);
+ }
+
// For FabricUIManager only
@Override
public void didScheduleMountItems(UIManager uiManager) {
@@ -166,7 +185,7 @@ public void didScheduleMountItems(UIManager uiManager) {
return;
}
- mFabricBatchCompleted = true;
+ mFrameNumber++;
}
// For FabricUIManager only
@@ -177,23 +196,66 @@ public void didDispatchMountItems(UIManager uiManager) {
return;
}
- if (mFabricBatchCompleted) {
- // This will execute all operations and preOperations queued
- // since the last time this was run, and will race with anything
- // being queued from the JS thread. That is, if the JS thread
- // is still queuing operations, we might execute some of them
- // at the very end until we exhaust the queue faster than the
- // JS thread can queue up new items.
- executeAllOperations(mPreOperations);
- executeAllOperations(mOperations);
- mFabricBatchCompleted = false;
+ // The problem we're trying to solve here: we could be in the middle of queueing
+ // a batch of related animation operations when Fabric flushes a batch of MountItems.
+ // It's visually bad if we execute half of the animation ops and then wait another frame
+ // (or more) to execute the rest.
+ // See mFrameNumber. If the dispatchedFrameNumber drifts too far - that
+ // is, if no MountItems are scheduled for a while, which can happen if a tree
+ // is committed but there are no changes - bring these counts back in sync and
+ // execute any queued operations. This number is arbitrary, but we want it low
+ // enough that the user shouldn't be able to see this delay in most cases.
+ mDispatchedFrameNumber++;
+ long currentFrameNo = mFrameNumber - 1;
+ if ((mDispatchedFrameNumber - mFrameNumber) > 2) {
+ mFrameNumber = mDispatchedFrameNumber;
+ currentFrameNo = mFrameNumber;
}
- }
- private void executeAllOperations(Queue operationQueue) {
+ // This will execute all operations and preOperations queued
+ // since the last time this was run, and will race with anything
+ // being queued from the JS thread. That is, if the JS thread
+ // is still queuing operations, we might execute some of them
+ // at the very end until we exhaust the queue faster than the
+ // JS thread can queue up new items.
+ // The reason we increment in scheduleMountItems and subtract 1 here
+ // is that `scheduleMountItems` happens as close to the JS commit as
+ // possible, whereas execution of those same items might happen sometime
+ // later on the UI thread while the JS thread keeps plugging along.
+ executeAllOperations(mPreOperations, currentFrameNo);
+ executeAllOperations(mOperations, currentFrameNo);
+ }
+
+ private void executeAllOperations(Queue operationQueue, long maxFrameNumber) {
NativeAnimatedNodesManager nodesManager = getNodesManager();
- while (!operationQueue.isEmpty()) {
- operationQueue.poll().execute(nodesManager);
+ while (true) {
+ // There is a race condition where `peek` may return a non-null value and isEmpty() is false,
+ // but `poll` returns a null value - it's not clear why since we only peek and poll on the UI
+ // thread, but it might be something that happens during teardown or a crash. Regardless, the
+ // root cause is not currently known so we're extra cautious here.
+ // It happens equally in Fabric and non-Fabric.
+ UIThreadOperation peekedOperation = operationQueue.peek();
+
+ // This is the same as operationQueue.isEmpty()
+ if (peekedOperation == null) {
+ return;
+ }
+ // The rest of the operations are for the next frame.
+ if (peekedOperation.getFrameNumber() > maxFrameNumber) {
+ return;
+ }
+
+ // Since we apparently can't guarantee that there is still an operation on the queue,
+ // much less the same operation, we do a poll and another null check. If this isn't
+ // the same operation as the peeked operation, we can't do anything about it - we still
+ // need to execute it, we have no mechanism to put it at the front of the queue, and it
+ // won't cause any errors to execute it earlier than expected (just a bit of UI jank at worst)
+ // so we just continue happily along.
+ UIThreadOperation polledOperation = operationQueue.poll();
+ if (polledOperation == null) {
+ return;
+ }
+ polledOperation.execute(nodesManager);
}
}
@@ -208,20 +270,13 @@ public void willDispatchViewUpdates(final UIManager uiManager) {
return;
}
- final Queue preOperations = new LinkedList<>();
- final Queue operations = new LinkedList<>();
- while (!mPreOperations.isEmpty()) {
- preOperations.add(mPreOperations.poll());
- }
- while (!mOperations.isEmpty()) {
- operations.add(mOperations.poll());
- }
+ final long frameNo = mFrameNumber++;
UIBlock preOperationsUIBlock =
new UIBlock() {
@Override
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
- executeAllOperations(preOperations);
+ executeAllOperations(mPreOperations, frameNo);
}
};
@@ -229,8 +284,7 @@ public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
new UIBlock() {
@Override
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
- NativeAnimatedNodesManager nodesManager = getNodesManager();
- executeAllOperations(operations);
+ executeAllOperations(mOperations, frameNo);
}
};
@@ -292,6 +346,77 @@ public void setNodesManager(NativeAnimatedNodesManager nodesManager) {
mNodesManager = nodesManager;
}
+ /**
+ * Given a viewTag, detect if we're running in Fabric or non-Fabric and attach an event listener
+ * to the correct UIManager, if necessary. This is expected to only be called from the JS thread,
+ * and not concurrently.
+ *
+ * @param viewTag
+ */
+ private void initializeLifecycleEventListenersForViewTag(final int viewTag) {
+ mUIManagerType = ViewUtil.getUIManagerType(viewTag);
+ if (mUIManagerType == UIManagerType.FABRIC) {
+ mNumFabricAnimations++;
+ } else {
+ mNumNonFabricAnimations++;
+ }
+
+ if (mNodesManager != null) {
+ mNodesManager.initializeEventListenerForUIManagerType(mUIManagerType);
+ }
+
+ // Subscribe to UIManager (Fabric or non-Fabric) lifecycle events if we haven't yet
+ if ((mInitializedForFabric && mUIManagerType == UIManagerType.FABRIC)
+ || (mInitializedForNonFabric && mUIManagerType == UIManagerType.DEFAULT)) {
+ return;
+ }
+
+ ReactApplicationContext reactApplicationContext = getReactApplicationContext();
+ if (reactApplicationContext != null) {
+ @Nullable
+ UIManager uiManager = UIManagerHelper.getUIManager(reactApplicationContext, mUIManagerType);
+ if (uiManager != null) {
+ uiManager.addUIManagerEventListener(this);
+ if (mUIManagerType == UIManagerType.FABRIC) {
+ mInitializedForFabric = true;
+ } else {
+ mInitializedForNonFabric = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Given a viewTag and the knowledge that a "disconnect" or "stop"-type imperative command is
+ * being executed, decrement the number of inflight animations and possibly switch UIManager
+ * modes.
+ *
+ * @param viewTag
+ */
+ private void decrementInFlightAnimationsForViewTag(final int viewTag) {
+ @UIManagerType int animationManagerType = ViewUtil.getUIManagerType(viewTag);
+ if (animationManagerType == UIManagerType.FABRIC) {
+ mNumFabricAnimations--;
+ } else {
+ mNumNonFabricAnimations--;
+ }
+
+ // Should we switch to a different animation mode?
+ // This can be useful when navigating between Fabric and non-Fabric screens:
+ // If there are ongoing Fabric animations from a previous screen,
+ // and we tear down the current non-Fabric screen, we should expect
+ // the animation mode to switch back - and vice-versa.
+ if (mNumNonFabricAnimations == 0
+ && mNumFabricAnimations > 0
+ && mUIManagerType != UIManagerType.FABRIC) {
+ mUIManagerType = UIManagerType.FABRIC;
+ } else if (mNumFabricAnimations == 0
+ && mNumNonFabricAnimations > 0
+ && mUIManagerType != UIManagerType.DEFAULT) {
+ mUIManagerType = UIManagerType.DEFAULT;
+ }
+ }
+
@Override
public void createAnimatedNode(final double tagDouble, final ReadableMap config) {
final int tag = (int) tagDouble;
@@ -300,7 +425,7 @@ public void createAnimatedNode(final double tagDouble, final ReadableMap config)
NAME, "queue createAnimatedNode: " + tag + " config: " + config.toHashMap().toString());
}
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -341,7 +466,7 @@ public void onValueUpdate(double value) {
}
};
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -360,7 +485,7 @@ public void stopListeningToAnimatedNodeValue(final double tagDouble) {
FLog.d(NAME, "queue stopListeningToAnimatedNodeValue: " + tag);
}
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -379,7 +504,7 @@ public void dropAnimatedNode(final double tagDouble) {
FLog.d(NAME, "queue dropAnimatedNode: " + tag);
}
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -398,7 +523,7 @@ public void setAnimatedNodeValue(final double tagDouble, final double value) {
FLog.d(NAME, "queue setAnimatedNodeValue: " + tag + " value: " + value);
}
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -417,7 +542,7 @@ public void setAnimatedNodeOffset(final double tagDouble, final double value) {
FLog.d(NAME, "queue setAnimatedNodeOffset: " + tag + " offset: " + value);
}
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -436,7 +561,7 @@ public void flattenAnimatedNodeOffset(final double tagDouble) {
FLog.d(NAME, "queue flattenAnimatedNodeOffset: " + tag);
}
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -455,7 +580,7 @@ public void extractAnimatedNodeOffset(final double tagDouble) {
FLog.d(NAME, "queue extractAnimatedNodeOffset: " + tag);
}
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -479,7 +604,7 @@ public void startAnimatingNode(
FLog.d(NAME, "queue startAnimatingNode: ID: " + animationId + " tag: " + animatedNodeTag);
}
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -501,7 +626,7 @@ public void stopAnimation(final double animationIdDouble) {
FLog.d(NAME, "queue stopAnimation: ID: " + animationId);
}
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -523,7 +648,7 @@ public void connectAnimatedNodes(
NAME, "queue connectAnimatedNodes: parent: " + parentNodeTag + " child: " + childNodeTag);
}
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -551,7 +676,7 @@ public void disconnectAnimatedNodes(
"queue disconnectAnimatedNodes: parent: " + parentNodeTag + " child: " + childNodeTag);
}
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -582,7 +707,9 @@ public void connectAnimatedNodeToView(
+ viewTag);
}
- mOperations.add(
+ initializeLifecycleEventListenersForViewTag(viewTag);
+
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -613,7 +740,9 @@ public void disconnectAnimatedNodeFromView(
+ viewTag);
}
- mOperations.add(
+ decrementInFlightAnimationsForViewTag(viewTag);
+
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -638,7 +767,7 @@ public void restoreDefaultValues(final double animatedNodeTagDouble) {
NAME, "queue restoreDefaultValues: disconnectAnimatedNodeFromView: " + animatedNodeTag);
}
- mPreOperations.add(
+ addPreOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -668,28 +797,9 @@ public void addAnimatedEventToView(
+ eventMapping.toHashMap().toString());
}
- mUIManagerType = ViewUtil.getUIManagerType(viewTag);
- if (mUIManagerType == UIManagerType.FABRIC) {
- mNumFabricAnimations++;
- } else {
- mNumNonFabricAnimations++;
- }
+ initializeLifecycleEventListenersForViewTag(viewTag);
- // Subscribe to FabricUIManager lifecycle events if we haven't yet
- if (!mInitializedForFabric && mUIManagerType == UIManagerType.FABRIC) {
- ReactApplicationContext reactApplicationContext = getReactApplicationContext();
- if (reactApplicationContext != null) {
- @Nullable
- UIManager uiManager =
- UIManagerHelper.getUIManager(reactApplicationContext, UIManagerType.FABRIC);
- if (uiManager != null) {
- uiManager.addUIManagerEventListener(this);
- mInitializedForFabric = true;
- }
- }
- }
-
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -724,29 +834,9 @@ public void removeAnimatedEventFromView(
+ animatedValueTag);
}
- @UIManagerType int animationManagerType = ViewUtil.getUIManagerType(viewTag);
- if (animationManagerType == UIManagerType.FABRIC) {
- mNumFabricAnimations--;
- } else {
- mNumNonFabricAnimations--;
- }
-
- // Should we switch to a different animation mode?
- // This can be useful when navigating between Fabric and non-Fabric screens:
- // If there are ongoing Fabric animations from a previous screen,
- // and we tear down the current non-Fabric screen, we should expect
- // the animation mode to switch back - and vice-versa.
- if (mNumNonFabricAnimations == 0
- && mNumFabricAnimations > 0
- && mUIManagerType != UIManagerType.FABRIC) {
- mUIManagerType = UIManagerType.FABRIC;
- } else if (mNumFabricAnimations == 0
- && mNumNonFabricAnimations > 0
- && mUIManagerType != UIManagerType.DEFAULT) {
- mUIManagerType = UIManagerType.DEFAULT;
- }
+ decrementInFlightAnimationsForViewTag(viewTag);
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
@@ -778,7 +868,7 @@ public void removeListeners(double count) {
@Override
public void getValue(final double animatedValueNodeTagDouble, final Callback callback) {
final int animatedValueNodeTag = (int) animatedValueNodeTagDouble;
- mOperations.add(
+ addOperation(
new UIThreadOperation() {
@Override
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java
index f1c76b780d10e4..179a6f0a7664e7 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java
@@ -9,8 +9,8 @@
import android.util.SparseArray;
import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
import com.facebook.common.logging.FLog;
-import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.JSApplicationCausedNativeException;
@@ -25,7 +25,7 @@
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.UIManagerHelper;
-import com.facebook.react.uimanager.UIManagerModule;
+import com.facebook.react.uimanager.common.UIManagerType;
import com.facebook.react.uimanager.events.Event;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.events.EventDispatcherListener;
@@ -62,23 +62,51 @@
// Mapping of a view tag and an event name to a list of event animation drivers. 99% of the time
// there will be only one driver per mapping so all code code should be optimized around that.
private final Map> mEventDrivers = new HashMap<>();
- private final UIManagerModule.CustomEventNamesResolver mCustomEventNamesResolver;
private final ReactApplicationContext mReactApplicationContext;
private int mAnimatedGraphBFSColor = 0;
private int mNumInconsistentFrames = 0;
// Used to avoid allocating a new array on every frame in `runUpdates` and `onEventDispatch`.
private final List mRunUpdateNodeList = new LinkedList<>();
+ private boolean mEventListenerInitializedForFabric = false;
+ private boolean mEventListenerInitializedForNonFabric = false;
+
public NativeAnimatedNodesManager(ReactApplicationContext reactApplicationContext) {
mReactApplicationContext = reactApplicationContext;
+ }
- UIManagerModule uiManager =
- Assertions.assertNotNull(reactApplicationContext.getNativeModule(UIManagerModule.class));
+ /**
+ * Initialize event listeners for Fabric UIManager or non-Fabric UIManager, exactly once. Once
+ * Fabric is the only UIManager, this logic can be simplified. This is only called on the JS
+ * thread.
+ *
+ * @param uiManagerType
+ */
+ @UiThread
+ public void initializeEventListenerForUIManagerType(@UIManagerType final int uiManagerType) {
+ if ((uiManagerType == UIManagerType.FABRIC && mEventListenerInitializedForFabric)
+ || (uiManagerType == UIManagerType.DEFAULT && mEventListenerInitializedForNonFabric)) {
+ return;
+ }
- uiManager.getEventDispatcher().addListener(this);
- // TODO T64216139 Remove dependency of UIManagerModule when the Constants are not in Native
- // anymore
- mCustomEventNamesResolver = uiManager.getDirectEventNamesResolver();
+ final NativeAnimatedNodesManager self = this;
+ mReactApplicationContext.runOnUiQueueThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ UIManager uiManager =
+ UIManagerHelper.getUIManager(mReactApplicationContext, uiManagerType);
+ if (uiManager != null) {
+ uiManager.getEventDispatcher().addListener(self);
+
+ if (uiManagerType == UIManagerType.FABRIC) {
+ mEventListenerInitializedForFabric = true;
+ } else {
+ mEventListenerInitializedForNonFabric = true;
+ }
+ }
+ }
+ });
}
/*package*/ @Nullable
@@ -90,6 +118,7 @@ public boolean hasActiveAnimations() {
return mActiveAnimations.size() > 0 || mUpdatedNodes.size() > 0;
}
+ @UiThread
public void createAnimatedNode(int tag, ReadableMap config) {
if (mAnimatedNodes.get(tag) != null) {
throw new JSApplicationIllegalArgumentException(
@@ -129,11 +158,13 @@ public void createAnimatedNode(int tag, ReadableMap config) {
mUpdatedNodes.put(tag, node);
}
+ @UiThread
public void dropAnimatedNode(int tag) {
mAnimatedNodes.remove(tag);
mUpdatedNodes.remove(tag);
}
+ @UiThread
public void startListeningToAnimatedNodeValue(int tag, AnimatedNodeValueListener listener) {
AnimatedNode node = mAnimatedNodes.get(tag);
if (node == null || !(node instanceof ValueAnimatedNode)) {
@@ -143,6 +174,7 @@ public void startListeningToAnimatedNodeValue(int tag, AnimatedNodeValueListener
((ValueAnimatedNode) node).setValueListener(listener);
}
+ @UiThread
public void stopListeningToAnimatedNodeValue(int tag) {
AnimatedNode node = mAnimatedNodes.get(tag);
if (node == null || !(node instanceof ValueAnimatedNode)) {
@@ -152,6 +184,7 @@ public void stopListeningToAnimatedNodeValue(int tag) {
((ValueAnimatedNode) node).setValueListener(null);
}
+ @UiThread
public void setAnimatedNodeValue(int tag, double value) {
AnimatedNode node = mAnimatedNodes.get(tag);
if (node == null || !(node instanceof ValueAnimatedNode)) {
@@ -163,6 +196,7 @@ public void setAnimatedNodeValue(int tag, double value) {
mUpdatedNodes.put(tag, node);
}
+ @UiThread
public void setAnimatedNodeOffset(int tag, double offset) {
AnimatedNode node = mAnimatedNodes.get(tag);
if (node == null || !(node instanceof ValueAnimatedNode)) {
@@ -173,6 +207,7 @@ public void setAnimatedNodeOffset(int tag, double offset) {
mUpdatedNodes.put(tag, node);
}
+ @UiThread
public void flattenAnimatedNodeOffset(int tag) {
AnimatedNode node = mAnimatedNodes.get(tag);
if (node == null || !(node instanceof ValueAnimatedNode)) {
@@ -182,6 +217,7 @@ public void flattenAnimatedNodeOffset(int tag) {
((ValueAnimatedNode) node).flattenOffset();
}
+ @UiThread
public void extractAnimatedNodeOffset(int tag) {
AnimatedNode node = mAnimatedNodes.get(tag);
if (node == null || !(node instanceof ValueAnimatedNode)) {
@@ -191,6 +227,7 @@ public void extractAnimatedNodeOffset(int tag) {
((ValueAnimatedNode) node).extractOffset();
}
+ @UiThread
public void startAnimatingNode(
int animationId, int animatedNodeTag, ReadableMap animationConfig, Callback endCallback) {
AnimatedNode node = mAnimatedNodes.get(animatedNodeTag);
@@ -228,6 +265,7 @@ public void startAnimatingNode(
mActiveAnimations.put(animationId, animation);
}
+ @UiThread
private void stopAnimationsForNode(AnimatedNode animatedNode) {
// in most of the cases there should never be more than a few active animations running at the
// same time. Therefore it does not make much sense to create an animationId -> animation
@@ -248,6 +286,7 @@ private void stopAnimationsForNode(AnimatedNode animatedNode) {
}
}
+ @UiThread
public void stopAnimation(int animationId) {
// in most of the cases there should never be more than a few active animations running at the
// same time. Therefore it does not make much sense to create an animationId -> animation
@@ -272,6 +311,7 @@ public void stopAnimation(int animationId) {
// when the animation is already over.
}
+ @UiThread
public void connectAnimatedNodes(int parentNodeTag, int childNodeTag) {
AnimatedNode parentNode = mAnimatedNodes.get(parentNodeTag);
if (parentNode == null) {
@@ -302,6 +342,7 @@ public void disconnectAnimatedNodes(int parentNodeTag, int childNodeTag) {
mUpdatedNodes.put(childNodeTag, childNode);
}
+ @UiThread
public void connectAnimatedNodeToView(int animatedNodeTag, int viewTag) {
AnimatedNode node = mAnimatedNodes.get(animatedNodeTag);
if (node == null) {
@@ -336,6 +377,7 @@ public void connectAnimatedNodeToView(int animatedNodeTag, int viewTag) {
mUpdatedNodes.put(animatedNodeTag, node);
}
+ @UiThread
public void disconnectAnimatedNodeFromView(int animatedNodeTag, int viewTag) {
AnimatedNode node = mAnimatedNodes.get(animatedNodeTag);
if (node == null) {
@@ -352,6 +394,7 @@ public void disconnectAnimatedNodeFromView(int animatedNodeTag, int viewTag) {
propsAnimatedNode.disconnectFromView(viewTag);
}
+ @UiThread
public void getValue(int tag, Callback callback) {
AnimatedNode node = mAnimatedNodes.get(tag);
if (node == null || !(node instanceof ValueAnimatedNode)) {
@@ -361,6 +404,7 @@ public void getValue(int tag, Callback callback) {
callback.invoke(((ValueAnimatedNode) node).getValue());
}
+ @UiThread
public void restoreDefaultValues(int animatedNodeTag) {
AnimatedNode node = mAnimatedNodes.get(animatedNodeTag);
// Restoring default values needs to happen before UIManager operations so it is
@@ -380,6 +424,7 @@ public void restoreDefaultValues(int animatedNodeTag) {
propsAnimatedNode.restoreDefaultValues();
}
+ @UiThread
public void addAnimatedEventToView(int viewTag, String eventName, ReadableMap eventMapping) {
int nodeTag = eventMapping.getInt("animatedValueTag");
AnimatedNode node = mAnimatedNodes.get(nodeTag);
@@ -411,6 +456,7 @@ public void addAnimatedEventToView(int viewTag, String eventName, ReadableMap ev
}
}
+ @UiThread
public void removeAnimatedEventFromView(int viewTag, String eventName, int animatedValueTag) {
String key = viewTag + eventName;
if (mEventDrivers.containsKey(key)) {
@@ -429,6 +475,7 @@ public void removeAnimatedEventFromView(int viewTag, String eventName, int anima
}
}
+ @UiThread
@Override
public void onEventDispatch(final Event event) {
// Events can be dispatched from any thread so we have to make sure handleEvent is run from the
@@ -446,10 +493,25 @@ public void run() {
}
}
+ @UiThread
private void handleEvent(Event event) {
if (!mEventDrivers.isEmpty()) {
// If the event has a different name in native convert it to it's JS name.
- String eventName = mCustomEventNamesResolver.resolveCustomEventName(event.getEventName());
+ // TODO T64216139 Remove dependency of UIManagerModule when the Constants are not in Native
+ // anymore
+ if (mReactApplicationContext == null) {
+ return;
+ }
+ UIManager uiManager =
+ UIManagerHelper.getUIManagerForReactTag(mReactApplicationContext, event.getViewTag());
+ if (uiManager == null) {
+ return;
+ }
+ String eventName = uiManager.resolveCustomDirectEventName(event.getEventName());
+ if (eventName == null) {
+ eventName = "";
+ }
+
List driversForKey = mEventDrivers.get(event.getViewTag() + eventName);
if (driversForKey != null) {
for (EventAnimationDriver driver : driversForKey) {
@@ -475,6 +537,7 @@ private void handleEvent(Event event) {
* sub-graph of *active* nodes. This is done by adding node to the BFS queue only if all its
* "predecessors" have already been visited.
*/
+ @UiThread
public void runUpdates(long frameTimeNanos) {
UiThreadUtil.assertOnUiThread();
boolean hasFinishedAnimations = false;
@@ -517,6 +580,7 @@ public void runUpdates(long frameTimeNanos) {
}
}
+ @UiThread
private void updateNodes(List nodes) {
int activeNodesCount = 0;
int updatedNodesCount = 0;
diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.java b/ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.java
index 42cb57a7ec3a8e..e8f77e0dbafdad 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.java
@@ -13,6 +13,8 @@
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.UIManager;
+import com.facebook.react.uimanager.common.UIManagerType;
+import com.facebook.react.uimanager.common.ViewUtil;
import java.util.HashMap;
import java.util.Map;
@@ -69,6 +71,14 @@ public void restoreDefaultValues() {
if (mConnectedViewTag == -1) {
return;
}
+ // Don't restore default values in Fabric.
+ // In Non-Fabric this had the effect of "restore the value to whatever the value was on the
+ // ShadowNode instead of in the View hierarchy". However, "synchronouslyUpdateViewOnUIThread"
+ // will not have that impact on Fabric, because the FabricUIManager doesn't have access to the
+ // ShadowNode layer.
+ if (ViewUtil.getUIManagerType(mConnectedViewTag) == UIManagerType.FABRIC) {
+ return;
+ }
ReadableMapKeySetIterator it = mPropMap.keySetIterator();
while (it.hasNextKey()) {
diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java
index dae969346aa9be..a61d7f4b108ad1 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java
@@ -339,9 +339,6 @@ public void invokeCallback(final int callbackID, final NativeArrayInterface argu
public void destroy() {
FLog.d(ReactConstants.TAG, "CatalystInstanceImpl.destroy() start");
UiThreadUtil.assertOnUiThread();
-
- UiThreadUtil.assertOnUiThread();
-
if (mDestroyed) {
return;
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java
index 27d44870176762..b74f9b4873df1d 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java
@@ -23,7 +23,6 @@
import com.facebook.react.bridge.queue.ReactQueueConfiguration;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.common.ReactConstants;
-import com.facebook.react.config.ReactFeatureFlags;
import java.lang.ref.WeakReference;
import java.util.concurrent.CopyOnWriteArraySet;
@@ -184,7 +183,7 @@ public LifecycleState getLifecycleState() {
public void addLifecycleEventListener(final LifecycleEventListener listener) {
mLifecycleEventListeners.add(listener);
- if (hasActiveCatalystInstance()) {
+ if (hasActiveCatalystInstance() || isBridgeless()) {
switch (mLifecycleState) {
case BEFORE_CREATE:
case BEFORE_RESUME:
@@ -446,7 +445,7 @@ public JavaScriptContextHolder getJavaScriptContextHolder() {
return mCatalystInstance.getJavaScriptContextHolder();
}
- public JSIModule getJSIModule(JSIModuleType moduleType) {
+ public @Nullable JSIModule getJSIModule(JSIModuleType moduleType) {
if (!hasActiveCatalystInstance()) {
throw new IllegalStateException(
"Unable to retrieve a JSIModule if CatalystInstance is not active.");
diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java
index 1707c623edbbc8..0bd6a2c649a15e 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java
@@ -123,4 +123,9 @@ int startSurface(
* @param event parameters
*/
void receiveEvent(int reactTag, String eventName, @Nullable WritableMap event);
+
+ /** Resolves Direct Event name exposed to JS from the one known to the Native side. */
+ @Deprecated
+ @Nullable
+ String resolveCustomDirectEventName(@Nullable String eventName);
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/DebugServerException.java b/ReactAndroid/src/main/java/com/facebook/react/common/DebugServerException.java
index 00f52a84c09a0b..1fbd8380b47920 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/common/DebugServerException.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/common/DebugServerException.java
@@ -21,7 +21,7 @@
public class DebugServerException extends RuntimeException {
private static final String GENERIC_ERROR_MESSAGE =
"\n\nTry the following to fix the issue:\n"
- + "\u2022 Ensure that the packager server is running\n"
+ + "\u2022 Ensure that Metro is running\n"
+ "\u2022 Ensure that your device/emulator is connected to your machine and has USB debugging enabled - run 'adb devices' to see a list of connected devices\n"
+ "\u2022 Ensure Airplane Mode is disabled\n"
+ "\u2022 If you're on a physical device connected to the same machine, run 'adb reverse tcp: tcp:' to forward requests from your device\n"
diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/LifecycleState.java b/ReactAndroid/src/main/java/com/facebook/react/common/LifecycleState.java
index ce15f5f19f8255..cc3238a210309b 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/common/LifecycleState.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/common/LifecycleState.java
@@ -10,9 +10,16 @@
/**
* Lifecycle state for an Activity. The state right after pause and right before resume are the
* basically the same so this enum is in terms of the forward lifecycle progression (onResume, etc).
- * Eventually, if necessary, it could contain something like:
*
- * BEFORE_CREATE, CREATED, VIEW_CREATED, STARTED, RESUMED
+ *
BEFORE_CREATE is used before a ReactRootView is attached to ReactInstanceManager, or after all
+ * the ReactRootView has been detached from the ReactInstanceManager.
+ *
+ *
BEFORE_RESUME is used after a ReactRootView is attached to ReactInstanceManager but before
+ * it's activity is resumed, or after its activity has been paused and before the ReactRootView has
+ * been detached from the ReactInstanceManager.
+ *
+ *
RESUMED is used when a ReactRootView is rendered on the screen and the user can interact with
+ * it.
*/
public enum LifecycleState {
BEFORE_CREATE,
diff --git a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java
index 94e6349153074a..acea75b442f4db 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java
@@ -41,7 +41,7 @@ public class ReactFeatureFlags {
* inside view manager will be called instead.
*/
public static boolean useViewManagerDelegatesForCommands = false;
-
+
/**
* This react flag enables a custom algorithm for the getChildVisibleRect() method in the classes
* ReactViewGroup, ReactHorizontalScrollView and ReactScrollView.
diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java
index 2e118ffa1ac93e..427c7f37a1ac16 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java
@@ -70,6 +70,12 @@
public abstract class DevSupportManagerBase
implements DevSupportManager, PackagerCommandListener, DevInternalSettings.Listener {
+ public interface CallbackWithBundleLoader {
+ void onSuccess(JSBundleLoader bundleLoader);
+
+ void onError(String url, Throwable cause);
+ }
+
private static final int JAVA_ERROR_COOKIE = -1;
private static final int JSEXCEPTION_ERROR_COOKIE = -1;
private static final String JS_BUNDLE_FILE_NAME = "ReactNativeDevBundle.js";
@@ -863,7 +869,29 @@ public void handleReloadJS() {
}
@Override
- public void loadSplitBundleFromServer(String bundlePath, final DevSplitBundleCallback callback) {
+ public void loadSplitBundleFromServer(
+ final String bundlePath, final DevSplitBundleCallback callback) {
+ fetchSplitBundleAndCreateBundleLoader(
+ bundlePath,
+ new CallbackWithBundleLoader() {
+ @Override
+ public void onSuccess(JSBundleLoader bundleLoader) {
+ bundleLoader.loadScript(mCurrentContext.getCatalystInstance());
+ mCurrentContext
+ .getJSModule(HMRClient.class)
+ .registerBundle(mDevServerHelper.getDevServerSplitBundleURL(bundlePath));
+ callback.onSuccess();
+ }
+
+ @Override
+ public void onError(String url, Throwable cause) {
+ callback.onError(url, cause);
+ }
+ });
+ }
+
+ public void fetchSplitBundleAndCreateBundleLoader(
+ String bundlePath, final CallbackWithBundleLoader callback) {
final String bundleUrl = mDevServerHelper.getDevServerSplitBundleURL(bundlePath);
// The bundle path may contain the '/' character, which is not allowed in file names.
final File bundleFile =
@@ -886,16 +914,16 @@ public void run() {
});
@Nullable ReactContext context = mCurrentContext;
- if (context == null || !context.hasActiveCatalystInstance()) {
+ if (context == null
+ || (!context.isBridgeless() && !context.hasActiveCatalystInstance())) {
return;
}
- JSBundleLoader.createCachedSplitBundleFromNetworkLoader(
- bundleUrl, bundleFile.getAbsolutePath())
- .loadScript(context.getCatalystInstance());
- context.getJSModule(HMRClient.class).registerBundle(bundleUrl);
+ JSBundleLoader bundleLoader =
+ JSBundleLoader.createCachedSplitBundleFromNetworkLoader(
+ bundleUrl, bundleFile.getAbsolutePath());
- callback.onSuccess();
+ callback.onSuccess(bundleLoader);
}
@Override
diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/HMRClient.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/HMRClient.java
index 97469c319d92ba..e10cdb81fd15ae 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/HMRClient.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/HMRClient.java
@@ -12,14 +12,14 @@
/**
* JS module interface for HMRClient
*
- *
The HMR(Hot Module Replacement)Client allows for the application to receive updates from the
- * packager server (over a web socket), allowing for injection of JavaScript to the running
- * application (without a refresh).
+ *
The HMR(Hot Module Replacement)Client allows for the application to receive updates from Metro
+ * (over a web socket), allowing for injection of JavaScript to the running application (without a
+ * refresh).
*/
public interface HMRClient extends JavaScriptModule {
/**
- * Enable the HMRClient so that the client will receive updates from the packager server.
+ * Enable the HMRClient so that the client will receive updates from Metro.
*
* @param platform The platform in which HMR updates will be enabled. Should be "android".
* @param bundleEntry The path to the bundle entry file (e.g. index.ios.bundle).
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 3f3eb338d13b17..603089a28d22d4 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java
@@ -314,7 +314,11 @@ private void preallocateView(
@Nullable ReadableMap props,
@Nullable Object stateWrapper,
boolean isLayoutable) {
- ThemedReactContext context = mReactContextForRootTag.get(rootTag);
+
+ // This could be null if teardown/navigation away from a surface on the main thread happens
+ // while a commit is being processed in a different thread. By contract we expect this to be
+ // possible at teardown, but this race should *never* happen at startup.
+ @Nullable ThemedReactContext context = mReactContextForRootTag.get(rootTag);
String component = getFabricComponentName(componentName);
synchronized (mPreMountItemsLock) {
@@ -342,10 +346,12 @@ private MountItem createMountItem(
int reactTag,
boolean isLayoutable) {
String component = getFabricComponentName(componentName);
- ThemedReactContext reactContext = mReactContextForRootTag.get(reactRootTag);
- if (reactContext == null) {
- throw new IllegalArgumentException("Unable to find ReactContext for root: " + reactRootTag);
- }
+
+ // This could be null if teardown/navigation away from a surface on the main thread happens
+ // while a commit is being processed in a different thread. By contract we expect this to be
+ // possible at teardown, but this race should *never* happen at startup.
+ @Nullable ThemedReactContext reactContext = mReactContextForRootTag.get(reactRootTag);
+
return new CreateMountItem(
reactContext,
reactRootTag,
@@ -460,8 +466,19 @@ private long measure(
float minHeight,
float maxHeight,
@Nullable float[] attachmentsPositions) {
+
+ // This could be null if teardown/navigation away from a surface on the main thread happens
+ // while a commit is being processed in a different thread. By contract we expect this to be
+ // possible at teardown, but this race should *never* happen at startup.
+ @Nullable
ReactContext context =
rootTag < 0 ? mReactApplicationContext : mReactContextForRootTag.get(rootTag);
+
+ // Don't both measuring if we can't get a context.
+ if (context == null) {
+ return 0;
+ }
+
return mMountingManager.measure(
context,
componentName,
@@ -1050,6 +1067,19 @@ public void profileNextBatch() {
// TODO T31905686: Remove this method and add support for multi-threading performance counters
}
+ @Override
+ @Deprecated
+ @Nullable
+ public String resolveCustomDirectEventName(@Nullable String eventName) {
+ if (eventName == null) {
+ return null;
+ }
+ if (eventName.substring(0, 3).equals("top")) {
+ return "on" + eventName.substring(3);
+ }
+ return eventName;
+ }
+
// Called from Binding.cpp
@DoNotStrip
@AnyThread
diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/CreateMountItem.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/CreateMountItem.java
index 6320eb1d04d242..d96bab3326e086 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/CreateMountItem.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/CreateMountItem.java
@@ -25,7 +25,7 @@ public class CreateMountItem implements MountItem {
private final boolean mIsLayoutable;
public CreateMountItem(
- @NonNull ThemedReactContext context,
+ @Nullable ThemedReactContext context,
int rootTag,
int reactTag,
@NonNull String component,
@@ -43,6 +43,13 @@ public CreateMountItem(
@Override
public void execute(@NonNull MountingManager mountingManager) {
+ if (mContext == null) {
+ throw new IllegalStateException(
+ "Cannot execute PreAllocateViewMountItem without Context for ReactTag: "
+ + mReactTag
+ + " and rootTag: "
+ + mRootTag);
+ }
mountingManager.createView(
mContext, mComponent, mReactTag, mProps, mStateWrapper, mIsLayoutable);
}
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 7f519e9bc6f2f2..0e3c7140eafbf2 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
@@ -31,7 +31,7 @@ public class PreAllocateViewMountItem implements MountItem {
private final boolean mIsLayoutable;
public PreAllocateViewMountItem(
- @NonNull ThemedReactContext context,
+ @Nullable ThemedReactContext context,
int rootTag,
int reactTag,
@NonNull String component,
@@ -56,6 +56,13 @@ public void execute(@NonNull MountingManager mountingManager) {
if (ENABLE_FABRIC_LOGS) {
FLog.d(TAG, "Executing pre-allocation of: " + toString());
}
+ if (mContext == null) {
+ throw new IllegalStateException(
+ "Cannot execute PreAllocateViewMountItem without Context for ReactTag: "
+ + mReactTag
+ + " and rootTag: "
+ + mRootTag);
+ }
mountingManager.preallocateView(
mContext, mComponent, mReactTag, mProps, mStateWrapper, mIsLayoutable);
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/camera/CameraRollManager.java b/ReactAndroid/src/main/java/com/facebook/react/modules/camera/CameraRollManager.java
deleted file mode 100644
index 7220d67a96b952..00000000000000
--- a/ReactAndroid/src/main/java/com/facebook/react/modules/camera/CameraRollManager.java
+++ /dev/null
@@ -1,545 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its 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.modules.camera;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.graphics.BitmapFactory;
-import android.media.MediaMetadataRetriever;
-import android.media.MediaScannerConnection;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Environment;
-import android.provider.MediaStore;
-import android.provider.MediaStore.Images;
-import android.text.TextUtils;
-import androidx.annotation.Nullable;
-import com.facebook.common.logging.FLog;
-import com.facebook.fbreact.specs.NativeCameraRollManagerSpec;
-import com.facebook.react.bridge.GuardedAsyncTask;
-import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
-import com.facebook.react.bridge.NativeModule;
-import com.facebook.react.bridge.Promise;
-import com.facebook.react.bridge.ReactApplicationContext;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.bridge.ReadableArray;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.bridge.WritableArray;
-import com.facebook.react.bridge.WritableMap;
-import com.facebook.react.bridge.WritableNativeArray;
-import com.facebook.react.bridge.WritableNativeMap;
-import com.facebook.react.common.ReactConstants;
-import com.facebook.react.module.annotations.ReactModule;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.net.URL;
-import java.nio.ByteBuffer;
-import java.nio.channels.Channels;
-import java.nio.channels.FileChannel;
-import java.nio.channels.ReadableByteChannel;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-// TODO #6015104: rename to something less iOSish
-/**
- * {@link NativeModule} that allows JS to interact with the photos and videos on the device (i.e.
- * {@link MediaStore.Images}).
- */
-@ReactModule(name = CameraRollManager.NAME)
-public class CameraRollManager extends NativeCameraRollManagerSpec {
-
- public static final String NAME = "CameraRollManager";
-
- private static final String ERROR_UNABLE_TO_LOAD = "E_UNABLE_TO_LOAD";
- private static final String ERROR_UNABLE_TO_LOAD_PERMISSION = "E_UNABLE_TO_LOAD_PERMISSION";
- private static final String ERROR_UNABLE_TO_SAVE = "E_UNABLE_TO_SAVE";
- private static final String ERROR_UNABLE_TO_FILTER = "E_UNABLE_TO_FILTER";
-
- private static final String ASSET_TYPE_PHOTOS = "Photos";
- private static final String ASSET_TYPE_VIDEOS = "Videos";
- private static final String ASSET_TYPE_ALL = "All";
-
- private static final String SELECTION_BUCKET = Images.Media.BUCKET_DISPLAY_NAME + " = ?";
- private static final String SELECTION_DATE_TAKEN = Images.Media.DATE_TAKEN + " < ?";
- private static final String SELECTION_MEDIA_SIZE = Images.Media.SIZE + " < ?";
-
- private static final int IMAGES_MEDIA_LATITUDE_LONGITUDE_DEPRECATED_API_LEVEL = 29;
- private static final String[] PROJECTION_LIST;
-
- static {
- ArrayList projection_list =
- new ArrayList<>(
- Arrays.asList(
- Images.Media._ID,
- Images.Media.MIME_TYPE,
- Images.Media.BUCKET_DISPLAY_NAME,
- Images.Media.DATE_TAKEN,
- MediaStore.MediaColumns.WIDTH,
- MediaStore.MediaColumns.HEIGHT,
- MediaStore.MediaColumns.DATA));
- if (Build.VERSION.SDK_INT < IMAGES_MEDIA_LATITUDE_LONGITUDE_DEPRECATED_API_LEVEL) {
- projection_list.add(Images.Media.LATITUDE);
- projection_list.add(Images.Media.LONGITUDE);
- PROJECTION_LIST = projection_list.toArray(new String[0]);
- } else {
- PROJECTION_LIST = projection_list.toArray(new String[0]);
- }
- }
-
- public CameraRollManager(ReactApplicationContext reactContext) {
- super(reactContext);
- }
-
- @Override
- public String getName() {
- return NAME;
- }
-
- /**
- * Save an image to the gallery (i.e. {@link MediaStore.Images}). This copies the original file
- * from wherever it may be to the external storage pictures directory, so that it can be scanned
- * by the MediaScanner.
- *
- * @param uri the file://, http:// or https:// URI of the image to save
- * @param promise to be resolved or rejected
- */
- @Override
- public void saveToCameraRoll(String uri, String type, Promise promise) {
- new SaveToCameraRoll(getReactApplicationContext(), Uri.parse(uri), promise)
- .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- private static class SaveToCameraRoll extends GuardedAsyncTask {
-
- private static final int SAVE_BUFFER_SIZE = 1048576; // 1MB
- private final Context mContext;
- private final Uri mUri;
- private final Promise mPromise;
-
- public SaveToCameraRoll(ReactContext context, Uri uri, Promise promise) {
- super(context);
- mContext = context;
- mUri = uri;
- mPromise = promise;
- }
-
- @Override
- protected void doInBackgroundGuarded(Void... params) {
- ReadableByteChannel input = null;
- FileChannel output = null;
- File source = new File(mUri.getPath());
- try {
- String scheme = mUri.getScheme();
- if (scheme.equals("http") || scheme.equals("https")) {
- input = Channels.newChannel(new URL(mUri.toString()).openStream());
- } else {
- input = new FileInputStream(source).getChannel();
- }
- File exportDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
- exportDir.mkdirs();
- if (!exportDir.isDirectory()) {
- mPromise.reject(ERROR_UNABLE_TO_LOAD, "External media storage directory not available");
- return;
- }
- File dest = new File(exportDir, source.getName());
- int n = 0;
- String fullSourceName = source.getName();
- String sourceName, sourceExt;
- if (fullSourceName.indexOf('.') >= 0) {
- sourceName = fullSourceName.substring(0, fullSourceName.lastIndexOf('.'));
- sourceExt = fullSourceName.substring(fullSourceName.lastIndexOf('.'));
- } else {
- sourceName = fullSourceName;
- sourceExt = "";
- }
- while (!dest.createNewFile()) {
- dest = new File(exportDir, sourceName + "_" + (n++) + sourceExt);
- }
- output = new FileOutputStream(dest).getChannel();
- // Performs a buffered copy
- final ByteBuffer buffer = ByteBuffer.allocate(SAVE_BUFFER_SIZE);
- while (input.read(buffer) > 0) {
- buffer.flip();
- output.write(buffer);
- buffer.compact();
- }
- // Drains the buffer
- buffer.flip();
- while (buffer.hasRemaining()) {
- output.write(buffer);
- }
- input.close();
- output.close();
-
- MediaScannerConnection.scanFile(
- mContext,
- new String[] {dest.getAbsolutePath()},
- null,
- new MediaScannerConnection.OnScanCompletedListener() {
- @Override
- public void onScanCompleted(String path, Uri uri) {
- if (uri != null) {
- mPromise.resolve(uri.toString());
- } else {
- mPromise.reject(ERROR_UNABLE_TO_SAVE, "Could not add image to gallery");
- }
- }
- });
- } catch (IOException e) {
- mPromise.reject(e);
- } finally {
- if (input != null && input.isOpen()) {
- try {
- input.close();
- } catch (IOException e) {
- FLog.e(ReactConstants.TAG, "Could not close input channel", e);
- }
- }
- if (output != null && output.isOpen()) {
- try {
- output.close();
- } catch (IOException e) {
- FLog.e(ReactConstants.TAG, "Could not close output channel", e);
- }
- }
- }
- }
- }
-
- /**
- * Get photos from {@link MediaStore.Images}, most recent first.
- *
- * @param params a map containing the following keys:
- *
- * - first (mandatory): a number representing the number of photos to fetch
- *
- after (optional): a cursor that matches page_info[end_cursor] returned by a previous
- * call to {@link #getPhotos}
- *
- groupName (optional): an album name
- *
- mimeType (optional): restrict returned images to a specific mimetype (e.g.
- * image/jpeg)
- *
- assetType (optional): chooses between either photos or videos from the camera roll.
- * Valid values are "Photos" or "Videos". Defaults to photos.
- *
- *
- * @param promise the Promise to be resolved when the photos are loaded; for a format of the
- * parameters passed to this callback, see {@code getPhotosReturnChecker} in CameraRoll.js
- */
- @Override
- public void getPhotos(final ReadableMap params, final Promise promise) {
- int first = params.getInt("first");
- String after = params.hasKey("after") ? params.getString("after") : null;
- String groupName = params.hasKey("groupName") ? params.getString("groupName") : null;
- String assetType =
- params.hasKey("assetType") ? params.getString("assetType") : ASSET_TYPE_PHOTOS;
- Integer maxSize = params.hasKey("maxSize") ? params.getInt("maxSize") : null;
- ReadableArray mimeTypes = params.hasKey("mimeTypes") ? params.getArray("mimeTypes") : null;
- if (params.hasKey("groupTypes")) {
- throw new JSApplicationIllegalArgumentException("groupTypes is not supported on Android");
- }
-
- new GetMediaTask(
- getReactApplicationContext(),
- first,
- after,
- groupName,
- mimeTypes,
- assetType,
- maxSize,
- promise)
- .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- private static class GetMediaTask extends GuardedAsyncTask {
- private final Context mContext;
- private final int mFirst;
- private final @Nullable String mAfter;
- private final @Nullable String mGroupName;
- private final @Nullable ReadableArray mMimeTypes;
- private final Promise mPromise;
- private final String mAssetType;
- private final @Nullable Integer mMaxSize;
-
- private GetMediaTask(
- ReactContext context,
- int first,
- @Nullable String after,
- @Nullable String groupName,
- @Nullable ReadableArray mimeTypes,
- String assetType,
- @Nullable Integer maxSize,
- Promise promise) {
- super(context);
- mContext = context;
- mFirst = first;
- mAfter = after;
- mGroupName = groupName;
- mMimeTypes = mimeTypes;
- mPromise = promise;
- mAssetType = assetType;
- mMaxSize = maxSize;
- }
-
- @Override
- protected void doInBackgroundGuarded(Void... params) {
- StringBuilder selection = new StringBuilder("1");
- List selectionArgs = new ArrayList<>();
- if (!TextUtils.isEmpty(mAfter)) {
- selection.append(" AND " + SELECTION_DATE_TAKEN);
- selectionArgs.add(mAfter);
- }
- if (!TextUtils.isEmpty(mGroupName)) {
- selection.append(" AND " + SELECTION_BUCKET);
- selectionArgs.add(mGroupName);
- }
- if (mMaxSize != null) {
- selection.append(" AND " + SELECTION_MEDIA_SIZE);
- selectionArgs.add(mMaxSize.toString());
- }
-
- switch (mAssetType) {
- case ASSET_TYPE_PHOTOS:
- selection.append(
- " AND "
- + MediaStore.Files.FileColumns.MEDIA_TYPE
- + " = "
- + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
- break;
- case ASSET_TYPE_VIDEOS:
- selection.append(
- " AND "
- + MediaStore.Files.FileColumns.MEDIA_TYPE
- + " = "
- + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO);
- break;
- case ASSET_TYPE_ALL:
- selection.append(
- " AND "
- + MediaStore.Files.FileColumns.MEDIA_TYPE
- + " IN ("
- + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO
- + ","
- + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
- + ")");
- break;
- default:
- mPromise.reject(
- ERROR_UNABLE_TO_FILTER,
- "Invalid filter option: '"
- + mAssetType
- + "'. Expected one of '"
- + ASSET_TYPE_PHOTOS
- + "', '"
- + ASSET_TYPE_VIDEOS
- + "' or '"
- + ASSET_TYPE_ALL
- + "'.");
- return;
- }
-
- if (mMimeTypes != null && mMimeTypes.size() > 0) {
- selection.append(" AND " + Images.Media.MIME_TYPE + " IN (");
- for (int i = 0; i < mMimeTypes.size(); i++) {
- selection.append("?,");
- selectionArgs.add(mMimeTypes.getString(i));
- }
- selection.replace(selection.length() - 1, selection.length(), ")");
- }
- WritableMap response = new WritableNativeMap();
- ContentResolver resolver = mContext.getContentResolver();
- // using LIMIT in the sortOrder is not explicitly supported by the SDK (which does not support
- // setting a limit at all), but it works because this specific ContentProvider is backed by
- // an SQLite DB and forwards parameters to it without doing any parsing / validation.
- try {
- Cursor media =
- resolver.query(
- MediaStore.Files.getContentUri("external"),
- PROJECTION_LIST,
- selection.toString(),
- selectionArgs.toArray(new String[selectionArgs.size()]),
- Images.Media.DATE_TAKEN
- + " DESC, "
- + Images.Media.DATE_MODIFIED
- + " DESC LIMIT "
- + (mFirst
- + 1)); // set LIMIT to first + 1 so that we know how to populate page_info
- if (media == null) {
- mPromise.reject(ERROR_UNABLE_TO_LOAD, "Could not get media");
- } else {
- try {
- putEdges(resolver, media, response, mFirst);
- putPageInfo(media, response, mFirst);
- } finally {
- media.close();
- mPromise.resolve(response);
- }
- }
- } catch (SecurityException e) {
- mPromise.reject(
- ERROR_UNABLE_TO_LOAD_PERMISSION,
- "Could not get media: need READ_EXTERNAL_STORAGE permission",
- e);
- }
- }
- }
-
- private static void putPageInfo(Cursor media, WritableMap response, int limit) {
- WritableMap pageInfo = new WritableNativeMap();
- pageInfo.putBoolean("has_next_page", limit < media.getCount());
- if (limit < media.getCount()) {
- media.moveToPosition(limit - 1);
- pageInfo.putString(
- "end_cursor", media.getString(media.getColumnIndex(Images.Media.DATE_TAKEN)));
- }
- response.putMap("page_info", pageInfo);
- }
-
- private static void putEdges(
- ContentResolver resolver, Cursor media, WritableMap response, int limit) {
- WritableArray edges = new WritableNativeArray();
- media.moveToFirst();
- int idIndex = media.getColumnIndex(Images.Media._ID);
- int mimeTypeIndex = media.getColumnIndex(Images.Media.MIME_TYPE);
- int groupNameIndex = media.getColumnIndex(Images.Media.BUCKET_DISPLAY_NAME);
- int dateTakenIndex = media.getColumnIndex(Images.Media.DATE_TAKEN);
- int widthIndex = media.getColumnIndex(MediaStore.MediaColumns.WIDTH);
- int heightIndex = media.getColumnIndex(MediaStore.MediaColumns.HEIGHT);
- int longitudeIndex = media.getColumnIndex(Images.Media.LONGITUDE);
- int latitudeIndex = media.getColumnIndex(Images.Media.LATITUDE);
- int dataIndex = media.getColumnIndex(MediaStore.MediaColumns.DATA);
-
- for (int i = 0; i < limit && !media.isAfterLast(); i++) {
- WritableMap edge = new WritableNativeMap();
- WritableMap node = new WritableNativeMap();
- boolean imageInfoSuccess =
- putImageInfo(
- resolver, media, node, idIndex, widthIndex, heightIndex, dataIndex, mimeTypeIndex);
- if (imageInfoSuccess) {
- putBasicNodeInfo(media, node, mimeTypeIndex, groupNameIndex, dateTakenIndex);
- if (Build.VERSION.SDK_INT < IMAGES_MEDIA_LATITUDE_LONGITUDE_DEPRECATED_API_LEVEL) {
- putLocationInfo(media, node, longitudeIndex, latitudeIndex);
- }
-
- edge.putMap("node", node);
- edges.pushMap(edge);
- } else {
- // we skipped an image because we couldn't get its details (e.g. width/height), so we
- // decrement i in order to correctly reach the limit, if the cursor has enough rows
- i--;
- }
- media.moveToNext();
- }
- response.putArray("edges", edges);
- }
-
- private static void putBasicNodeInfo(
- Cursor media, WritableMap node, int mimeTypeIndex, int groupNameIndex, int dateTakenIndex) {
- node.putString("type", media.getString(mimeTypeIndex));
- node.putString("group_name", media.getString(groupNameIndex));
- node.putDouble("timestamp", media.getLong(dateTakenIndex) / 1000d);
- }
-
- private static boolean putImageInfo(
- ContentResolver resolver,
- Cursor media,
- WritableMap node,
- int idIndex,
- int widthIndex,
- int heightIndex,
- int dataIndex,
- int mimeTypeIndex) {
- WritableMap image = new WritableNativeMap();
- Uri photoUri = Uri.parse("file://" + media.getString(dataIndex));
- image.putString("uri", photoUri.toString());
- float width = media.getInt(widthIndex);
- float height = media.getInt(heightIndex);
-
- String mimeType = media.getString(mimeTypeIndex);
-
- if (mimeType != null && mimeType.startsWith("video")) {
- try {
- AssetFileDescriptor photoDescriptor = resolver.openAssetFileDescriptor(photoUri, "r");
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- retriever.setDataSource(photoDescriptor.getFileDescriptor());
-
- try {
- if (width <= 0 || height <= 0) {
- width =
- Integer.parseInt(
- retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
- height =
- Integer.parseInt(
- retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
- }
- int timeInMillisec =
- Integer.parseInt(
- retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
- int playableDuration = timeInMillisec / 1000;
- image.putInt("playableDuration", playableDuration);
- } catch (NumberFormatException e) {
- FLog.e(
- ReactConstants.TAG,
- "Number format exception occurred while trying to fetch video metadata for "
- + photoUri.toString(),
- e);
- return false;
- } finally {
- retriever.release();
- photoDescriptor.close();
- }
- } catch (Exception e) {
- FLog.e(ReactConstants.TAG, "Could not get video metadata for " + photoUri.toString(), e);
- return false;
- }
- }
-
- if (width <= 0 || height <= 0) {
- try {
- AssetFileDescriptor photoDescriptor = resolver.openAssetFileDescriptor(photoUri, "r");
- BitmapFactory.Options options = new BitmapFactory.Options();
- // Set inJustDecodeBounds to true so we don't actually load the Bitmap, but only get its
- // dimensions instead.
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFileDescriptor(photoDescriptor.getFileDescriptor(), null, options);
- width = options.outWidth;
- height = options.outHeight;
- photoDescriptor.close();
- } catch (IOException e) {
- FLog.e(ReactConstants.TAG, "Could not get width/height for " + photoUri.toString(), e);
- return false;
- }
- }
- image.putDouble("width", width);
- image.putDouble("height", height);
- node.putMap("image", image);
-
- return true;
- }
-
- private static void putLocationInfo(
- Cursor media, WritableMap node, int longitudeIndex, int latitudeIndex) {
- double longitude = media.getDouble(longitudeIndex);
- double latitude = media.getDouble(latitudeIndex);
- if (longitude > 0 || latitude > 0) {
- WritableMap location = new WritableNativeMap();
- location.putDouble("longitude", longitude);
- location.putDouble("latitude", latitude);
- node.putMap("location", location);
- }
- }
-
- @Override
- public void deletePhotos(ReadableArray assets, Promise promise) {
- // iOS only
- }
-}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/BUCK b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/BUCK
new file mode 100644
index 00000000000000..d8d99e703d2b02
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/BUCK
@@ -0,0 +1,22 @@
+load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library")
+
+rn_android_library(
+ name = "reactperflogger",
+ srcs = glob(
+ [
+ "*.java",
+ ],
+ ),
+ labels = [
+ "supermodule:xplat/default/public.react_native.infra",
+ ],
+ required_for_source_only_abi = True,
+ visibility = [
+ "PUBLIC",
+ ],
+ deps = [
+ react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
+ react_native_dep("libraries/fbjni:java"),
+ react_native_target("java/com/facebook/react/reactperflogger/jni:jni"),
+ ],
+)
diff --git a/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/NativeModulePerfLogger.java b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/NativeModulePerfLogger.java
new file mode 100644
index 00000000000000..56f9adfbbfb8c3
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/NativeModulePerfLogger.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) Facebook, Inc. and its 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.perflogger;
+
+import com.facebook.jni.HybridData;
+import com.facebook.soloader.SoLoader;
+
+public abstract class NativeModulePerfLogger {
+ private final HybridData mHybridData;
+
+ private static volatile boolean sIsSoLibraryLoaded;
+
+ protected abstract HybridData initHybrid();
+
+ protected NativeModulePerfLogger() {
+ maybeLoadOtherSoLibraries();
+ maybeLoadSoLibrary();
+ mHybridData = initHybrid();
+ }
+
+ public abstract void moduleDataCreateStart(String moduleName, int id);
+
+ public abstract void moduleDataCreateEnd(String moduleName, int id);
+
+ public abstract void moduleCreateStart(String moduleName, int id);
+
+ public abstract void moduleCreateCacheHit(String moduleName, int id);
+
+ public abstract void moduleCreateConstructStart(String moduleName, int id);
+
+ public abstract void moduleCreateConstructEnd(String moduleName, int id);
+
+ public abstract void moduleCreateSetUpStart(String moduleName, int id);
+
+ public abstract void moduleCreateSetUpEnd(String moduleName, int id);
+
+ public abstract void moduleCreateEnd(String moduleName, int id);
+
+ public abstract void moduleCreateFail(String moduleName, int id);
+
+ // Prevents issues with initializer interruptions. See T38996825 and D13793825 for more context.
+ private static synchronized void maybeLoadSoLibrary() {
+ if (!sIsSoLibraryLoaded) {
+ SoLoader.loadLibrary("reactperfloggerjni");
+ sIsSoLibraryLoaded = true;
+ }
+ }
+
+ /** Subclasses will override this method to load their own SO libraries. */
+ protected synchronized void maybeLoadOtherSoLibraries() {}
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/Android.mk b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/Android.mk
new file mode 100644
index 00000000000000..c44496918d50fc
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (c) Facebook, Inc. and its affiliates.
+#
+# This source code is licensed under the MIT license found in the
+# LICENSE file in the root directory of this source tree.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Header search path for all source files in this module.
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/reactperflogger
+
+# Header search path for modules that depend on this module
+LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
+
+LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall
+
+LOCAL_LDLIBS += -landroid
+
+LOCAL_STATIC_LIBRARIES = libreactperflogger
+
+LOCAL_SHARED_LIBRARIES = libfb libfbjni
+
+# Name of this module.
+LOCAL_MODULE := reactperfloggerjni
+
+# Compile all local c++ files
+LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/reactperflogger/*.cpp)
+
+# Build the files in this directory as a shared library
+include $(BUILD_SHARED_LIBRARY)
diff --git a/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/BUCK b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/BUCK
new file mode 100644
index 00000000000000..481682cb97c8c3
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/BUCK
@@ -0,0 +1,37 @@
+load("@fbsource//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "FBJNI_TARGET", "react_native_xplat_target", "rn_xplat_cxx_library")
+
+rn_xplat_cxx_library(
+ name = "jni",
+ srcs = [
+ "reactperflogger/OnLoad.cpp",
+ ],
+ header_namespace = "",
+ exported_headers = {
+ "reactperflogger/JNativeModulePerfLogger.h": "reactperflogger/JNativeModulePerfLogger.h",
+ },
+ compiler_flags = [
+ "-fexceptions",
+ "-frtti",
+ "-std=c++14",
+ "-Wall",
+ ],
+ fbandroid_allow_jni_merging = True,
+ fbandroid_labels = [
+ "supermodule:xplat/default/public.react_native.infra",
+ ],
+ platforms = ANDROID,
+ preprocessor_flags = [
+ "-DLOG_TAG=\"ReactNative\"",
+ "-DWITH_FBSYSTRACE=1",
+ ],
+ soname = "libreactperfloggerjni.$(ext)",
+ visibility = [
+ "PUBLIC",
+ ],
+ deps = [
+ FBJNI_TARGET,
+ ],
+ exported_deps = [
+ react_native_xplat_target("reactperflogger:reactperflogger"),
+ ],
+)
diff --git a/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/reactperflogger/.clang-tidy b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/reactperflogger/.clang-tidy
new file mode 100644
index 00000000000000..c98fd78ff64baa
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/reactperflogger/.clang-tidy
@@ -0,0 +1,5 @@
+---
+Checks: '>
+clang-diagnostic-*,
+'
+...
diff --git a/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/reactperflogger/JNativeModulePerfLogger.h b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/reactperflogger/JNativeModulePerfLogger.h
new file mode 100644
index 00000000000000..018d8de782deaf
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/reactperflogger/JNativeModulePerfLogger.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#pragma once
+
+#include
+#include
+#include
+
+namespace facebook {
+namespace react {
+
+class JNativeModulePerfLogger
+ : public jni::HybridClass {
+ public:
+ static auto constexpr kJavaDescriptor =
+ "Lcom/facebook/react/perflogger/NativeModulePerfLogger;";
+
+ virtual std::unique_ptr get() = 0;
+
+ private:
+ friend HybridBase;
+};
+
+} // namespace react
+} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/state/ARTElement.cpp b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/reactperflogger/OnLoad.cpp
similarity index 53%
rename from ReactCommon/fabric/components/art/state/ARTElement.cpp
rename to ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/reactperflogger/OnLoad.cpp
index 5b1ed74cb61c3a..4ac3d476441645 100644
--- a/ReactCommon/fabric/components/art/state/ARTElement.cpp
+++ b/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/reactperflogger/OnLoad.cpp
@@ -5,8 +5,10 @@
* LICENSE file in the root directory of this source tree.
*/
-#include
+#include
-namespace facebook {
-namespace react {} // namespace react
-} // namespace facebook
+#include "JNativeModulePerfLogger.h"
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
+ return facebook::jni::initialize(vm, [] {});
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java
index 669718e63cc353..35ce74f69b5f58 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java
@@ -21,7 +21,6 @@
import com.facebook.react.modules.appstate.AppStateModule;
import com.facebook.react.modules.blob.BlobModule;
import com.facebook.react.modules.blob.FileReaderModule;
-import com.facebook.react.modules.camera.CameraRollManager;
import com.facebook.react.modules.camera.ImageStoreManager;
import com.facebook.react.modules.clipboard.ClipboardModule;
import com.facebook.react.modules.datepicker.DatePickerDialogModule;
@@ -73,7 +72,6 @@
BlobModule.class,
FileReaderModule.class,
AsyncStorageModule.class,
- CameraRollManager.class,
ClipboardModule.class,
DatePickerDialogModule.class,
DialogModule.class,
@@ -118,8 +116,6 @@ public MainReactPackage(MainPackageConfig config) {
return new FileReaderModule(context);
case AsyncStorageModule.NAME:
return new AsyncStorageModule(context);
- case CameraRollManager.NAME:
- return new CameraRollManager(context);
case ClipboardModule.NAME:
return new ClipboardModule(context);
case DatePickerDialogModule.FRAGMENT_TAG:
@@ -203,7 +199,6 @@ public ReactModuleInfoProvider getReactModuleInfoProvider() {
BlobModule.class,
FileReaderModule.class,
AsyncStorageModule.class,
- CameraRollManager.class,
ClipboardModule.class,
DatePickerDialogModule.class,
DialogModule.class,
diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK
index 50e716a39a8cb6..721fe18d0cc7f1 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK
+++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK
@@ -22,6 +22,7 @@ rn_android_library(
react_native_dep("third-party/java/infer-annotations:infer-annotations"),
react_native_dep("third-party/java/jsr-305:jsr-305"),
react_native_target("java/com/facebook/react/common:common"),
+ react_native_target("java/com/facebook/react/reactperflogger:reactperflogger"),
react_native_target("java/com/facebook/react/turbomodule/core/jni:jni"),
react_native_target("java/com/facebook/debug/holder:holder"),
react_native_target("java/com/facebook/react/bridge:interfaces"),
diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModulePerfLogger.java b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModulePerfLogger.java
new file mode 100644
index 00000000000000..19d84a41a5a963
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModulePerfLogger.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) Facebook, Inc. and its 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.turbomodule.core;
+
+import com.facebook.react.perflogger.NativeModulePerfLogger;
+import com.facebook.soloader.SoLoader;
+import javax.annotation.Nullable;
+
+public class TurboModulePerfLogger {
+ @Nullable private static NativeModulePerfLogger sNativeModulePerfLogger = null;
+
+ static {
+ SoLoader.loadLibrary("turbomodulejsijni");
+ }
+
+ public static void moduleDataCreateStart(String moduleName, int id) {
+ if (sNativeModulePerfLogger != null) {
+ sNativeModulePerfLogger.moduleDataCreateStart(moduleName, id);
+ }
+ }
+
+ public static void moduleDataCreateEnd(String moduleName, int id) {
+ if (sNativeModulePerfLogger != null) {
+ sNativeModulePerfLogger.moduleDataCreateEnd(moduleName, id);
+ }
+ }
+
+ public static void moduleCreateStart(String moduleName, int id) {
+ if (sNativeModulePerfLogger != null) {
+ sNativeModulePerfLogger.moduleCreateStart(moduleName, id);
+ }
+ }
+
+ public static void moduleCreateCacheHit(String moduleName, int id) {
+ if (sNativeModulePerfLogger != null) {
+ sNativeModulePerfLogger.moduleCreateCacheHit(moduleName, id);
+ }
+ }
+
+ public static void moduleCreateConstructStart(String moduleName, int id) {
+ if (sNativeModulePerfLogger != null) {
+ sNativeModulePerfLogger.moduleCreateConstructStart(moduleName, id);
+ }
+ }
+
+ public static void moduleCreateConstructEnd(String moduleName, int id) {
+ if (sNativeModulePerfLogger != null) {
+ sNativeModulePerfLogger.moduleCreateConstructEnd(moduleName, id);
+ }
+ }
+
+ public static void moduleCreateSetUpStart(String moduleName, int id) {
+ if (sNativeModulePerfLogger != null) {
+ sNativeModulePerfLogger.moduleCreateSetUpStart(moduleName, id);
+ }
+ }
+
+ public static void moduleCreateSetUpEnd(String moduleName, int id) {
+ if (sNativeModulePerfLogger != null) {
+ sNativeModulePerfLogger.moduleCreateSetUpEnd(moduleName, id);
+ }
+ }
+
+ public static void moduleCreateEnd(String moduleName, int id) {
+ if (sNativeModulePerfLogger != null) {
+ sNativeModulePerfLogger.moduleCreateEnd(moduleName, id);
+ }
+ }
+
+ private static native void jniEnableCppLogging(NativeModulePerfLogger perfLogger);
+
+ public static void enableLogging(NativeModulePerfLogger perfLogger) {
+ if (perfLogger != null) {
+ sNativeModulePerfLogger = perfLogger;
+ jniEnableCppLogging(perfLogger);
+ }
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk
index 1633f5ad878a53..80ba4096da6480 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk
+++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk
@@ -15,7 +15,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall
-LOCAL_STATIC_LIBRARIES = libcallinvoker
+LOCAL_STATIC_LIBRARIES = libcallinvoker libreactperfloggerjni
LOCAL_SHARED_LIBRARIES = libfb libfbjni
diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK
index 9a7eb46af4bf50..84258474072b91 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK
+++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK
@@ -35,6 +35,7 @@ rn_xplat_cxx_library(
":callinvokerholder",
"//xplat/jsi:jsi",
react_native_xplat_target("turbomodule/core:core"),
+ react_native_target("java/com/facebook/react/reactperflogger/jni:jni"),
],
)
diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/OnLoad.cpp b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/OnLoad.cpp
index 65dc87dd688c10..9cc583c7231849 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/OnLoad.cpp
+++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/OnLoad.cpp
@@ -5,15 +5,29 @@
* LICENSE file in the root directory of this source tree.
*/
+#include
#include
#include
+#include
#include "TurboModuleManager.h"
+void jniEnableCppLogging(
+ jni::alias_ref cls,
+ jni::alias_ref
+ perfLogger) {
+ facebook::react::TurboModulePerfLogger::enableLogging(
+ perfLogger->cthis()->get());
+}
+
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return facebook::xplat::initialize(vm, [] {
// TODO: dvacca ramanpreet unify this with the way
// "ComponentDescriptorFactory" is defined in Fabric
facebook::react::TurboModuleManager::registerNatives();
+
+ facebook::jni::registerNatives(
+ "com/facebook/react/turbomodule/core/TurboModulePerfLogger",
+ {makeNativeMethod("jniEnableCppLogging", jniEnableCppLogging)});
});
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.cpp b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.cpp
index ac3a74c20660c9..c2a3b76df36d3c 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.cpp
+++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.cpp
@@ -13,6 +13,7 @@
#include
#include
+#include
#include
#include "TurboModuleManager.h"
@@ -81,11 +82,19 @@ void TurboModuleManager::installJSIBindings() {
return nullptr;
}
+ const char *moduleName = name.c_str();
+
+ TurboModulePerfLogger::moduleJSRequireBeginningStart(moduleName);
+
auto turboModuleLookup = turboModuleCache->find(name);
if (turboModuleLookup != turboModuleCache->end()) {
+ TurboModulePerfLogger::moduleJSRequireBeginningCacheHit(moduleName);
+ TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName);
return turboModuleLookup->second;
}
+ TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName);
+
auto cxxModule = delegate->cthis()->getTurboModule(name, jsCallInvoker);
if (cxxModule) {
turboModuleCache->insert({name, cxxModule});
@@ -99,9 +108,13 @@ void TurboModuleManager::installJSIBindings() {
auto legacyCxxModule = getLegacyCxxModule(javaPart.get(), name);
if (legacyCxxModule) {
+ TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName);
+
auto turboModule = std::make_shared(
legacyCxxModule->cthis()->getModule(), jsCallInvoker);
turboModuleCache->insert({name, turboModule});
+
+ TurboModulePerfLogger::moduleJSRequireEndingEnd(moduleName);
return turboModule;
}
@@ -112,6 +125,7 @@ void TurboModuleManager::installJSIBindings() {
auto moduleInstance = getJavaModule(javaPart.get(), name);
if (moduleInstance) {
+ TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName);
JavaTurboModule::InitParams params = {.moduleName = name,
.instance = moduleInstance,
.jsInvoker = jsCallInvoker,
@@ -119,6 +133,7 @@ void TurboModuleManager::installJSIBindings() {
auto turboModule = delegate->cthis()->getTurboModule(name, params);
turboModuleCache->insert({name, turboModule});
+ TurboModulePerfLogger::moduleJSRequireEndingEnd(moduleName);
return turboModule;
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java
index ef39975d4936ba..2028bdb6082536 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java
@@ -95,6 +95,15 @@ public void setElevation(@NonNull T view, float elevation) {
ViewCompat.setElevation(view, PixelUtil.toPixelFromDIP(elevation));
}
+ @Override
+ @ReactProp(name = ViewProps.SHADOW_COLOR, defaultInt = Color.BLACK, customType = "Color")
+ public void setShadowColor(@NonNull T view, int shadowColor) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ view.setOutlineAmbientShadowColor(shadowColor);
+ view.setOutlineSpotShadowColor(shadowColor);
+ }
+ }
+
@Override
@ReactProp(name = ViewProps.Z_INDEX)
public void setZIndex(@NonNull T view, float zIndex) {
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerAdapter.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerAdapter.java
index 6af559a15c90f2..c0e21dece66f2a 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerAdapter.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerAdapter.java
@@ -54,6 +54,9 @@ public void setBorderTopRightRadius(@NonNull T view, float borderRadius) {}
@Override
public void setElevation(@NonNull T view, float elevation) {}
+ @Override
+ public void setShadowColor(@NonNull T view, int shadowColor) {}
+
@Override
public void setImportantForAccessibility(
@NonNull T view, @Nullable String importantForAccessibility) {}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.java
index f430398bacf208..1598b578877b2f 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.java
@@ -74,6 +74,10 @@ public void setProperty(T view, String propName, @Nullable Object value) {
case ViewProps.ELEVATION:
mViewManager.setElevation(view, value == null ? 0.0f : ((Double) value).floatValue());
break;
+ case ViewProps.SHADOW_COLOR:
+ mViewManager.setShadowColor(
+ view, value == null ? 0 : ColorPropConverter.getColor(value, view.getContext()));
+ break;
case ViewProps.IMPORTANT_FOR_ACCESSIBILITY:
mViewManager.setImportantForAccessibility(view, (String) value);
break;
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.java
index fb194e213699f2..5423eeedd91a71 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.java
@@ -43,6 +43,8 @@ public interface BaseViewManagerInterface {
void setElevation(T view, float elevation);
+ void setShadowColor(T view, int shadowColor);
+
void setImportantForAccessibility(T view, @Nullable String importantForAccessibility);
void setNativeId(T view, @Nullable String nativeId);
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java
index 039798dcaf321f..834208a962e676 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java
@@ -10,6 +10,8 @@
import android.app.Activity;
import android.content.Context;
import androidx.annotation.Nullable;
+import com.facebook.react.bridge.JSIModule;
+import com.facebook.react.bridge.JSIModuleType;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
@@ -77,4 +79,12 @@ public ReactApplicationContext getReactApplicationContext() {
public boolean isBridgeless() {
return mReactApplicationContext.isBridgeless();
}
+
+ @Override
+ public JSIModule getJSIModule(JSIModuleType moduleType) {
+ if (isBridgeless()) {
+ return mReactApplicationContext.getJSIModule(moduleType);
+ }
+ return super.getJSIModule(moduleType);
+ }
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java
index 30133a5388815b..954dfe95edfb3c 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java
@@ -51,6 +51,22 @@ private static UIManager getUIManager(
ReactContext context,
@UIManagerType int uiManagerType,
boolean returnNullIfCatalystIsInactive) {
+ if (context.isBridgeless()) {
+ @Nullable
+ UIManager uiManager =
+ context.getJSIModule(JSIModuleType.UIManager) != null
+ ? (UIManager) context.getJSIModule(JSIModuleType.UIManager)
+ : null;
+ if (uiManager == null) {
+ ReactSoftException.logSoftException(
+ "UIManagerHelper",
+ new ReactNoCrashSoftException(
+ "Cannot get UIManager because the instance hasn't been initialized yet."));
+ return null;
+ }
+ return uiManager;
+ }
+
if (!context.hasCatalystInstance()) {
ReactSoftException.logSoftException(
"UIManagerHelper",
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java
index 52750844ed8d26..2336cf82953f57 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java
@@ -51,6 +51,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* Native module to allow JS to create and update native Views.
@@ -119,7 +120,8 @@ public interface CustomEventNamesResolver {
private final UIImplementation mUIImplementation;
private final MemoryTrimCallback mMemoryTrimCallback = new MemoryTrimCallback();
private final List mListeners = new ArrayList<>();
- private final List mUIManagerListeners = new ArrayList<>();
+ private final CopyOnWriteArrayList mUIManagerListeners =
+ new CopyOnWriteArrayList<>();
private @Nullable Map mViewManagerConstantsCache;
private volatile int mViewManagerConstantsCacheSize;
@@ -355,20 +357,30 @@ public WritableMap getDefaultEventTypes() {
}
/** Resolves Direct Event name exposed to JS from the one known to the Native side. */
+ @Deprecated
public CustomEventNamesResolver getDirectEventNamesResolver() {
return new CustomEventNamesResolver() {
@Override
- public @Nullable String resolveCustomEventName(String eventName) {
- Map customEventType =
- (Map) mCustomDirectEvents.get(eventName);
- if (customEventType != null) {
- return customEventType.get("registrationName");
- }
- return eventName;
+ public @Nullable String resolveCustomEventName(@Nullable String eventName) {
+ return resolveCustomDirectEventName(eventName);
}
};
}
+ @Override
+ @Deprecated
+ @Nullable
+ public String resolveCustomDirectEventName(@Nullable String eventName) {
+ if (eventName != null) {
+ Map customEventType =
+ (Map) mCustomDirectEvents.get(eventName);
+ if (customEventType != null) {
+ return customEventType.get("registrationName");
+ }
+ }
+ return eventName;
+ }
+
@Override
public void profileNextBatch() {
mUIImplementation.profileNextBatch();
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java
index 69530675e32b31..bb3df5f827e710 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java
@@ -866,8 +866,7 @@ public void run() {
long runStartTime = SystemClock.uptimeMillis();
// All ViewCommands should be executed first as a perf optimization.
- // This entire block is only executed if there's a separate viewCommand queue,
- // which is currently gated by a ReactFeatureFlag.
+ // This entire block is only executed if there's at least one ViewCommand queued.
if (viewCommandOperations != null) {
for (DispatchCommandViewOperation op : viewCommandOperations) {
try {
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java
index fcb4d2bb4f15cc..ac780e8bb4f91b 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java
@@ -140,6 +140,7 @@ public class ViewProps {
public static final String TRANSFORM = "transform";
public static final String ELEVATION = "elevation";
+ public static final String SHADOW_COLOR = "shadowColor";
public static final String Z_INDEX = "zIndex";
public static final String RENDER_TO_HARDWARE_TEXTURE = "renderToHardwareTextureAndroid";
public static final String ACCESSIBILITY_LABEL = "accessibilityLabel";
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java
index 542f03616f1d33..9754b2de085471 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java
@@ -22,6 +22,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -88,7 +89,8 @@ public int compare(Event lhs, Event rhs) {
private final Map mEventNameToEventId = MapBuilder.newHashMap();
private final DispatchEventsRunnable mDispatchEventsRunnable = new DispatchEventsRunnable();
private final ArrayList mEventStaging = new ArrayList<>();
- private final ArrayList mListeners = new ArrayList<>();
+ private final CopyOnWriteArrayList mListeners =
+ new CopyOnWriteArrayList<>();
private final List mPostEventDispatchListeners = new ArrayList<>();
private final ScheduleDispatchFrameCallback mCurrentFrameCallback =
new ScheduleDispatchFrameCallback();
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java
index 4fdbd76ababee3..ffc8655c0a47c1 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java
@@ -7,6 +7,7 @@
package com.facebook.react.views.text;
+import android.os.Build;
import android.text.Layout;
import android.text.Spannable;
import android.text.TextUtils;
@@ -14,6 +15,7 @@
import android.view.Gravity;
import android.view.View;
import androidx.annotation.Nullable;
+import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.uimanager.BaseViewManager;
import com.facebook.react.uimanager.PixelUtil;
@@ -39,6 +41,7 @@ public abstract class ReactTextAnchorViewManagersetLogger(nullptr);
+ YGConfigSetLogger(config, nullptr);
}
}
diff --git a/ReactAndroid/src/main/jni/react/jni/Android.mk b/ReactAndroid/src/main/jni/react/jni/Android.mk
index 38a51019ee0f44..b649da6329c84e 100644
--- a/ReactAndroid/src/main/jni/react/jni/Android.mk
+++ b/ReactAndroid/src/main/jni/react/jni/Android.mk
@@ -73,6 +73,7 @@ $(call import-module,reactperflogger)
$(call import-module,hermes)
$(call import-module,runtimeexecutor)
+include $(REACT_SRC_DIR)/reactperflogger/jni/Android.mk
include $(REACT_SRC_DIR)/turbomodule/core/jni/Android.mk
# TODO(ramanpreet):
diff --git a/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp b/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp
index 4c76a79c7c91dc..2b07dcb7530b38 100644
--- a/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp
+++ b/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp
@@ -66,7 +66,7 @@ loadScriptFromAssets(AAssetManager *manager, const std::string &assetName) {
throw std::runtime_error(folly::to(
"Unable to load script. Make sure you're "
- "either running a Metro server (run 'react-native start') or that your bundle '",
+ "either running Metro (run 'react-native start') or that your bundle '",
assetName,
"' is packaged correctly for release."));
}
diff --git a/ReactAndroid/src/test/java/com/facebook/react/animated/NativeAnimatedNodeTraversalTest.java b/ReactAndroid/src/test/java/com/facebook/react/animated/NativeAnimatedNodeTraversalTest.java
index 30a9a3bc41e675..fc08cc8177bbb4 100644
--- a/ReactAndroid/src/test/java/com/facebook/react/animated/NativeAnimatedNodeTraversalTest.java
+++ b/ReactAndroid/src/test/java/com/facebook/react/animated/NativeAnimatedNodeTraversalTest.java
@@ -183,6 +183,15 @@ public String resolveCustomEventName(String eventName) {
};
}
});
+ PowerMockito.when(mUIManagerMock.resolveCustomDirectEventName(any(String.class)))
+ .thenAnswer(
+ new Answer() {
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ String arg = invocation.getArguments()[0].toString();
+ return "on" + arg.substring(3);
+ }
+ });
mNativeAnimatedNodesManager = new NativeAnimatedNodesManager(mReactApplicationContextMock);
}
@@ -946,7 +955,7 @@ public void testNativeAnimatedEventDoUpdate() {
mNativeAnimatedNodesManager.addAnimatedEventToView(
viewTag,
- "topScroll",
+ "onScroll",
JavaOnlyMap.of(
"animatedValueTag", 1, "nativeEventPath", JavaOnlyArray.of("contentOffset", "y")));
@@ -999,7 +1008,7 @@ public void testNativeAnimatedEventCustomMapping() {
public Object answer(InvocationOnMock invocation) throws Throwable {
return MapBuilder.of(
"customDirectEventTypes",
- MapBuilder.of("topScroll", MapBuilder.of("registrationName", "onScroll")));
+ MapBuilder.of("onScroll", MapBuilder.of("registrationName", "onScroll")));
}
});
mNativeAnimatedNodesManager = new NativeAnimatedNodesManager(mReactApplicationContextMock);
@@ -1024,7 +1033,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable {
@Test
public void testRestoreDefaultProps() {
- int viewTag = 1000;
+ int viewTag = 1001; // restoreDefaultProps not called in Fabric, make sure it's a non-Fabric tag
int propsNodeTag = 3;
mNativeAnimatedNodesManager.createAnimatedNode(
1, JavaOnlyMap.of("type", "value", "value", 1d, "offset", 0d));
diff --git a/ReactCommon/cxxreact/BUCK b/ReactCommon/cxxreact/BUCK
index 9fffd1a414cc2a..5e0d7af0a0f32a 100644
--- a/ReactCommon/cxxreact/BUCK
+++ b/ReactCommon/cxxreact/BUCK
@@ -87,7 +87,6 @@ CXXREACT_PUBLIC_HEADERS = [
"ErrorUtils.h",
"Instance.h",
"JSBundleType.h",
- "JSDeltaBundleClient.h",
"JSExecutor.h",
"JSIndexedRAMBundle.h",
"JSModulesUnbundle.h",
diff --git a/ReactCommon/cxxreact/JSDeltaBundleClient.cpp b/ReactCommon/cxxreact/JSDeltaBundleClient.cpp
deleted file mode 100644
index 3f3e01add36442..00000000000000
--- a/ReactCommon/cxxreact/JSDeltaBundleClient.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#include "JSDeltaBundleClient.h"
-
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-namespace {
-std::string startupCode(const folly::dynamic *pre, const folly::dynamic *post) {
- std::ostringstream startupCode;
-
- for (auto section : {pre, post}) {
- if (section != nullptr) {
- startupCode << section->getString() << '\n';
- }
- }
-
- return startupCode.str();
-}
-} // namespace
-
-void JSDeltaBundleClient::patchModules(const folly::dynamic *modules) {
- for (const folly::dynamic &pair : *modules) {
- auto id = pair[0].getInt();
- auto module = pair[1];
- modules_[id] = std::move(module.getString());
- }
-}
-
-void JSDeltaBundleClient::patch(const folly::dynamic &delta) {
- auto const base = delta.get_ptr("base");
-
- if (base != nullptr && base->asBool()) {
- clear();
-
- auto const pre = delta.get_ptr("pre");
- auto const post = delta.get_ptr("post");
-
- startupCode_ = startupCode(pre, post);
-
- const folly::dynamic *modules = delta.get_ptr("modules");
- if (modules != nullptr) {
- patchModules(modules);
- }
- } else {
- const folly::dynamic *deleted = delta.get_ptr("deleted");
- if (deleted != nullptr) {
- for (const folly::dynamic &id : *deleted) {
- modules_.erase(id.getInt());
- }
- }
-
- // TODO T37123645 This is deprecated but necessary in order to support older
- // versions of the Metro server.
- const folly::dynamic *modules = delta.get_ptr("modules");
- if (modules != nullptr) {
- patchModules(modules);
- }
-
- const folly::dynamic *added = delta.get_ptr("added");
- if (added != nullptr) {
- patchModules(added);
- }
-
- const folly::dynamic *modified = delta.get_ptr("modified");
- if (modified != nullptr) {
- patchModules(modified);
- }
- }
-}
-
-JSModulesUnbundle::Module JSDeltaBundleClient::getModule(
- uint32_t moduleId) const {
- auto search = modules_.find(moduleId);
- if (search != modules_.end()) {
- return {folly::to(search->first, ".js"), search->second};
- }
-
- throw JSModulesUnbundle::ModuleNotFound(moduleId);
-}
-
-std::unique_ptr JSDeltaBundleClient::getStartupCode() const {
- return std::make_unique(startupCode_);
-}
-
-void JSDeltaBundleClient::clear() {
- modules_.clear();
- startupCode_.clear();
-}
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/cxxreact/JSDeltaBundleClient.h b/ReactCommon/cxxreact/JSDeltaBundleClient.h
deleted file mode 100644
index 9c9db775d5d0e1..00000000000000
--- a/ReactCommon/cxxreact/JSDeltaBundleClient.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#pragma once
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-class JSDeltaBundleClient {
- public:
- void patch(const folly::dynamic &delta);
- JSModulesUnbundle::Module getModule(uint32_t moduleId) const;
- std::unique_ptr getStartupCode() const;
- void clear();
-
- private:
- std::unordered_map modules_;
- std::string startupCode_;
-
- void patchModules(const folly::dynamic *delta);
-};
-
-class JSDeltaBundleClientRAMBundle : public JSModulesUnbundle {
- public:
- JSDeltaBundleClientRAMBundle(
- std::shared_ptr client)
- : client_(client) {}
-
- Module getModule(uint32_t moduleId) const override {
- return client_->getModule(moduleId);
- }
-
- private:
- const std::shared_ptr client_;
-};
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/cxxreact/tests/BUCK b/ReactCommon/cxxreact/tests/BUCK
index dbd72f23d99048..03bb59f6277501 100644
--- a/ReactCommon/cxxreact/tests/BUCK
+++ b/ReactCommon/cxxreact/tests/BUCK
@@ -8,7 +8,6 @@ load(
TEST_SRCS = [
"RecoverableErrorTest.cpp",
- "JSDeltaBundleClientTest.cpp",
"jsarg_helpers.cpp",
"jsbigstring.cpp",
"methodcall.cpp",
diff --git a/ReactCommon/cxxreact/tests/JSDeltaBundleClientTest.cpp b/ReactCommon/cxxreact/tests/JSDeltaBundleClientTest.cpp
deleted file mode 100644
index 2ec8b9a1423e31..00000000000000
--- a/ReactCommon/cxxreact/tests/JSDeltaBundleClientTest.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#include
-
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-using namespace facebook::react;
-
-TEST(JSDeltaBundleClient, PatchStartupCode) {
- JSDeltaBundleClient client;
-
- folly::dynamic delta1 = folly::parseJson(R"({
- "base": true,
- "revisionId": "rev0",
- "pre": "pre",
- "post": "post",
- "modules": [
- [0, "0"],
- [1, "1"]
- ]
- })");
-
- client.patch(delta1);
-
- EXPECT_STREQ(client.getStartupCode()->c_str(), "pre\npost\n");
-
- folly::dynamic delta2 = folly::parseJson(R"({
- "base": true,
- "revisionId": "rev1",
- "pre": "pre2",
- "post": "post2",
- "modules": []
- })");
-
- client.patch(delta2);
-
- EXPECT_STREQ(client.getStartupCode()->c_str(), "pre2\npost2\n");
-}
-
-TEST(JSDeltaBundleClient, PatchModule) {
- JSDeltaBundleClient client;
-
- folly::dynamic delta1 = folly::parseJson(R"({
- "base": true,
- "revisionId": "rev0",
- "pre": "pre",
- "post": "post",
- "modules": [
- [0, "0"],
- [1, "1"]
- ]
- })");
-
- client.patch(delta1);
-
- EXPECT_EQ(client.getModule(0).code, "0");
- EXPECT_EQ(client.getModule(1).code, "1");
-
- ASSERT_THROW(client.getModule(2), JSModulesUnbundle::ModuleNotFound);
-
- folly::dynamic delta2 = folly::parseJson(R"({
- "base": false,
- "revisionId": "rev1",
- "added": [
- [2, "2"]
- ],
- "modified": [
- [0, "0.1"]
- ],
- "deleted": [1]
- })");
-
- client.patch(delta2);
-
- EXPECT_EQ(client.getModule(0).code, "0.1");
- EXPECT_EQ(client.getModule(2).code, "2");
- ASSERT_THROW(client.getModule(1), JSModulesUnbundle::ModuleNotFound);
-
- folly::dynamic delta3 = folly::parseJson(R"({
- "base": true,
- "revisionId": "rev2",
- "pre": "pre",
- "post": "post",
- "modules": [
- [3, "3"],
- [4, "4"]
- ]
- })");
-
- client.patch(delta3);
-
- ASSERT_THROW(client.getModule(0), JSModulesUnbundle::ModuleNotFound);
- ASSERT_THROW(client.getModule(1), JSModulesUnbundle::ModuleNotFound);
- ASSERT_THROW(client.getModule(2), JSModulesUnbundle::ModuleNotFound);
-
- EXPECT_EQ(client.getModule(3).code, "3");
- EXPECT_EQ(client.getModule(4).code, "4");
-}
-
-TEST(JSDeltaBundleClient, Clear) {
- JSDeltaBundleClient client;
-
- folly::dynamic delta1 = folly::parseJson(R"({
- "base": true,
- "revisionId": "rev0",
- "pre": "pre",
- "post": "post",
- "modules": [
- [0, "0"],
- [1, "1"]
- ]
- })");
-
- client.patch(delta1);
-
- client.clear();
-
- ASSERT_THROW(client.getModule(0), JSModulesUnbundle::ModuleNotFound);
- ASSERT_THROW(client.getModule(1), JSModulesUnbundle::ModuleNotFound);
-
- EXPECT_STREQ(client.getStartupCode()->c_str(), "");
-}
diff --git a/ReactCommon/fabric/attributedstring/TextAttributes.cpp b/ReactCommon/fabric/attributedstring/TextAttributes.cpp
index a33b92c9ad5bcb..c30817a8ea8ad6 100644
--- a/ReactCommon/fabric/attributedstring/TextAttributes.cpp
+++ b/ReactCommon/fabric/attributedstring/TextAttributes.cpp
@@ -94,6 +94,9 @@ void TextAttributes::apply(TextAttributes textAttributes) {
layoutDirection = textAttributes.layoutDirection.hasValue()
? textAttributes.layoutDirection
: layoutDirection;
+ accessibilityRole = textAttributes.accessibilityRole.hasValue()
+ ? textAttributes.accessibilityRole
+ : accessibilityRole;
}
#pragma mark - Operators
@@ -116,7 +119,8 @@ bool TextAttributes::operator==(const TextAttributes &rhs) const {
textShadowOffset,
textShadowColor,
isHighlighted,
- layoutDirection) ==
+ layoutDirection,
+ accessibilityRole) ==
std::tie(
rhs.foregroundColor,
rhs.backgroundColor,
@@ -134,7 +138,8 @@ bool TextAttributes::operator==(const TextAttributes &rhs) const {
rhs.textShadowOffset,
rhs.textShadowColor,
rhs.isHighlighted,
- rhs.layoutDirection) &&
+ rhs.layoutDirection,
+ rhs.accessibilityRole) &&
floatEquality(opacity, rhs.opacity) &&
floatEquality(fontSize, rhs.fontSize) &&
floatEquality(fontSizeMultiplier, rhs.fontSizeMultiplier) &&
@@ -202,6 +207,7 @@ SharedDebugStringConvertibleList TextAttributes::getDebugProps() const {
// Special
debugStringConvertibleItem("isHighlighted", isHighlighted),
debugStringConvertibleItem("layoutDirection", layoutDirection),
+ debugStringConvertibleItem("accessibilityRole", accessibilityRole),
};
}
#endif
diff --git a/ReactCommon/fabric/attributedstring/TextAttributes.h b/ReactCommon/fabric/attributedstring/TextAttributes.h
index 1c28424fad61c4..2589d89dfd9131 100644
--- a/ReactCommon/fabric/attributedstring/TextAttributes.h
+++ b/ReactCommon/fabric/attributedstring/TextAttributes.h
@@ -77,6 +77,7 @@ class TextAttributes : public DebugStringConvertible {
// Currently, it is intentionally *not* being set as part of BaseTextProps
// construction.
folly::Optional layoutDirection{};
+ folly::Optional accessibilityRole{};
#pragma mark - Operations
@@ -127,7 +128,8 @@ struct hash {
textAttributes.textShadowRadius,
textAttributes.textShadowColor,
textAttributes.isHighlighted,
- textAttributes.layoutDirection);
+ textAttributes.layoutDirection,
+ textAttributes.accessibilityRole);
}
};
} // namespace std
diff --git a/ReactCommon/fabric/attributedstring/conversions.h b/ReactCommon/fabric/attributedstring/conversions.h
index 3046e8f79830d3..9e7d484ade0f84 100644
--- a/ReactCommon/fabric/attributedstring/conversions.h
+++ b/ReactCommon/fabric/attributedstring/conversions.h
@@ -412,6 +412,178 @@ inline std::string toString(
}
}
+inline std::string toString(const AccessibilityRole &accessibilityRole) {
+ switch (accessibilityRole) {
+ case AccessibilityRole::None:
+ return "none";
+ case AccessibilityRole::Button:
+ return "button";
+ case AccessibilityRole::Link:
+ return "link";
+ case AccessibilityRole::Search:
+ return "search";
+ case AccessibilityRole::Image:
+ return "image";
+ case AccessibilityRole::Imagebutton:
+ return "imagebutton";
+ case AccessibilityRole::Keyboardkey:
+ return "keyboardkey";
+ case AccessibilityRole::Text:
+ return "text";
+ case AccessibilityRole::Adjustable:
+ return "adjustable";
+ case AccessibilityRole::Summary:
+ return "summary";
+ case AccessibilityRole::Header:
+ return "header";
+ case AccessibilityRole::Alert:
+ return "alert";
+ case AccessibilityRole::Checkbox:
+ return "checkbox";
+ case AccessibilityRole::Combobox:
+ return "combobox";
+ case AccessibilityRole::Menu:
+ return "menu";
+ case AccessibilityRole::Menubar:
+ return "menubar";
+ case AccessibilityRole::Menuitem:
+ return "menuitem";
+ case AccessibilityRole::Progressbar:
+ return "progressbar";
+ case AccessibilityRole::Radio:
+ return "radio";
+ case AccessibilityRole::Radiogroup:
+ return "radiogroup";
+ case AccessibilityRole::Scrollbar:
+ return "scrollbar";
+ case AccessibilityRole::Spinbutton:
+ return "spinbutton";
+ case AccessibilityRole::Switch:
+ return "switch";
+ case AccessibilityRole::Tab:
+ return "tab";
+ case AccessibilityRole::Tablist:
+ return "tablist";
+ case AccessibilityRole::Timer:
+ return "timer";
+ case AccessibilityRole::Toolbar:
+ return "toolbar";
+ }
+}
+
+inline void fromRawValue(const RawValue &value, AccessibilityRole &result) {
+ auto string = (std::string)value;
+ if (string == "none") {
+ result = AccessibilityRole::None;
+ return;
+ }
+ if (string == "button") {
+ result = AccessibilityRole::Button;
+ return;
+ }
+ if (string == "link") {
+ result = AccessibilityRole::Link;
+ return;
+ }
+ if (string == "search") {
+ result = AccessibilityRole::Search;
+ return;
+ }
+ if (string == "image") {
+ result = AccessibilityRole::Image;
+ return;
+ }
+ if (string == "imagebutton") {
+ result = AccessibilityRole::Imagebutton;
+ return;
+ }
+ if (string == "keyboardkey") {
+ result = AccessibilityRole::Keyboardkey;
+ return;
+ }
+ if (string == "text") {
+ result = AccessibilityRole::Text;
+ return;
+ }
+ if (string == "adjustable") {
+ result = AccessibilityRole::Adjustable;
+ return;
+ }
+ if (string == "summary") {
+ result = AccessibilityRole::Summary;
+ return;
+ }
+ if (string == "header") {
+ result = AccessibilityRole::Header;
+ return;
+ }
+ if (string == "alert") {
+ result = AccessibilityRole::Alert;
+ return;
+ }
+ if (string == "checkbox") {
+ result = AccessibilityRole::Checkbox;
+ return;
+ }
+ if (string == "combobox") {
+ result = AccessibilityRole::Combobox;
+ return;
+ }
+ if (string == "menu") {
+ result = AccessibilityRole::Menu;
+ return;
+ }
+ if (string == "menubar") {
+ result = AccessibilityRole::Menubar;
+ return;
+ }
+ if (string == "menuitem") {
+ result = AccessibilityRole::Menuitem;
+ return;
+ }
+ if (string == "progressbar") {
+ result = AccessibilityRole::Progressbar;
+ return;
+ }
+ if (string == "radio") {
+ result = AccessibilityRole::Radio;
+ return;
+ }
+ if (string == "radiogroup") {
+ result = AccessibilityRole::Radiogroup;
+ return;
+ }
+ if (string == "scrollbar") {
+ result = AccessibilityRole::Scrollbar;
+ return;
+ }
+ if (string == "spinbutton") {
+ result = AccessibilityRole::Spinbutton;
+ return;
+ }
+ if (string == "switch") {
+ result = AccessibilityRole::Switch;
+ return;
+ }
+ if (string == "tab") {
+ result = AccessibilityRole::Tab;
+ return;
+ }
+ if (string == "tablist") {
+ result = AccessibilityRole::Tablist;
+ return;
+ }
+ if (string == "timer") {
+ result = AccessibilityRole::Timer;
+ return;
+ }
+ if (string == "toolbar") {
+ result = AccessibilityRole::Toolbar;
+ return;
+ }
+ abort();
+}
+
inline ParagraphAttributes convertRawProp(
RawProps const &rawProps,
ParagraphAttributes const &sourceParagraphAttributes,
diff --git a/ReactCommon/fabric/attributedstring/primitives.h b/ReactCommon/fabric/attributedstring/primitives.h
index c44538c546a550..13946ec180fc81 100644
--- a/ReactCommon/fabric/attributedstring/primitives.h
+++ b/ReactCommon/fabric/attributedstring/primitives.h
@@ -87,6 +87,36 @@ enum class TextDecorationLinePattern {
DashDotDot,
};
+enum class AccessibilityRole {
+ None,
+ Button,
+ Link,
+ Search,
+ Image,
+ Imagebutton,
+ Keyboardkey,
+ Text,
+ Adjustable,
+ Summary,
+ Header,
+ Alert,
+ Checkbox,
+ Combobox,
+ Menu,
+ Menubar,
+ Menuitem,
+ Progressbar,
+ Radio,
+ Radiogroup,
+ Scrollbar,
+ Spinbutton,
+ Switch,
+ Tab,
+ Tablist,
+ Timer,
+ Toolbar,
+};
+
} // namespace react
} // namespace facebook
@@ -160,4 +190,11 @@ struct hash {
return hash()(static_cast(v));
}
};
+
+template <>
+struct hash {
+ size_t operator()(const facebook::react::AccessibilityRole &v) const {
+ return hash()(static_cast(v));
+ }
+};
} // namespace std
diff --git a/ReactCommon/fabric/components/art/ARTBaseShadowNode.h b/ReactCommon/fabric/components/art/ARTBaseShadowNode.h
deleted file mode 100644
index e674551dc23d4e..00000000000000
--- a/ReactCommon/fabric/components/art/ARTBaseShadowNode.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#pragma once
-
-#include
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-class ARTBaseShadowNode {
- public:
- int test;
- virtual ARTElement::Shared getARTElement() const = 0;
-
- static void buildElements(
- ShadowNode const &parentNode,
- ARTElement::ListOfShared &outNodes) {
- for (auto const &child : parentNode.getChildren()) {
- auto baseShadowNode =
- std::dynamic_pointer_cast(child);
- if (baseShadowNode) {
- outNodes.push_back(baseShadowNode->getARTElement());
- }
- }
- }
-};
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/BUCK b/ReactCommon/fabric/components/art/BUCK
deleted file mode 100644
index 50ba2970986320..00000000000000
--- a/ReactCommon/fabric/components/art/BUCK
+++ /dev/null
@@ -1,94 +0,0 @@
-load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode")
-load(
- "//tools/build_defs/oss:rn_defs.bzl",
- "ANDROID",
- "APPLE",
- "CXX",
- "YOGA_CXX_TARGET",
- "fb_xplat_cxx_test",
- "get_apple_compiler_flags",
- "get_apple_inspector_flags",
- "react_native_xplat_target",
- "rn_xplat_cxx_library",
- "subdir_glob",
-)
-
-APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
-
-rn_xplat_cxx_library(
- name = "art",
- srcs = glob(
- ["**/*.cpp"],
- exclude = glob(["tests/**/*.cpp"]),
- ),
- headers = glob(
- ["**/*.h"],
- exclude = glob(["tests/**/*.h"]),
- ),
- header_namespace = "",
- exported_headers = subdir_glob(
- [
- ("", "*.h"),
- ("group", "*.h"),
- ("shape", "*.h"),
- ("state", "*.h"),
- ("surfaceview", "*.h"),
- ("text", "*.h"),
- ],
- prefix = "react/components/art",
- ),
- compiler_flags = [
- "-fexceptions",
- "-frtti",
- "-std=c++14",
- "-Wall",
- ],
- cxx_tests = [":tests"],
- fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
- fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(),
- force_static = True,
- labels = ["supermodule:xplat/default/public.react_native.playground"],
- platforms = (ANDROID, APPLE, CXX),
- preprocessor_flags = [
- "-DLOG_TAG=\"ReactNative\"",
- "-DWITH_FBSYSTRACE=1",
- ],
- visibility = ["PUBLIC"],
- deps = [
- "//third-party/glog:glog",
- "//xplat/fbsystrace:fbsystrace",
- "//xplat/folly:container_evicting_cache_map",
- "//xplat/folly:headers_only",
- "//xplat/folly:memory",
- "//xplat/folly:molly",
- YOGA_CXX_TARGET,
- react_native_xplat_target("utils:utils"),
- react_native_xplat_target("fabric/core:core"),
- react_native_xplat_target("fabric/debug:debug"),
- react_native_xplat_target("fabric/graphics:graphics"),
- react_native_xplat_target("fabric/uimanager:uimanager"),
- ],
-)
-
-fb_xplat_cxx_test(
- name = "tests",
- srcs = glob(["tests/**/*.cpp"]),
- headers = glob(["tests/**/*.h"]),
- compiler_flags = [
- "-fexceptions",
- "-frtti",
- "-std=c++14",
- "-Wall",
- ],
- contacts = ["oncall+react_native@xmail.facebook.com"],
- platforms = (
- # ANDROID,
- # APPLE,
- CXX
- ),
- deps = [
- ":art",
- "//xplat/folly:molly",
- "//xplat/third-party/gmock:gtest",
- ],
-)
diff --git a/ReactCommon/fabric/components/art/conversions.h b/ReactCommon/fabric/components/art/conversions.h
deleted file mode 100644
index b234c6d0dbdfe8..00000000000000
--- a/ReactCommon/fabric/components/art/conversions.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-#ifdef ANDROID
-
-inline folly::dynamic toDynamic(ARTGroup const &group);
-inline folly::dynamic toDynamic(ARTShape const &shape);
-inline folly::dynamic toDynamic(ARTText const &text);
-inline folly::dynamic toDynamic(ARTElement const &element);
-
-inline folly::dynamic toDynamic(std::vector const &elements) {
- folly::dynamic result = folly::dynamic::array();
- for (auto const &element : elements) {
- result.push_back(element);
- }
- return result;
-}
-
-inline void addOptionalKey(
- folly::dynamic &map,
- std::string const &key,
- std::vector const &values) {
- if (values.size() > 0) {
- map[key] = toDynamic(values);
- }
-}
-
-inline folly::dynamic toDynamic(ARTElement::ListOfShared const &elements) {
- folly::dynamic children = folly::dynamic::array();
- for (auto const &element : elements) {
- children.push_back(element->getDynamic());
- }
- return children;
-}
-
-inline folly::dynamic toDynamic(ARTGroup const &group) {
- folly::dynamic result = folly::dynamic::object();
- result["opacity"] = group.opacity;
- result["type"] = 1;
- if (group.elements.size() > 0) {
- result["elements"] = toDynamic(group.elements);
- }
- addOptionalKey(result, "clipping", group.clipping);
- result["transform"] = toDynamic(group.transform);
- return result;
-}
-
-inline folly::dynamic toDynamic(ARTShape const &shape) {
- folly::dynamic result = folly::dynamic::object();
- result["type"] = 2;
- result["opacity"] = shape.opacity;
- result["transform"] = toDynamic(shape.transform);
- addOptionalKey(result, "d", shape.d);
- addOptionalKey(result, "stroke", shape.stroke);
- addOptionalKey(result, "strokeDash", shape.strokeDash);
- addOptionalKey(result, "fill", shape.fill);
- result["strokeWidth"] = shape.strokeWidth;
- result["strokeCap"] = shape.strokeCap;
- result["strokeJoin"] = shape.strokeJoin;
- return result;
-}
-
-inline folly::dynamic toDynamic(ARTTextAlignment const &aligment) {
- switch (aligment) {
- case ARTTextAlignment::Right:
- return 1;
- break;
- case ARTTextAlignment::Center:
- return 2;
- break;
- case ARTTextAlignment::Default:
- default:
- return 0;
- break;
- }
-}
-
-inline folly::dynamic toDynamic(ARTTextFrame const &frame) {
- folly::dynamic result = folly::dynamic::object();
- folly::dynamic font = folly::dynamic::object();
- font["fontSize"] = frame.font.fontSize;
- font["fontStyle"] = frame.font.fontStyle;
- font["fontFamily"] = frame.font.fontFamily;
- font["fontWeight"] = frame.font.fontWeight;
- result["font"] = font;
- auto lines = frame.lines;
- if (lines.size() > 0) {
- folly::dynamic serializedLines = folly::dynamic::array();
- for (int i = 0; i < lines.size(); i++) {
- serializedLines.push_back(lines[i]);
- }
- result["lines"] = serializedLines;
- }
- return result;
-}
-
-inline folly::dynamic toDynamic(ARTText const &text) {
- folly::dynamic result = folly::dynamic::object();
- result["type"] = 3;
- result["opacity"] = text.opacity;
- result["transform"] = toDynamic(text.transform);
- addOptionalKey(result, "d", text.d);
- addOptionalKey(result, "stroke", text.stroke);
- addOptionalKey(result, "strokeDash", text.strokeDash);
- addOptionalKey(result, "fill", text.fill);
- result["strokeWidth"] = text.strokeWidth;
- result["strokeCap"] = text.strokeCap;
- result["strokeJoin"] = text.strokeJoin;
- result["alignment"] = toDynamic(text.alignment);
- result["frame"] = toDynamic(text.frame);
- return result;
-}
-
-inline folly::dynamic toDynamic(ARTSurfaceViewState const &surfaceViewState) {
- folly::dynamic result = folly::dynamic::object();
- result["elements"] = toDynamic(surfaceViewState.elements);
- return result;
-}
-#endif
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/group/ARTGroupComponentDescriptor.h b/ReactCommon/fabric/components/art/group/ARTGroupComponentDescriptor.h
deleted file mode 100644
index d2c3bd9424ce03..00000000000000
--- a/ReactCommon/fabric/components/art/group/ARTGroupComponentDescriptor.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#pragma once
-
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-using ARTGroupComponentDescriptor =
- ConcreteComponentDescriptor;
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/group/ARTGroupProps.cpp b/ReactCommon/fabric/components/art/group/ARTGroupProps.cpp
deleted file mode 100644
index 2d735569efe33a..00000000000000
--- a/ReactCommon/fabric/components/art/group/ARTGroupProps.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#include
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-ARTGroupProps::ARTGroupProps(
- const ARTGroupProps &sourceProps,
- const RawProps &rawProps)
- : Props(sourceProps, rawProps),
- opacity(convertRawProp(rawProps, "opacity", sourceProps.opacity, {1.0})),
- transform(
- convertRawProp(rawProps, "transform", sourceProps.transform, {})),
- clipping(
- convertRawProp(rawProps, "clipping", sourceProps.clipping, {})){};
-
-#pragma mark - DebugStringConvertible
-
-#if RN_DEBUG_STRING_CONVERTIBLE
-SharedDebugStringConvertibleList ARTGroupProps::getDebugProps() const {
- return SharedDebugStringConvertibleList{
- debugStringConvertibleItem("opacity", opacity)};
-}
-#endif
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/group/ARTGroupProps.h b/ReactCommon/fabric/components/art/group/ARTGroupProps.h
deleted file mode 100644
index 234e530d3a01ed..00000000000000
--- a/ReactCommon/fabric/components/art/group/ARTGroupProps.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-class ARTGroupProps;
-
-class ARTGroupProps : public Props {
- public:
- ARTGroupProps() = default;
- ARTGroupProps(const ARTGroupProps &sourceProps, const RawProps &rawProps);
-
-#pragma mark - Props
-
- Float opacity{1.0};
- std::vector transform{};
- std::vector clipping{};
-
-#pragma mark - DebugStringConvertible
-
-#if RN_DEBUG_STRING_CONVERTIBLE
- SharedDebugStringConvertibleList getDebugProps() const override;
-#endif
-};
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/group/ARTGroupShadowNode.cpp b/ReactCommon/fabric/components/art/group/ARTGroupShadowNode.cpp
deleted file mode 100644
index 7d5301a53c43b2..00000000000000
--- a/ReactCommon/fabric/components/art/group/ARTGroupShadowNode.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#include
-#include
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-extern const char ARTGroupComponentName[] = "ARTGroup";
-
-ARTElement::Shared ARTGroupShadowNode::getARTElement() const {
- auto elements = ARTElement::ListOfShared{};
- for (auto const &child : getChildren()) {
- auto node = std::dynamic_pointer_cast(child);
- if (node) {
- elements.push_back(node->getARTElement());
- }
- }
-
- auto props = getConcreteProps();
- return std::make_shared(
- props.opacity, props.transform, elements, props.clipping);
-}
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/group/ARTGroupShadowNode.h b/ReactCommon/fabric/components/art/group/ARTGroupShadowNode.h
deleted file mode 100644
index dd85dffe66eaae..00000000000000
--- a/ReactCommon/fabric/components/art/group/ARTGroupShadowNode.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-extern const char ARTGroupComponentName[];
-
-/*
- * `ShadowNode` for component.
- */
-class ARTGroupShadowNode : public ConcreteShadowNode<
- ARTGroupComponentName,
- ShadowNode,
- ARTGroupProps>,
- public ARTBaseShadowNode {
- public:
- using ConcreteShadowNode::ConcreteShadowNode;
-
- virtual ARTElement::Shared getARTElement() const override;
-};
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/shape/ARTShapeComponentDescriptor.h b/ReactCommon/fabric/components/art/shape/ARTShapeComponentDescriptor.h
deleted file mode 100644
index 5d65696f7dbaf2..00000000000000
--- a/ReactCommon/fabric/components/art/shape/ARTShapeComponentDescriptor.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#pragma once
-
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-using ARTShapeComponentDescriptor =
- ConcreteComponentDescriptor;
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/shape/ARTShapeProps.cpp b/ReactCommon/fabric/components/art/shape/ARTShapeProps.cpp
deleted file mode 100644
index 9a170c9203e6ca..00000000000000
--- a/ReactCommon/fabric/components/art/shape/ARTShapeProps.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#include
-#include
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-ARTShapeProps::ARTShapeProps(
- const ARTShapeProps &sourceProps,
- const RawProps &rawProps)
- : Props(sourceProps, rawProps),
-
- opacity(convertRawProp(rawProps, "opacity", sourceProps.opacity, {1.0})),
- transform(
- convertRawProp(rawProps, "transform", sourceProps.transform, {})),
- d(convertRawProp(rawProps, "d", sourceProps.d, {})),
- stroke(convertRawProp(rawProps, "stroke", sourceProps.stroke, {})),
- strokeDash(
- convertRawProp(rawProps, "strokeDash", sourceProps.strokeDash, {})),
- fill(convertRawProp(rawProps, "fill", sourceProps.fill, {})),
- strokeWidth(convertRawProp(
- rawProps,
- "strokeWidth",
- sourceProps.strokeWidth,
- {1.0})),
- strokeCap(
- convertRawProp(rawProps, "strokeCap", sourceProps.strokeCap, {1})),
- strokeJoin(
- convertRawProp(rawProps, "strokeJoin", sourceProps.strokeJoin, {1})){
-
- };
-
-#pragma mark - DebugStringConvertible
-
-#if RN_DEBUG_STRING_CONVERTIBLE
-SharedDebugStringConvertibleList ARTShapeProps::getDebugProps() const {
- return SharedDebugStringConvertibleList{
- debugStringConvertibleItem("opacity", opacity)};
-}
-#endif
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/shape/ARTShapeProps.h b/ReactCommon/fabric/components/art/shape/ARTShapeProps.h
deleted file mode 100644
index 7323f7ccd07b25..00000000000000
--- a/ReactCommon/fabric/components/art/shape/ARTShapeProps.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#pragma once
-
-#include
-#include
-
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-class ARTShapeProps;
-
-class ARTShapeProps : public Props {
- public:
- ARTShapeProps() = default;
- ARTShapeProps(const ARTShapeProps &sourceProps, const RawProps &rawProps);
-
-#pragma mark - Props
-
- Float opacity{1.0};
- std::vector transform{};
- std::vector d{};
- std::vector stroke{};
- std::vector strokeDash{};
- std::vector fill{};
- Float strokeWidth{1.0};
- int strokeCap{1};
- int strokeJoin{1};
-
-#pragma mark - DebugStringConvertible
-
-#if RN_DEBUG_STRING_CONVERTIBLE
- SharedDebugStringConvertibleList getDebugProps() const override;
-#endif
-};
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.cpp b/ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.cpp
deleted file mode 100644
index cb11199fd06576..00000000000000
--- a/ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#include "ARTShapeShadowNode.h"
-#include
-#include
-namespace facebook {
-namespace react {
-
-extern const char ARTShapeComponentName[] = "ARTShape";
-
-ARTElement::Shared ARTShapeShadowNode::getARTElement() const {
- auto props = getConcreteProps();
- return std::make_shared(
- props.opacity,
- props.transform,
- props.d,
- props.stroke,
- props.strokeDash,
- props.fill,
- props.strokeWidth,
- props.strokeCap,
- props.strokeJoin);
-}
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.h b/ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.h
deleted file mode 100644
index 0373aef45cf34c..00000000000000
--- a/ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#pragma once
-
-#include
-#include
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-extern const char ARTShapeComponentName[];
-
-/*
- * `ShadowNode` for component.
- */
-class ARTShapeShadowNode : public ConcreteShadowNode<
- ARTShapeComponentName,
- ShadowNode,
- ARTShapeProps>,
- public ARTBaseShadowNode {
- public:
- using ConcreteShadowNode::ConcreteShadowNode;
-
- virtual ARTElement::Shared getARTElement() const override;
-};
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/state/ARTElement.h b/ReactCommon/fabric/components/art/state/ARTElement.h
deleted file mode 100644
index 1b3b82b1685a9e..00000000000000
--- a/ReactCommon/fabric/components/art/state/ARTElement.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-/*
- * Simple, cross-platfrom, React-specific implementation of base ART Element
- */
-class ARTElement {
- public:
- using Shared = std::shared_ptr;
- using ListOfShared = better::small_vector;
-
- ARTElement() = default;
- ARTElement(
- ARTElementType elementType,
- Float opacity,
- std::vector transform)
- : elementType(elementType), opacity(opacity), transform(transform){};
- virtual ~ARTElement(){};
-
- ARTElementType elementType;
- Float opacity;
- std::vector transform;
-
- virtual bool operator==(const ARTElement &rhs) const = 0;
- virtual bool operator!=(const ARTElement &rhs) const = 0;
- friend bool operator==(ListOfShared e1, ListOfShared e2) {
- bool equals = e1.size() == e2.size();
- for (int i = 0; i < equals && e1.size(); i++) {
- // Pointer equality - this will work if both are pointing at the same
- // object, or both are nullptr
- if (e1[i] == e2[i]) {
- continue;
- }
-
- // Get pointers from both
- // If one is null, we know they can't both be null because of the above
- // check
- auto ptr1 = e1[i].get();
- auto ptr2 = e2[i].get();
- if (ptr1 == nullptr || ptr2 == nullptr) {
- equals = false;
- break;
- }
-
- // Dereference and compare objects
- if (*ptr1 != *ptr2) {
- equals = false;
- break;
- }
- }
-
- return equals;
- };
-
-#ifdef ANDROID
- virtual folly::dynamic getDynamic() const = 0;
-#endif
-};
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/state/ARTGroup.cpp b/ReactCommon/fabric/components/art/state/ARTGroup.cpp
deleted file mode 100644
index f3a8c6ee59b5c6..00000000000000
--- a/ReactCommon/fabric/components/art/state/ARTGroup.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#include
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-bool ARTGroup::operator==(const ARTElement &rhs) const {
- if (rhs.elementType != ARTElementType::Group) {
- return false;
- }
- auto group = (const ARTGroup &)(rhs);
- return std::tie(elementType, opacity, transform, clipping) ==
- std::tie(
- group.elementType,
- group.opacity,
- group.transform,
- group.clipping) &&
- elements == group.elements;
-}
-
-bool ARTGroup::operator!=(const ARTElement &rhs) const {
- return !(*this == rhs);
-}
-
-#ifdef ANDROID
-folly::dynamic ARTGroup::getDynamic() const {
- return toDynamic(*this);
-}
-#endif
-
-} // namespace react
-} // namespace facebook
diff --git a/ReactCommon/fabric/components/art/state/ARTGroup.h b/ReactCommon/fabric/components/art/state/ARTGroup.h
deleted file mode 100644
index fa86e7923655c0..00000000000000
--- a/ReactCommon/fabric/components/art/state/ARTGroup.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-
-namespace facebook {
-namespace react {
-
-/*
- * Simple, cross-platfrom, React-specific implementation of ART Group element
- */
-class ARTGroup : public ARTElement {
- public:
- using Shared = std::shared_ptr