chore: update map

This commit is contained in:
2026-02-19 08:55:45 +01:00
parent 3308adf1ea
commit 900c54c20b
1845 changed files with 3677 additions and 84269 deletions
+292 -75
View File
@@ -1,102 +1,319 @@
project(Reanimated)
cmake_minimum_required(VERSION 3.8)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set (CMAKE_VERBOSE_MAKEFILE ON)
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 73)
set (CMAKE_CXX_STANDARD 20)
else()
set (CMAKE_CXX_STANDARD 17)
endif()
set(CMAKE_CXX_STANDARD 20)
# default CMAKE_CXX_FLAGS: "-g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-protector-all"
# default CMAKE_CXX_FLAGS: "-g -DANDROID -fdata-sections -ffunction-sections
# -funwind-tables -fstack-protector-strong -no-canonical-prefixes
# -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-protector-all"
include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake")
add_compile_options(${folly_FLAGS})
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake")
add_compile_options(${folly_FLAGS})
else()
set(folly_FLAGS "-DFOLLY_NO_CONFIG=1 -DFOLLY_HAVE_CLOCK_GETTIME=1 -DFOLLY_HAVE_MEMRCHR=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_MOBILE=1 -DFOLLY_HAVE_RECVMMSG=1 -DFOLLY_HAVE_PTHREAD=1")
string(APPEND CMAKE_CXX_FLAGS " ${folly_FLAGS}")
endif()
string(
APPEND
CMAKE_CXX_FLAGS
" -DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}\
-DREANIMATED_VERSION=${REANIMATED_VERSION}\
-DREANIMATED_FEATURE_FLAGS=\"${REANIMATED_FEATURE_FLAGS}\"")
string(APPEND CMAKE_CXX_FLAGS " -fno-omit-frame-pointer -fstack-protector-all")
# flags to optimize the binary size
string(APPEND CMAKE_CXX_FLAGS " -fvisibility=hidden -ffunction-sections -fdata-sections")
string(APPEND CMAKE_CXX_FLAGS " -DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION} -DREANIMATED_VERSION=${REANIMATED_VERSION} -DHERMES_ENABLE_DEBUGGER=${HERMES_ENABLE_DEBUGGER}")
if(${REANIMATED_PROFILING})
string(APPEND CMAKE_CXX_FLAGS " -DREANIMATED_PROFILING")
string(APPEND CMAKE_CXX_FLAGS " -fexceptions -fno-omit-frame-pointer -frtti -fstack-protector-all -std=c++${CMAKE_CXX_STANDARD} -Wall -Werror")
if(${IS_NEW_ARCHITECTURE_ENABLED})
string(APPEND CMAKE_CXX_FLAGS " -DRCT_NEW_ARCH_ENABLED")
endif()
if(${IS_REANIMATED_EXAMPLE_APP})
string(APPEND CMAKE_CXX_FLAGS " -DIS_REANIMATED_EXAMPLE_APP -Wpedantic")
string(APPEND CMAKE_CXX_FLAGS " -DIS_REANIMATED_EXAMPLE_APP")
endif()
if(NOT ${CMAKE_BUILD_TYPE} MATCHES "Debug")
string(APPEND CMAKE_CXX_FLAGS " -DNDEBUG")
string(APPEND CMAKE_CXX_FLAGS " -DNDEBUG")
endif()
set(BUILD_DIR "${CMAKE_SOURCE_DIR}/build")
set(ANDROID_CPP_DIR "${CMAKE_SOURCE_DIR}/src/main/cpp")
set(COMMON_CPP_DIR "${CMAKE_SOURCE_DIR}/../Common/cpp")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
file(GLOB_RECURSE REANIMATED_COMMON_CPP_SOURCES CONFIGURE_DEPENDS
"${COMMON_CPP_DIR}/reanimated/*.cpp")
file(GLOB_RECURSE REANIMATED_ANDROID_CPP_SOURCES CONFIGURE_DEPENDS
"${ANDROID_CPP_DIR}/reanimated/*.cpp")
set (PACKAGE_NAME "reanimated")
set (BUILD_DIR ${CMAKE_SOURCE_DIR}/build)
set (SRC_DIR ${CMAKE_SOURCE_DIR}/src)
set (COMMON_SRC_DIR "${CMAKE_SOURCE_DIR}/../Common")
find_package(fbjni REQUIRED CONFIG)
find_package(ReactAndroid REQUIRED CONFIG)
file(GLOB_RECURSE SOURCES_COMMON CONFIGURE_DEPENDS "${COMMON_SRC_DIR}/cpp/**.cpp")
file(GLOB_RECURSE SOURCES_ANDROID CONFIGURE_DEPENDS "${SRC_DIR}/main/cpp/**.cpp")
add_library(reanimated SHARED ${REANIMATED_COMMON_CPP_SOURCES}
${REANIMATED_ANDROID_CPP_SOURCES})
if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 80)
include(
"${REACT_NATIVE_DIR}/ReactCommon/cmake-utils/react-native-flags.cmake")
target_compile_reactnative_options(reanimated PUBLIC)
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
# Consume shared libraries and headers from prefabs
find_package(fbjni REQUIRED CONFIG)
find_package(ReactAndroid REQUIRED CONFIG)
if(${JS_RUNTIME} STREQUAL "hermes")
find_package(hermes-engine REQUIRED CONFIG)
endif()
else()
string(APPEND CMAKE_CXX_FLAGS
" -fexceptions -frtti -std=c++${CMAKE_CXX_STANDARD} -Wall -Werror")
# Consume shared libraries from found .so files
if(${IS_NEW_ARCHITECTURE_ENABLED})
message(FATAL_ERROR "not supported")
else()
set (RN_SO_DIR "${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/first-party/react/jni")
set (FBJNI_HEADERS_DIR "${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/first-party/fbjni/headers")
endif()
file (GLOB LIBRN_DIR "${RN_SO_DIR}/${ANDROID_ABI}")
endif()
target_include_directories(
reanimated
PRIVATE "${COMMON_CPP_DIR}"
"${ANDROID_CPP_DIR}"
"${REACT_NATIVE_DIR}/ReactCommon"
"${REACT_NATIVE_DIR}/ReactCommon/yoga"
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/react/turbomodule"
"${REACT_NATIVE_DIR}/ReactCommon/callinvoker"
"${REACT_NATIVE_DIR}/ReactCommon/runtimeexecutor"
"${REACT_NATIVE_DIR}/ReactCommon/jsiexecutor"
"${REACT_NATIVE_DIR}/ReactCommon/react/renderer/graphics/platform/cxx"
"${REACT_NATIVE_WORKLETS_DIR}/Common/cpp"
"${REACT_NATIVE_WORKLETS_DIR}/android/src/main/cpp")
set_target_properties(reanimated PROPERTIES LINKER_LANGUAGE CXX)
if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
set(BUILD_TYPE "debug")
else()
set(BUILD_TYPE "release")
endif()
add_library(worklets SHARED IMPORTED)
set_target_properties(
worklets
PROPERTIES
IMPORTED_LOCATION
"${REACT_NATIVE_WORKLETS_DIR}/android/build/intermediates/cmake/${BUILD_TYPE}/obj/${ANDROID_ABI}/libworklets.so"
add_library(
${PACKAGE_NAME}
SHARED
${SOURCES_COMMON}
${SOURCES_ANDROID}
${INCLUDE_JSI_CPP}
${INCLUDE_JSIDYNAMIC_CPP}
)
set_target_properties(reanimated PROPERTIES LINKER_LANGUAGE CXX)
# remove dead code sections
set_target_properties(reanimated PROPERTIES LINK_FLAGS "-Wl,--gc-sections")
# includes
target_link_libraries(reanimated log ReactAndroid::jsi fbjni::fbjni android
worklets)
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${COMMON_SRC_DIR}/cpp/AnimatedSensor"
"${COMMON_SRC_DIR}/cpp/Fabric"
"${COMMON_SRC_DIR}/cpp/hidden_headers"
"${COMMON_SRC_DIR}/cpp/LayoutAnimations"
"${COMMON_SRC_DIR}/cpp/NativeModules"
"${COMMON_SRC_DIR}/cpp/ReanimatedRuntime"
"${COMMON_SRC_DIR}/cpp/Registries"
"${COMMON_SRC_DIR}/cpp/SharedItems"
"${COMMON_SRC_DIR}/cpp/Tools"
"${SRC_DIR}/main/cpp"
)
if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76)
target_link_libraries(reanimated ReactAndroid::reactnative)
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/react/turbomodule"
"${REACT_NATIVE_DIR}/ReactCommon"
"${REACT_NATIVE_DIR}/ReactCommon/callinvoker"
"${REACT_NATIVE_DIR}/ReactCommon/react/renderer/graphics/platform/cxx"
"${REACT_NATIVE_DIR}/ReactCommon/runtimeexecutor"
"${REACT_NATIVE_DIR}/ReactCommon/yoga"
)
else()
target_link_libraries(reanimated ReactAndroid::react_nativemodule_core)
file(GLOB LIBFBJNI_INCLUDE_DIR ${FBJNI_HEADERS_DIR})
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${LIBFBJNI_INCLUDE_DIR}"
"${BUILD_DIR}/third-party-ndk/boost/boost_${BOOST_VERSION}"
"${BUILD_DIR}/third-party-ndk/double-conversion"
"${BUILD_DIR}/third-party-ndk/folly"
"${BUILD_DIR}/third-party-ndk/glog/exported"
"${REACT_NATIVE_DIR}/React"
"${REACT_NATIVE_DIR}/React/Base"
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni"
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni"
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/java/com/facebook/react/fabric/jni"
"${REACT_NATIVE_DIR}/ReactCommon"
"${REACT_NATIVE_DIR}/ReactCommon/callinvoker"
"${REACT_NATIVE_DIR}/ReactCommon/jsi"
"${REACT_NATIVE_DIR}/ReactCommon/hermes"
"${REACT_NATIVE_DIR}/ReactCommon/react/renderer/graphics/platform/cxx"
"${REACT_NATIVE_DIR}/ReactCommon/runtimeexecutor"
"${REACT_NATIVE_DIR}/ReactCommon/turbomodule/core"
"${REACT_NATIVE_DIR}/ReactCommon/turbomodule"
"${REACT_NATIVE_DIR}/ReactCommon/yoga"
)
endif()
# build shared lib
set_target_properties(${PACKAGE_NAME} PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(
${PACKAGE_NAME}
log
android
)
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
target_link_libraries(
${PACKAGE_NAME}
ReactAndroid::folly_runtime
ReactAndroid::glog
ReactAndroid::jsi
ReactAndroid::reactnativejni
fbjni::fbjni
)
else()
find_library(
JSI_LIB
jsi
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
find_library(
GLOG_LIB
glog
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
find_library(
FBJNI_LIB
fbjni
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
if(${REACT_NATIVE_MINOR_VERSION} LESS 69)
find_library(
FOLLY_LIB
folly_json
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
else()
find_library(
FOLLY_LIB
folly_runtime
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
endif()
find_library(
REACTNATIVEJNI_LIB
reactnativejni
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
target_link_libraries(
${PACKAGE_NAME}
${JSI_LIB}
${GLOG_LIB}
${FBJNI_LIB}
${FOLLY_LIB}
${REACTNATIVEJNI_LIB}
)
endif()
if(${JS_RUNTIME} STREQUAL "hermes")
string(APPEND CMAKE_CXX_FLAGS " -DJS_RUNTIME_HERMES=1")
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
# From prefab from module `com.facebook.react:hermes-android`
set(HERMES_LIB hermes-engine::libhermes)
elseif(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 69)
# Bundled Hermes from module `com.facebook.react:hermes-engine` or project `:ReactAndroid:hermes-engine`
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${JS_RUNTIME_DIR}/API"
"${JS_RUNTIME_DIR}/public"
)
set(HERMES_LIB "${BUILD_DIR}/third-party-ndk/hermes/jni/${ANDROID_ABI}/libhermes.so")
else()
# From `hermes-engine` npm package
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${JS_RUNTIME_DIR}/android/include"
)
set(HERMES_LIB "${BUILD_DIR}/third-party-ndk/hermes/jni/${ANDROID_ABI}/libhermes.so")
endif()
target_link_libraries(
${PACKAGE_NAME}
${HERMES_LIB}
)
if (${HERMES_ENABLE_DEBUGGER})
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
set(HERMES_EXECUTOR_LIB ReactAndroid::hermes_executor)
else()
find_library(
HERMES_EXECUTOR_LIB
hermes-executor-debug
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
endif()
if(${REACT_NATIVE_MINOR_VERSION} LESS_EQUAL 67)
find_library(
HERMES_INSPECTOR_LIB
hermes-inspector
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
target_link_libraries(
${PACKAGE_NAME}
${HERMES_INSPECTOR_LIB}
)
endif()
target_link_libraries(
${PACKAGE_NAME}
${HERMES_EXECUTOR_LIB}
)
endif()
elseif(${JS_RUNTIME} STREQUAL "v8")
string(APPEND CMAKE_CXX_FLAGS " -DJS_RUNTIME_V8=1")
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${JS_RUNTIME_DIR}/src"
)
file (GLOB V8_SO_DIR "${JS_RUNTIME_DIR}/android/build/intermediates/library_jni/*/jni/${ANDROID_ABI}")
find_library(
V8EXECUTOR_LIB
v8executor
PATHS ${V8_SO_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
target_link_libraries(
${PACKAGE_NAME}
${V8EXECUTOR_LIB}
)
elseif(${JS_RUNTIME} STREQUAL "jsc")
string(APPEND CMAKE_CXX_FLAGS " -DJS_RUNTIME_JSC=1")
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
set(JSEXECUTOR_LIB ReactAndroid::jscexecutor)
else()
find_library(
JSEXECUTOR_LIB
jscexecutor
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
endif()
target_link_libraries(${PACKAGE_NAME} ${JSEXECUTOR_LIB})
else()
message(FATAL_ERROR "Unknown JS runtime ${JS_RUNTIME}.")
endif()
if(${IS_NEW_ARCHITECTURE_ENABLED})
target_link_libraries(
${PACKAGE_NAME}
ReactAndroid::fabricjni
ReactAndroid::react_debug
ReactAndroid::react_render_core
ReactAndroid::react_render_mounting
ReactAndroid::react_render_scheduler
ReactAndroid::react_render_uimanager
ReactAndroid::rrc_view
)
endif()
# Resolves "CMake Warning: Manually-specified variables were not used by the project"
# when any of the following variables is not used in some build configuration.
set (ignoreMe "${JS_RUNTIME_DIR}")
set (ignoreMe "${BOOST_VERSION}")
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
@@ -1,3 +1,2 @@
-keep class com.swmansion.reanimated.** { *; }
-keep class com.facebook.react.turbomodule.** { *; }
-keep class com.facebook.react.fabric.** { *; }
@@ -3,7 +3,7 @@ apply plugin: 'com.diffplug.spotless'
spotless {
java {
target 'src/**/*.java'
target 'src/main/java/**/*.java'
googleJavaFormat()
}
}
@@ -1,2 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"/>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.swmansion.reanimated">
</manifest>
@@ -1,37 +0,0 @@
#pragma once
#include <fbjni/fbjni.h>
#include <utility>
namespace reanimated {
using namespace facebook;
using namespace facebook::jni;
class AnimationFrameCallback : public HybridClass<AnimationFrameCallback> {
public:
static auto constexpr kJavaDescriptor =
"Lcom/swmansion/reanimated/nativeProxy/AnimationFrameCallback;";
void onAnimationFrame(double timestampMs) {
callback_(timestampMs);
}
static void registerNatives() {
javaClassStatic()->registerNatives({
makeNativeMethod(
"onAnimationFrame", AnimationFrameCallback::onAnimationFrame),
});
}
private:
friend HybridBase;
explicit AnimationFrameCallback(std::function<void(double)> callback)
: callback_(std::move(callback)) {}
std::function<void(double)> callback_;
};
} // namespace reanimated
@@ -1,48 +0,0 @@
#pragma once
#include <reanimated/Tools/ReanimatedSystraceSection.h>
#include <fbjni/fbjni.h>
#include <react/jni/WritableNativeMap.h>
#include <utility>
namespace reanimated {
using namespace facebook;
using namespace facebook::jni;
class EventHandler : public HybridClass<EventHandler> {
public:
static auto constexpr kJavaDescriptor =
"Lcom/swmansion/reanimated/nativeProxy/EventHandler;";
void receiveEvent(
jni::alias_ref<JString> eventKey,
jint emitterReactTag,
jni::alias_ref<react::WritableMap> event) {
ReanimatedSystraceSection s("EventHandler::receiveEvent");
handler_(eventKey, emitterReactTag, event);
}
static void registerNatives() {
javaClassStatic()->registerNatives({
makeNativeMethod("receiveEvent", EventHandler::receiveEvent),
});
}
private:
friend HybridBase;
explicit EventHandler(std::function<void(
jni::alias_ref<JString>,
jint emitterReactTag,
jni::alias_ref<react::WritableMap>)> handler)
: handler_(std::move(handler)) {}
std::function<
void(jni::alias_ref<JString>, jint, jni::alias_ref<react::WritableMap>)>
handler_;
};
} // namespace reanimated
@@ -1,36 +0,0 @@
#pragma once
#include <fbjni/fbjni.h>
#include <utility>
namespace reanimated {
using namespace facebook;
using namespace facebook::jni;
class KeyboardWorkletWrapper : public HybridClass<KeyboardWorkletWrapper> {
public:
static auto constexpr kJavaDescriptor =
"Lcom/swmansion/reanimated/keyboard/KeyboardWorkletWrapper;";
void invoke(int keyboardState, int height) {
callback_(keyboardState, height);
}
static void registerNatives() {
javaClassStatic()->registerNatives({
makeNativeMethod("invoke", KeyboardWorkletWrapper::invoke),
});
}
private:
friend HybridBase;
explicit KeyboardWorkletWrapper(std::function<void(int, int)> callback)
: callback_(std::move(callback)) {}
std::function<void(int, int)> callback_;
};
} // namespace reanimated
@@ -1,324 +0,0 @@
#include <reanimated/RuntimeDecorators/RNRuntimeDecorator.h>
#include <reanimated/Tools/PlatformDepMethodsHolder.h>
#include <reanimated/Tools/ReanimatedVersion.h>
#include <reanimated/android/AnimationFrameCallback.h>
#include <reanimated/android/EventHandler.h>
#include <reanimated/android/KeyboardWorkletWrapper.h>
#include <reanimated/android/NativeProxy.h>
#include <reanimated/android/SensorSetter.h>
#include <react/fabric/Binding.h>
namespace reanimated {
using namespace facebook;
using namespace react;
NativeProxy::NativeProxy(
jni::alias_ref<NativeProxy::javaobject> jThis,
const std::shared_ptr<WorkletsModuleProxy> &workletsModuleProxy,
jsi::Runtime *rnRuntime,
const std::shared_ptr<facebook::react::CallInvoker> &jsCallInvoker,
jni::alias_ref<facebook::react::JFabricUIManager::javaobject>
fabricUIManager)
: javaPart_(jni::make_global(jThis)),
rnRuntime_(rnRuntime),
workletsModuleProxy_(workletsModuleProxy),
reanimatedModuleProxy_(std::make_shared<ReanimatedModuleProxy>(
workletsModuleProxy,
*rnRuntime,
jsCallInvoker,
getPlatformDependentMethods(),
getIsReducedMotion())) {
#ifndef NDEBUG
checkJavaVersion();
injectCppVersion();
#endif // NDEBUG
reanimatedModuleProxy_->init(getPlatformDependentMethods());
const auto &uiManager =
fabricUIManager->getBinding()->getScheduler()->getUIManager();
reanimatedModuleProxy_->initializeFabric(uiManager);
registerEventHandler();
// removed temporarily, event listener mechanism needs to be fixed on RN side
// eventListener_ = std::make_shared<EventListener>(
// [reanimatedModuleProxy,
// getAnimationTimestamp](const RawEvent &rawEvent) {
// return reanimatedModuleProxy->handleRawEvent(
// rawEvent, getAnimationTimestamp());
// });
// reactScheduler_ = binding->getScheduler();
// reactScheduler_->addEventListener(eventListener_);
}
NativeProxy::~NativeProxy() {
// removed temporary, new event listener mechanism need fix on the RN side
// reactScheduler_->removeEventListener(eventListener_);
}
jni::local_ref<NativeProxy::jhybriddata> NativeProxy::initHybrid(
jni::alias_ref<jhybridobject> jThis,
jni::alias_ref<WorkletsModule::javaobject> jWorkletsModule,
jlong jsContext,
jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>
jsCallInvokerHolder,
jni::alias_ref<facebook::react::JFabricUIManager::javaobject>
fabricUIManager) {
auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker();
auto workletsModuleProxy = jWorkletsModule->cthis()->getWorkletsModuleProxy();
return makeCxxInstance(
jThis,
workletsModuleProxy,
(jsi::Runtime *)jsContext,
jsCallInvoker,
fabricUIManager);
}
#ifndef NDEBUG
void NativeProxy::checkJavaVersion() {
std::string javaVersion;
try {
javaVersion =
getJniMethod<jstring()>("getReanimatedJavaVersion")(javaPart_.get())
->toStdString();
} catch (std::exception &) {
throw std::runtime_error(
std::string(
"[Reanimated] C++ side failed to resolve Java code version.\n") +
"See `https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#c-side-failed-to-resolve-java-code-version` for more details.");
}
auto cppVersion = getReanimatedCppVersion();
if (cppVersion != javaVersion) {
throw std::runtime_error(
std::string(
"[Reanimated] Mismatch between C++ code version and Java code version (") +
cppVersion + " vs. " + javaVersion + " respectively).\n" +
"See `https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#mismatch-between-c-code-version-and-java-code-version` for more details.");
}
}
void NativeProxy::injectCppVersion() {
auto cppVersion = getReanimatedCppVersion();
try {
static const auto method =
getJniMethod<void(jni::local_ref<JString>)>("setCppVersion");
method(javaPart_.get(), make_jstring(cppVersion));
} catch (std::exception &) {
throw std::runtime_error(
std::string(
"[Reanimated] C++ side failed to resolve Java code version (injection).\n") +
"See `https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#c-side-failed-to-resolve-java-code-version` for more details.");
}
}
#endif // NDEBUG
void NativeProxy::installJSIBindings() {
jsi::Runtime &rnRuntime = *rnRuntime_;
RNRuntimeDecorator::decorate(
rnRuntime,
workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(),
reanimatedModuleProxy_);
}
bool NativeProxy::isAnyHandlerWaitingForEvent(
const std::string &eventName,
const int emitterReactTag) {
return reanimatedModuleProxy_->isAnyHandlerWaitingForEvent(
eventName, emitterReactTag);
}
void NativeProxy::performOperations() {
reanimatedModuleProxy_->performOperations();
}
bool NativeProxy::getIsReducedMotion() {
static const auto method = getJniMethod<jboolean()>("getIsReducedMotion");
return method(javaPart_.get());
}
void NativeProxy::registerNatives() {
registerHybrid(
{makeNativeMethod("initHybrid", NativeProxy::initHybrid),
makeNativeMethod("installJSIBindings", NativeProxy::installJSIBindings),
makeNativeMethod(
"isAnyHandlerWaitingForEvent",
NativeProxy::isAnyHandlerWaitingForEvent),
makeNativeMethod("performOperations", NativeProxy::performOperations),
makeNativeMethod("invalidateCpp", NativeProxy::invalidateCpp)});
}
void NativeProxy::requestRender(std::function<void(double)> onRender) {
static const auto method =
getJniMethod<void(AnimationFrameCallback::javaobject)>("requestRender");
method(
javaPart_.get(),
AnimationFrameCallback::newObjectCxxArgs(std::move(onRender)).get());
}
void NativeProxy::registerEventHandler() {
auto eventHandler = bindThis(&NativeProxy::handleEvent);
static const auto method =
getJniMethod<void(EventHandler::javaobject)>("registerEventHandler");
method(
javaPart_.get(),
EventHandler::newObjectCxxArgs(std::move(eventHandler)).get());
}
void NativeProxy::maybeFlushUIUpdatesQueue() {
// Module might be already destroyed.
if (!javaPart_) {
return;
}
static const auto method = getJniMethod<void()>("maybeFlushUIUpdatesQueue");
method(javaPart_.get());
}
void NativeProxy::synchronouslyUpdateUIProps(
const std::vector<int> &intBuffer,
const std::vector<double> &doubleBuffer) {
static const auto method = getJniMethod<void(
jni::alias_ref<jni::JArrayInt>, jni::alias_ref<jni::JArrayDouble>)>(
"synchronouslyUpdateUIProps");
auto jArrayInt = jni::JArrayInt::newArray(intBuffer.size());
auto jArrayDouble = jni::JArrayDouble::newArray(doubleBuffer.size());
jArrayInt->setRegion(0, intBuffer.size(), intBuffer.data());
jArrayDouble->setRegion(0, doubleBuffer.size(), doubleBuffer.data());
method(javaPart_.get(), jArrayInt, jArrayDouble);
}
int NativeProxy::registerSensor(
int sensorType,
int interval,
int,
std::function<void(double[], int)> setter) {
static const auto method =
getJniMethod<int(int, int, SensorSetter::javaobject)>("registerSensor");
return method(
javaPart_.get(),
sensorType,
interval,
SensorSetter::newObjectCxxArgs(std::move(setter)).get());
}
void NativeProxy::unregisterSensor(int sensorId) {
static const auto method = getJniMethod<void(int)>("unregisterSensor");
method(javaPart_.get(), sensorId);
}
void NativeProxy::setGestureState(int handlerTag, int newState) {
static const auto method = getJniMethod<void(int, int)>("setGestureState");
method(javaPart_.get(), handlerTag, newState);
}
int NativeProxy::subscribeForKeyboardEvents(
std::function<void(int, int)> callback,
bool isStatusBarTranslucent,
bool isNavigationBarTranslucent) {
static const auto method =
getJniMethod<int(KeyboardWorkletWrapper::javaobject, bool, bool)>(
"subscribeForKeyboardEvents");
return method(
javaPart_.get(),
KeyboardWorkletWrapper::newObjectCxxArgs(std::move(callback)).get(),
isStatusBarTranslucent,
isNavigationBarTranslucent);
}
void NativeProxy::unsubscribeFromKeyboardEvents(int listenerId) {
static const auto method =
getJniMethod<void(int)>("unsubscribeFromKeyboardEvents");
method(javaPart_.get(), listenerId);
}
double NativeProxy::getAnimationTimestamp() {
static const auto method = getJniMethod<jlong()>("getAnimationTimestamp");
jlong output = method(javaPart_.get());
return static_cast<double>(output);
}
void NativeProxy::handleEvent(
jni::alias_ref<JString> eventName,
jint emitterReactTag,
jni::alias_ref<react::WritableMap> event) {
// handles RCTEvents from RNGestureHandler
if (event.get() == nullptr) {
// Ignore events with null payload.
return;
}
// TODO: convert event directly to jsi::Value without JSON serialization
std::string eventAsString;
try {
eventAsString = event->toString();
} catch (std::exception &) {
// Events from other libraries may contain NaN or INF values which
// cannot be represented in JSON. See
// https://github.com/software-mansion/react-native-reanimated/issues/1776
// for details.
return;
}
std::string eventJSON = eventAsString;
if (eventJSON == "null") {
return;
}
jsi::Runtime &rt =
workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime();
jsi::Value payload;
try {
payload = jsi::Value::createFromJsonUtf8(
rt, reinterpret_cast<uint8_t *>(&eventJSON[0]), eventJSON.size());
} catch (std::exception &) {
// Ignore events with malformed JSON payload.
return;
}
reanimatedModuleProxy_->handleEvent(
eventName->toString(), emitterReactTag, payload, getAnimationTimestamp());
}
PlatformDepMethodsHolder NativeProxy::getPlatformDependentMethods() {
auto getAnimationTimestamp = bindThis(&NativeProxy::getAnimationTimestamp);
auto requestRender = bindThis(&NativeProxy::requestRender);
auto synchronouslyUpdateUIPropsFunction =
bindThis(&NativeProxy::synchronouslyUpdateUIProps);
auto registerSensorFunction = bindThis(&NativeProxy::registerSensor);
auto unregisterSensorFunction = bindThis(&NativeProxy::unregisterSensor);
auto setGestureStateFunction = bindThis(&NativeProxy::setGestureState);
auto subscribeForKeyboardEventsFunction =
bindThis(&NativeProxy::subscribeForKeyboardEvents);
auto unsubscribeFromKeyboardEventsFunction =
bindThis(&NativeProxy::unsubscribeFromKeyboardEvents);
auto maybeFlushUiUpdatesQueueFunction =
bindThis(&NativeProxy::maybeFlushUIUpdatesQueue);
return {
requestRender,
synchronouslyUpdateUIPropsFunction,
getAnimationTimestamp,
registerSensorFunction,
unregisterSensorFunction,
setGestureStateFunction,
subscribeForKeyboardEventsFunction,
unsubscribeFromKeyboardEventsFunction,
maybeFlushUiUpdatesQueueFunction,
};
}
void NativeProxy::invalidateCpp() {
workletsModuleProxy_.reset();
// cleanup all animated sensors here, since the next line resets
// the pointer and it will be too late after it
reanimatedModuleProxy_->cleanupSensors();
reanimatedModuleProxy_.reset();
javaPart_ = nullptr;
}
} // namespace reanimated
@@ -1,120 +0,0 @@
#pragma once
#include <reanimated/NativeModules/ReanimatedModuleProxy.h>
#include <worklets/android/WorkletsModule.h>
#include <ReactCommon/CallInvokerHolder.h>
#include <fbjni/fbjni.h>
#include <jsi/jsi.h>
#include <react/fabric/JFabricUIManager.h>
#include <react/jni/WritableNativeMap.h>
#include <react/renderer/scheduler/Scheduler.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace reanimated {
using namespace facebook;
using namespace facebook::jni;
class NativeProxy : public jni::HybridClass<NativeProxy>,
std::enable_shared_from_this<NativeProxy> {
public:
static auto constexpr kJavaDescriptor =
"Lcom/swmansion/reanimated/NativeProxy;";
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jhybridobject> jThis,
jni::alias_ref<WorkletsModule::javaobject> jWorkletsModule,
jlong jsContext,
jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>
jsCallInvokerHolder,
jni::alias_ref<facebook::react::JFabricUIManager::javaobject>
fabricUIManager);
static void registerNatives();
~NativeProxy();
private:
friend HybridBase;
jni::global_ref<NativeProxy::javaobject> javaPart_;
jsi::Runtime *rnRuntime_;
std::shared_ptr<WorkletsModuleProxy> workletsModuleProxy_;
std::shared_ptr<ReanimatedModuleProxy> reanimatedModuleProxy_;
#ifndef NDEBUG
void checkJavaVersion();
void injectCppVersion();
#endif // NDEBUG
// removed temporarily, event listener mechanism needs to be fixed on RN side
// std::shared_ptr<facebook::react::Scheduler> reactScheduler_;
// std::shared_ptr<EventListener> eventListener_;
void installJSIBindings();
void synchronouslyUpdateUIProps(
const std::vector<int> &intBuffer,
const std::vector<double> &doubleBuffer);
PlatformDepMethodsHolder getPlatformDependentMethods();
double getAnimationTimestamp();
bool isAnyHandlerWaitingForEvent(
const std::string &eventName,
const int emitterReactTag);
void performOperations();
bool getIsReducedMotion();
void requestRender(std::function<void(double)> onRender);
void registerEventHandler();
void maybeFlushUIUpdatesQueue();
void setGestureState(int handlerTag, int newState);
int registerSensor(
int sensorType,
int interval,
int iosReferenceFrame,
std::function<void(double[], int)> setter);
void unregisterSensor(int sensorId);
int subscribeForKeyboardEvents(
std::function<void(int, int)> callback,
bool isStatusBarTranslucent,
bool isNavigationBarTranslucent);
void unsubscribeFromKeyboardEvents(int listenerId);
void handleEvent(
jni::alias_ref<JString> eventName,
jint emitterReactTag,
jni::alias_ref<react::WritableMap> event);
/***
* Wraps a method of `NativeProxy` in a function object capturing `this`
* @tparam TReturn return type of passed method
* @tparam TParams parameter types of passed method
* @param methodPtr pointer to method to be wrapped
* @return a function object with the same signature as the method, calling
* that method on `this`
*/
template <class TReturn, class... TParams>
std::function<TReturn(TParams...)> bindThis(
TReturn (NativeProxy::*methodPtr)(TParams...)) {
// It's probably safe to pass `this` as reference here...
return [this, methodPtr](TParams &&...args) {
return (this->*methodPtr)(std::forward<TParams>(args)...);
};
}
template <class Signature>
JMethod<Signature> getJniMethod(std::string const &methodName) {
return javaPart_->getClass()->getMethod<Signature>(methodName.c_str());
}
explicit NativeProxy(
jni::alias_ref<NativeProxy::jhybridobject> jThis,
const std::shared_ptr<WorkletsModuleProxy> &workletsModuleProxy,
jsi::Runtime *rnRuntime,
const std::shared_ptr<facebook::react::CallInvoker> &jsCallInvoker,
jni::alias_ref<facebook::react::JFabricUIManager::javaobject>
fabricUIManager);
void invalidateCpp();
};
} // namespace reanimated
@@ -1,17 +0,0 @@
#include <fbjni/fbjni.h>
#include <reanimated/android/AnimationFrameCallback.h>
#include <reanimated/android/EventHandler.h>
#include <reanimated/android/KeyboardWorkletWrapper.h>
#include <reanimated/android/NativeProxy.h>
#include <reanimated/android/SensorSetter.h>
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return facebook::jni::initialize(vm, [] {
reanimated::NativeProxy::registerNatives();
reanimated::AnimationFrameCallback::registerNatives();
reanimated::EventHandler::registerNatives();
reanimated::SensorSetter::registerNatives();
reanimated::KeyboardWorkletWrapper::registerNatives();
});
}
@@ -1,42 +0,0 @@
#pragma once
#include <fbjni/fbjni.h>
#include <utility>
namespace reanimated {
using namespace facebook;
using namespace facebook::jni;
class SensorSetter : public HybridClass<SensorSetter> {
public:
static auto constexpr kJavaDescriptor =
"Lcom/swmansion/reanimated/nativeProxy/SensorSetter;";
void sensorSetter(jni::alias_ref<JArrayFloat> value, int orientationDegrees) {
size_t size = value->size();
auto elements = value->getRegion(0, size);
double array[7];
for (size_t i = 0; i < size; i++) {
array[i] = elements[i];
}
callback_(array, orientationDegrees);
}
static void registerNatives() {
javaClassStatic()->registerNatives({
makeNativeMethod("sensorSetter", SensorSetter::sensorSetter),
});
}
private:
friend HybridBase;
explicit SensorSetter(std::function<void(double[], int)> callback)
: callback_(std::move(callback)) {}
std::function<void(double[], int)> callback_;
};
} // namespace reanimated
@@ -1,25 +0,0 @@
package com.swmansion.reanimated;
import com.facebook.react.ReactApplication;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.devsupport.interfaces.DevOptionHandler;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
public class DevMenuUtils {
public static void addDevMenuOption(ReactApplicationContext context, DevOptionHandler handler) {
// In Expo, `ApplicationContext` is not an instance of `ReactApplication`
if (context.getApplicationContext() instanceof ReactApplication) {
DevSupportManager devSupportManager =
((ReactApplication) context.getApplicationContext())
.getReactHost()
.getDevSupportManager();
if (devSupportManager != null) {
devSupportManager.addCustomDevOption("Toggle slow animations (Reanimated)", handler);
} else {
throw new RuntimeException("[Reanimated] DevSupportManager is not available");
}
}
}
}
@@ -1,494 +0,0 @@
package com.swmansion.reanimated;
import android.content.ContentResolver;
import android.os.SystemClock;
import android.provider.Settings;
import androidx.annotation.OptIn;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.JavaOnlyMap;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UiThreadUtil;
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.facebook.soloader.SoLoader;
import com.swmansion.common.GestureHandlerStateManager;
import com.swmansion.reanimated.keyboard.KeyboardAnimationManager;
import com.swmansion.reanimated.keyboard.KeyboardWorkletWrapper;
import com.swmansion.reanimated.nativeProxy.AnimationFrameCallback;
import com.swmansion.reanimated.nativeProxy.EventHandler;
import com.swmansion.reanimated.nativeProxy.SensorSetter;
import com.swmansion.reanimated.sensor.ReanimatedSensorContainer;
import com.swmansion.reanimated.sensor.ReanimatedSensorType;
import com.swmansion.worklets.JSCallInvokerResolver;
import com.swmansion.worklets.WorkletsModule;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Objects;
import java.util.PrimitiveIterator;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @noinspection JavaJniMissingFunction
*/
public class NativeProxy {
static {
SoLoader.loadLibrary("reanimated");
}
protected final WorkletsModule mWorkletsModule;
protected NodesManager mNodesManager;
protected final FabricUIManager mFabricUIManager;
protected final WeakReference<ReactApplicationContext> mContext;
private final ReanimatedSensorContainer reanimatedSensorContainer;
private final GestureHandlerStateManager gestureHandlerStateManager;
private final KeyboardAnimationManager keyboardAnimationManager;
private Long firstUptime = SystemClock.uptimeMillis();
private boolean slowAnimationsEnabled = false;
private final int ANIMATIONS_DRAG_FACTOR = 10;
protected String cppVersion = null;
/**
* Invalidating concurrently could be fatal. It shouldn't happen in a normal flow, but it doesn't
* cost us much to add synchronization for extra safety.
*/
private final AtomicBoolean mInvalidated = new AtomicBoolean(false);
@DoNotStrip
@SuppressWarnings("unused")
private final HybridData mHybridData;
public @OptIn(markerClass = FrameworkAPI.class) NativeProxy(
ReactApplicationContext context, WorkletsModule workletsModule, NodesManager nodesManager) {
context.assertOnJSQueueThread();
mWorkletsModule = workletsModule;
mContext = new WeakReference<>(context);
reanimatedSensorContainer = new ReanimatedSensorContainer(mContext);
keyboardAnimationManager = new KeyboardAnimationManager(mContext);
addDevMenuOption();
GestureHandlerStateManager tempHandlerStateManager;
try {
Class<NativeModule> gestureHandlerModuleClass =
(Class<NativeModule>)
Class.forName("com.swmansion.gesturehandler.react.RNGestureHandlerModule");
tempHandlerStateManager =
(GestureHandlerStateManager) context.getNativeModule(gestureHandlerModuleClass);
} catch (ClassCastException | ClassNotFoundException e) {
tempHandlerStateManager = null;
}
gestureHandlerStateManager = tempHandlerStateManager;
mNodesManager = nodesManager;
mFabricUIManager =
(FabricUIManager) UIManagerHelper.getUIManager(context, UIManagerType.FABRIC);
CallInvokerHolderImpl callInvokerHolder = JSCallInvokerResolver.getJSCallInvokerHolder(context);
mHybridData =
initHybrid(
workletsModule,
Objects.requireNonNull(context.getJavaScriptContextHolder()).get(),
callInvokerHolder,
mFabricUIManager);
if (BuildConfig.DEBUG) {
checkCppVersion(); // injectCppVersion should be called during initHybrid above
}
}
@OptIn(markerClass = FrameworkAPI.class)
private native HybridData initHybrid(
WorkletsModule workletsModule,
long jsContext,
CallInvokerHolderImpl jsCallInvokerHolder,
FabricUIManager fabricUIManager);
public native boolean isAnyHandlerWaitingForEvent(String eventName, int emitterReactTag);
public native void performOperations();
protected native void installJSIBindings();
private native void invalidateCpp();
protected HybridData getHybridData() {
return mHybridData;
}
protected void invalidate() {
if (mInvalidated.getAndSet(true)) {
return;
}
if (mHybridData != null && mHybridData.isValid()) {
invalidateCpp();
}
}
private void toggleSlowAnimations() {
slowAnimationsEnabled = !slowAnimationsEnabled;
if (slowAnimationsEnabled) {
firstUptime = SystemClock.uptimeMillis();
}
mNodesManager.enableSlowAnimations(slowAnimationsEnabled, ANIMATIONS_DRAG_FACTOR);
mWorkletsModule.toggleSlowAnimations();
}
private void addDevMenuOption() {
// In Expo, `ApplicationContext` is not an instance of `ReactApplication`
DevMenuUtils.addDevMenuOption(mContext.get(), this::toggleSlowAnimations);
}
@DoNotStrip
public void requestRender(AnimationFrameCallback callback) {
UiThreadUtil.assertOnUiThread();
mNodesManager.postOnAnimation(callback);
}
@DoNotStrip
public String getReanimatedJavaVersion() {
return BuildConfig.REANIMATED_VERSION_JAVA;
}
@DoNotStrip
@SuppressWarnings("unused")
// It turns out it's pretty difficult to set a member of a class
// instance through JNI so we decided to use a setter instead.
protected void setCppVersion(String version) {
cppVersion = version;
}
protected void checkCppVersion() {
if (cppVersion == null) {
throw new RuntimeException(
"[Reanimated] Java side failed to resolve C++ code version. "
+ "See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#java-side-failed-to-resolve-c-code-version for more information.");
}
String javaVersion = getReanimatedJavaVersion();
if (!cppVersion.equals(javaVersion)) {
throw new RuntimeException(
"[Reanimated] Mismatch between Java code version and C++ code version ("
+ javaVersion
+ " vs. "
+ cppVersion
+ " respectively). See "
+ "https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#mismatch-between-java-code-version-and-c-code-version for more information.");
}
}
// NOTE: Keep in sync with ReanimatedModuleProxy::performOperations
private static final int CMD_START_OF_VIEW = 1;
private static final int CMD_START_OF_TRANSFORM = 2;
private static final int CMD_END_OF_TRANSFORM = 3;
private static final int CMD_END_OF_VIEW = 4;
private static final int CMD_OPACITY = 10;
private static final int CMD_ELEVATION = 11;
private static final int CMD_Z_INDEX = 12;
private static final int CMD_SHADOW_OPACITY = 13;
private static final int CMD_SHADOW_RADIUS = 14;
private static final int CMD_BACKGROUND_COLOR = 15;
private static final int CMD_COLOR = 16;
private static final int CMD_TINT_COLOR = 17;
private static final int CMD_BORDER_RADIUS = 20;
private static final int CMD_BORDER_TOP_LEFT_RADIUS = 21;
private static final int CMD_BORDER_TOP_RIGHT_RADIUS = 22;
private static final int CMD_BORDER_TOP_START_RADIUS = 23;
private static final int CMD_BORDER_TOP_END_RADIUS = 24;
private static final int CMD_BORDER_BOTTOM_LEFT_RADIUS = 25;
private static final int CMD_BORDER_BOTTOM_RIGHT_RADIUS = 26;
private static final int CMD_BORDER_BOTTOM_START_RADIUS = 27;
private static final int CMD_BORDER_BOTTOM_END_RADIUS = 28;
private static final int CMD_BORDER_START_START_RADIUS = 29;
private static final int CMD_BORDER_START_END_RADIUS = 30;
private static final int CMD_BORDER_END_START_RADIUS = 31;
private static final int CMD_BORDER_END_END_RADIUS = 32;
private static final int CMD_BORDER_COLOR = 40;
private static final int CMD_BORDER_TOP_COLOR = 41;
private static final int CMD_BORDER_BOTTOM_COLOR = 42;
private static final int CMD_BORDER_LEFT_COLOR = 43;
private static final int CMD_BORDER_RIGHT_COLOR = 44;
private static final int CMD_BORDER_START_COLOR = 45;
private static final int CMD_BORDER_END_COLOR = 46;
private static final int CMD_TRANSFORM_TRANSLATE_X = 100;
private static final int CMD_TRANSFORM_TRANSLATE_Y = 101;
private static final int CMD_TRANSFORM_SCALE = 102;
private static final int CMD_TRANSFORM_SCALE_X = 103;
private static final int CMD_TRANSFORM_SCALE_Y = 104;
private static final int CMD_TRANSFORM_ROTATE = 105;
private static final int CMD_TRANSFORM_ROTATE_X = 106;
private static final int CMD_TRANSFORM_ROTATE_Y = 107;
private static final int CMD_TRANSFORM_ROTATE_Z = 108;
private static final int CMD_TRANSFORM_SKEW_X = 109;
private static final int CMD_TRANSFORM_SKEW_Y = 110;
private static final int CMD_TRANSFORM_MATRIX = 111;
private static final int CMD_TRANSFORM_PERSPECTIVE = 112;
private static final int CMD_UNIT_DEG = 200;
private static final int CMD_UNIT_RAD = 201;
private static final int CMD_UNIT_PX = 202;
private static final int CMD_UNIT_PERCENT = 203;
private static String commandToString(int command) {
return switch (command) {
case CMD_OPACITY -> "opacity";
case CMD_ELEVATION -> "elevation";
case CMD_Z_INDEX -> "zIndex";
case CMD_SHADOW_OPACITY -> "shadowOpacity";
case CMD_SHADOW_RADIUS -> "shadowRadius";
case CMD_BACKGROUND_COLOR -> "backgroundColor";
case CMD_COLOR -> "color";
case CMD_TINT_COLOR -> "tintColor";
case CMD_BORDER_RADIUS -> "borderRadius";
case CMD_BORDER_TOP_LEFT_RADIUS -> "borderTopLeftRadius";
case CMD_BORDER_TOP_RIGHT_RADIUS -> "borderTopRightRadius";
case CMD_BORDER_TOP_START_RADIUS -> "borderTopStartRadius";
case CMD_BORDER_TOP_END_RADIUS -> "borderTopEndRadius";
case CMD_BORDER_BOTTOM_LEFT_RADIUS -> "borderBottomLeftRadius";
case CMD_BORDER_BOTTOM_RIGHT_RADIUS -> "borderBottomRightRadius";
case CMD_BORDER_BOTTOM_START_RADIUS -> "borderBottomStartRadius";
case CMD_BORDER_BOTTOM_END_RADIUS -> "borderBottomEndRadius";
case CMD_BORDER_START_START_RADIUS -> "borderStartStartRadius";
case CMD_BORDER_START_END_RADIUS -> "borderStartEndRadius";
case CMD_BORDER_END_START_RADIUS -> "borderEndStartRadius";
case CMD_BORDER_END_END_RADIUS -> "borderEndEndRadius";
case CMD_BORDER_COLOR -> "borderColor";
case CMD_BORDER_TOP_COLOR -> "borderTopColor";
case CMD_BORDER_BOTTOM_COLOR -> "borderBottomColor";
case CMD_BORDER_LEFT_COLOR -> "borderLeftColor";
case CMD_BORDER_RIGHT_COLOR -> "borderRightColor";
case CMD_BORDER_START_COLOR -> "borderStartColor";
case CMD_BORDER_END_COLOR -> "borderEndColor";
default -> throw new RuntimeException("Unknown command: " + command);
};
}
private static String transformCommandToString(int transformCommand) {
return switch (transformCommand) {
case CMD_TRANSFORM_TRANSLATE_X -> "translateX";
case CMD_TRANSFORM_TRANSLATE_Y -> "translateY";
case CMD_TRANSFORM_SCALE -> "scale";
case CMD_TRANSFORM_SCALE_X -> "scaleX";
case CMD_TRANSFORM_SCALE_Y -> "scaleY";
case CMD_TRANSFORM_ROTATE -> "rotate";
case CMD_TRANSFORM_ROTATE_X -> "rotateX";
case CMD_TRANSFORM_ROTATE_Y -> "rotateY";
case CMD_TRANSFORM_ROTATE_Z -> "rotateZ";
case CMD_TRANSFORM_SKEW_X -> "skewX";
case CMD_TRANSFORM_SKEW_Y -> "skewY";
case CMD_TRANSFORM_MATRIX -> "matrix";
case CMD_TRANSFORM_PERSPECTIVE -> "perspective";
default -> throw new RuntimeException("Unknown transform command: " + transformCommand);
};
}
@DoNotStrip
public void synchronouslyUpdateUIProps(int[] intBuffer, double[] doubleBuffer) {
PrimitiveIterator.OfInt intIterator = Arrays.stream(intBuffer).iterator();
PrimitiveIterator.OfDouble doubleIterator = Arrays.stream(doubleBuffer).iterator();
int viewTag = -1;
JavaOnlyMap props = new JavaOnlyMap();
while (intIterator.hasNext()) {
int command = intIterator.nextInt();
switch (command) {
case CMD_START_OF_VIEW:
viewTag = intIterator.nextInt();
props = new JavaOnlyMap();
break;
case CMD_OPACITY:
case CMD_ELEVATION:
case CMD_Z_INDEX:
case CMD_SHADOW_OPACITY:
case CMD_SHADOW_RADIUS:
{
String name = commandToString(command);
props.putDouble(name, doubleIterator.nextDouble());
break;
}
case CMD_BACKGROUND_COLOR:
case CMD_COLOR:
case CMD_TINT_COLOR:
case CMD_BORDER_COLOR:
case CMD_BORDER_TOP_COLOR:
case CMD_BORDER_BOTTOM_COLOR:
case CMD_BORDER_LEFT_COLOR:
case CMD_BORDER_RIGHT_COLOR:
case CMD_BORDER_START_COLOR:
case CMD_BORDER_END_COLOR:
{
String name = commandToString(command);
props.putInt(name, intIterator.nextInt());
break;
}
case CMD_BORDER_RADIUS:
case CMD_BORDER_TOP_LEFT_RADIUS:
case CMD_BORDER_TOP_RIGHT_RADIUS:
case CMD_BORDER_TOP_START_RADIUS:
case CMD_BORDER_TOP_END_RADIUS:
case CMD_BORDER_BOTTOM_LEFT_RADIUS:
case CMD_BORDER_BOTTOM_RIGHT_RADIUS:
case CMD_BORDER_BOTTOM_START_RADIUS:
case CMD_BORDER_BOTTOM_END_RADIUS:
case CMD_BORDER_START_START_RADIUS:
case CMD_BORDER_START_END_RADIUS:
case CMD_BORDER_END_START_RADIUS:
case CMD_BORDER_END_END_RADIUS:
{
String name = commandToString(command);
double value = doubleIterator.nextDouble();
switch (intIterator.nextInt()) {
case CMD_UNIT_PX -> props.putDouble(name, value);
case CMD_UNIT_PERCENT -> props.putString(name, value + "%");
default -> throw new RuntimeException("Unknown unit command");
}
break;
}
case CMD_START_OF_TRANSFORM:
JavaOnlyArray transform = new JavaOnlyArray();
while (true) {
int transformCommand = intIterator.nextInt();
if (transformCommand == CMD_END_OF_TRANSFORM) {
props.putArray("transform", transform);
break;
}
String name = transformCommandToString(transformCommand);
switch (transformCommand) {
case CMD_TRANSFORM_TRANSLATE_X:
case CMD_TRANSFORM_TRANSLATE_Y:
{
double value = doubleIterator.nextDouble();
switch (intIterator.nextInt()) {
case CMD_UNIT_PX -> transform.pushMap(JavaOnlyMap.of(name, value));
case CMD_UNIT_PERCENT -> transform.pushMap(JavaOnlyMap.of(name, value + "%"));
default -> throw new RuntimeException("Unknown unit command");
}
break;
}
case CMD_TRANSFORM_SCALE:
case CMD_TRANSFORM_SCALE_X:
case CMD_TRANSFORM_SCALE_Y:
case CMD_TRANSFORM_PERSPECTIVE:
{
double value = doubleIterator.nextDouble();
transform.pushMap(JavaOnlyMap.of(name, value));
break;
}
case CMD_TRANSFORM_ROTATE:
case CMD_TRANSFORM_ROTATE_X:
case CMD_TRANSFORM_ROTATE_Y:
case CMD_TRANSFORM_ROTATE_Z:
case CMD_TRANSFORM_SKEW_X:
case CMD_TRANSFORM_SKEW_Y:
double angle = doubleIterator.nextDouble();
String unit =
switch (intIterator.nextInt()) {
case CMD_UNIT_DEG -> "deg";
case CMD_UNIT_RAD -> "rad";
default -> throw new RuntimeException("Unknown unit command");
};
transform.pushMap(JavaOnlyMap.of(name, angle + unit));
break;
case CMD_TRANSFORM_MATRIX:
int length = intIterator.nextInt();
JavaOnlyArray matrix = new JavaOnlyArray();
for (int i = 0; i < length; i++) {
matrix.pushDouble(doubleIterator.nextDouble());
}
transform.pushMap(JavaOnlyMap.of(name, matrix));
break;
default:
throw new RuntimeException("Unknown transform type: " + transformCommand);
}
}
break;
case CMD_END_OF_VIEW:
mFabricUIManager.synchronouslyUpdateViewOnUIThread(viewTag, props);
break;
default:
throw new RuntimeException("Unexcepted command: " + command);
}
}
}
@DoNotStrip
public void setGestureState(int handlerTag, int newState) {
if (gestureHandlerStateManager != null) {
gestureHandlerStateManager.setGestureHandlerState(handlerTag, newState);
}
}
@DoNotStrip
public long getAnimationTimestamp() {
if (slowAnimationsEnabled) {
return this.firstUptime
+ (SystemClock.uptimeMillis() - this.firstUptime) / ANIMATIONS_DRAG_FACTOR;
} else {
return SystemClock.uptimeMillis();
}
}
@DoNotStrip
public void registerEventHandler(EventHandler handler) {
handler.mCustomEventNamesResolver = mNodesManager.getEventNameResolver();
mNodesManager.registerEventHandler(handler);
}
@DoNotStrip
public int registerSensor(int sensorType, int interval, SensorSetter setter) {
return reanimatedSensorContainer.registerSensor(
ReanimatedSensorType.getInstanceById(sensorType), interval, setter);
}
@DoNotStrip
public void unregisterSensor(int sensorId) {
reanimatedSensorContainer.unregisterSensor(sensorId);
}
@DoNotStrip
public int subscribeForKeyboardEvents(
KeyboardWorkletWrapper keyboardWorkletWrapper,
boolean isStatusBarTranslucent,
boolean isNavigationBarTranslucent) {
return keyboardAnimationManager.subscribeForKeyboardUpdates(
keyboardWorkletWrapper, isStatusBarTranslucent, isNavigationBarTranslucent);
}
@DoNotStrip
public void unsubscribeFromKeyboardEvents(int listenerId) {
keyboardAnimationManager.unsubscribeFromKeyboardUpdates(listenerId);
}
@DoNotStrip
public boolean getIsReducedMotion() {
ContentResolver mContentResolver = mContext.get().getContentResolver();
String rawValue =
Settings.Global.getString(mContentResolver, Settings.Global.TRANSITION_ANIMATION_SCALE);
float parsedValue = rawValue != null ? Float.parseFloat(rawValue) : 1f;
return parsedValue == 0f;
}
@DoNotStrip
void maybeFlushUIUpdatesQueue() {
UiThreadUtil.assertOnUiThread();
if (!mNodesManager.isAnimationRunning()) {
mNodesManager.performOperations();
}
}
}
@@ -1,49 +1,103 @@
package com.swmansion.reanimated;
import android.os.SystemClock;
import androidx.tracing.Trace;
import static java.lang.Float.NaN;
import android.view.View;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.GuardedRunnable;
import com.facebook.react.bridge.JavaOnlyMap;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.fabric.FabricUIManager;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.uimanager.GuardedFrameCallback;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.UIImplementation;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.common.UIManagerType;
import com.facebook.react.uimanager.UIManagerReanimatedHelper;
import com.facebook.react.uimanager.events.Event;
import com.facebook.react.uimanager.events.EventDispatcherListener;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.swmansion.reanimated.layoutReanimation.AnimationsManager;
import com.swmansion.reanimated.nativeProxy.NoopEventHandler;
import com.swmansion.worklets.WorkletsModule;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable;
public class NodesManager implements EventDispatcherListener {
private Long mFirstUptime = SystemClock.uptimeMillis();
private boolean mSlowAnimationsEnabled = false;
private int mAnimationsDragFactor;
public void scrollTo(int viewTag, double x, double y, boolean animated) {
View view;
try {
view = mUIManager.resolveView(viewTag);
} catch (IllegalViewOperationException e) {
e.printStackTrace();
return;
}
NativeMethodsHelper.scrollTo(view, x, y, animated);
}
public void dispatchCommand(int viewTag, String commandId, ReadableArray commandArgs) {
// mUIManager.dispatchCommand must be called from native modules queue thread
// because of an assert in ShadowNodeRegistry.getNode
mContext.runOnNativeModulesQueueThread(
new GuardedRunnable(mContext.getExceptionHandler()) {
@Override
public void runGuarded() {
mUIManager.dispatchCommand(viewTag, commandId, commandArgs);
}
});
}
public float[] measure(int viewTag) {
View view;
try {
view = mUIManager.resolveView(viewTag);
} catch (IllegalViewOperationException e) {
e.printStackTrace();
return (new float[] {NaN, NaN, NaN, NaN, NaN, NaN});
}
return NativeMethodsHelper.measure(view);
}
public interface OnAnimationFrame {
void onAnimationFrame(double timestampMs);
}
private AnimationsManager mAnimationManager;
private final UIImplementation mUIImplementation;
private final DeviceEventManagerModule.RCTDeviceEventEmitter mEventEmitter;
private final ReactChoreographer mReactChoreographer;
private final GuardedFrameCallback mChoreographerCallback;
protected final UIManagerModule.CustomEventNamesResolver mCustomEventNamesResolver;
private final AtomicBoolean mCallbackPosted = new AtomicBoolean();
private final ReactContext mContext;
private final UIManagerModule mUIManager;
private ReactApplicationContext mReactApplicationContext;
private RCTEventEmitter mCustomEventHandler = new NoopEventHandler();
private List<OnAnimationFrame> mFrameCallbacks = new ArrayList<>();
private ConcurrentLinkedQueue<CopiedEvent> mEventQueue = new ConcurrentLinkedQueue<>();
private double lastFrameTimeMs;
private FabricUIManager mFabricUIManager;
public Set<String> uiProps = Collections.emptySet();
public Set<String> nativeProps = Collections.emptySet();
private ReaCompatibility compatibility;
public NativeProxy getNativeProxy() {
return mNativeProxy;
@@ -51,23 +105,47 @@ public class NodesManager implements EventDispatcherListener {
private NativeProxy mNativeProxy;
public void invalidate() {
if (mNativeProxy != null) {
mNativeProxy.invalidate();
mNativeProxy = null;
public AnimationsManager getAnimationsManager() {
return mAnimationManager;
}
public void onCatalystInstanceDestroy() {
if (mAnimationManager != null) {
mAnimationManager.onCatalystInstanceDestroy();
}
if (mFabricUIManager != null) {
mFabricUIManager.getEventDispatcher().removeListener(this);
if (mNativeProxy != null) {
mNativeProxy.onCatalystInstanceDestroy();
mNativeProxy = null;
}
}
public NodesManager(ReactApplicationContext context, WorkletsModule workletsModule) {
context.assertOnJSQueueThread();
public void initWithContext(ReactApplicationContext reactApplicationContext) {
mReactApplicationContext = reactApplicationContext;
mNativeProxy = new NativeProxy(reactApplicationContext);
mAnimationManager.setAndroidUIScheduler(getNativeProxy().getAndroidUIScheduler());
compatibility = new ReaCompatibility(reactApplicationContext);
compatibility.registerFabricEventListener(this);
}
UIManager uiManager = UIManagerHelper.getUIManager(context, UIManagerType.FABRIC);
assert uiManager != null;
mCustomEventNamesResolver = uiManager::resolveCustomDirectEventName;
private final class NativeUpdateOperation {
public int mViewTag;
public WritableMap mNativeProps;
public NativeUpdateOperation(int viewTag, WritableMap nativeProps) {
mViewTag = viewTag;
mNativeProps = nativeProps;
}
}
private Queue<NativeUpdateOperation> mOperationsInBatch = new LinkedList<>();
private boolean mTryRunBatchUpdatesSynchronously = false;
public NodesManager(ReactContext context) {
mContext = context;
mUIManager = context.getNativeModule(UIManagerModule.class);
mUIImplementation = mUIManager.getUIImplementation();
mCustomEventNamesResolver = mUIManager.getDirectEventNamesResolver();
mEventEmitter = context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
mReactChoreographer = ReactChoreographer.getInstance();
@@ -79,9 +157,17 @@ public class NodesManager implements EventDispatcherListener {
}
};
mNativeProxy = new NativeProxy(context, workletsModule, this);
mFabricUIManager = (FabricUIManager) uiManager;
mFabricUIManager.getEventDispatcher().addListener(this);
// We register as event listener at the end, because we pass `this` and we haven't finished
// contructing an object yet.
// This lead to a crash described in
// https://github.com/software-mansion/react-native-reanimated/issues/604 which was caused by
// Nodes Manager being constructed on UI thread and registering for events.
// Events are handled in the native modules thread in the `onEventDispatch()` method.
// This method indirectly uses `mChoreographerCallback` which was created after event
// registration, creating race condition
mUIManager.getEventDispatcher().addListener(this);
mAnimationManager = new AnimationsManager(mContext, mUIManager);
}
public void onHostPause() {
@@ -116,58 +202,99 @@ public class NodesManager implements EventDispatcherListener {
}
public void performOperations() {
UiThreadUtil.assertOnUiThread();
if (mNativeProxy != null) {
mNativeProxy.performOperations();
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
if (mNativeProxy != null) {
mNativeProxy.performOperations();
}
} else if (!mOperationsInBatch.isEmpty()) {
final Queue<NativeUpdateOperation> copiedOperationsQueue = mOperationsInBatch;
mOperationsInBatch = new LinkedList<>();
final boolean trySynchronously = mTryRunBatchUpdatesSynchronously;
mTryRunBatchUpdatesSynchronously = false;
final Semaphore semaphore = new Semaphore(0);
mContext.runOnNativeModulesQueueThread(
new GuardedRunnable(mContext.getExceptionHandler()) {
@Override
public void runGuarded() {
boolean queueWasEmpty =
UIManagerReanimatedHelper.isOperationQueueEmpty(mUIImplementation);
boolean shouldDispatchUpdates = trySynchronously && queueWasEmpty;
if (!shouldDispatchUpdates) {
semaphore.release();
}
while (!copiedOperationsQueue.isEmpty()) {
NativeUpdateOperation op = copiedOperationsQueue.remove();
ReactShadowNode shadowNode = mUIImplementation.resolveShadowNode(op.mViewTag);
if (shadowNode != null) {
mUIManager.updateView(op.mViewTag, shadowNode.getViewClass(), op.mNativeProps);
}
}
if (queueWasEmpty) {
mUIImplementation.dispatchViewUpdates(-1); // no associated batchId
}
if (shouldDispatchUpdates) {
semaphore.release();
}
}
});
if (trySynchronously) {
try {
semaphore.tryAcquire(16, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// if the thread is interruped we just continue and let the layout update happen
// asynchronously
}
}
}
}
private void onAnimationFrame(long frameTimeNanos) {
UiThreadUtil.assertOnUiThread();
// Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "onAnimationFrame");
try {
if (BuildConfig.REANIMATED_PROFILING) {
Trace.beginSection("onAnimationFrame");
double currentFrameTimeMs = frameTimeNanos / 1000000.;
if (currentFrameTimeMs > lastFrameTimeMs) {
// It is possible for ChoreographerCallback to be executed twice within the same frame
// due to frame drops. If this occurs, the additional callback execution should be ignored.
lastFrameTimeMs = currentFrameTimeMs;
while (!mEventQueue.isEmpty()) {
CopiedEvent copiedEvent = mEventQueue.poll();
handleEvent(
copiedEvent.getTargetTag(), copiedEvent.getEventName(), copiedEvent.getPayload());
}
double currentFrameTimeMs = frameTimeNanos / 1000000.;
if (mSlowAnimationsEnabled) {
currentFrameTimeMs =
mFirstUptime + (currentFrameTimeMs - mFirstUptime) / mAnimationsDragFactor;
}
if (currentFrameTimeMs > lastFrameTimeMs) {
// It is possible for ChoreographerCallback to be executed twice within the same frame
// due to frame drops. If this occurs, the additional callback execution should be ignored.
lastFrameTimeMs = currentFrameTimeMs;
while (!mEventQueue.isEmpty()) {
CopiedEvent copiedEvent = mEventQueue.poll();
handleEvent(
copiedEvent.getTargetTag(), copiedEvent.getEventName(), copiedEvent.getPayload());
if (!mFrameCallbacks.isEmpty()) {
List<OnAnimationFrame> frameCallbacks = mFrameCallbacks;
mFrameCallbacks = new ArrayList<>(frameCallbacks.size());
for (int i = 0, size = frameCallbacks.size(); i < size; i++) {
frameCallbacks.get(i).onAnimationFrame(currentFrameTimeMs);
}
if (!mFrameCallbacks.isEmpty()) {
List<OnAnimationFrame> frameCallbacks = mFrameCallbacks;
mFrameCallbacks = new ArrayList<>(frameCallbacks.size());
for (int i = 0, size = frameCallbacks.size(); i < size; i++) {
frameCallbacks.get(i).onAnimationFrame(currentFrameTimeMs);
}
}
performOperations();
}
mCallbackPosted.set(false);
if (!mFrameCallbacks.isEmpty() || !mEventQueue.isEmpty()) {
// enqueue next frame
startUpdatingOnAnimationFrame();
}
} finally {
if (BuildConfig.REANIMATED_PROFILING) {
Trace.endSection();
}
performOperations();
}
mCallbackPosted.set(false);
if (!mFrameCallbacks.isEmpty() || !mEventQueue.isEmpty()) {
// enqueue next frame
startUpdatingOnAnimationFrame();
}
// Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
public void enqueueUpdateViewOnNativeThread(
int viewTag, WritableMap nativeProps, boolean trySynchronously) {
if (trySynchronously) {
mTryRunBatchUpdatesSynchronously = true;
}
mOperationsInBatch.add(new NativeUpdateOperation(viewTag, nativeProps));
}
public void configureProps(Set<String> uiPropsSet, Set<String> nativePropsSet) {
uiProps = uiPropsSet;
nativeProps = nativePropsSet;
}
public void postOnAnimation(OnAnimationFrame onAnimationFrame) {
@@ -177,32 +304,22 @@ public class NodesManager implements EventDispatcherListener {
@Override
public void onEventDispatch(Event event) {
try {
if (BuildConfig.REANIMATED_PROFILING) {
Trace.beginSection("onEventDispatch");
}
if (mNativeProxy == null) {
return;
}
// Events can be dispatched from any thread so we have to make sure handleEvent is run from
// the UI thread.
if (UiThreadUtil.isOnUiThread()) {
handleEvent(event);
performOperations();
} else {
String eventName = mCustomEventNamesResolver.resolveCustomEventName(event.getEventName());
int viewTag = event.getViewTag();
boolean shouldSaveEvent = mNativeProxy.isAnyHandlerWaitingForEvent(eventName, viewTag);
if (shouldSaveEvent) {
mEventQueue.offer(new CopiedEvent(event));
}
startUpdatingOnAnimationFrame();
}
} finally {
if (BuildConfig.REANIMATED_PROFILING) {
Trace.endSection();
if (mNativeProxy == null) {
return;
}
// Events can be dispatched from any thread so we have to make sure handleEvent is run from the
// UI thread.
if (UiThreadUtil.isOnUiThread()) {
handleEvent(event);
performOperations();
} else {
String eventName = mCustomEventNamesResolver.resolveCustomEventName(event.getEventName());
int viewTag = event.getViewTag();
boolean shouldSaveEvent = mNativeProxy.isAnyHandlerWaitingForEvent(eventName, viewTag);
if (shouldSaveEvent) {
mEventQueue.offer(new CopiedEvent(event));
}
startUpdatingOnAnimationFrame();
}
}
@@ -226,11 +343,128 @@ public class NodesManager implements EventDispatcherListener {
mEventEmitter.emit(name, body);
}
public void enableSlowAnimations(boolean slowAnimationsEnabled, int animationsDragFactor) {
mSlowAnimationsEnabled = slowAnimationsEnabled;
mAnimationsDragFactor = animationsDragFactor;
if (slowAnimationsEnabled) {
mFirstUptime = SystemClock.uptimeMillis();
public void updateProps(int viewTag, Map<String, Object> props) {
// TODO: update PropsNode to use this method instead of its own way of updating props
boolean hasUIProps = false;
boolean hasNativeProps = false;
boolean hasJSProps = false;
JavaOnlyMap newUIProps = new JavaOnlyMap();
WritableMap newJSProps = Arguments.createMap();
WritableMap newNativeProps = Arguments.createMap();
for (Map.Entry<String, Object> entry : props.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (uiProps.contains(key)) {
hasUIProps = true;
addProp(newUIProps, key, value);
} else if (nativeProps.contains(key)) {
hasNativeProps = true;
addProp(newNativeProps, key, value);
} else {
hasJSProps = true;
addProp(newJSProps, key, value);
}
}
if (viewTag != View.NO_ID) {
if (hasUIProps) {
mUIImplementation.synchronouslyUpdateViewOnUIThread(
viewTag, new ReactStylesDiffMap(newUIProps));
}
if (hasNativeProps) {
enqueueUpdateViewOnNativeThread(viewTag, newNativeProps, true);
}
if (hasJSProps) {
WritableMap evt = Arguments.createMap();
evt.putInt("viewTag", viewTag);
evt.putMap("props", newJSProps);
sendEvent("onReanimatedPropsChange", evt);
}
}
}
public void synchronouslyUpdateUIProps(int viewTag, ReadableMap uiProps) {
compatibility.synchronouslyUpdateUIProps(viewTag, uiProps);
}
public String obtainProp(int viewTag, String propName) {
View view = mUIManager.resolveView(viewTag);
String result =
"error: unknown propName " + propName + ", currently supported: opacity, zIndex";
if (propName.equals("opacity")) {
Float opacity = view.getAlpha();
result = Float.toString(opacity);
} else if (propName.equals("zIndex")) {
Float zIndex = view.getElevation();
result = Float.toString(zIndex);
}
return result;
}
private static WritableMap copyReadableMap(ReadableMap map) {
WritableMap copy = Arguments.createMap();
copy.merge(map);
return copy;
}
private static WritableArray copyReadableArray(ReadableArray array) {
WritableArray copy = Arguments.createArray();
for (int i = 0; i < array.size(); i++) {
ReadableType type = array.getType(i);
switch (type) {
case Boolean:
copy.pushBoolean(array.getBoolean(i));
break;
case String:
copy.pushString(array.getString(i));
break;
case Null:
copy.pushNull();
break;
case Number:
copy.pushDouble(array.getDouble(i));
break;
case Map:
copy.pushMap(copyReadableMap(array.getMap(i)));
break;
case Array:
copy.pushArray(copyReadableArray(array.getArray(i)));
break;
default:
throw new IllegalStateException("[Reanimated] Unknown type of ReadableArray.");
}
}
return copy;
}
private static void addProp(WritableMap propMap, String key, Object value) {
if (value == null) {
propMap.putNull(key);
} else if (value instanceof Double) {
propMap.putDouble(key, (Double) value);
} else if (value instanceof Integer) {
propMap.putInt(key, (Integer) value);
} else if (value instanceof Number) {
propMap.putDouble(key, ((Number) value).doubleValue());
} else if (value instanceof Boolean) {
propMap.putBoolean(key, (Boolean) value);
} else if (value instanceof String) {
propMap.putString(key, (String) value);
} else if (value instanceof ReadableArray) {
if (!(value instanceof WritableArray)) {
propMap.putArray(key, copyReadableArray((ReadableArray) value));
} else {
propMap.putArray(key, (ReadableArray) value);
}
} else if (value instanceof ReadableMap) {
if (!(value instanceof WritableMap)) {
propMap.putMap(key, copyReadableMap((ReadableMap) value));
} else {
propMap.putMap(key, (ReadableMap) value);
}
} else {
throw new IllegalStateException("[Reanimated] Unknown type of animated value.");
}
}
}
@@ -1,38 +1,55 @@
package com.swmansion.reanimated;
import android.util.Log;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.module.annotations.ReactModule;
import com.swmansion.worklets.WorkletsModule;
import com.facebook.react.uimanager.NativeViewHierarchyManager;
import com.facebook.react.uimanager.UIBlock;
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 {
private final NodesManager mNodesManager;
private final WorkletsModule mWorkletsModule;
public class ReanimatedModule extends ReactContextBaseJavaModule
implements LifecycleEventListener, UIManagerModuleListener {
public static final String NAME = "ReanimatedModule";
private interface UIThreadOperation {
void execute(NodesManager nodesManager);
}
private ArrayList<UIThreadOperation> mOperations = new ArrayList<>();
private @Nullable NodesManager mNodesManager;
public ReanimatedModule(ReactApplicationContext reactContext) {
super(reactContext);
reactContext.assertOnJSQueueThread();
mWorkletsModule = reactContext.getNativeModule(WorkletsModule.class);
mNodesManager = new NodesManager(reactContext, mWorkletsModule);
}
@Override
public void initialize() {
ReactApplicationContext reactContext = getReactApplicationContext();
reactContext.assertOnJSQueueThread();
reactContext.addLifecycleEventListener(this);
ReactApplicationContext reactCtx = getReactApplicationContext();
UIManagerModule uiManager = reactCtx.getNativeModule(UIManagerModule.class);
reactCtx.addLifecycleEventListener(this);
uiManager.addUIManagerListener(this);
}
@Override
public void onHostPause() {
mNodesManager.onHostPause();
if (mNodesManager != null) {
mNodesManager.onHostPause();
}
}
@Override
public void onHostResume() {
mNodesManager.onHostResume();
if (mNodesManager != null) {
mNodesManager.onHostResume();
}
}
@Override
@@ -40,33 +57,70 @@ public class ReanimatedModule extends NativeReanimatedModuleSpec implements Life
// do nothing
}
@Override
public void willDispatchViewUpdates(final UIManagerModule uiManager) {
if (mOperations.isEmpty()) {
return;
}
final ArrayList<UIThreadOperation> operations = mOperations;
mOperations = new ArrayList<>();
uiManager.addUIBlock(
new UIBlock() {
@Override
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
NodesManager nodesManager = getNodesManager();
for (UIThreadOperation operation : operations) {
operation.execute(nodesManager);
}
}
});
}
@Override
public String getName() {
return NAME;
}
/*package*/
public NodesManager getNodesManager() {
// This method is called from react-native-gesture-handler.
if (mNodesManager == null) {
mNodesManager = new NodesManager(getReactApplicationContext());
}
return mNodesManager;
}
@ReactMethod(isBlockingSynchronousMethod = true)
public boolean installTurboModule() {
getReactApplicationContext().assertOnJSQueueThread();
mNodesManager.getNativeProxy().installJSIBindings();
return true;
public void installTurboModule() {
// 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());
} else {
Log.w(
"[REANIMATED]",
"Unable to create Reanimated Native Module. You can ignore this message if you are using Chrome Debugger now.");
}
}
@ReactMethod
public void addListener(String ignoredEventName) {
public void addListener(String eventName) {
// Keep: Required for RN built in Event Emitter Calls.
}
@ReactMethod
public void removeListeners(Integer ignoredCount) {
public void removeListeners(Integer count) {
// Keep: Required for RN built in Event Emitter Calls.
}
@Override
public void invalidate() {
super.invalidate();
mNodesManager.invalidate();
ReactApplicationContext reactContext = getReactApplicationContext();
reactContext.removeLifecycleEventListener(this);
public void onCatalystInstanceDestroy() {
super.onCatalystInstanceDestroy();
if (mNodesManager != null) {
mNodesManager.onCatalystInstanceDestroy();
}
}
}
@@ -1,58 +1,84 @@
package com.swmansion.reanimated;
import androidx.annotation.NonNull;
import com.facebook.react.BaseReactPackage;
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_UI_MANAGER_MODULE_END;
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_UI_MANAGER_MODULE_START;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.TurboReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMarker;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.module.annotations.ReactModuleList;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
import com.facebook.react.uimanager.ReanimatedUIManager;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.systrace.Systrace;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ReactModuleList(
nativeModules = {
ReanimatedModule.class,
})
public class ReanimatedPackage extends BaseReactPackage implements ReactPackage {
public class ReanimatedPackage extends TurboReactPackage implements ReactPackage {
@Override
public NativeModule getModule(
@NonNull String name, @NonNull ReactApplicationContext reactContext) {
return switch (name) {
case ReanimatedModule.NAME -> new ReanimatedModule(reactContext);
default -> null;
};
public NativeModule getModule(String name, ReactApplicationContext reactContext) {
if (name.equals(ReanimatedModule.NAME)) {
return new ReanimatedModule(reactContext);
}
if (name.equals(ReanimatedUIManager.NAME)) {
return createUIManager(reactContext);
}
return null;
}
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
Class<? extends NativeModule>[] moduleList =
new Class[] {
ReanimatedModule.class,
ReanimatedModule.class, ReanimatedUIManager.class,
};
final Map<String, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
for (Class<? extends NativeModule> moduleClass : moduleList) {
ReactModule reactModule =
Objects.requireNonNull(moduleClass.getAnnotation(ReactModule.class));
ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);
reactModuleInfoMap.put(
reactModule.name(),
new ReactModuleInfo(
reactModule.name(),
moduleClass.getName(),
reactModule.canOverrideExistingModule(),
true,
reactModule.needsEagerInit(),
reactModule.hasConstants(),
reactModule.isCxxModule(),
true));
TurboModule.class.isAssignableFrom(moduleClass)));
}
return () -> reactModuleInfoMap;
return new ReactModuleInfoProvider() {
@Override
public Map<String, ReactModuleInfo> getReactModuleInfos() {
return reactModuleInfoMap;
}
};
}
private UIManagerModule createUIManager(final ReactApplicationContext reactContext) {
ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_START);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createUIManagerModule");
final ReactInstanceManager reactInstanceManager = getReactInstanceManager(reactContext);
List<ViewManager> viewManagers = reactInstanceManager.getOrCreateViewManagers(reactContext);
int minTimeLeftInFrameForNonBatchedOperationMs = -1;
try {
return ReanimatedUIManagerFactory.create(
reactContext, viewManagers, minTimeLeftInFrameForNonBatchedOperationMs);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_END);
}
}
/**
@@ -8,6 +8,8 @@ import java.util.Map;
public class Utils {
public static boolean isChromeDebugger = false;
public static Map<String, Integer> processMapping(ReadableMap style) {
ReadableMapKeySetIterator iter = style.keySetIterator();
HashMap<String, Integer> mapping = new HashMap<>();
@@ -44,14 +46,4 @@ public class Utils {
}
return 0;
}
public static Runnable combineRunnables(final Runnable... runnables) {
return new Runnable() {
public void run() {
for (Runnable r : runnables) {
r.run();
}
}
};
}
}
@@ -1,53 +0,0 @@
package com.swmansion.reanimated.keyboard;
import androidx.core.view.WindowInsetsCompat;
import com.facebook.react.uimanager.PixelUtil;
public class Keyboard {
private KeyboardState mState = KeyboardState.UNKNOWN;
private int mHeight = 0;
private int mActiveTransitionCounter = 0;
private static final int CONTENT_TYPE_MASK = WindowInsetsCompat.Type.ime();
private static final int SYSTEM_BAR_TYPE_MASK = WindowInsetsCompat.Type.systemBars();
public KeyboardState getState() {
return mState;
}
public int getHeight() {
return mHeight;
}
public void updateHeight(WindowInsetsCompat insets, boolean isNavigationBarTranslucent) {
int contentBottomInset = insets.getInsets(CONTENT_TYPE_MASK).bottom;
int systemBarBottomInset =
isNavigationBarTranslucent ? 0 : insets.getInsets(SYSTEM_BAR_TYPE_MASK).bottom;
int keyboardHeightDip = contentBottomInset - systemBarBottomInset;
int keyboardHeight = (int) PixelUtil.toDIPFromPixel(Math.max(0, keyboardHeightDip));
if (keyboardHeight <= 0 && mState == KeyboardState.OPEN) {
/*
When the keyboard is being canceling, for one frame the insets show a keyboard height of 0,
causing a jump of the keyboard. We can avoid it by ignoring that frame and calling
the listeners on the following frame.
*/
return;
}
mHeight = keyboardHeight;
}
public void onAnimationStart() {
if (mActiveTransitionCounter > 0) {
mState = mState == KeyboardState.OPENING ? KeyboardState.CLOSING : KeyboardState.OPENING;
} else {
mState = mHeight <= 0 ? KeyboardState.OPENING : KeyboardState.CLOSING;
}
mActiveTransitionCounter++;
}
public void onAnimationEnd() {
mActiveTransitionCounter--;
if (mActiveTransitionCounter == 0) {
mState = mHeight <= 0 ? KeyboardState.CLOSED : KeyboardState.OPEN;
}
}
}
@@ -1,67 +0,0 @@
package com.swmansion.reanimated.keyboard;
import androidx.annotation.NonNull;
import androidx.core.view.WindowInsetsAnimationCompat;
import androidx.core.view.WindowInsetsCompat;
import java.util.List;
public class KeyboardAnimationCallback extends WindowInsetsAnimationCompat.Callback {
private final Keyboard mKeyboard;
private final NotifyAboutKeyboardChangeFunction mNotifyAboutKeyboardChange;
private static final int CONTENT_TYPE_MASK = WindowInsetsCompat.Type.ime();
private final boolean mIsNavigationBarTranslucent;
public KeyboardAnimationCallback(
Keyboard keyboard,
NotifyAboutKeyboardChangeFunction notifyAboutKeyboardChange,
boolean isNavigationBarTranslucent) {
super(WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE);
mNotifyAboutKeyboardChange = notifyAboutKeyboardChange;
mIsNavigationBarTranslucent = isNavigationBarTranslucent;
mKeyboard = keyboard;
}
@NonNull
@Override
public WindowInsetsAnimationCompat.BoundsCompat onStart(
@NonNull WindowInsetsAnimationCompat animation,
@NonNull WindowInsetsAnimationCompat.BoundsCompat bounds) {
if (!isKeyboardAnimation(animation)) {
return bounds;
}
mKeyboard.onAnimationStart();
mNotifyAboutKeyboardChange.call();
return super.onStart(animation, bounds);
}
@NonNull
@Override
public WindowInsetsCompat onProgress(
@NonNull WindowInsetsCompat insets,
@NonNull List<WindowInsetsAnimationCompat> runningAnimations) {
boolean isAnyKeyboardAnimationRunning = false;
for (WindowInsetsAnimationCompat animation : runningAnimations) {
if (isKeyboardAnimation(animation)) {
isAnyKeyboardAnimationRunning = true;
break;
}
}
if (isAnyKeyboardAnimationRunning) {
mKeyboard.updateHeight(insets, mIsNavigationBarTranslucent);
mNotifyAboutKeyboardChange.call();
}
return insets;
}
@Override
public void onEnd(@NonNull WindowInsetsAnimationCompat animation) {
if (isKeyboardAnimation(animation)) {
mKeyboard.onAnimationEnd();
mNotifyAboutKeyboardChange.call();
}
}
private static boolean isKeyboardAnimation(@NonNull WindowInsetsAnimationCompat animation) {
return (animation.getTypeMask() & CONTENT_TYPE_MASK) != 0;
}
}
@@ -1,52 +0,0 @@
package com.swmansion.reanimated.keyboard;
import com.facebook.react.bridge.ReactApplicationContext;
import java.lang.ref.WeakReference;
import java.util.concurrent.ConcurrentHashMap;
@FunctionalInterface
interface NotifyAboutKeyboardChangeFunction {
void call();
}
public class KeyboardAnimationManager {
private int mNextListenerId = 0;
private final ConcurrentHashMap<Integer, KeyboardWorkletWrapper> mListeners =
new ConcurrentHashMap<>();
private final Keyboard mKeyboard = new Keyboard();
private final WindowsInsetsManager mWindowsInsetsManager;
public KeyboardAnimationManager(WeakReference<ReactApplicationContext> reactContext) {
mWindowsInsetsManager =
new WindowsInsetsManager(reactContext, mKeyboard, this::notifyAboutKeyboardChange);
}
public int subscribeForKeyboardUpdates(
KeyboardWorkletWrapper callback,
boolean isStatusBarTranslucent,
boolean isNavigationBarTranslucent) {
int listenerId = mNextListenerId++;
if (mListeners.isEmpty()) {
KeyboardAnimationCallback keyboardAnimationCallback =
new KeyboardAnimationCallback(
mKeyboard, this::notifyAboutKeyboardChange, isNavigationBarTranslucent);
mWindowsInsetsManager.startObservingChanges(
keyboardAnimationCallback, isStatusBarTranslucent, isNavigationBarTranslucent);
}
mListeners.put(listenerId, callback);
return listenerId;
}
public void unsubscribeFromKeyboardUpdates(int listenerId) {
mListeners.remove(listenerId);
if (mListeners.isEmpty()) {
mWindowsInsetsManager.stopObservingChanges();
}
}
public void notifyAboutKeyboardChange() {
for (KeyboardWorkletWrapper listener : mListeners.values()) {
listener.invoke(mKeyboard.getState().asInt(), mKeyboard.getHeight());
}
}
}
@@ -1,19 +0,0 @@
package com.swmansion.reanimated.keyboard;
public enum KeyboardState {
UNKNOWN(0),
OPENING(1),
OPEN(2),
CLOSING(3),
CLOSED(4);
private final int mValue;
KeyboardState(int value) {
mValue = value;
}
public int asInt() {
return mValue;
}
}
@@ -1,16 +0,0 @@
package com.swmansion.reanimated.keyboard;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
@DoNotStrip
public class KeyboardWorkletWrapper {
@DoNotStrip private final HybridData mHybridData;
@DoNotStrip
private KeyboardWorkletWrapper(HybridData hybridData) {
mHybridData = hybridData;
}
public native void invoke(int keyboardState, int height);
}
@@ -1,135 +0,0 @@
package com.swmansion.reanimated.keyboard;
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import com.facebook.react.bridge.ReactApplicationContext;
import java.lang.ref.WeakReference;
public class WindowsInsetsManager {
private boolean mIsStatusBarTranslucent = false;
private boolean mIsNavigationBarTranslucent = false;
private final WeakReference<ReactApplicationContext> mReactContext;
private final Keyboard mKeyboard;
private final NotifyAboutKeyboardChangeFunction mNotifyAboutKeyboardChange;
private final String MissingContextErrorMsg = "Unable to get reference to react activity";
public WindowsInsetsManager(
WeakReference<ReactApplicationContext> reactContext,
Keyboard keyboard,
NotifyAboutKeyboardChangeFunction notifyAboutKeyboardChange) {
mReactContext = reactContext;
mKeyboard = keyboard;
mNotifyAboutKeyboardChange = notifyAboutKeyboardChange;
}
private Activity getCurrentActivity() {
return mReactContext.get().getCurrentActivity();
}
public void startObservingChanges(
KeyboardAnimationCallback keyboardAnimationCallback,
boolean isStatusBarTranslucent,
boolean isNavigationBarTranslucent) {
mIsStatusBarTranslucent = isStatusBarTranslucent;
mIsNavigationBarTranslucent = isNavigationBarTranslucent;
updateWindowDecor(false);
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
Log.e("Reanimated", MissingContextErrorMsg);
return;
}
View rootView = currentActivity.getWindow().getDecorView();
ViewCompat.setOnApplyWindowInsetsListener(rootView, this::onApplyWindowInsetsListener);
ViewCompat.setWindowInsetsAnimationCallback(rootView, keyboardAnimationCallback);
}
public void stopObservingChanges() {
updateWindowDecor(!mIsStatusBarTranslucent && !mIsNavigationBarTranslucent);
updateInsets(0, 0);
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
Log.e("Reanimated", MissingContextErrorMsg);
return;
}
View rootView = currentActivity.getWindow().getDecorView();
ViewCompat.setWindowInsetsAnimationCallback(rootView, null);
ViewCompat.setOnApplyWindowInsetsListener(rootView, null);
}
private void updateWindowDecor(boolean decorFitsSystemWindow) {
new Handler(Looper.getMainLooper())
.post(
() -> {
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
Log.e("Reanimated", MissingContextErrorMsg);
return;
}
WindowCompat.setDecorFitsSystemWindows(
currentActivity.getWindow(), decorFitsSystemWindow);
});
}
private WindowInsetsCompat onApplyWindowInsetsListener(View view, WindowInsetsCompat insets) {
WindowInsetsCompat defaultInsets = ViewCompat.onApplyWindowInsets(view, insets);
if (mKeyboard.getState() == KeyboardState.OPEN) {
mKeyboard.updateHeight(insets, mIsNavigationBarTranslucent);
mNotifyAboutKeyboardChange.call();
}
setWindowInsets(defaultInsets);
return defaultInsets;
}
private void setWindowInsets(WindowInsetsCompat insets) {
int systemBarsTypeMask = WindowInsetsCompat.Type.systemBars();
int paddingTop = insets.getInsets(systemBarsTypeMask).top;
int paddingBottom = insets.getInsets(systemBarsTypeMask).bottom;
updateInsets(paddingTop, paddingBottom);
}
private void updateInsets(int paddingTop, int paddingBottom) {
new Handler(Looper.getMainLooper())
.post(
() -> {
FrameLayout.LayoutParams params = getLayoutParams(paddingTop, paddingBottom);
int actionBarId = androidx.appcompat.R.id.action_bar_root;
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
Log.e("Reanimated", MissingContextErrorMsg);
return;
}
View actionBarRootView =
currentActivity.getWindow().getDecorView().findViewById(actionBarId);
actionBarRootView.setLayoutParams(params);
});
}
private FrameLayout.LayoutParams getLayoutParams(int paddingTop, int paddingBottom) {
int matchParentFlag = FrameLayout.LayoutParams.MATCH_PARENT;
FrameLayout.LayoutParams params =
new FrameLayout.LayoutParams(matchParentFlag, matchParentFlag);
params.setMargins(
0,
mIsStatusBarTranslucent ? 0 : paddingTop,
0,
mIsNavigationBarTranslucent ? 0 : paddingBottom);
return params;
}
}
@@ -12,8 +12,7 @@ import com.facebook.react.uimanager.events.RCTEventEmitter;
public class EventHandler implements RCTEventEmitter {
@DoNotStrip private final HybridData mHybridData;
public UIManagerModule.CustomEventNamesResolver mCustomEventNamesResolver;
UIManagerModule.CustomEventNamesResolver mCustomEventNamesResolver;
@DoNotStrip
private EventHandler(HybridData hybridData) {
@@ -17,7 +17,6 @@ public class ReanimatedSensor {
Sensor sensor;
ReanimatedSensorType sensorType;
int interval;
private static final int DEFAULT_INTERVAL = 8;
ReanimatedSensor(
WeakReference<ReactApplicationContext> reactContext,
@@ -31,7 +30,7 @@ public class ReanimatedSensor {
(SensorManager) reactContext.get().getSystemService(reactContext.get().SENSOR_SERVICE);
this.sensorType = sensorType;
if (interval == -1) {
this.interval = DEFAULT_INTERVAL;
this.interval = SensorManager.SENSOR_DELAY_UI;
} else {
this.interval = interval;
}