react-native-reanimated 4.7.0 → 4.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.cpp +34 -2
- package/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.h +15 -1
- package/Common/cpp/reanimated/Fabric/ReanimatedMountHook.cpp +9 -0
- package/Common/cpp/reanimated/Fabric/ReanimatedMountHook.h +3 -0
- package/Common/cpp/reanimated/Fabric/updates/SynchronousWritesTracker.cpp +103 -0
- package/Common/cpp/reanimated/Fabric/updates/SynchronousWritesTracker.h +74 -0
- package/Common/cpp/reanimated/Fabric/updates/UpdatesRegistry.cpp +15 -0
- package/Common/cpp/reanimated/Fabric/updates/UpdatesRegistry.h +5 -0
- package/Common/cpp/reanimated/Fabric/updates/UpdatesRegistryManager.cpp +10 -0
- package/Common/cpp/reanimated/Fabric/updates/UpdatesRegistryManager.h +3 -0
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +97 -54
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +5 -1
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxyCommon.cpp +23 -5
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxyCommon.h +17 -1
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp +9 -9
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h +35 -8
- package/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.h +2 -2
- package/Common/cpp/reanimated/LayoutAnimations/SharedTransitions.cpp +11 -11
- package/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +75 -2
- package/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h +11 -1
- package/Common/cpp/reanimated/Tools/PlatformDepMethodsHolder.h +4 -1
- package/android/src/main/cpp/reanimated/android/NativeProxy.cpp +13 -3
- package/android/src/main/cpp/reanimated/android/NativeProxy.h +2 -0
- package/android/src/main/java/com/swmansion/reanimated/NativeProxy.kt +84 -9
- package/android/src/main/java/com/swmansion/reanimated/NodesManager.kt +2 -4
- package/android/src/main/java/com/swmansion/reanimated/nativeProxy/EventHandler.kt +4 -3
- package/android/src/main/java/com/swmansion/reanimated/sensor/ReanimatedSensor.kt +1 -1
- package/android/src/main/java/com/swmansion/reanimated/sensor/ReanimatedSensorListener.kt +44 -33
- package/apple/reanimated/apple/REASynchronousPropsRewriter.h +18 -0
- package/apple/reanimated/apple/REASynchronousPropsRewriter.mm +37 -0
- package/apple/reanimated/apple/ReanimatedModule.mm +7 -0
- package/apple/reanimated/apple/keyboardObserver/REAKeyboardEventObserver.mm +20 -3
- package/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.mm +24 -1
- package/lib/module/animation/timing.js +15 -2
- package/lib/module/animation/timing.js.map +1 -1
- package/lib/module/platform-specific/jsVersion.js +1 -1
- package/lib/typescript/animation/timing.d.ts.map +1 -1
- package/lib/typescript/platform-specific/jsVersion.d.ts +1 -1
- package/package.json +3 -1
- package/src/animation/timing.ts +20 -2
- package/src/platform-specific/jsVersion.ts +1 -1
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <folly/dynamic.h>
|
|
4
4
|
#include <react/debug/react_native_assert.h>
|
|
5
5
|
#include <react/renderer/components/rnreanimated/Props.h>
|
|
6
|
+
#include <react/renderer/components/view/ViewProps.h>
|
|
6
7
|
#include <react/renderer/mounting/MountingOverrideDelegate.h>
|
|
7
8
|
#include <react/renderer/mounting/ShadowView.h>
|
|
8
9
|
#include <reanimated/LayoutAnimations/LayoutAnimationsManager.h>
|
|
@@ -59,11 +60,18 @@ struct Snapshot {
|
|
|
59
60
|
};
|
|
60
61
|
|
|
61
62
|
typedef enum class ExitingState : std::uint8_t {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
// React still renders the view
|
|
64
|
+
LIVE = 1,
|
|
65
|
+
// React deletes the view in the current transaction and the proxy has not decided how it exits yet
|
|
66
|
+
DECISION_PENDING = 2,
|
|
67
|
+
// withheld until its exiting descendants finish
|
|
68
|
+
WAITING = 3,
|
|
69
|
+
// withheld while its own exiting animation runs
|
|
70
|
+
ANIMATING = 4,
|
|
71
|
+
// its exiting animation finished, it is torn down at the next flush
|
|
72
|
+
COMPLETED = 5,
|
|
73
|
+
// the proxy has emitted its Remove and Delete
|
|
74
|
+
TORN_DOWN = 6,
|
|
67
75
|
} ExitingState;
|
|
68
76
|
|
|
69
77
|
struct MutationNode;
|
|
@@ -102,17 +110,18 @@ struct LightNode {
|
|
|
102
110
|
folly::dynamic accumulatedRawProps = nullptr;
|
|
103
111
|
bool propsNeedResolve = false;
|
|
104
112
|
#endif
|
|
105
|
-
ExitingState state = ExitingState::
|
|
113
|
+
ExitingState state = ExitingState::LIVE;
|
|
106
114
|
std::weak_ptr<LightNode> parent;
|
|
107
115
|
std::vector<std::shared_ptr<LightNode>> children;
|
|
108
116
|
int exitingChildrenCount = 0;
|
|
109
117
|
|
|
110
118
|
bool isExiting() const {
|
|
111
|
-
return state != ExitingState::
|
|
119
|
+
return state != ExitingState::LIVE;
|
|
112
120
|
}
|
|
113
121
|
|
|
114
122
|
void setExitingState(ExitingState newState) {
|
|
115
|
-
|
|
123
|
+
react_native_assert(newState != ExitingState::LIVE && "A light node never becomes live again");
|
|
124
|
+
const bool startsExiting = !isExiting();
|
|
116
125
|
state = newState;
|
|
117
126
|
if (!startsExiting) {
|
|
118
127
|
return;
|
|
@@ -255,6 +264,15 @@ static inline std::shared_ptr<LightNode> findParentRNSScreen(const std::shared_p
|
|
|
255
264
|
return current;
|
|
256
265
|
}
|
|
257
266
|
|
|
267
|
+
static inline bool isInSubtree(std::shared_ptr<LightNode> node, const std::shared_ptr<LightNode> &root) {
|
|
268
|
+
for (; node; node = node->parent.lock()) {
|
|
269
|
+
if (node == root) {
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
|
|
258
276
|
static inline bool isSETBoundary(const std::shared_ptr<LightNode> &node) {
|
|
259
277
|
return !std::strcmp(node->current.componentName, "REASharedTransitionBoundary");
|
|
260
278
|
}
|
|
@@ -283,4 +301,13 @@ static inline bool hasLayoutChanged(const ShadowViewMutation &mutation) {
|
|
|
283
301
|
return mutation.oldChildShadowView.layoutMetrics.frame != mutation.newChildShadowView.layoutMetrics.frame;
|
|
284
302
|
}
|
|
285
303
|
|
|
304
|
+
static inline bool isViewKind(const ShadowView &view) {
|
|
305
|
+
return view.traits.check(ShadowNodeTraits::Trait::ViewKind);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
static inline const ViewProps &getViewProps(const ShadowView &view) {
|
|
309
|
+
react_native_assert(isViewKind(view) && "Only ViewKind views have ViewProps");
|
|
310
|
+
return static_cast<const ViewProps &>(*view.props);
|
|
311
|
+
}
|
|
312
|
+
|
|
286
313
|
} // namespace reanimated
|
|
@@ -90,8 +90,8 @@ class PropsDiffer {
|
|
|
90
90
|
explicit PropsDiffer(jsi::Runtime &rt, const ShadowView &sourceView, const ShadowView &targetView)
|
|
91
91
|
: sourceView_(sourceView),
|
|
92
92
|
targetView_(targetView),
|
|
93
|
-
sourceViewProps_(
|
|
94
|
-
targetViewProps_(
|
|
93
|
+
sourceViewProps_(getViewProps(sourceView)),
|
|
94
|
+
targetViewProps_(getViewProps(targetView)),
|
|
95
95
|
sourceValues_(rt),
|
|
96
96
|
targetValues_(rt) {}
|
|
97
97
|
|
|
@@ -59,7 +59,7 @@ void LayoutAnimationsProxy::findSharedElementsOnScreen(
|
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
61
|
std::optional<SharedTag> sharedTag;
|
|
62
|
-
{
|
|
62
|
+
if (node->current.traits.check(ShadowNodeTraits::Trait::ViewKind)) {
|
|
63
63
|
auto lock = std::unique_lock<std::mutex>(sharedTransitionManager_->mutex_);
|
|
64
64
|
const auto it = sharedTransitionManager_->tagToName_.find(node->current.tag);
|
|
65
65
|
if (it != sharedTransitionManager_->tagToName_.end()) {
|
|
@@ -316,6 +316,7 @@ void LayoutAnimationsProxy::overrideTransform(
|
|
|
316
316
|
if (!transform) {
|
|
317
317
|
return;
|
|
318
318
|
}
|
|
319
|
+
react_native_assert(isViewKind(shadowView) && "Only ViewKind views have ViewProps");
|
|
319
320
|
#ifdef ANDROID
|
|
320
321
|
auto array = folly::dynamic::array(folly::dynamic::object("matrix", transform->operator folly::dynamic()));
|
|
321
322
|
const folly::dynamic newTransformDynamic = folly::dynamic::object("transform", array);
|
|
@@ -492,9 +493,10 @@ void LayoutAnimationsProxy::hideTransitioningViews(
|
|
|
492
493
|
}
|
|
493
494
|
|
|
494
495
|
// The hide in hideTransitioningViews is not stored in the light tree, so a
|
|
495
|
-
// later Update for the same view carries full opacity and would show
|
|
496
|
-
// again. Force opacity 0 on every outgoing Update for a
|
|
497
|
-
// restore in cleanupSharedTransitions removes its tag
|
|
496
|
+
// later Update or Insert for the same view carries full opacity and would show
|
|
497
|
+
// the view again. Force opacity 0 on every outgoing Update and Insert for a
|
|
498
|
+
// hidden view until the restore in cleanupSharedTransitions removes its tag
|
|
499
|
+
// from hiddenViewTags_.
|
|
498
500
|
void LayoutAnimationsProxy::keepTransitioningViewsHidden(
|
|
499
501
|
ShadowViewMutationList &filteredMutations,
|
|
500
502
|
const PropsParserContext &propsParserContext) const {
|
|
@@ -502,11 +504,9 @@ void LayoutAnimationsProxy::keepTransitioningViewsHidden(
|
|
|
502
504
|
return;
|
|
503
505
|
}
|
|
504
506
|
for (auto &mutation : filteredMutations) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
cloneViewWithoutOpacity(mutation.newChildShadowView, propsParserContext),
|
|
509
|
-
mutation.parentTag);
|
|
507
|
+
const bool writesProps = mutation.type == ShadowViewMutation::Update || mutation.type == ShadowViewMutation::Insert;
|
|
508
|
+
if (writesProps && hiddenViewTags_.contains(mutation.newChildShadowView.tag)) {
|
|
509
|
+
mutation.newChildShadowView = cloneViewWithoutOpacity(mutation.newChildShadowView, propsParserContext);
|
|
510
510
|
}
|
|
511
511
|
}
|
|
512
512
|
}
|
|
@@ -642,7 +642,7 @@ void LayoutAnimationsProxy::cleanupSharedTransitions(
|
|
|
642
642
|
if (!parent) {
|
|
643
643
|
continue;
|
|
644
644
|
}
|
|
645
|
-
const auto opacity =
|
|
645
|
+
const auto opacity = getViewProps(view).opacity;
|
|
646
646
|
filteredMutations.push_back(ShadowViewMutation::UpdateMutation(
|
|
647
647
|
cloneViewWithoutOpacity(view, propsParserContext),
|
|
648
648
|
cloneViewWithOpacity(view, opacity, propsParserContext),
|
|
@@ -721,7 +721,7 @@ std::optional<Transform> LayoutAnimationsProxy::parseParentTransforms(
|
|
|
721
721
|
while (currentNode) {
|
|
722
722
|
resolveLightNodeProps(currentNode);
|
|
723
723
|
const auto &view = useViewsOnScreen ? viewOnScreen(currentNode) : currentNode->current;
|
|
724
|
-
const auto &props =
|
|
724
|
+
const auto &props = getViewProps(view);
|
|
725
725
|
auto origin = props.transformOrigin;
|
|
726
726
|
const auto &viewSize = view.layoutMetrics.frame.size;
|
|
727
727
|
if (origin.xy[0].unit == facebook::react::UnitType::Percent) {
|
|
@@ -78,6 +78,14 @@ constexpr bool shouldUseSynchronousUpdatesInPerformOperations() {
|
|
|
78
78
|
}
|
|
79
79
|
#endif
|
|
80
80
|
|
|
81
|
+
std::shared_ptr<SynchronousWritesTracker> makeSynchronousWritesTracker() {
|
|
82
|
+
if constexpr (
|
|
83
|
+
shouldUseSynchronousUpdatesInPerformOperations() && !StaticFeatureFlags::getFlag("USE_ANIMATION_BACKEND")) {
|
|
84
|
+
return std::make_shared<SynchronousWritesTracker>();
|
|
85
|
+
}
|
|
86
|
+
return nullptr;
|
|
87
|
+
}
|
|
88
|
+
|
|
81
89
|
std::pair<UpdatesBatch, UpdatesBatch> partitionUpdates(UpdatesBatch &&updatesBatch, const bool allowPartialUpdates) {
|
|
82
90
|
const auto isSynchronous = [&](const std::string &keyStr, [[maybe_unused]] const folly::dynamic &value) {
|
|
83
91
|
if (!isSynchronousPropName(keyStr)) {
|
|
@@ -162,6 +170,7 @@ ReanimatedModuleProxy::ReanimatedModuleProxy(
|
|
|
162
170
|
getAnimationTimestamp_(platformDepMethodsHolder.getAnimationTimestamp),
|
|
163
171
|
#ifdef __APPLE__
|
|
164
172
|
forceScreenSnapshot_(platformDepMethodsHolder.forceScreenSnapshotFunction),
|
|
173
|
+
readMountedViewProps_(platformDepMethodsHolder.readMountedViewPropsFunction),
|
|
165
174
|
#endif
|
|
166
175
|
staticPropsRegistry_(std::make_shared<StaticPropsRegistry>()),
|
|
167
176
|
updatesRegistryManager_(std::make_shared<UpdatesRegistryManager>(staticPropsRegistry_)),
|
|
@@ -193,6 +202,7 @@ ReanimatedModuleProxy::ReanimatedModuleProxy(
|
|
|
193
202
|
#ifdef ANDROID
|
|
194
203
|
filterUnmountedTagsFunction_(platformDepMethodsHolder.filterUnmountedTagsFunction),
|
|
195
204
|
#endif // ANDROID
|
|
205
|
+
synchronousWritesTracker_(makeSynchronousWritesTracker()),
|
|
196
206
|
subscribeForKeyboardEventsFunction_(platformDepMethodsHolder.subscribeForKeyboardEvents),
|
|
197
207
|
unsubscribeFromKeyboardEventsFunction_(platformDepMethodsHolder.unsubscribeFromKeyboardEvents) {
|
|
198
208
|
// Add registries in order of their priority (from the lowest to the
|
|
@@ -868,6 +878,30 @@ void ReanimatedModuleProxy::performNonLayoutOperations() {
|
|
|
868
878
|
applySynchronousUpdates(partitionUpdates(std::move(updatesBatch), true).first);
|
|
869
879
|
}
|
|
870
880
|
|
|
881
|
+
bool ReanimatedModuleProxy::hasSynchronousWritesTracker() const {
|
|
882
|
+
return synchronousWritesTracker_ != nullptr;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
void ReanimatedModuleProxy::rewriteSynchronousProps() {
|
|
886
|
+
if (!synchronousWritesTracker_) {
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
const auto families = synchronousWritesTracker_->getFamiliesToRewrite();
|
|
890
|
+
if (!families.empty()) {
|
|
891
|
+
UpdatesBatch registryValues;
|
|
892
|
+
{
|
|
893
|
+
auto lock = updatesRegistryManager_->lock();
|
|
894
|
+
for (const auto &family : families) {
|
|
895
|
+
folly::dynamic props = folly::dynamic::object;
|
|
896
|
+
updatesRegistryManager_->mergeRegistryProps(family->getTag(), props);
|
|
897
|
+
registryValues.emplace_back(family, std::move(props));
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
writeSynchronousPropsToViews(partitionUpdates(std::move(registryValues), true).first);
|
|
901
|
+
}
|
|
902
|
+
synchronousWritesTracker_->onRewrite();
|
|
903
|
+
}
|
|
904
|
+
|
|
871
905
|
AnimationMutations ReanimatedModuleProxy::collectNonLayoutAnimationUpdates() {
|
|
872
906
|
ReanimatedSystraceSection s("ReanimatedModuleProxy::collectNonLayoutAnimationUpdates");
|
|
873
907
|
|
|
@@ -1040,7 +1074,13 @@ void ReanimatedModuleProxy::applySynchronousUpdates(const UpdatesBatch &synchron
|
|
|
1040
1074
|
layoutAnimationsProxyRegistry_->applySynchronousProps(
|
|
1041
1075
|
synchronousUpdatesBatch, DynamicFeatureFlags::getFlag("TRACK_SYNCHRONOUS_PROPS_IN_LAYOUT_ANIMATIONS"));
|
|
1042
1076
|
}
|
|
1077
|
+
if (synchronousWritesTracker_) {
|
|
1078
|
+
synchronousWritesTracker_->onSynchronousWrite(synchronousUpdatesBatch);
|
|
1079
|
+
}
|
|
1080
|
+
writeSynchronousPropsToViews(synchronousUpdatesBatch);
|
|
1081
|
+
}
|
|
1043
1082
|
|
|
1083
|
+
void ReanimatedModuleProxy::writeSynchronousPropsToViews(const UpdatesBatch &synchronousUpdatesBatch) {
|
|
1044
1084
|
#ifdef ANDROID
|
|
1045
1085
|
if (!synchronousUpdatesBatch.empty()) {
|
|
1046
1086
|
serializeSynchronousPropsToBuffers(
|
|
@@ -1201,10 +1241,19 @@ void ReanimatedModuleProxy::initializeFabric(const std::shared_ptr<UIManager> &u
|
|
|
1201
1241
|
// TODO: with the animation backend we still need a way to handleNodeRemovals,
|
|
1202
1242
|
// for now we leave this to leak the memory, a fix will come in a follow-up
|
|
1203
1243
|
mountHook_ = std::make_shared<ReanimatedMountHook>(
|
|
1204
|
-
uiManager_,
|
|
1244
|
+
uiManager_,
|
|
1245
|
+
updatesRegistryManager_,
|
|
1246
|
+
viewStylesRepository_,
|
|
1247
|
+
layoutAnimationsProxyRegistry_,
|
|
1248
|
+
synchronousWritesTracker_,
|
|
1249
|
+
request);
|
|
1205
1250
|
|
|
1206
1251
|
commitHook_ = std::make_shared<ReanimatedCommitHook>(
|
|
1207
|
-
uiManager_,
|
|
1252
|
+
uiManager_,
|
|
1253
|
+
updatesRegistryManager_,
|
|
1254
|
+
viewStylesRepository_,
|
|
1255
|
+
layoutAnimationsProxyRegistry_,
|
|
1256
|
+
synchronousWritesTracker_);
|
|
1208
1257
|
}
|
|
1209
1258
|
|
|
1210
1259
|
void ReanimatedModuleProxy::initializeLayoutAnimationsProxyRegistry() {
|
|
@@ -1234,6 +1283,9 @@ void ReanimatedModuleProxy::initializeLayoutAnimationsProxyRegistry() {
|
|
|
1234
1283
|
});
|
|
1235
1284
|
};
|
|
1236
1285
|
|
|
1286
|
+
constexpr bool keepsSynchronousPropsInReinserts =
|
|
1287
|
+
shouldUseSynchronousUpdatesInPerformOperations() && !StaticFeatureFlags::getFlag("USE_ANIMATION_BACKEND");
|
|
1288
|
+
|
|
1237
1289
|
const LayoutAnimationsProxyDependencies dependencies{
|
|
1238
1290
|
layoutAnimationsManager_,
|
|
1239
1291
|
componentDescriptorRegistry,
|
|
@@ -1245,9 +1297,11 @@ void ReanimatedModuleProxy::initializeLayoutAnimationsProxyRegistry() {
|
|
|
1245
1297
|
#ifdef ANDROID
|
|
1246
1298
|
filterUnmountedTagsFunction_,
|
|
1247
1299
|
jsInvoker_,
|
|
1300
|
+
keepsSynchronousPropsInReinserts ? makeSynchronousPropsReader() : nullptr,
|
|
1248
1301
|
#endif
|
|
1249
1302
|
#ifdef __APPLE__
|
|
1250
1303
|
forceScreenSnapshot_,
|
|
1304
|
+
keepsSynchronousPropsInReinserts ? readMountedViewProps_ : nullptr,
|
|
1251
1305
|
#endif
|
|
1252
1306
|
};
|
|
1253
1307
|
|
|
@@ -1258,6 +1312,25 @@ void ReanimatedModuleProxy::initializeLayoutAnimationsProxyRegistry() {
|
|
|
1258
1312
|
}
|
|
1259
1313
|
}
|
|
1260
1314
|
|
|
1315
|
+
#ifdef ANDROID
|
|
1316
|
+
SynchronousPropsReader ReanimatedModuleProxy::makeSynchronousPropsReader() {
|
|
1317
|
+
if (!ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid()) {
|
|
1318
|
+
return nullptr;
|
|
1319
|
+
}
|
|
1320
|
+
return [weakThis = weak_from_this()](const Tag tag) {
|
|
1321
|
+
const auto strongThis = weakThis.lock();
|
|
1322
|
+
return strongThis ? strongThis->readSynchronousProps(tag) : folly::dynamic::object();
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
folly::dynamic ReanimatedModuleProxy::readSynchronousProps(const Tag tag) {
|
|
1327
|
+
folly::dynamic synchronousProps = folly::dynamic::object;
|
|
1328
|
+
auto lock = updatesRegistryManager_->lock();
|
|
1329
|
+
updatesRegistryManager_->mergeRegistryProps(tag, synchronousProps, isSynchronousPropName);
|
|
1330
|
+
return synchronousProps;
|
|
1331
|
+
}
|
|
1332
|
+
#endif // ANDROID
|
|
1333
|
+
|
|
1261
1334
|
#ifdef IS_REANIMATED_EXAMPLE_APP
|
|
1262
1335
|
|
|
1263
1336
|
std::string format(bool b) {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
#include <reanimated/Fabric/ShadowTreeCloner.h>
|
|
22
22
|
#include <reanimated/Fabric/updates/AnimatedPropsRegistry.h>
|
|
23
23
|
#include <reanimated/Fabric/updates/OperationsLoop.h>
|
|
24
|
+
#include <reanimated/Fabric/updates/SynchronousWritesTracker.h>
|
|
24
25
|
#include <reanimated/Fabric/updates/UpdatesRegistryManager.h>
|
|
25
26
|
#include <reanimated/LayoutAnimations/LayoutAnimationsManager.h>
|
|
26
27
|
#include <reanimated/LayoutAnimations/LayoutAnimationsProxyRegistry.h>
|
|
@@ -108,6 +109,8 @@ class ReanimatedModuleProxy : public std::enable_shared_from_this<ReanimatedModu
|
|
|
108
109
|
|
|
109
110
|
void performOperations();
|
|
110
111
|
void performNonLayoutOperations();
|
|
112
|
+
bool hasSynchronousWritesTracker() const;
|
|
113
|
+
void rewriteSynchronousProps();
|
|
111
114
|
void flushLayoutAnimationOperations();
|
|
112
115
|
void executeLayoutAnimationsRequests();
|
|
113
116
|
|
|
@@ -205,6 +208,11 @@ class ReanimatedModuleProxy : public std::enable_shared_from_this<ReanimatedModu
|
|
|
205
208
|
|
|
206
209
|
void commitUpdates(const std::unordered_map<SurfaceId, PropsMap> &propsMapBySurface);
|
|
207
210
|
void applySynchronousUpdates(const UpdatesBatch &synchronousUpdatesBatch);
|
|
211
|
+
void writeSynchronousPropsToViews(const UpdatesBatch &synchronousUpdatesBatch);
|
|
212
|
+
#ifdef ANDROID
|
|
213
|
+
SynchronousPropsReader makeSynchronousPropsReader();
|
|
214
|
+
folly::dynamic readSynchronousProps(Tag tag);
|
|
215
|
+
#endif
|
|
208
216
|
|
|
209
217
|
std::shared_ptr<UIManagerAnimationBackend> getAnimationBackend();
|
|
210
218
|
AnimationMutations runGrandCallback(AnimationTimestamp timestamp, GrandCallbackSource source);
|
|
@@ -239,6 +247,7 @@ class ReanimatedModuleProxy : public std::enable_shared_from_this<ReanimatedModu
|
|
|
239
247
|
|
|
240
248
|
#ifdef __APPLE__
|
|
241
249
|
ForceScreenSnapshotFunction forceScreenSnapshot_;
|
|
250
|
+
ReadMountedViewPropsFunction readMountedViewProps_;
|
|
242
251
|
#endif
|
|
243
252
|
const std::shared_ptr<StaticPropsRegistry> staticPropsRegistry_;
|
|
244
253
|
const std::shared_ptr<UpdatesRegistryManager> updatesRegistryManager_;
|
|
@@ -253,9 +262,10 @@ class ReanimatedModuleProxy : public std::enable_shared_from_this<ReanimatedModu
|
|
|
253
262
|
|
|
254
263
|
const SynchronouslyUpdateUIPropsFunction synchronouslyUpdateUIPropsFunction_;
|
|
255
264
|
const PreserveMountedTagsFunction filterUnmountedTagsFunction_;
|
|
265
|
+
const std::shared_ptr<SynchronousWritesTracker> synchronousWritesTracker_;
|
|
256
266
|
|
|
257
267
|
#ifdef ANDROID
|
|
258
|
-
// Reused across `
|
|
268
|
+
// Reused across `writeSynchronousPropsToViews` calls to avoid per-frame heap
|
|
259
269
|
// allocations. Access only on the UI thread.
|
|
260
270
|
std::vector<int> synchronousPropsIntBuffer_;
|
|
261
271
|
std::vector<double> synchronousPropsDoubleBuffer_;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
#include <folly/dynamic.h>
|
|
8
8
|
#include <jsi/jsi.h>
|
|
9
|
+
#include <react/renderer/core/Props.h>
|
|
9
10
|
#include <react/renderer/core/ReactPrimitives.h>
|
|
10
11
|
|
|
11
12
|
#include <memory>
|
|
@@ -47,7 +48,8 @@ using KeyboardEventSubscribeFunction = std::function<int(std::function<void(int,
|
|
|
47
48
|
using KeyboardEventUnsubscribeFunction = std::function<void(int)>;
|
|
48
49
|
using MaybeFlushUIUpdatesQueueFunction = std::function<void()>;
|
|
49
50
|
|
|
50
|
-
using ForceScreenSnapshotFunction = std::function<
|
|
51
|
+
using ForceScreenSnapshotFunction = std::function<bool(Tag tag)>;
|
|
52
|
+
using ReadMountedViewPropsFunction = std::function<Props::Shared(Tag tag)>;
|
|
51
53
|
|
|
52
54
|
using PlatformAttachPseudoSelectorFunction = std::function<void(Tag, PseudoSelector, std::function<void(bool)>)>;
|
|
53
55
|
using PlatformDetachPseudoSelectorFunction = std::function<void(Tag, PseudoSelector)>;
|
|
@@ -59,6 +61,7 @@ struct PlatformDepMethodsHolder {
|
|
|
59
61
|
#endif // ANDROID
|
|
60
62
|
#ifdef __APPLE__
|
|
61
63
|
ForceScreenSnapshotFunction forceScreenSnapshotFunction;
|
|
64
|
+
ReadMountedViewPropsFunction readMountedViewPropsFunction;
|
|
62
65
|
#endif
|
|
63
66
|
SynchronouslyUpdateUIPropsFunction synchronouslyUpdateUIPropsFunction;
|
|
64
67
|
GetAnimationTimestampFunction getAnimationTimestamp;
|
|
@@ -138,6 +138,14 @@ void NativeProxy::performNonLayoutOperations() {
|
|
|
138
138
|
reanimatedModuleProxy_->performNonLayoutOperations();
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
bool NativeProxy::hasSynchronousWritesTracker() {
|
|
142
|
+
return reanimatedModuleProxy_->hasSynchronousWritesTracker();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
void NativeProxy::rewriteSynchronousProps() {
|
|
146
|
+
reanimatedModuleProxy_->rewriteSynchronousProps();
|
|
147
|
+
}
|
|
148
|
+
|
|
141
149
|
bool NativeProxy::getIsReducedMotion() {
|
|
142
150
|
static const auto method = getJniMethod<jboolean()>("getIsReducedMotion");
|
|
143
151
|
return method(javaPart_.get());
|
|
@@ -151,9 +159,11 @@ void NativeProxy::registerNatives() {
|
|
|
151
159
|
registerHybrid(
|
|
152
160
|
{makeNativeMethod("initHybrid", NativeProxy::initHybrid),
|
|
153
161
|
makeNativeMethod("installJSIBindings", NativeProxy::installJSIBindings),
|
|
154
|
-
makeNativeMethod("
|
|
155
|
-
makeNativeMethod("
|
|
156
|
-
makeNativeMethod("
|
|
162
|
+
makeNativeMethod("isAnyHandlerWaitingForEventCpp", NativeProxy::isAnyHandlerWaitingForEvent),
|
|
163
|
+
makeNativeMethod("performOperationsCpp", NativeProxy::performOperations),
|
|
164
|
+
makeNativeMethod("performNonLayoutOperationsCpp", NativeProxy::performNonLayoutOperations),
|
|
165
|
+
makeNativeMethod("hasSynchronousWritesTracker", NativeProxy::hasSynchronousWritesTracker),
|
|
166
|
+
makeNativeMethod("rewriteSynchronousProps", NativeProxy::rewriteSynchronousProps),
|
|
157
167
|
makeNativeMethod("invalidateCpp", NativeProxy::invalidateCpp),
|
|
158
168
|
makeNativeMethod("toggleSlowAnimationsOnUIRuntime", NativeProxy::toggleSlowAnimationsOnUIRuntime)});
|
|
159
169
|
}
|
|
@@ -55,6 +55,8 @@ class NativeProxy : public jni::HybridClass<NativeProxy>, std::enable_shared_fro
|
|
|
55
55
|
bool isAnyHandlerWaitingForEvent(const std::string &eventName, const int emitterReactTag);
|
|
56
56
|
void performOperations();
|
|
57
57
|
void performNonLayoutOperations();
|
|
58
|
+
bool hasSynchronousWritesTracker();
|
|
59
|
+
void rewriteSynchronousProps();
|
|
58
60
|
bool getIsReducedMotion();
|
|
59
61
|
void requestRender(std::function<void(double)> onRender);
|
|
60
62
|
void registerEventHandler();
|
|
@@ -8,8 +8,11 @@ import com.facebook.jni.HybridData
|
|
|
8
8
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
9
9
|
import com.facebook.react.bridge.NativeModule
|
|
10
10
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
11
|
+
import com.facebook.react.bridge.UIManager
|
|
12
|
+
import com.facebook.react.bridge.UIManagerListener
|
|
11
13
|
import com.facebook.react.bridge.UiThreadUtil
|
|
12
14
|
import com.facebook.react.common.annotations.FrameworkAPI
|
|
15
|
+
import com.facebook.react.common.annotations.UnstableReactNativeAPI
|
|
13
16
|
import com.facebook.react.fabric.FabricUIManager
|
|
14
17
|
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl
|
|
15
18
|
import com.facebook.react.uimanager.IllegalViewOperationException
|
|
@@ -30,6 +33,8 @@ import com.swmansion.reanimated.sensor.ReanimatedSensorContainer
|
|
|
30
33
|
import com.swmansion.reanimated.sensor.ReanimatedSensorType
|
|
31
34
|
import java.lang.ref.WeakReference
|
|
32
35
|
import java.util.concurrent.atomic.AtomicBoolean
|
|
36
|
+
import java.util.concurrent.locks.ReentrantReadWriteLock
|
|
37
|
+
import kotlin.concurrent.write
|
|
33
38
|
|
|
34
39
|
@Suppress("KotlinJniMissingFunction")
|
|
35
40
|
@OptIn(FrameworkAPI::class)
|
|
@@ -64,10 +69,27 @@ open class NativeProxy {
|
|
|
64
69
|
*/
|
|
65
70
|
private val mInvalidated = AtomicBoolean(false)
|
|
66
71
|
|
|
72
|
+
// Guards the C++ state that invalidateCpp() destroys against calls from the UI thread.
|
|
73
|
+
private val mNativeStateLock = ReentrantReadWriteLock()
|
|
74
|
+
|
|
67
75
|
@field:DoNotStrip
|
|
68
76
|
@Suppress("unused")
|
|
69
77
|
private val mHybridData: HybridData
|
|
70
78
|
|
|
79
|
+
@OptIn(UnstableReactNativeAPI::class)
|
|
80
|
+
private val mountListener =
|
|
81
|
+
object : UIManagerListener {
|
|
82
|
+
override fun willDispatchViewUpdates(uiManager: UIManager) = Unit
|
|
83
|
+
|
|
84
|
+
override fun willMountItems(uiManager: UIManager) = Unit
|
|
85
|
+
|
|
86
|
+
override fun didMountItems(uiManager: UIManager) = ifNotInvalidated(Unit) { rewriteSynchronousProps() }
|
|
87
|
+
|
|
88
|
+
override fun didDispatchMountItems(uiManager: UIManager) = Unit
|
|
89
|
+
|
|
90
|
+
override fun didScheduleMountItems(uiManager: UIManager) = Unit
|
|
91
|
+
}
|
|
92
|
+
|
|
71
93
|
constructor(context: ReactApplicationContext, nodesManager: NodesManager) {
|
|
72
94
|
context.assertOnJSQueueThread()
|
|
73
95
|
|
|
@@ -109,6 +131,10 @@ open class NativeProxy {
|
|
|
109
131
|
if (BuildConfig.DEBUG) {
|
|
110
132
|
checkCppVersion() // injectCppVersion should be called during initHybrid above
|
|
111
133
|
}
|
|
134
|
+
if (hasSynchronousWritesTracker()) {
|
|
135
|
+
@OptIn(UnstableReactNativeAPI::class)
|
|
136
|
+
mFabricUIManager.addUIManagerEventListener(mountListener)
|
|
137
|
+
}
|
|
112
138
|
}
|
|
113
139
|
|
|
114
140
|
private external fun initHybrid(
|
|
@@ -117,31 +143,67 @@ open class NativeProxy {
|
|
|
117
143
|
fabricUIManager: FabricUIManager,
|
|
118
144
|
): HybridData
|
|
119
145
|
|
|
120
|
-
external fun
|
|
146
|
+
private external fun isAnyHandlerWaitingForEventCpp(
|
|
121
147
|
eventName: String,
|
|
122
148
|
emitterReactTag: Int,
|
|
123
149
|
): Boolean
|
|
124
150
|
|
|
125
|
-
external fun
|
|
151
|
+
private external fun performOperationsCpp()
|
|
152
|
+
|
|
153
|
+
private external fun performNonLayoutOperationsCpp()
|
|
154
|
+
|
|
155
|
+
private external fun hasSynchronousWritesTracker(): Boolean
|
|
126
156
|
|
|
127
|
-
external fun
|
|
157
|
+
private external fun rewriteSynchronousProps()
|
|
128
158
|
|
|
129
159
|
external fun installJSIBindings()
|
|
130
160
|
|
|
131
161
|
private external fun invalidateCpp()
|
|
132
162
|
|
|
133
|
-
external fun toggleSlowAnimationsOnUIRuntime()
|
|
163
|
+
private external fun toggleSlowAnimationsOnUIRuntime()
|
|
134
164
|
|
|
135
165
|
protected fun getHybridData(): HybridData = mHybridData
|
|
136
166
|
|
|
167
|
+
// tryLock, because the UI thread may hold the UI runtime lock, which could deadlock with invalidate()
|
|
168
|
+
private inline fun <T> ifNotInvalidated(
|
|
169
|
+
fallback: T,
|
|
170
|
+
block: () -> T,
|
|
171
|
+
): T {
|
|
172
|
+
val readLock = mNativeStateLock.readLock()
|
|
173
|
+
if (!readLock.tryLock()) {
|
|
174
|
+
return fallback
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
if (mInvalidated.get()) {
|
|
178
|
+
return fallback
|
|
179
|
+
}
|
|
180
|
+
return block()
|
|
181
|
+
} finally {
|
|
182
|
+
readLock.unlock()
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
fun isAnyHandlerWaitingForEvent(
|
|
187
|
+
eventName: String,
|
|
188
|
+
emitterReactTag: Int,
|
|
189
|
+
): Boolean = ifNotInvalidated(false) { isAnyHandlerWaitingForEventCpp(eventName, emitterReactTag) }
|
|
190
|
+
|
|
191
|
+
fun performOperations() = ifNotInvalidated(Unit) { performOperationsCpp() }
|
|
192
|
+
|
|
193
|
+
fun performNonLayoutOperations() = ifNotInvalidated(Unit) { performNonLayoutOperationsCpp() }
|
|
194
|
+
|
|
137
195
|
fun invalidate() {
|
|
138
196
|
if (mInvalidated.getAndSet(true)) {
|
|
139
197
|
return
|
|
140
198
|
}
|
|
199
|
+
@OptIn(UnstableReactNativeAPI::class)
|
|
200
|
+
mFabricUIManager.removeUIManagerEventListener(mountListener)
|
|
141
201
|
pseudoSelectorManager.invalidate()
|
|
142
202
|
cssPlatformTransitionsManager.invalidate()
|
|
143
|
-
|
|
144
|
-
|
|
203
|
+
mNativeStateLock.write {
|
|
204
|
+
if (mHybridData.isValid) {
|
|
205
|
+
invalidateCpp()
|
|
206
|
+
}
|
|
145
207
|
}
|
|
146
208
|
}
|
|
147
209
|
|
|
@@ -152,7 +214,7 @@ open class NativeProxy {
|
|
|
152
214
|
}
|
|
153
215
|
mNodesManager!!.enableSlowAnimations(slowAnimationsEnabled, animationsDragFactor)
|
|
154
216
|
cssPlatformTransitionsManager.enableSlowAnimations(slowAnimationsEnabled, animationsDragFactor)
|
|
155
|
-
toggleSlowAnimationsOnUIRuntime()
|
|
217
|
+
ifNotInvalidated(Unit) { toggleSlowAnimationsOnUIRuntime() }
|
|
156
218
|
}
|
|
157
219
|
|
|
158
220
|
private fun addDevMenuOption() {
|
|
@@ -175,8 +237,12 @@ open class NativeProxy {
|
|
|
175
237
|
|
|
176
238
|
@DoNotStrip
|
|
177
239
|
fun requestRender(callback: AnimationFrameCallback) {
|
|
240
|
+
val guardedCallback =
|
|
241
|
+
object : NodesManager.OnAnimationFrame {
|
|
242
|
+
override fun onAnimationFrame(timestampMs: Double) = ifNotInvalidated(Unit) { callback.onAnimationFrame(timestampMs) }
|
|
243
|
+
}
|
|
178
244
|
UiThreadUtil.assertOnUiThread()
|
|
179
|
-
mNodesManager!!.postOnAnimation(
|
|
245
|
+
mNodesManager!!.postOnAnimation(guardedCallback)
|
|
180
246
|
}
|
|
181
247
|
|
|
182
248
|
@DoNotStrip fun getReanimatedJavaVersion(): String = BuildConfig.REANIMATED_VERSION_JAVA
|
|
@@ -250,6 +316,10 @@ open class NativeProxy {
|
|
|
250
316
|
}.apply { isAccessible = true }
|
|
251
317
|
}
|
|
252
318
|
|
|
319
|
+
private val getViewExistsMethod by lazy {
|
|
320
|
+
runCatching { mountingManager.javaClass.getMethod("getViewExists", Int::class.javaPrimitiveType) }.getOrNull()
|
|
321
|
+
}
|
|
322
|
+
|
|
253
323
|
@DoNotStrip
|
|
254
324
|
fun synchronouslyUpdateUIProps(
|
|
255
325
|
intBuffer: IntArray,
|
|
@@ -258,9 +328,13 @@ open class NativeProxy {
|
|
|
258
328
|
cssPlatformTransitionsManager.onPropsWrittenSynchronously()
|
|
259
329
|
SynchronousPropsBufferParser.parse(intBuffer, doubleBuffer) { viewTag, props ->
|
|
260
330
|
try {
|
|
331
|
+
// The props stay in the registry, and the commit hook applies them to the shadow tree.
|
|
332
|
+
if (getViewExistsMethod?.invoke(mountingManager, viewTag) == false) {
|
|
333
|
+
return@parse
|
|
334
|
+
}
|
|
261
335
|
updatePropsSynchronouslyMethod.invoke(mountingManager, viewTag, props)
|
|
262
336
|
} catch (e: Exception) {
|
|
263
|
-
Log.w("Reanimated", "synchronouslyUpdateUIProps failed for tag $viewTag
|
|
337
|
+
Log.w("Reanimated", "synchronouslyUpdateUIProps failed for tag $viewTag: ${e.cause ?: e}")
|
|
264
338
|
}
|
|
265
339
|
}
|
|
266
340
|
}
|
|
@@ -330,6 +404,7 @@ open class NativeProxy {
|
|
|
330
404
|
fun registerEventHandler(handler: EventHandler) {
|
|
331
405
|
handler.mCustomEventNamesResolver = mNodesManager!!.getEventNameResolver()
|
|
332
406
|
handler.isInDrawPassProvider = { mNodesManager!!.isInDrawPass() }
|
|
407
|
+
handler.nativeCallGuard = { receive -> ifNotInvalidated(Unit, receive) }
|
|
333
408
|
mNodesManager!!.registerEventHandler(handler)
|
|
334
409
|
}
|
|
335
410
|
|
|
@@ -199,9 +199,7 @@ class NodesManager(
|
|
|
199
199
|
Trace.beginSection("onEventDispatch")
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
return
|
|
204
|
-
}
|
|
202
|
+
val nativeProxy = mNativeProxy ?: return
|
|
205
203
|
// Events can be dispatched from any thread so we have to make sure handleEvent is run from
|
|
206
204
|
// the UI thread.
|
|
207
205
|
if (UiThreadUtil.isOnUiThread()) {
|
|
@@ -213,7 +211,7 @@ class NodesManager(
|
|
|
213
211
|
} else {
|
|
214
212
|
val eventName = mCustomEventNamesResolver.resolveCustomEventName(event.eventName) ?: return
|
|
215
213
|
val viewTag = event.viewTag
|
|
216
|
-
val shouldSaveEvent =
|
|
214
|
+
val shouldSaveEvent = nativeProxy.isAnyHandlerWaitingForEvent(eventName, viewTag)
|
|
217
215
|
if (shouldSaveEvent) {
|
|
218
216
|
mEventQueue.offer(CopiedEvent(event))
|
|
219
217
|
}
|
|
@@ -14,6 +14,7 @@ class EventHandler : RCTModernEventEmitter {
|
|
|
14
14
|
|
|
15
15
|
var mCustomEventNamesResolver: UIManagerModule.CustomEventNamesResolver? = null
|
|
16
16
|
internal var isInDrawPassProvider: (() -> Boolean)? = null
|
|
17
|
+
internal var nativeCallGuard: (() -> Unit) -> Unit = { it() }
|
|
17
18
|
|
|
18
19
|
@DoNotStrip
|
|
19
20
|
private constructor(hybridData: HybridData) {
|
|
@@ -32,7 +33,7 @@ class EventHandler : RCTModernEventEmitter {
|
|
|
32
33
|
category: Int,
|
|
33
34
|
) {
|
|
34
35
|
val resolvedEventName = mCustomEventNamesResolver!!.resolveCustomEventName(eventName) ?: eventName
|
|
35
|
-
receiveEvent(resolvedEventName, targetTag, params, isInDrawPass())
|
|
36
|
+
nativeCallGuard { receiveEvent(resolvedEventName, targetTag, params, isInDrawPass()) }
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
override fun receiveEvent(
|
|
@@ -42,7 +43,7 @@ class EventHandler : RCTModernEventEmitter {
|
|
|
42
43
|
params: WritableMap?,
|
|
43
44
|
) {
|
|
44
45
|
val resolvedEventName = mCustomEventNamesResolver!!.resolveCustomEventName(eventName) ?: eventName
|
|
45
|
-
receiveEvent(resolvedEventName, targetTag, params, isInDrawPass())
|
|
46
|
+
nativeCallGuard { receiveEvent(resolvedEventName, targetTag, params, isInDrawPass()) }
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
override fun receiveEvent(
|
|
@@ -51,7 +52,7 @@ class EventHandler : RCTModernEventEmitter {
|
|
|
51
52
|
params: WritableMap?,
|
|
52
53
|
) {
|
|
53
54
|
val resolvedEventName = mCustomEventNamesResolver!!.resolveCustomEventName(eventName) ?: eventName
|
|
54
|
-
receiveEvent(resolvedEventName, targetTag, params, isInDrawPass())
|
|
55
|
+
nativeCallGuard { receiveEvent(resolvedEventName, targetTag, params, isInDrawPass()) }
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
override fun receiveTouches(
|
|
@@ -26,7 +26,7 @@ internal class ReanimatedSensor(
|
|
|
26
26
|
init {
|
|
27
27
|
val wm = reactContext.get()!!.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
|
28
28
|
val display = wm.defaultDisplay
|
|
29
|
-
listener = ReanimatedSensorListener(setter, interval.toDouble(), display)
|
|
29
|
+
listener = ReanimatedSensorListener(setter, interval.toDouble(), display, sensorType)
|
|
30
30
|
sensorManager =
|
|
31
31
|
reactContext.get()!!.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
|
32
32
|
this.interval = if (interval == -1) DEFAULT_INTERVAL else interval
|