diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/AnimatedCxxReactPackage.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/AnimatedCxxReactPackage.kt new file mode 100644 index 00000000000000..658a5a8ba1e330 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/AnimatedCxxReactPackage.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + + package com.facebook.react.animated + import com.facebook.jni.HybridData + import com.facebook.react.bridge.ReactApplicationContext + import com.facebook.react.runtime.cxxreactpackage.CxxReactPackage + import com.facebook.soloader.SoLoader + + public class AnimatedCxxReactPackage(public val context: ReactApplicationContext) : CxxReactPackage(null) { + init { + mHybridData = initHybrid() + } + + private external fun initHybrid(): HybridData + + public companion object { + init { + SoLoader.loadLibrary("react_animatedjni") + } + } + } \ No newline at end of file diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt index 5aae109d581980..5060d5f5753d71 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt @@ -21,6 +21,7 @@ import com.facebook.react.fabric.ComponentFactory import com.facebook.react.runtime.BindingsInstaller import com.facebook.react.runtime.JSRuntimeFactory import com.facebook.react.runtime.ReactHostImpl +import com.facebook.react.animated.AnimatedCxxReactPackage import com.facebook.react.runtime.cxxreactpackage.CxxReactPackage import com.facebook.react.runtime.hermes.HermesInstance import java.lang.Exception @@ -85,6 +86,7 @@ public object DefaultReactHost { } val defaultTmmDelegateBuilder = DefaultTurboModuleManagerDelegate.Builder() cxxReactPackageProviders.forEach { defaultTmmDelegateBuilder.addCxxReactPackage(it) } + defaultTmmDelegateBuilder.addCxxReactPackage { context -> AnimatedCxxReactPackage(context) } val defaultReactHostDelegate = DefaultReactHostDelegate( jsMainModulePath = jsMainModulePath, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index 4417da7e206a31..f03e62a370e413 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -27,7 +27,7 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun cdpInteractionMetricsEnabled(): Boolean = false - override fun cxxNativeAnimatedEnabled(): Boolean = false + override fun cxxNativeAnimatedEnabled(): Boolean = true override fun cxxNativeAnimatedRemoveJsSync(): Boolean = false @@ -189,7 +189,7 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun useShadowNodeStateOnClone(): Boolean = false - override fun useSharedAnimatedBackend(): Boolean = false + override fun useSharedAnimatedBackend(): Boolean = true override fun useTraitHiddenOnAndroid(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/soloader/OpenSourceMergedSoMapping.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/soloader/OpenSourceMergedSoMapping.kt index c8ba2b2190c4af..e3866f71ccc045 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/soloader/OpenSourceMergedSoMapping.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/soloader/OpenSourceMergedSoMapping.kt @@ -30,6 +30,7 @@ public object OpenSourceMergedSoMapping : ExternalSoMapping { "react_newarchdefaults", "reactnativeblob", "reactnativejni", + "react_animatedjni", "reactnativejni_common", "rninstance", "turbomodulejsijni", @@ -55,6 +56,7 @@ public object OpenSourceMergedSoMapping : ExternalSoMapping { "jsinspector" -> libjsinspector_so() "mapbufferjni" -> libmapbufferjni_so() "react_devsupportjni" -> libreact_devsupportjni_so() + "react_animatedjni" -> libreact_animatedjni_so() "react_featureflagsjni" -> libreact_featureflagsjni_so() "react_newarchdefaults" -> libreact_newarchdefaults_so() "reactnative" -> libreactnative_so() @@ -84,6 +86,8 @@ public object OpenSourceMergedSoMapping : ExternalSoMapping { public external fun libreact_devsupportjni_so(): Int + public external fun libreact_animatedjni_so(): Int + public external fun libreact_featureflagsjni_so(): Int public external fun libreact_newarchdefaults_so(): Int diff --git a/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt b/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt index 0e7e429d2c0e83..c43ebe8bcc1fdd 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt +++ b/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt @@ -83,6 +83,8 @@ add_react_common_subdir(react/featureflags) add_react_common_subdir(react/performance/cdpmetrics) add_react_common_subdir(react/performance/timeline) add_react_common_subdir(react/renderer/animations) +add_react_common_subdir(react/renderer/animated) +add_react_common_subdir(react/renderer/animationbackend) add_react_common_subdir(react/renderer/attributedstring) add_react_common_subdir(react/renderer/componentregistry) add_react_common_subdir(react/renderer/mounting) @@ -155,6 +157,7 @@ add_react_android_subdir(src/main/jni/react/runtime/cxxreactpackage) add_react_android_subdir(src/main/jni/react/runtime/jni) add_react_android_subdir(src/main/jni/react/runtime/hermes/jni) add_react_android_subdir(src/main/jni/react/devsupport) +add_react_android_subdir(src/main/jni/react/animated) # SoMerging Utils include(${REACT_ANDROID_DIR}/src/main/jni/first-party/jni-lib-merge/SoMerging-utils.cmake) @@ -201,6 +204,9 @@ add_library(reactnative $ $ $ + $ + $ + $ $ $ $ @@ -294,6 +300,9 @@ target_include_directories(reactnative $ $ $ + $ + $ + $ $ $ $ diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/animated/AnimatedCxxReactPackage.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/animated/AnimatedCxxReactPackage.cpp new file mode 100644 index 00000000000000..de49b5492effaf --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/jni/react/animated/AnimatedCxxReactPackage.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +#include "AnimatedCxxReactPackage.h" +#include +#include + +namespace facebook::react { + +AnimatedCxxReactPackage::AnimatedCxxReactPackage( + jni::alias_ref jobj) {} + +jni::local_ref +AnimatedCxxReactPackage::initHybrid( + jni::alias_ref jobj) { + return makeCxxInstance(jobj); +} + +void AnimatedCxxReactPackage::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", AnimatedCxxReactPackage::initHybrid), + }); +} + +std::shared_ptr AnimatedCxxReactPackage::getModule( + const std::string& moduleName, + const std::shared_ptr& jsInvoker) { + if (moduleName == "NativeAnimatedModule") { + return std::make_shared( + jsInvoker, getNativeAnimatedNodesManagerProvider()); + } + + return nullptr; +} + +std::shared_ptr +AnimatedCxxReactPackage::getNativeAnimatedNodesManagerProvider() { + if (auto provider = nativeAnimatedNodesManagerProvider_.lock()) { + return provider; + } + + auto nativeAnimatedNodesManagerProvider = + std::make_shared(nullptr, nullptr); + + nativeAnimatedNodesManagerProvider_ = nativeAnimatedNodesManagerProvider; + return nativeAnimatedNodesManagerProvider; +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/animated/AnimatedCxxReactPackage.h b/packages/react-native/ReactAndroid/src/main/jni/react/animated/AnimatedCxxReactPackage.h new file mode 100644 index 00000000000000..61b5eff3af7dcc --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/jni/react/animated/AnimatedCxxReactPackage.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook::react { +class AnimatedCxxReactPackage + : public jni::HybridClass { + public: + static constexpr auto kJavaDescriptor = + "Lcom/facebook/react/animated/AnimatedCxxReactPackage;"; + + static jni::local_ref initHybrid( + jni::alias_ref + jobj); + + static void registerNatives(); + + AnimatedCxxReactPackage( + jni::alias_ref jobj); + + std::shared_ptr getModule( + const std::string& moduleName, + const std::shared_ptr& jsInvoker) override; + + private: + std::weak_ptr + nativeAnimatedNodesManagerProvider_; + + std::shared_ptr + getNativeAnimatedNodesManagerProvider(); + + friend HybridBase; + using HybridBase::HybridBase; +}; +} // namespace facebook::react diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/animated/CMakeLists.txt b/packages/react-native/ReactAndroid/src/main/jni/react/animated/CMakeLists.txt new file mode 100644 index 00000000000000..3748866985a98b --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/jni/react/animated/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +cmake_minimum_required(VERSION 3.13) +set(CMAKE_VERBOSE_MAKEFILE on) + +include(${REACT_ANDROID_DIR}/src/main/jni/first-party/jni-lib-merge/SoMerging-utils.cmake) +include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake) + +file(GLOB react_animatedjni_SRC CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + +add_library(react_animatedjni OBJECT ${react_animatedjni_SRC}) + +target_include_directories(react_animatedjni PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_merge_so(react_animatedjni) + +target_link_libraries(react_animatedjni + fbjni + react_codegen_rncore + react_debug + react_renderer_core + react_renderer_graphics + react_renderer_mounting + react_renderer_uimanager + react_renderer_scheduler + react_renderer_animationbackend + react_cxxreactpackage + glog + folly_runtime) + +target_compile_reactnative_options(react_animatedjni PRIVATE) diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/animated/OnLoad.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/animated/OnLoad.cpp new file mode 100644 index 00000000000000..87433525952b28 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/jni/react/animated/OnLoad.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include "AnimatedCxxReactPackage.h" + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /*reserved*/) { + return facebook::jni::initialize( + vm, [] { facebook::react::AnimatedCxxReactPackage::registerNatives(); }); +} diff --git a/packages/react-native/ReactApple/RCTAnimatedModuleProvider/React-RCTAnimatedModuleProvider.podspec b/packages/react-native/ReactApple/RCTAnimatedModuleProvider/React-RCTAnimatedModuleProvider.podspec new file mode 100644 index 00000000000000..947aae0f8db166 --- /dev/null +++ b/packages/react-native/ReactApple/RCTAnimatedModuleProvider/React-RCTAnimatedModuleProvider.podspec @@ -0,0 +1,60 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json"))) +version = package['version'] + +source = { :git => 'https://github.com/facebook/react-native.git' } +if version == '1000.0.0' + # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") +else + source[:tag] = "v#{version}" +end + +header_search_paths = [ + "\"$(PODS_TARGET_SRCROOT)/../../ReactCommon\"", + "\"$(PODS_ROOT)/Headers/Public/React-Core\"", + "\"$(PODS_ROOT)/Headers/Private/Yoga\"" +] + +Pod::Spec.new do |s| + s.name = "React-RCTAnimatedModuleProvider" + s.version = version + s.summary = "Animated TurboModule provider for iOS (bridgeless)" + s.homepage = "https://reactnative.dev/" + s.license = package["license"] + s.author = "Meta Platforms, Inc. and its affiliates" + s.platforms = min_supported_versions + s.source = source + s.source_files = podspec_sources("*.{m,mm}", "**/*.h") + s.public_header_files = "*.h" + s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs" + s.header_dir = "RCTAnimatedModuleProvider" + s.pod_target_xcconfig = { + "USE_HEADERMAP" => "YES", + "CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(), + # "HEADER_SEARCH_PATHS" => '$(inherited) "$(PODS_ROOT)/Headers/Public/Yoga" "$(PODS_ROOT)/Headers/Private/Yoga"' + "HEADER_SEARCH_PATHS" => header_search_paths.join(' ') + } + +# resolve_use_frameworks(s, header_mappings_dir: "./", module_name: "React_RCTAnimatedModuleProvider") + resolve_use_frameworks(s, header_mappings_dir: "./") + + + # Dependencies needed for TurboModule types and feature flags access + s.dependency "React-Core" + s.dependency "React-jsi" + add_dependency(s, "ReactCommon", :subspec => "turbomodule/core") + add_dependency(s, "React-callinvoker") + add_dependency(s, "React-featureflags") + add_dependency(s, "React-Fabric", :subspec => "animated", :additional_framework_paths => ["react/renderer/animated"]) + add_dependency(s, "Yoga") + + add_rn_third_party_dependencies(s) + add_rncore_dependency(s) +end diff --git a/packages/react-native/ReactCommon/React-Fabric.podspec b/packages/react-native/ReactCommon/React-Fabric.podspec index 17d987fa6bd665..b27aa5e88f8798 100644 --- a/packages/react-native/ReactCommon/React-Fabric.podspec +++ b/packages/react-native/ReactCommon/React-Fabric.podspec @@ -59,6 +59,9 @@ Pod::Spec.new do |s| ss.source_files = podspec_sources("react/renderer/animated/**/*.{m,mm,cpp,h}", "react/renderer/animated/**/*.{h}") ss.exclude_files = "react/renderer/animated/tests" ss.header_dir = "react/renderer/animated" + ss.pod_target_xcconfig = { + "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) RN_USE_ANIMATION_BACKEND=1", + } end s.subspec "animations" do |ss| diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 34f17523df22d0..2989ca415386f5 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -36,7 +36,7 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { } bool cxxNativeAnimatedEnabled() override { - return false; + return true; } bool cxxNativeAnimatedRemoveJsSync() override { @@ -360,7 +360,7 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { } bool useSharedAnimatedBackend() override { - return false; + return true; } bool useTraitHiddenOnAndroid() override { diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm index 0f11ef114b9fbf..5ee73cbded270a 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm @@ -15,6 +15,7 @@ #import +#import #import #import #import @@ -30,6 +31,7 @@ #import #import #import +#import using namespace facebook; using namespace facebook::react; @@ -214,6 +216,7 @@ @implementation RCTTurboModuleManager { RCTBridgeProxy *_bridgeProxy; RCTBridgeModuleDecorator *_bridgeModuleDecorator; RCTDevMenuConfigurationDecorator *_devMenuConfigurationDecorator; + RCTAnimatedModuleProvider *animatedModuleProvider; dispatch_queue_t _sharedModuleQueue; } @@ -257,6 +260,10 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge _legacyEagerlyRegisteredModuleClasses = legacyEagerlyRegisteredModuleClasses; } + if (ReactNativeFeatureFlags::cxxNativeAnimatedEnabled()) { + animatedModuleProvider = [RCTAnimatedModuleProvider new]; + } + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bridgeWillInvalidateModules:) name:RCTBridgeWillInvalidateModulesNotification @@ -353,6 +360,12 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy return turboModule; } + if (ReactNativeFeatureFlags::cxxNativeAnimatedEnabled()) { + if (auto module = [animatedModuleProvider getTurboModule:moduleName jsInvoker:_jsInvoker]) { + return module; + } + } + /** * Step 2: Look for platform-specific modules. */ diff --git a/packages/react-native/scripts/react_native_pods.rb b/packages/react-native/scripts/react_native_pods.rb index 75b21b4861366e..e6994d6620e5af 100644 --- a/packages/react-native/scripts/react_native_pods.rb +++ b/packages/react-native/scripts/react_native_pods.rb @@ -154,6 +154,7 @@ def use_react_native! ( pod 'React-jsi', :path => "#{prefix}/ReactCommon/jsi" pod 'RCTSwiftUI', :path => "#{prefix}/ReactApple/RCTSwiftUI" pod 'RCTSwiftUIWrapper', :path => "#{prefix}/ReactApple/RCTSwiftUIWrapper" + pod 'React-RCTAnimatedModuleProvider', :path => "#{prefix}/ReactApple/RCTAnimatedModuleProvider" if hermes_enabled setup_hermes!(:react_native_path => prefix)