diff --git a/Common/cpp/NativeModules/NativeReanimatedModule.cpp b/Common/cpp/NativeModules/NativeReanimatedModule.cpp index 613f673a50bf..87a040d09a45 100644 --- a/Common/cpp/NativeModules/NativeReanimatedModule.cpp +++ b/Common/cpp/NativeModules/NativeReanimatedModule.cpp @@ -44,6 +44,7 @@ bool CoreFeatures::useNativeState; namespace reanimated { +// With `jsInvoker`. NativeReanimatedModule::NativeReanimatedModule( jsi::Runtime &rnRuntime, const std::shared_ptr &jsInvoker, @@ -86,7 +87,61 @@ NativeReanimatedModule::NativeReanimatedModule( platformDepMethodsHolder.subscribeForKeyboardEvents), unsubscribeFromKeyboardEventsFunction_( platformDepMethodsHolder.unsubscribeFromKeyboardEvents), - isBridgeless_(jsInvoker == nullptr) { + isBridgeless_(false) { + commonInit(platformDepMethodsHolder); +} + +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) +// With `runtimeExecutor`. +NativeReanimatedModule::NativeReanimatedModule( + jsi::Runtime &rnRuntime, + RuntimeExecutor runtimeExecutor, + const std::shared_ptr &jsQueue, + const std::shared_ptr &uiScheduler, + const PlatformDepMethodsHolder &platformDepMethodsHolder, + const std::string &valueUnpackerCode) + : NativeReanimatedModuleSpec(nullptr), + jsQueue_(jsQueue), + jsScheduler_(std::make_shared(rnRuntime, runtimeExecutor)), + uiScheduler_(uiScheduler), + uiWorkletRuntime_(std::make_shared( + rnRuntime, + jsQueue, + jsScheduler_, + "Reanimated UI runtime", + true /* supportsLocking */, + valueUnpackerCode)), + valueUnpackerCode_(valueUnpackerCode), + eventHandlerRegistry_(std::make_unique()), + requestRender_(platformDepMethodsHolder.requestRender), + onRenderCallback_([this](const double timestampMs) { + renderRequested_ = false; + onRender(timestampMs); + }), + animatedSensorModule_(platformDepMethodsHolder), + jsLogger_(std::make_shared(jsScheduler_)), + layoutAnimationsManager_(jsLogger_), +#ifdef RCT_NEW_ARCH_ENABLED + synchronouslyUpdateUIPropsFunction_( + platformDepMethodsHolder.synchronouslyUpdateUIPropsFunction), + propsRegistry_(std::make_shared()), +#else + obtainPropFunction_(platformDepMethodsHolder.obtainPropFunction), + configurePropsPlatformFunction_( + platformDepMethodsHolder.configurePropsFunction), + updatePropsFunction_(platformDepMethodsHolder.updatePropsFunction), +#endif + subscribeForKeyboardEventsFunction_( + platformDepMethodsHolder.subscribeForKeyboardEvents), + unsubscribeFromKeyboardEventsFunction_( + platformDepMethodsHolder.unsubscribeFromKeyboardEvents), + isBridgeless_(true) { + commonInit(platformDepMethodsHolder); +} +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + +void NativeReanimatedModule::commonInit( + const PlatformDepMethodsHolder &platformDepMethodsHolder) { auto requestAnimationFrame = [this](jsi::Runtime &rt, const jsi::Value &callback) { this->requestAnimationFrame(rt, callback); diff --git a/Common/cpp/NativeModules/NativeReanimatedModule.h b/Common/cpp/NativeModules/NativeReanimatedModule.h index 4f428721877b..885011f01096 100644 --- a/Common/cpp/NativeModules/NativeReanimatedModule.h +++ b/Common/cpp/NativeModules/NativeReanimatedModule.h @@ -31,6 +31,7 @@ namespace reanimated { class NativeReanimatedModule : public NativeReanimatedModuleSpec { public: + // With `jsInvoker`. NativeReanimatedModule( jsi::Runtime &rnRuntime, const std::shared_ptr &jsInvoker, @@ -39,6 +40,17 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec { const PlatformDepMethodsHolder &platformDepMethodsHolder, const std::string &valueUnpackerCode); +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + // With `runtimeExecutor`. + NativeReanimatedModule( + jsi::Runtime &rnRuntime, + RuntimeExecutor runtimeExecutor, + const std::shared_ptr &jsQueue, + const std::shared_ptr &uiScheduler, + const PlatformDepMethodsHolder &platformDepMethodsHolder, + const std::string &valueUnpackerCode); +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED + ~NativeReanimatedModule(); jsi::Value makeShareableClone( @@ -172,6 +184,8 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec { } private: + void commonInit(const PlatformDepMethodsHolder &platformDepMethodsHolder); + void requestAnimationFrame(jsi::Runtime &rt, const jsi::Value &callback); #ifdef RCT_NEW_ARCH_ENABLED diff --git a/Common/cpp/ReanimatedRuntime/WorkletRuntimeDecorator.cpp b/Common/cpp/ReanimatedRuntime/WorkletRuntimeDecorator.cpp index 94b940805827..cf1a33cd254c 100644 --- a/Common/cpp/ReanimatedRuntime/WorkletRuntimeDecorator.cpp +++ b/Common/cpp/ReanimatedRuntime/WorkletRuntimeDecorator.cpp @@ -62,7 +62,7 @@ void WorkletRuntimeDecorator::decorate( jsi::PropNameID::forAscii(rt, "evalWithSourceUrl"), 1, evalWithSourceUrl)); -#endif +#endif // NDEBUG jsi_utils::installJsiFunction( rt, "_toString", [](jsi::Runtime &rt, const jsi::Value &value) { diff --git a/Common/cpp/Tools/JSScheduler.cpp b/Common/cpp/Tools/JSScheduler.cpp index 6478cc543dd4..ab9aae64208e 100644 --- a/Common/cpp/Tools/JSScheduler.cpp +++ b/Common/cpp/Tools/JSScheduler.cpp @@ -1,12 +1,30 @@ #include "JSScheduler.h" -#include +using namespace facebook; +using namespace react; namespace reanimated { +JSScheduler::JSScheduler( + jsi::Runtime &rnRuntime, + const std::shared_ptr &jsCallInvoker) + : rnRuntime_(rnRuntime), + jsCallInvoker_(jsCallInvoker), + scheduleOnJS([&](Job job) { + jsCallInvoker_->invokeAsync( + [job = std::move(job), &rt = rnRuntime_] { job(rt); }); + }) {} -void JSScheduler::scheduleOnJS(std::function job) { - jsCallInvoker_->invokeAsync( - [job = std::move(job), &rt = rnRuntime_] { job(rt); }); -} +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) +// With `runtimeExecutor`. +JSScheduler::JSScheduler( + jsi::Runtime &rnRuntime, + RuntimeExecutor runtimeExecutor) + : rnRuntime_(rnRuntime), + runtimeExecutor_(runtimeExecutor), + scheduleOnJS([&](Job job) { + runtimeExecutor_( + [job = std::move(job)](jsi::Runtime &runtime) { job(runtime); }); + }) {} +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED } // namespace reanimated diff --git a/Common/cpp/Tools/JSScheduler.h b/Common/cpp/Tools/JSScheduler.h index f91217f34331..a7d3deae9d41 100644 --- a/Common/cpp/Tools/JSScheduler.h +++ b/Common/cpp/Tools/JSScheduler.h @@ -1,25 +1,42 @@ #pragma once #include +#include #include #include +#include using namespace facebook; +using namespace react; + +using Job = std::function; namespace reanimated { class JSScheduler { public: + // With `jsCallInvoker`. + explicit JSScheduler( + jsi::Runtime &rnRuntime, + const std::shared_ptr &jsCallInvoker); + +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + // With `runtimeExecutor`. explicit JSScheduler( jsi::Runtime &rnRuntime, - const std::shared_ptr &jsCallInvoker) - : rnRuntime_(rnRuntime), jsCallInvoker_(jsCallInvoker) {} - void scheduleOnJS(std::function job); + RuntimeExecutor runtimeExecutor); +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED protected: jsi::Runtime &rnRuntime_; - const std::shared_ptr jsCallInvoker_; + const std::shared_ptr jsCallInvoker_ = nullptr; +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + RuntimeExecutor runtimeExecutor_ = nullptr; +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED + + public: + const std::function scheduleOnJS = nullptr; }; } // namespace reanimated diff --git a/FabricExample/android/app/src/main/java/com/fabricexample/MainApplication.kt b/FabricExample/android/app/src/main/java/com/fabricexample/MainApplication.kt index f5add319fcce..dff3af292ba3 100644 --- a/FabricExample/android/app/src/main/java/com/fabricexample/MainApplication.kt +++ b/FabricExample/android/app/src/main/java/com/fabricexample/MainApplication.kt @@ -37,7 +37,7 @@ class MainApplication : Application(), ReactApplication { SoLoader.init(this, false) if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { // If you opted-in for the New Architecture, we load the native entry point for this app. - load(bridgelessEnabled = false) + load(bridgelessEnabled = true) } } } diff --git a/FabricExample/ios/FabricExample/AppDelegate.mm b/FabricExample/ios/FabricExample/AppDelegate.mm index a6b0a16e69d9..a2a83d49ad61 100644 --- a/FabricExample/ios/FabricExample/AppDelegate.mm +++ b/FabricExample/ios/FabricExample/AppDelegate.mm @@ -30,7 +30,7 @@ - (NSURL *)bundleURL - (BOOL)bridgelessEnabled { - return NO; + return YES; } @end diff --git a/android/build.gradle b/android/build.gradle index 122f8e344a69..b9aab4871d12 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -388,6 +388,16 @@ android { srcDirs += "src/reactNativeVersionPatch/ReactFeatureFlagsWrapper/latest" } } + + // RuntimeExecutor + if (IS_NEW_ARCHITECTURE_ENABLED) { + if (REACT_NATIVE_MINOR_VERSION <= 73) { + srcDirs += "src/reactNativeVersionPatch/RuntimeExecutor/73" + } else { + srcDirs += "src/reactNativeVersionPatch/RuntimeExecutor/latest" + } + } + } } } diff --git a/android/src/main/cpp/NativeProxy.cpp b/android/src/main/cpp/NativeProxy.cpp index a90c1f189c35..779fccece4a1 100644 --- a/android/src/main/cpp/NativeProxy.cpp +++ b/android/src/main/cpp/NativeProxy.cpp @@ -51,6 +51,40 @@ NativeProxy::NativeProxy( valueUnpackerCode)), layoutAnimations_(std::move(layoutAnimations)) { #ifdef RCT_NEW_ARCH_ENABLED + commonInit(fabricUIManager); +#endif // RCT_NEW_ARCH_ENABLED +} + +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) +NativeProxy::NativeProxy( + jni::alias_ref jThis, + jsi::Runtime *rnRuntime, + RuntimeExecutor runtimeExecutor, + const std::shared_ptr &uiScheduler, + jni::global_ref layoutAnimations, + jni::alias_ref messageQueueThread, + jni::alias_ref + fabricUIManager, + + const std::string &valueUnpackerCode) + : javaPart_(jni::make_global(jThis)), + rnRuntime_(rnRuntime), + nativeReanimatedModule_(std::make_shared( + *rnRuntime, + runtimeExecutor, + std::make_shared(messageQueueThread), + uiScheduler, + getPlatformDependentMethods(), + valueUnpackerCode)), + layoutAnimations_(std::move(layoutAnimations)) { + commonInit(fabricUIManager); +} +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED + +#ifdef RCT_NEW_ARCH_ENABLED +void NativeProxy::commonInit( + jni::alias_ref + &fabricUIManager) { const auto &uiManager = fabricUIManager->getBinding()->getScheduler()->getUIManager(); nativeReanimatedModule_->initializeFabric(uiManager); @@ -63,8 +97,8 @@ NativeProxy::NativeProxy( // }); // reactScheduler_ = binding->getScheduler(); // reactScheduler_->addEventListener(eventListener_); -#endif } +#endif // RCT_NEW_ARCH_ENABLED NativeProxy::~NativeProxy() { // removed temporary, new event listener mechanism need fix on the RN side @@ -104,6 +138,31 @@ jni::local_ref NativeProxy::initHybrid( valueUnpackerCode); } +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) +jni::local_ref NativeProxy::initHybridBridgeless( + jni::alias_ref jThis, + jlong jsContext, + jni::alias_ref runtimeExecutorHolder, + jni::alias_ref androidUiScheduler, + jni::alias_ref layoutAnimations, + jni::alias_ref messageQueueThread, + jni::alias_ref + fabricUIManager, + const std::string &valueUnpackerCode) { + auto uiScheduler = androidUiScheduler->cthis()->getUIScheduler(); + auto runtimeExecutor = runtimeExecutorHolder->cthis()->get(); + return makeCxxInstance( + jThis, + (jsi::Runtime *)jsContext, + runtimeExecutor, + uiScheduler, + make_global(layoutAnimations), + messageQueueThread, + fabricUIManager, + valueUnpackerCode); +} +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED + #ifndef NDEBUG void NativeProxy::checkJavaVersion(jsi::Runtime &rnRuntime) { std::string javaVersion; @@ -177,13 +236,18 @@ bool NativeProxy::getIsReducedMotion() { } void NativeProxy::registerNatives() { - registerHybrid( - {makeNativeMethod("initHybrid", NativeProxy::initHybrid), - makeNativeMethod("installJSIBindings", NativeProxy::installJSIBindings), - makeNativeMethod( - "isAnyHandlerWaitingForEvent", - NativeProxy::isAnyHandlerWaitingForEvent), - makeNativeMethod("performOperations", NativeProxy::performOperations)}); + registerHybrid({ + makeNativeMethod("initHybrid", NativeProxy::initHybrid), +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + makeNativeMethod( + "initHybridBridgeless", NativeProxy::initHybridBridgeless), +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + makeNativeMethod("installJSIBindings", NativeProxy::installJSIBindings), + makeNativeMethod( + "isAnyHandlerWaitingForEvent", + NativeProxy::isAnyHandlerWaitingForEvent), + makeNativeMethod("performOperations", NativeProxy::performOperations) + }); } void NativeProxy::requestRender( diff --git a/android/src/main/cpp/NativeProxy.h b/android/src/main/cpp/NativeProxy.h index deb2542f90b9..e7488dc711ab 100644 --- a/android/src/main/cpp/NativeProxy.h +++ b/android/src/main/cpp/NativeProxy.h @@ -3,6 +3,9 @@ #ifdef RCT_NEW_ARCH_ENABLED #include #include +#if REACT_NATIVE_MINOR_VERSION >= 74 +#include +#endif // REACT_NATIVE_MINOR_VERSION >= 74 #endif #include @@ -158,6 +161,19 @@ class NativeProxy : public jni::HybridClass { fabricUIManager, #endif const std::string &valueUnpackerCode); + +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + static jni::local_ref initHybridBridgeless( + jni::alias_ref jThis, + jlong jsContext, + jni::alias_ref runtimeExecutorHolder, + jni::alias_ref androidUiScheduler, + jni::alias_ref layoutAnimations, + jni::alias_ref messageQueueThread, + jni::alias_ref + fabricUIManager, + const std::string &valueUnpackerCode); +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED static void registerNatives(); ~NativeProxy(); @@ -269,6 +285,24 @@ class NativeProxy : public jni::HybridClass { fabricUIManager, #endif const std::string &valueUnpackerCode); + +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + explicit NativeProxy( + jni::alias_ref jThis, + jsi::Runtime *rnRuntime, + RuntimeExecutor runtimeExecutor, + const std::shared_ptr &uiScheduler, + jni::global_ref layoutAnimations, + jni::alias_ref messageQueueThread, + jni::alias_ref + fabricUIManager, + const std::string &valueUnpackerCode); +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED + +#ifdef RCT_NEW_ARCH_ENABLED + void commonInit(jni::alias_ref + &fabricUIManager); +#endif // RCT_NEW_ARCH_ENABLED }; } // namespace reanimated diff --git a/android/src/main/java/com/swmansion/reanimated/ReanimatedModule.java b/android/src/reactNativeVersionPatch/ReanimatedUIManager/73/com/swmansion/ReanimatedModule.java similarity index 100% rename from android/src/main/java/com/swmansion/reanimated/ReanimatedModule.java rename to android/src/reactNativeVersionPatch/ReanimatedUIManager/73/com/swmansion/ReanimatedModule.java diff --git a/android/src/reactNativeVersionPatch/ReanimatedUIManager/latest/com/swmansion/reanimated/ReanimatedModule.java b/android/src/reactNativeVersionPatch/ReanimatedUIManager/latest/com/swmansion/reanimated/ReanimatedModule.java new file mode 100644 index 000000000000..2801227ab03f --- /dev/null +++ b/android/src/reactNativeVersionPatch/ReanimatedUIManager/latest/com/swmansion/reanimated/ReanimatedModule.java @@ -0,0 +1,176 @@ +package com.swmansion.reanimated; + +import android.util.Log; +import androidx.annotation.NonNull; +import com.facebook.react.bridge.LifecycleEventListener; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.UIManager; +import com.facebook.react.bridge.UIManagerListener; +import com.facebook.react.fabric.FabricUIManager; +import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.uimanager.UIManagerModule; +import com.facebook.react.uimanager.UIManagerModuleListener; +import java.util.ArrayList; +import javax.annotation.Nullable; + +@ReactModule(name = ReanimatedModule.NAME) +public class ReanimatedModule extends NativeReanimatedModuleSpec + implements LifecycleEventListener, UIManagerModuleListener, + UIManagerListener { + public static final String NAME = "ReanimatedModule"; + + public void didDispatchMountItems(@NonNull UIManager uiManager) { + // Keep: Required for UIManagerListener + } + + public void didMountItems(@NonNull UIManager uiManager) { + // Keep: Required for UIManagerListener + } + + public void didScheduleMountItems(@NonNull UIManager uiManager) { + // Keep: Required for UIManagerListener + } + + public void willDispatchViewUpdates(@NonNull UIManager uiManager) { + // This method is called for the interface of UIManagerListener on Fabric. + // The below function with the same name won't be called. + if (mOperations.isEmpty()) { + return; + } + final ArrayList operations = mOperations; + mOperations = new ArrayList<>(); + if (uiManager instanceof FabricUIManager) { + ((FabricUIManager)uiManager).addUIBlock(uiBlockViewResolver -> { + NodesManager nodesManager = getNodesManager(); + for (UIThreadOperation operation : operations) { + operation.execute(nodesManager); + } + }); + } else { + throw new RuntimeException( + "[Reanimated] Failed to obtain instance of FabricUIManager."); + } + } + + public void willMountItems(@NonNull UIManager uiManager) {} + + private interface UIThreadOperation { + void execute(NodesManager nodesManager); + } + + private ArrayList mOperations = new ArrayList<>(); + private @Nullable NodesManager mNodesManager; + + public ReanimatedModule(ReactApplicationContext reactContext) { + super(reactContext); + } + + @Override + public void initialize() { + ReactApplicationContext reactCtx = getReactApplicationContext(); + + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + UIManager uiManager = reactCtx.getFabricUIManager(); + if (uiManager instanceof FabricUIManager) { + ((FabricUIManager)uiManager).addUIManagerEventListener(this); + } else { + throw new RuntimeException( + "[Reanimated] Failed to obtain instance of FabricUIManager."); + } + } else { + UIManagerModule uiManager = + reactCtx.getNativeModule(UIManagerModule.class); + uiManager.addUIManagerListener(this); + } + reactCtx.addLifecycleEventListener(this); + } + + @Override + public void onHostPause() { + if (mNodesManager != null) { + mNodesManager.onHostPause(); + } + } + + @Override + public void onHostResume() { + if (mNodesManager != null) { + mNodesManager.onHostResume(); + } + } + + @Override + public void onHostDestroy() { + // do nothing + } + + @Override + public void willDispatchViewUpdates(final UIManagerModule uiManager) { + // This method is called for the interface of UIManagerModuleListener on + // Paper. The below function with the same name won't be called. + if (mOperations.isEmpty()) { + return; + } + final ArrayList operations = mOperations; + mOperations = new ArrayList<>(); + uiManager.addUIBlock(nativeViewHierarchyManager -> { + NodesManager nodesManager = getNodesManager(); + for (UIThreadOperation operation : operations) { + operation.execute(nodesManager); + } + }); + } + + @Override + public String getName() { + return NAME; + } + + /*package*/ + public NodesManager getNodesManager() { + if (mNodesManager == null) { + mNodesManager = new NodesManager(getReactApplicationContext()); + } + + return mNodesManager; + } + + @ReactMethod(isBlockingSynchronousMethod = true) + public boolean installTurboModule(String valueUnpackerCode) { + // When debugging in chrome the JS context is not available. + // https://github.com/facebook/react-native/blob/v0.67.0-rc.6/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BlobCollector.java#L25 + Utils.isChromeDebugger = + getReactApplicationContext().getJavaScriptContextHolder().get() == 0; + + if (!Utils.isChromeDebugger) { + this.getNodesManager().initWithContext( + getReactApplicationContext(), valueUnpackerCode); + return true; + } else { + Log.w( + "[REANIMATED]", + "Unable to create Reanimated Native Module. You can ignore this message if you are using Chrome Debugger now."); + return false; + } + } + + @ReactMethod + public void addListener(String eventName) { + // Keep: Required for RN built in Event Emitter Calls. + } + + @ReactMethod + public void removeListeners(Integer count) { + // Keep: Required for RN built in Event Emitter Calls. + } + + @Override + public void invalidate() { + super.invalidate(); + + if (mNodesManager != null) { + mNodesManager.invalidate(); + } + } +} diff --git a/android/src/fabric/java/com/swmansion/reanimated/NativeProxy.java b/android/src/reactNativeVersionPatch/RuntimeExecutor/73/com/swmansion/reanimated/NativeProxy.java similarity index 100% rename from android/src/fabric/java/com/swmansion/reanimated/NativeProxy.java rename to android/src/reactNativeVersionPatch/RuntimeExecutor/73/com/swmansion/reanimated/NativeProxy.java diff --git a/android/src/reactNativeVersionPatch/RuntimeExecutor/latest/com/swmansion/reanimated/NativeProxy.java b/android/src/reactNativeVersionPatch/RuntimeExecutor/latest/com/swmansion/reanimated/NativeProxy.java new file mode 100644 index 000000000000..56fb4bb218d3 --- /dev/null +++ b/android/src/reactNativeVersionPatch/RuntimeExecutor/latest/com/swmansion/reanimated/NativeProxy.java @@ -0,0 +1,143 @@ +package com.swmansion.reanimated; + +import androidx.annotation.OptIn; + +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.RuntimeExecutor; +import com.facebook.react.bridge.queue.MessageQueueThread; +import com.facebook.react.common.annotations.FrameworkAPI; +import com.facebook.react.fabric.FabricUIManager; +import com.facebook.react.turbomodule.core.CallInvokerHolderImpl; +import com.facebook.react.uimanager.UIManagerHelper; +import com.facebook.react.uimanager.common.UIManagerType; +import com.swmansion.reanimated.layoutReanimation.LayoutAnimations; +import com.swmansion.reanimated.layoutReanimation.NativeMethodsHolder; +import com.swmansion.reanimated.nativeProxy.NativeProxyCommon; + +import java.util.HashMap; +import java.util.Objects; + +public class NativeProxy extends NativeProxyCommon { + @DoNotStrip + @SuppressWarnings("unused") + private final HybridData mHybridData; + + public @OptIn(markerClass = FrameworkAPI.class) NativeProxy(ReactApplicationContext context, String valueUnpackerCode) { + super(context); + ReactFeatureFlagsWrapper.enableMountHooks(); + + FabricUIManager fabricUIManager = + (FabricUIManager) UIManagerHelper.getUIManager(context, UIManagerType.FABRIC); + + LayoutAnimations LayoutAnimations = new LayoutAnimations(context); + + ReanimatedMessageQueueThread messageQueueThread = new ReanimatedMessageQueueThread(); + + + if (context.isBridgeless()) { + RuntimeExecutor runtimeExecutor = context.getRuntimeExecutor(); + mHybridData = initHybridBridgeless( + Objects.requireNonNull(context.getJavaScriptContextHolder()).get(), + runtimeExecutor, + mAndroidUIScheduler, + LayoutAnimations, + messageQueueThread, + fabricUIManager, + valueUnpackerCode + ); + } else { + CallInvokerHolderImpl callInvokerHolder = (CallInvokerHolderImpl) context.getCatalystInstance().getJSCallInvokerHolder(); + mHybridData = + initHybrid( + Objects.requireNonNull(context.getJavaScriptContextHolder()).get(), + callInvokerHolder, + mAndroidUIScheduler, + LayoutAnimations, + messageQueueThread, + fabricUIManager, + valueUnpackerCode); + } + prepareLayoutAnimations(LayoutAnimations); + installJSIBindings(); + if (BuildConfig.DEBUG) { + checkCppVersion(); + } + } + + private native HybridData initHybrid( + long jsContext, + CallInvokerHolderImpl jsCallInvokerHolder, + AndroidUIScheduler androidUIScheduler, + LayoutAnimations LayoutAnimations, + MessageQueueThread messageQueueThread, + FabricUIManager fabricUIManager, + String valueUnpackerCode); + + private native HybridData initHybridBridgeless( + long jsContext, + RuntimeExecutor runtimeExecutor, + AndroidUIScheduler androidUIScheduler, + LayoutAnimations LayoutAnimations, + MessageQueueThread messageQueueThread, + FabricUIManager fabricUIManager, + String valueUnpackerCode); + + public native boolean isAnyHandlerWaitingForEvent(String eventName, int emitterReactTag); + + public native void performOperations(); + + @Override + protected HybridData getHybridData() { + return mHybridData; + } + + public static NativeMethodsHolder createNativeMethodsHolder(LayoutAnimations layoutAnimations) { + return new NativeMethodsHolder() { + @Override + public void startAnimation(int tag, int type, HashMap values) { + // NOT IMPLEMENTED + } + + @Override + public boolean isLayoutAnimationEnabled() { + // NOT IMPLEMENTED + return false; + } + + @Override + public int findPrecedingViewTagForTransition(int tag) { + // NOT IMPLEMENTED + return -1; + } + + @Override + public boolean shouldAnimateExiting(int tag, boolean shouldAnimate) { + // NOT IMPLEMENTED + return false; + } + + @Override + public boolean hasAnimation(int tag, int type) { + // NOT IMPLEMENTED + return false; + } + + @Override + public void clearAnimationConfig(int tag) { + // NOT IMPLEMENTED + } + + @Override + public void cancelAnimation(int tag) { + // NOT IMPLEMENTED + } + + @Override + public void checkDuplicateSharedTag(int viewTag, int screenTag) { + // NOT IMPLEMENTED + } + }; + } +} diff --git a/apple/REAModule.h b/apple/REAModule.h index 33b40365a377..62dd42ee8f7e 100644 --- a/apple/REAModule.h +++ b/apple/REAModule.h @@ -1,20 +1,29 @@ #ifdef RCT_NEW_ARCH_ENABLED +#import +#if REACT_NATIVE_MINOR_VERSION >= 74 +#import +#import +#endif // REACT_NATIVE_MINOR_VERSION >= 74 #import -#else +#else // RCT_NEW_ARCH_ENABLED #import #endif // RCT_NEW_ARCH_ENABLED - -#import -#import #import #import #import #import #import +#import +#import + @interface REAModule : RCTEventEmitter #ifdef RCT_NEW_ARCH_ENABLED = 74 + RCTRuntimeExecutorModule, +#endif // REACT_NATIVE_MINOR_VERSION >= 74 #else = 74 && defined(RCT_NEW_ARCH_ENABLED) +@synthesize runtimeExecutor = _runtimeExecutor; +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + RCT_EXPORT_MODULE(ReanimatedModule); #ifdef RCT_NEW_ARCH_ENABLED @@ -149,40 +154,71 @@ - (void)handleJavaScriptDidLoadNotification:(NSNotification *)notification }); } +#pragma mark-- Bridgeless methods + +/* + * Taken from RCTNativeAnimatedTurboModule: + * In bridgeless mode, `setBridge` is never called during initialization. Instead this selector is invoked via + * BridgelessTurboModuleSetup. + */ +- (void)setSurfacePresenter:(id)surfacePresenter +{ + _surfacePresenter = surfacePresenter; +} + - (void)setBridge:(RCTBridge *)bridge { + // This method isn't called on Bridgeless mode. [super setBridge:bridge]; + [bridge.uiManager.observerCoordinator addObserver:self]; + + // only within the first loading `self.bridge.surfacePresenter` exists + // during the reload `self.bridge.surfacePresenter` is null + _surfacePresenter = self.bridge.surfacePresenter; + +#ifndef NDEBUG + [self setReaSurfacePresenter]; +#endif // NDEBUG + + [self setNodesManager:self.bridge]; +} + +- (void)initialize +{ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleJavaScriptDidLoadNotification:) name:RCTJavaScriptDidLoadNotification object:nil]; [[self.moduleRegistry moduleForName:"EventDispatcher"] addDispatchObserver:self]; - [bridge.uiManager.observerCoordinator addObserver:self]; - // only within the first loading `self.bridge.surfacePresenter` exists - // during the reload `self.bridge.surfacePresenter` is null - _surfacePresenter = self.bridge.surfacePresenter; +// [bridge.uiManager.observerCoordinator addObserver:self]; // TODO: Check if it's needed on new arch. #ifndef NDEBUG + [self setReaSurfacePresenter]; +#endif // NDEBUG + + [self setNodesManager:nil]; +} + +#ifndef NDEBUG +- (void)setReaSurfacePresenter +{ if (reaSurface == nil) { // we need only one instance because SurfacePresenter is the same during the application lifetime reaSurface = [[REAInitializerRCTFabricSurface alloc] init]; [_surfacePresenter registerSurface:reaSurface]; } reaSurface.reaModule = self; +} #endif // NDEBUG - if (_surfacePresenter == nil) { - // _surfacePresenter will be set in installReanimatedAfterReload - _nodesManager = [[REANodesManager alloc] initWithModule:self bridge:self.bridge surfacePresenter:nil]; - return; - } - - _nodesManager = [[REANodesManager alloc] initWithModule:self bridge:self.bridge surfacePresenter:_surfacePresenter]; +- (void)setNodesManager:(RCTBridge *)bridge +{ + _nodesManager = [[REANodesManager alloc] initWithModule:self bridge:bridge surfacePresenter:_surfacePresenter]; } -#else +#else // RCT_NEW_ARCH_ENABLED - (void)setBridge:(RCTBridge *)bridge { @@ -259,35 +295,38 @@ - (void)sendEventWithName:(NSString *)eventName body:(id)body RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(installTurboModule : (nonnull NSString *)valueUnpackerCode) { - facebook::jsi::Runtime *jsiRuntime = [self.bridge respondsToSelector:@selector(runtime)] - ? reinterpret_cast(self.bridge.runtime) - : nullptr; - - if (jsiRuntime) { - auto nativeReanimatedModule = reanimated::createReanimatedModule( - self.bridge, self.bridge.jsCallInvoker, std::string([valueUnpackerCode UTF8String])); - - jsi::Runtime &rnRuntime = *jsiRuntime; - WorkletRuntimeCollector::install(rnRuntime); - -#if __has_include() - auto isReducedMotion = UIAccessibilityIsReduceMotionEnabled(); -#else - auto isReducedMotion = NSWorkspace.sharedWorkspace.accessibilityDisplayShouldReduceMotion; -#endif - - RNRuntimeDecorator::decorate(rnRuntime, nativeReanimatedModule, isReducedMotion); - -#ifdef RCT_NEW_ARCH_ENABLED - weakNativeReanimatedModule_ = nativeReanimatedModule; - if (_surfacePresenter != nil) { - // reload, uiManager is null right now, we need to wait for `installReanimatedAfterReload` - [self injectDependencies:rnRuntime]; + if (!self.bridge) { +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + RCTCxxBridge *cxxBridge = (RCTCxxBridge *)[RCTBridge currentBridge]; + auto &rnRuntime = *(jsi::Runtime *)cxxBridge.runtime; + auto executorFunction = ([executor = _runtimeExecutor](std::function &&callback) { + // Convert to Objective-C block so it can be captured properly. + __block auto callbackBlock = callback; + + [executor execute:^(jsi::Runtime &runtime) { + callbackBlock(runtime); + }]; + }); + auto nativeReanimatedModule = reanimated::createReanimatedModuleBridgeless( + _moduleRegistry, rnRuntime, std::string([valueUnpackerCode UTF8String]), executorFunction); + [self commonInit:nativeReanimatedModule withRnRuntime:rnRuntime]; +#else // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + [NSException raise:@"Missing bridge" format:@"[Reanimated] Failed to obtain the bridge."]; +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + } else { + facebook::jsi::Runtime *jsiRuntime = [self.bridge respondsToSelector:@selector(runtime)] + ? reinterpret_cast(self.bridge.runtime) + : nullptr; + + if (jsiRuntime) { + auto nativeReanimatedModule = reanimated::createReanimatedModule( + self.bridge, self.bridge.jsCallInvoker, std::string([valueUnpackerCode UTF8String])); + jsi::Runtime &rnRuntime = *jsiRuntime; + + [self commonInit:nativeReanimatedModule withRnRuntime:rnRuntime]; } -#endif // RCT_NEW_ARCH_ENABLED } - - return nil; + return @YES; } #ifdef RCT_NEW_ARCH_ENABLED @@ -298,4 +337,23 @@ - (void)sendEventWithName:(NSString *)eventName body:(id)body } #endif // RCT_NEW_ARCH_ENABLED +- (void)commonInit:(std::shared_ptr)nativeReanimatedModule + withRnRuntime:(jsi::Runtime &)rnRuntime +{ +#if __has_include() + auto isReducedMotion = UIAccessibilityIsReduceMotionEnabled(); +#else + auto isReducedMotion = NSWorkspace.sharedWorkspace.accessibilityDisplayShouldReduceMotion; +#endif + WorkletRuntimeCollector::install(rnRuntime); + RNRuntimeDecorator::decorate(rnRuntime, nativeReanimatedModule, isReducedMotion); +#ifdef RCT_NEW_ARCH_ENABLED + weakNativeReanimatedModule_ = nativeReanimatedModule; + if (self->_surfacePresenter != nil) { + // reload, uiManager is null right now, we need to wait for `installReanimatedAfterReload` + [self injectDependencies:rnRuntime]; + } +#endif // RCT_NEW_ARCH_ENABLED +} + @end diff --git a/apple/native/NativeProxy.h b/apple/native/NativeProxy.h index 809b169608b1..7863d15cfbfa 100644 --- a/apple/native/NativeProxy.h +++ b/apple/native/NativeProxy.h @@ -1,7 +1,12 @@ #if __cplusplus +#import +#import +#import +#import #import #import +#import #include namespace reanimated { @@ -11,6 +16,27 @@ std::shared_ptr createReanimatedModule( const std::shared_ptr &jsInvoker, const std::string &valueUnpackerCode); -} +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) +std::shared_ptr +createReanimatedModuleBridgeless( + RCTModuleRegistry *moduleRegistry, + jsi::Runtime &runtime, + const std::string &valueUnpackerCode, + RuntimeExecutor runtimeExecutor); +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) -#endif +void commonInit( + REAModule *reaModule, + std::shared_ptr nativeReanimatedModule); + +#ifdef RCT_NEW_ARCH_ENABLED +// nothing +#else // RCT_NEW_ARCH_ENABLED +void setupLayoutAnimationCallbacks( + std::shared_ptr nativeReanimatedModule, + REAAnimationsManager *animationsManager); +#endif // RCT_NEW_ARCH_ENABLED + +} // namespace reanimated + +#endif //__cplusplus diff --git a/apple/native/NativeProxy.mm b/apple/native/NativeProxy.mm index 925c384cd487..8ea27de24d5d 100644 --- a/apple/native/NativeProxy.mm +++ b/apple/native/NativeProxy.mm @@ -1,6 +1,8 @@ #import #import #import +#import +#import #import #import #import @@ -38,28 +40,11 @@ @interface RCTBridge (JSIRuntime) - (void *)runtime; @end -@interface RCTUIManager (DispatchCommand) -- (void)dispatchViewManagerCommand:(nonnull NSNumber *)reactTag - commandID:(id /*(NSString or NSNumber) */)commandID - commandArgs:(NSArray *)commandArgs; -@end - namespace reanimated { using namespace facebook; using namespace react; -static NSSet *convertProps(jsi::Runtime &rt, const jsi::Value &props) -{ - NSMutableSet *propsSet = [[NSMutableSet alloc] init]; - jsi::Array propsNames = props.asObject(rt).asArray(rt); - for (int i = 0; i < propsNames.size(rt); i++) { - NSString *propName = @(propsNames.getValueAtIndex(rt, i).asString(rt).utf8(rt).c_str()); - [propsSet addObject:propName]; - } - return propsSet; -} - std::shared_ptr createReanimatedModule( RCTBridge *bridge, const std::shared_ptr &jsInvoker, @@ -67,195 +52,65 @@ - (void)dispatchViewManagerCommand:(nonnull NSNumber *)reactTag { REAModule *reaModule = [bridge moduleForClass:[REAModule class]]; -#ifdef RCT_NEW_ARCH_ENABLED - // nothing -#else - RCTUIManager *uiManager = reaModule.nodesManager.uiManager; - auto updatePropsFunction = [reaModule](jsi::Runtime &rt, const jsi::Value &operations) -> void { - auto array = operations.asObject(rt).asArray(rt); - size_t length = array.size(rt); - for (size_t i = 0; i < length; ++i) { - auto item = array.getValueAtIndex(rt, i).asObject(rt); - int viewTag = item.getProperty(rt, "tag").asNumber(); - const jsi::Value &viewName = item.getProperty(rt, "name"); - const jsi::Object &props = item.getProperty(rt, "updates").asObject(rt); - - NSString *nsViewName = [NSString stringWithCString:viewName.asString(rt).utf8(rt).c_str() - encoding:[NSString defaultCStringEncoding]]; - - NSDictionary *propsDict = convertJSIObjectToNSDictionary(rt, props); - [reaModule.nodesManager updateProps:propsDict ofViewWithTag:[NSNumber numberWithInt:viewTag] withName:nsViewName]; - } - }; - - auto measureFunction = [uiManager](int viewTag) -> std::vector> { - return measure(viewTag, uiManager); - }; - - auto scrollToFunction = [uiManager](int viewTag, double x, double y, bool animated) { - scrollTo(viewTag, uiManager, x, y, animated); - }; - - auto dispatchCommandFunction = - [uiManager]( - jsi::Runtime &rt, const int tag, const jsi::Value &commandNameValue, const jsi::Value &argsValue) -> void { - NSNumber *viewTag = [NSNumber numberWithInt:tag]; - NSString *commandID = [NSString stringWithCString:commandNameValue.asString(rt).utf8(rt).c_str() - encoding:[NSString defaultCStringEncoding]]; - NSArray *commandArgs = convertJSIArrayToNSArray(rt, argsValue.asObject(rt).asArray(rt)); - RCTExecuteOnUIManagerQueue(^{ - [uiManager dispatchViewManagerCommand:viewTag commandID:commandID commandArgs:commandArgs]; - }); - }; - -#endif - - id gestureHandlerStateManager = nil; - auto setGestureStateFunction = [gestureHandlerStateManager, bridge](int handlerTag, int newState) mutable { - if (gestureHandlerStateManager == nil) { - gestureHandlerStateManager = [bridge moduleForName:@"RNGestureHandlerModule"]; - } - - setGestureState(gestureHandlerStateManager, handlerTag, newState); - }; + auto nodesManager = reaModule.nodesManager; -#ifdef RCT_NEW_ARCH_ENABLED - // nothing -#else - auto obtainPropFunction = [reaModule](jsi::Runtime &rt, const int viewTag, const jsi::Value &propName) -> jsi::Value { - NSString *propNameConverted = [NSString stringWithFormat:@"%s", propName.asString(rt).utf8(rt).c_str()]; - std::string resultStr = std::string([[reaModule.nodesManager obtainProp:[NSNumber numberWithInt:viewTag] - propName:propNameConverted] UTF8String]); - jsi::Value val = jsi::String::createFromUtf8(rt, resultStr); - return val; - }; -#endif + jsi::Runtime &rnRuntime = *reinterpret_cast(reaModule.bridge.runtime); auto jsQueue = std::make_shared([NSRunLoop currentRunLoop], ^(NSError *error) { throw error; }); - jsi::Runtime &rnRuntime = *reinterpret_cast(reaModule.bridge.runtime); - std::shared_ptr uiScheduler = std::make_shared(); - auto nodesManager = reaModule.nodesManager; + PlatformDepMethodsHolder platformDepMethodsHolder = makePlatformDepMethodsHolder(bridge, nodesManager, reaModule); - auto maybeFlushUIUpdatesQueueFunction = [nodesManager]() { [nodesManager maybeFlushUIUpdatesQueue]; }; + auto nativeReanimatedModule = std::make_shared( + rnRuntime, jsInvoker, jsQueue, uiScheduler, platformDepMethodsHolder, valueUnpackerCode); - auto requestRender = [nodesManager](std::function onRender, jsi::Runtime &rt) { - [nodesManager postOnAnimation:^(READisplayLink *displayLink) { -#if !TARGET_OS_OSX - auto targetTimestamp = displayLink.targetTimestamp; + commonInit(reaModule, nativeReanimatedModule); + // Layout Animation callbacks setup +#ifdef RCT_NEW_ARCH_ENABLED + // nothing #else - // TODO macOS targetTimestamp isn't available on macOS - auto targetTimestamp = displayLink.timestamp + displayLink.duration; -#endif - double frameTimestamp = calculateTimestampWithSlowAnimations(targetTimestamp) * 1000; - onRender(frameTimestamp); - }]; - }; + REAAnimationsManager *animationsManager = reaModule.animationsManager; + setupLayoutAnimationCallbacks(nativeReanimatedModule, animationsManager); -#ifdef RCT_NEW_ARCH_ENABLED - auto synchronouslyUpdateUIPropsFunction = [nodesManager](jsi::Runtime &rt, Tag tag, const jsi::Object &props) { - NSNumber *viewTag = @(tag); - NSDictionary *uiProps = convertJSIObjectToNSDictionary(rt, props); - [nodesManager synchronouslyUpdateViewOnUIThread:viewTag props:uiProps]; - }; +#endif // RCT_NEW_ARCH_ENABLED + + return nativeReanimatedModule; +} - auto progressLayoutAnimation = [=](jsi::Runtime &rt, int tag, const jsi::Object &newStyle, bool isSharedTransition) { - // noop - }; +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) +std::shared_ptr createReanimatedModuleBridgeless( + RCTModuleRegistry *moduleRegistry, + jsi::Runtime &runtime, + const std::string &valueUnpackerCode, + RuntimeExecutor runtimeExecutor) +{ + REAModule *reaModule = [moduleRegistry moduleForName:"ReanimatedModule"]; - auto endLayoutAnimation = [=](int tag, bool removeView) { - // noop - }; + auto nodesManager = reaModule.nodesManager; -#else - // Layout Animations start - REAAnimationsManager *animationsManager = reaModule.animationsManager; - __weak REAAnimationsManager *weakAnimationsManager = animationsManager; - - auto progressLayoutAnimation = [=](jsi::Runtime &rt, int tag, const jsi::Object &newStyle, bool isSharedTransition) { - NSDictionary *propsDict = convertJSIObjectToNSDictionary(rt, newStyle); - [weakAnimationsManager progressLayoutAnimationWithStyle:propsDict - forTag:@(tag) - isSharedTransition:isSharedTransition]; - }; - - auto endLayoutAnimation = [=](int tag, bool removeView) { - [weakAnimationsManager endLayoutAnimationForTag:@(tag) removeView:removeView]; - }; - - auto configurePropsFunction = [reaModule]( - jsi::Runtime &rt, const jsi::Value &uiProps, const jsi::Value &nativeProps) { - NSSet *uiPropsSet = convertProps(rt, uiProps); - NSSet *nativePropsSet = convertProps(rt, nativeProps); - [reaModule.nodesManager configureUiProps:uiPropsSet andNativeProps:nativePropsSet]; - }; - - // Layout Animations end -#endif + auto jsQueue = std::make_shared([NSRunLoop currentRunLoop], ^(NSError *error) { + throw error; + }); - auto getAnimationTimestamp = []() { return calculateTimestampWithSlowAnimations(CACurrentMediaTime()) * 1000; }; - - // sensors - ReanimatedSensorContainer *reanimatedSensorContainer = [[ReanimatedSensorContainer alloc] init]; - auto registerSensorFunction = - [=](int sensorType, int interval, int iosReferenceFrame, std::function setter) -> int { - return [reanimatedSensorContainer registerSensor:(ReanimatedSensorType)sensorType - interval:interval - iosReferenceFrame:iosReferenceFrame - setter:^(double *data, int orientationDegrees) { - setter(data, orientationDegrees); - }]; - }; - - auto unregisterSensorFunction = [=](int sensorId) { [reanimatedSensorContainer unregisterSensor:sensorId]; }; - // end sensors - - // keyboard events - - REAKeyboardEventObserver *keyboardObserver = [[REAKeyboardEventObserver alloc] init]; - auto subscribeForKeyboardEventsFunction = - [=](std::function keyboardEventDataUpdater, bool isStatusBarTranslucent) { - // ignore isStatusBarTranslucent - it's Android only - return [keyboardObserver subscribeForKeyboardEvents:^(int keyboardState, int height) { - keyboardEventDataUpdater(keyboardState, height); - }]; - }; - - auto unsubscribeFromKeyboardEventsFunction = [=](int listenerId) { - [keyboardObserver unsubscribeFromKeyboardEvents:listenerId]; - }; - // end keyboard events - - PlatformDepMethodsHolder platformDepMethodsHolder = { - requestRender, -#ifdef RCT_NEW_ARCH_ENABLED - synchronouslyUpdateUIPropsFunction, -#else - updatePropsFunction, - scrollToFunction, - dispatchCommandFunction, - measureFunction, - configurePropsFunction, - obtainPropFunction, -#endif - getAnimationTimestamp, - progressLayoutAnimation, - endLayoutAnimation, - registerSensorFunction, - unregisterSensorFunction, - setGestureStateFunction, - subscribeForKeyboardEventsFunction, - unsubscribeFromKeyboardEventsFunction, - maybeFlushUIUpdatesQueueFunction, - }; + std::shared_ptr uiScheduler = std::make_shared(); + + PlatformDepMethodsHolder platformDepMethodsHolder = + makePlatformDepMethodsHolderBridgeless(moduleRegistry, nodesManager, reaModule); auto nativeReanimatedModule = std::make_shared( - rnRuntime, jsInvoker, jsQueue, uiScheduler, platformDepMethodsHolder, valueUnpackerCode); + runtime, runtimeExecutor, jsQueue, uiScheduler, platformDepMethodsHolder, valueUnpackerCode); + commonInit(reaModule, nativeReanimatedModule); + + return nativeReanimatedModule; +} +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + +void commonInit(REAModule *reaModule, std::shared_ptr nativeReanimatedModule) +{ [reaModule.nodesManager registerEventHandler:^(id event) { // handles RCTEvents from RNGestureHandler std::string eventName = [event.eventName UTF8String]; @@ -267,15 +122,24 @@ - (void)dispatchViewManagerCommand:(nonnull NSNumber *)reactTag nativeReanimatedModule->handleEvent(eventName, emitterReactTag, payload, currentTime); }]; - std::weak_ptr weakNativeReanimatedModule = nativeReanimatedModule; // to avoid retain cycle #ifdef RCT_NEW_ARCH_ENABLED + std::weak_ptr weakNativeReanimatedModule = nativeReanimatedModule; // to avoid retain cycle [reaModule.nodesManager registerPerformOperations:^() { if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { nativeReanimatedModule->performOperations(); } }]; -#else - // Layout Animation callbacks setup +#endif // RCT_NEW_ARCH_ENABLED +} + +#ifdef RCT_NEW_ARCH_ENABLED +// nothing +#else // RCT_NEW_ARCH_ENABLED +void setupLayoutAnimationCallbacks( + std::shared_ptr nativeReanimatedModule, + REAAnimationsManager *animationsManager) +{ + std::weak_ptr weakNativeReanimatedModule = nativeReanimatedModule; // to avoid retain cycle [animationsManager setAnimationStartingBlock:^(NSNumber *_Nonnull tag, LayoutAnimationType type, NSDictionary *_Nonnull values) { if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { @@ -353,10 +217,7 @@ - (void)dispatchViewManagerCommand:(nonnull NSNumber *)reactTag } }]; #endif // NDEBUG - -#endif - - return nativeReanimatedModule; } +#endif // RCT_NEW_ARCH_ENABLED } // namespace reanimated diff --git a/apple/native/PlatformDepMethodsHolderImpl.h b/apple/native/PlatformDepMethodsHolderImpl.h new file mode 100644 index 000000000000..39595a6a374e --- /dev/null +++ b/apple/native/PlatformDepMethodsHolderImpl.h @@ -0,0 +1,59 @@ +#if __cplusplus + +#import +#import +#import +#import +#import +#import +#import +#include + +namespace reanimated { + +PlatformDepMethodsHolder makePlatformDepMethodsHolder( + RCTBridge *bridge, + REANodesManager *nodesManager, + REAModule *reaModule); + +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) +PlatformDepMethodsHolder makePlatformDepMethodsHolderBridgeless( + RCTModuleRegistry *moduleRegistry, + REANodesManager *nodesManager, + REAModule *reaModule); +SetGestureStateFunction makeSetGestureStateFunctionBridgeless( + RCTModuleRegistry *moduleRegistry); +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + +#ifdef RCT_NEW_ARCH_ENABLED +SynchronouslyUpdateUIPropsFunction makeSynchronouslyUpdateUIPropsFunction( + REANodesManager *nodesManager); +#else // RCT_NEW_ARCH_ENABLED +UpdatePropsFunction makeUpdatePropsFunction(REAModule *reaModule); +MeasureFunction makeMeasureFunction(RCTUIManager *uiManager); +ScrollToFunction makeScrollToFunction(RCTUIManager *uiManager); +DispatchCommandFunction makeDispatchCommandFunction(RCTUIManager *uiManager); +ConfigurePropsFunction makeConfigurePropsFunction(REAModule *reaModule); +ObtainPropFunction makeObtainPropFunction(REAModule *reaModule); +#endif // RCT_NEW_ARCH_ENABLED + +SetGestureStateFunction makeSetGestureStateFunction(RCTBridge *bridge); +RequestRenderFunction makeRequestRender(REANodesManager *nodesManager); +GetAnimationTimestampFunction makeGetAnimationTimestamp(); +ProgressLayoutAnimationFunction makeProgressLayoutAnimation( + REAModule *reaModule); +EndLayoutAnimationFunction makeEndLayoutAnimation(REAModule *reaModule); +MaybeFlushUIUpdatesQueueFunction makeMaybeFlushUIUpdatesQueueFunction( + REANodesManager *nodesManager); +RegisterSensorFunction makeRegisterSensorFunction( + ReanimatedSensorContainer *reanimatedSensorContainer); +UnregisterSensorFunction makeUnregisterSensorFunction( + ReanimatedSensorContainer *reanimatedSensorContainer); +KeyboardEventSubscribeFunction makeSubscribeForKeyboardEventsFunction( + REAKeyboardEventObserver *keyboardObserver); +KeyboardEventUnsubscribeFunction makeUnsubscribeFromKeyboardEventsFunction( + REAKeyboardEventObserver *keyboardObserver); + +} // namespace reanimated + +#endif //__cplusplus diff --git a/apple/native/PlatformDepMethodsHolderImpl.mm b/apple/native/PlatformDepMethodsHolderImpl.mm new file mode 100644 index 000000000000..b8e458f12da1 --- /dev/null +++ b/apple/native/PlatformDepMethodsHolderImpl.mm @@ -0,0 +1,429 @@ +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#ifndef NDEBUG +#import +#endif + +#ifdef RCT_NEW_ARCH_ENABLED +#import +#import +#import +#import +#import +#endif + +#import + +#if TARGET_IPHONE_SIMULATOR +#import +#endif + +#import + +@interface RCTUIManager (DispatchCommand) +- (void)dispatchViewManagerCommand:(nonnull NSNumber *)reactTag + commandID:(id /*(NSString or NSNumber) */)commandID + commandArgs:(NSArray *)commandArgs; +@end + +namespace reanimated { + +using namespace facebook; +using namespace react; + +static NSSet *convertProps(jsi::Runtime &rt, const jsi::Value &props) +{ + NSMutableSet *propsSet = [[NSMutableSet alloc] init]; + jsi::Array propsNames = props.asObject(rt).asArray(rt); + for (int i = 0; i < propsNames.size(rt); i++) { + NSString *propName = @(propsNames.getValueAtIndex(rt, i).asString(rt).utf8(rt).c_str()); + [propsSet addObject:propName]; + } + return propsSet; +} + +SetGestureStateFunction makeSetGestureStateFunction(RCTBridge *bridge) +{ + id gestureHandlerStateManager = nil; + auto setGestureStateFunction = [gestureHandlerStateManager, bridge](int handlerTag, int newState) mutable { + if (gestureHandlerStateManager == nil) { + gestureHandlerStateManager = [bridge moduleForName:@"RNGestureHandlerModule"]; + } + + setGestureState(gestureHandlerStateManager, handlerTag, newState); + }; + return setGestureStateFunction; +} + +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) +SetGestureStateFunction makeSetGestureStateFunctionBridgeless(RCTModuleRegistry *moduleRegistry) +{ + id gestureHandlerStateManager = nil; + auto setGestureStateFunction = [gestureHandlerStateManager, moduleRegistry](int handlerTag, int newState) mutable { + if (gestureHandlerStateManager == nil) { + gestureHandlerStateManager = [moduleRegistry moduleForName:"RNGestureHandlerModule"]; + } + setGestureState(gestureHandlerStateManager, handlerTag, newState); + }; + return setGestureStateFunction; +} +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + +RequestRenderFunction makeRequestRender(REANodesManager *nodesManager) +{ + auto requestRender = [nodesManager](std::function onRender, jsi::Runtime &rt) { + [nodesManager postOnAnimation:^(READisplayLink *displayLink) { +#if !TARGET_OS_OSX + auto targetTimestamp = displayLink.targetTimestamp; +#else + // TODO macOS targetTimestamp isn't available on macOS + auto targetTimestamp = displayLink.timestamp + displayLink.duration; +#endif + double frameTimestamp = + + (targetTimestamp)*1000; + onRender(frameTimestamp); + }]; + }; + + return requestRender; +} + +#ifdef RCT_NEW_ARCH_ENABLED +SynchronouslyUpdateUIPropsFunction makeSynchronouslyUpdateUIPropsFunction(REANodesManager *nodesManager) +{ + auto synchronouslyUpdateUIPropsFunction = [nodesManager](jsi::Runtime &rt, Tag tag, const jsi::Object &props) { + NSNumber *viewTag = @(tag); + NSDictionary *uiProps = convertJSIObjectToNSDictionary(rt, props); + [nodesManager synchronouslyUpdateViewOnUIThread:viewTag props:uiProps]; + }; + return synchronouslyUpdateUIPropsFunction; +} +#endif // RCT_NEW_ARCH_ENABLED + +#ifdef RCT_NEW_ARCH_ENABLED +// nothing +#else // RCT_NEW_ARCH_ENABLED +UpdatePropsFunction makeUpdatePropsFunction(REAModule *reaModule) +{ + auto updatePropsFunction = [reaModule](jsi::Runtime &rt, const jsi::Value &operations) -> void { + auto array = operations.asObject(rt).asArray(rt); + size_t length = array.size(rt); + for (size_t i = 0; i < length; ++i) { + auto item = array.getValueAtIndex(rt, i).asObject(rt); + int viewTag = item.getProperty(rt, "tag").asNumber(); + const jsi::Value &viewName = item.getProperty(rt, "name"); + const jsi::Object &props = item.getProperty(rt, "updates").asObject(rt); + + NSString *nsViewName = [NSString stringWithCString:viewName.asString(rt).utf8(rt).c_str() + encoding:[NSString defaultCStringEncoding]]; + + NSDictionary *propsDict = convertJSIObjectToNSDictionary(rt, props); + [reaModule.nodesManager updateProps:propsDict ofViewWithTag:[NSNumber numberWithInt:viewTag] withName:nsViewName]; + } + }; + return updatePropsFunction; +} + +MeasureFunction makeMeasureFunction(RCTUIManager *uiManager) +{ + auto measureFunction = [uiManager](int viewTag) -> std::vector> { + return measure(viewTag, uiManager); + }; + return measureFunction; +} + +ScrollToFunction makeScrollToFunction(RCTUIManager *uiManager) +{ + auto scrollToFunction = [uiManager](int viewTag, double x, double y, bool animated) { + scrollTo(viewTag, uiManager, x, y, animated); + }; + return scrollToFunction; +} + +DispatchCommandFunction makeDispatchCommandFunction(RCTUIManager *uiManager) +{ + auto dispatchCommandFunction = + [uiManager]( + jsi::Runtime &rt, const int tag, const jsi::Value &commandNameValue, const jsi::Value &argsValue) -> void { + NSNumber *viewTag = [NSNumber numberWithInt:tag]; + NSString *commandID = [NSString stringWithCString:commandNameValue.asString(rt).utf8(rt).c_str() + encoding:[NSString defaultCStringEncoding]]; + NSArray *commandArgs = convertJSIArrayToNSArray(rt, argsValue.asObject(rt).asArray(rt)); + RCTExecuteOnUIManagerQueue(^{ + [uiManager dispatchViewManagerCommand:viewTag commandID:commandID commandArgs:commandArgs]; + }); + }; + return dispatchCommandFunction; +} + +ConfigurePropsFunction makeConfigurePropsFunction(REAModule *reaModule) +{ + auto configurePropsFunction = [reaModule]( + jsi::Runtime &rt, const jsi::Value &uiProps, const jsi::Value &nativeProps) { + NSSet *uiPropsSet = convertProps(rt, uiProps); + NSSet *nativePropsSet = convertProps(rt, nativeProps); + [reaModule.nodesManager configureUiProps:uiPropsSet andNativeProps:nativePropsSet]; + }; + return configurePropsFunction; +} + +ObtainPropFunction makeObtainPropFunction(REAModule *reaModule) +{ + auto obtainPropFunction = [reaModule](jsi::Runtime &rt, const int viewTag, const jsi::Value &propName) -> jsi::Value { + NSString *propNameConverted = [NSString stringWithFormat:@"%s", propName.asString(rt).utf8(rt).c_str()]; + std::string resultStr = std::string([[reaModule.nodesManager obtainProp:[NSNumber numberWithInt:viewTag] + propName:propNameConverted] UTF8String]); + jsi::Value val = jsi::String::createFromUtf8(rt, resultStr); + return val; + }; + return obtainPropFunction; +} + +#endif // RCT_NEW_ARCH_ENABLED + +GetAnimationTimestampFunction makeGetAnimationTimestamp() +{ + auto getAnimationTimestamp = []() { return calculateTimestampWithSlowAnimations(CACurrentMediaTime()) * 1000; }; + return getAnimationTimestamp; +} + +ProgressLayoutAnimationFunction makeProgressLayoutAnimation(REAModule *reaModule) +{ +#ifdef RCT_NEW_ARCH_ENABLED + auto progressLayoutAnimation = [=](jsi::Runtime &rt, int tag, const jsi::Object &newStyle, bool isSharedTransition) { + // noop + }; +#else // RCT_NEW_ARCH_ENABLED + REAAnimationsManager *animationsManager = reaModule.animationsManager; + __weak REAAnimationsManager *weakAnimationsManager = animationsManager; + + auto progressLayoutAnimation = [=](jsi::Runtime &rt, int tag, const jsi::Object &newStyle, bool isSharedTransition) { + NSDictionary *propsDict = convertJSIObjectToNSDictionary(rt, newStyle); + [weakAnimationsManager progressLayoutAnimationWithStyle:propsDict + forTag:@(tag) + isSharedTransition:isSharedTransition]; + }; +#endif // RCT_NEW_ARCH_ENABLED + return progressLayoutAnimation; +} + +EndLayoutAnimationFunction makeEndLayoutAnimation(REAModule *reaModule) +{ +#ifdef RCT_NEW_ARCH_ENABLED + auto endLayoutAnimation = [=](int tag, bool removeView) { + // noop + }; +#else // RCT_NEW_ARCH_ENABLED + REAAnimationsManager *animationsManager = reaModule.animationsManager; + __weak REAAnimationsManager *weakAnimationsManager = animationsManager; + + auto endLayoutAnimation = [=](int tag, bool removeView) { + [weakAnimationsManager endLayoutAnimationForTag:@(tag) removeView:removeView]; + }; +#endif // RCT_NEW_ARCH_ENABLED + return endLayoutAnimation; +} + +MaybeFlushUIUpdatesQueueFunction makeMaybeFlushUIUpdatesQueueFunction(REANodesManager *nodesManager) +{ + auto maybeFlushUIUpdatesQueueFunction = [nodesManager]() { [nodesManager maybeFlushUIUpdatesQueue]; }; + return maybeFlushUIUpdatesQueueFunction; +} + +RegisterSensorFunction makeRegisterSensorFunction(ReanimatedSensorContainer *reanimatedSensorContainer) +{ + auto registerSensorFunction = + [=](int sensorType, int interval, int iosReferenceFrame, std::function setter) -> int { + return [reanimatedSensorContainer registerSensor:(ReanimatedSensorType)sensorType + interval:interval + iosReferenceFrame:iosReferenceFrame + setter:^(double *data, int orientationDegrees) { + setter(data, orientationDegrees); + }]; + }; + return registerSensorFunction; +} + +UnregisterSensorFunction makeUnregisterSensorFunction(ReanimatedSensorContainer *reanimatedSensorContainer) +{ + auto unregisterSensorFunction = [=](int sensorId) { [reanimatedSensorContainer unregisterSensor:sensorId]; }; + return unregisterSensorFunction; +} + +KeyboardEventSubscribeFunction makeSubscribeForKeyboardEventsFunction(REAKeyboardEventObserver *keyboardObserver) +{ + auto subscribeForKeyboardEventsFunction = + [=](std::function keyboardEventDataUpdater, bool isStatusBarTranslucent) { + // ignore isStatusBarTranslucent - it's Android only + return [keyboardObserver subscribeForKeyboardEvents:^(int keyboardState, int height) { + keyboardEventDataUpdater(keyboardState, height); + }]; + }; + return subscribeForKeyboardEventsFunction; +} + +KeyboardEventUnsubscribeFunction makeUnsubscribeFromKeyboardEventsFunction(REAKeyboardEventObserver *keyboardObserver) +{ + auto unsubscribeFromKeyboardEventsFunction = [=](int listenerId) { + [keyboardObserver unsubscribeFromKeyboardEvents:listenerId]; + }; + return unsubscribeFromKeyboardEventsFunction; +} + +PlatformDepMethodsHolder +makePlatformDepMethodsHolder(RCTBridge *bridge, REANodesManager *nodesManager, REAModule *reaModule) +{ + auto requestRender = makeRequestRender(nodesManager); + +#ifdef RCT_NEW_ARCH_ENABLED + auto synchronouslyUpdateUIPropsFunction = makeSynchronouslyUpdateUIPropsFunction(nodesManager); +#endif // RCT_NEW_ARCH_ENABLED + +#ifdef RCT_NEW_ARCH_ENABLED + // nothing +#else + RCTUIManager *uiManager = nodesManager.uiManager; + auto updatePropsFunction = makeUpdatePropsFunction(reaModule); + + auto measureFunction = makeMeasureFunction(uiManager); + + auto scrollToFunction = makeScrollToFunction(uiManager); + + auto dispatchCommandFunction = makeDispatchCommandFunction(uiManager); + +#endif // RCT_NEW_ARCH_ENABLED + + auto getAnimationTimestamp = makeGetAnimationTimestamp(); + + auto setGestureStateFunction = makeSetGestureStateFunction(bridge); + +#ifdef RCT_NEW_ARCH_ENABLED + + auto progressLayoutAnimation = makeProgressLayoutAnimation(reaModule); + + auto endLayoutAnimation = makeEndLayoutAnimation(reaModule); + +#else + // Layout Animations start + + auto progressLayoutAnimation = makeProgressLayoutAnimation(reaModule); + + auto endLayoutAnimation = makeEndLayoutAnimation(reaModule); + + auto configurePropsFunction = makeConfigurePropsFunction(reaModule); + + // Layout Animations end +#endif + + auto maybeFlushUIUpdatesQueueFunction = makeMaybeFlushUIUpdatesQueueFunction(nodesManager); + + ReanimatedSensorContainer *reanimatedSensorContainer = [[ReanimatedSensorContainer alloc] init]; + + auto registerSensorFunction = makeRegisterSensorFunction(reanimatedSensorContainer); + + auto unregisterSensorFunction = makeUnregisterSensorFunction(reanimatedSensorContainer); + + REAKeyboardEventObserver *keyboardObserver = [[REAKeyboardEventObserver alloc] init]; + + auto subscribeForKeyboardEventsFunction = makeSubscribeForKeyboardEventsFunction(keyboardObserver); + + auto unsubscribeFromKeyboardEventsFunction = makeUnsubscribeFromKeyboardEventsFunction(keyboardObserver); + // end keyboard events + +#ifdef RCT_NEW_ARCH_ENABLED + // nothing +#else + auto obtainPropFunction = makeObtainPropFunction(reaModule); +#endif + + PlatformDepMethodsHolder platformDepMethodsHolder = { + requestRender, +#ifdef RCT_NEW_ARCH_ENABLED + synchronouslyUpdateUIPropsFunction, +#else + updatePropsFunction, + scrollToFunction, + dispatchCommandFunction, + measureFunction, + configurePropsFunction, + obtainPropFunction, +#endif + getAnimationTimestamp, + progressLayoutAnimation, + endLayoutAnimation, + registerSensorFunction, + unregisterSensorFunction, + setGestureStateFunction, + subscribeForKeyboardEventsFunction, + unsubscribeFromKeyboardEventsFunction, + maybeFlushUIUpdatesQueueFunction, + }; + return platformDepMethodsHolder; +} + +#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) +PlatformDepMethodsHolder makePlatformDepMethodsHolderBridgeless( + RCTModuleRegistry *moduleRegistry, + REANodesManager *nodesManager, + REAModule *reaModule) +{ + auto requestRender = makeRequestRender(nodesManager); + + auto synchronouslyUpdateUIPropsFunction = makeSynchronouslyUpdateUIPropsFunction(nodesManager); + + auto getAnimationTimestamp = makeGetAnimationTimestamp(); + + auto progressLayoutAnimation = makeProgressLayoutAnimation(reaModule); + + auto endLayoutAnimation = makeEndLayoutAnimation(reaModule); + + ReanimatedSensorContainer *reanimatedSensorContainer = [[ReanimatedSensorContainer alloc] init]; + + auto registerSensorFunction = makeRegisterSensorFunction(reanimatedSensorContainer); + + auto unregisterSensorFunction = makeUnregisterSensorFunction(reanimatedSensorContainer); + + auto setGestureStateFunction = makeSetGestureStateFunctionBridgeless(moduleRegistry); + + REAKeyboardEventObserver *keyboardObserver = [[REAKeyboardEventObserver alloc] init]; + + auto subscribeForKeyboardEventsFunction = makeSubscribeForKeyboardEventsFunction(keyboardObserver); + + auto unsubscribeFromKeyboardEventsFunction = makeUnsubscribeFromKeyboardEventsFunction(keyboardObserver); + + auto maybeFlushUIUpdatesQueueFunction = makeMaybeFlushUIUpdatesQueueFunction(nodesManager); + + PlatformDepMethodsHolder platformDepMethodsHolder = { + requestRender, + synchronouslyUpdateUIPropsFunction, + getAnimationTimestamp, + progressLayoutAnimation, + endLayoutAnimation, + registerSensorFunction, + unregisterSensorFunction, + setGestureStateFunction, + subscribeForKeyboardEventsFunction, + unsubscribeFromKeyboardEventsFunction, + maybeFlushUIUpdatesQueueFunction, + }; + return platformDepMethodsHolder; +} +#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED) + +} // namespace reanimated