LayoutAnimations: call onSuccess, onFailure callbacks
Summary: Hook up onSuccess and onFailure callbacks to LayoutAnimations. Note that in non-Fabric RN, onSuccess is not known to work in Android. I could not find any uses of onFailure and it's not documented, so for now, it is only called if the setup of the animation fails. Changelog: [Internal] Reviewed By: shergin Differential Revision: D22889352 fbshipit-source-id: 4306debb350388dd2b7a2cbfe295eb99723988e2
This commit is contained in:
Родитель
777957c6fb
Коммит
e3302eeeab
|
@ -114,7 +114,8 @@ class LayoutAnimationDelegateProxy : public LayoutAnimationStatusDelegate, publi
|
|||
|
||||
if (_layoutAnimationsEnabled) {
|
||||
_layoutAnimationDelegateProxy = std::make_shared<LayoutAnimationDelegateProxy>((__bridge void *)self);
|
||||
_animationDriver = std::make_shared<LayoutAnimationDriver>(_layoutAnimationDelegateProxy.get());
|
||||
_animationDriver =
|
||||
std::make_shared<LayoutAnimationDriver>(toolbox.runtimeExecutor, _layoutAnimationDelegateProxy.get());
|
||||
_uiRunLoopObserver =
|
||||
toolbox.mainRunLoopObserverFactory(RunLoopObserver::Activity::BeforeWaiting, _layoutAnimationDelegateProxy);
|
||||
_uiRunLoopObserver->setDelegate(_layoutAnimationDelegateProxy.get());
|
||||
|
|
|
@ -285,7 +285,8 @@ void Binding::installFabricUIManager(
|
|||
}
|
||||
|
||||
if (enableLayoutAnimations) {
|
||||
animationDriver_ = std::make_shared<LayoutAnimationDriver>(this);
|
||||
animationDriver_ =
|
||||
std::make_shared<LayoutAnimationDriver>(runtimeExecutor, this);
|
||||
}
|
||||
scheduler_ = std::make_shared<Scheduler>(
|
||||
toolbox, (animationDriver_ ? animationDriver_.get() : nullptr), this);
|
||||
|
|
|
@ -179,6 +179,8 @@ void LayoutAnimationDriver::animationMutationsForFrame(
|
|||
it != inflightAnimations_.end();) {
|
||||
const auto &animation = *it;
|
||||
if (animation.completed) {
|
||||
callCallback(animation.successCallback);
|
||||
|
||||
// Queue up "final" mutations for all keyframes in the completed animation
|
||||
for (auto const &keyframe : animation.keyFrames) {
|
||||
if (keyframe.finalMutationForKeyFrame.hasValue()) {
|
||||
|
|
|
@ -23,8 +23,10 @@ namespace react {
|
|||
|
||||
class LayoutAnimationDriver : public LayoutAnimationKeyFrameManager {
|
||||
public:
|
||||
LayoutAnimationDriver(LayoutAnimationStatusDelegate *delegate)
|
||||
: LayoutAnimationKeyFrameManager(delegate) {}
|
||||
LayoutAnimationDriver(
|
||||
RuntimeExecutor runtimeExecutor,
|
||||
LayoutAnimationStatusDelegate *delegate)
|
||||
: LayoutAnimationKeyFrameManager(runtimeExecutor, delegate) {}
|
||||
|
||||
virtual ~LayoutAnimationDriver() {}
|
||||
|
||||
|
|
|
@ -225,28 +225,45 @@ static better::optional<LayoutAnimationConfig> parseLayoutAnimationConfig(
|
|||
|
||||
/**
|
||||
* Globally configure next LayoutAnimation.
|
||||
* This is guaranteed to be called only on the JS thread.
|
||||
*/
|
||||
void LayoutAnimationKeyFrameManager::uiManagerDidConfigureNextLayoutAnimation(
|
||||
jsi::Runtime &runtime,
|
||||
RawValue const &config,
|
||||
std::shared_ptr<const EventTarget> successCallback,
|
||||
std::shared_ptr<const EventTarget> errorCallback) const {
|
||||
const jsi::Value &successCallbackValue,
|
||||
const jsi::Value &failureCallbackValue) const {
|
||||
bool hasSuccessCallback = successCallbackValue.isObject() &&
|
||||
successCallbackValue.getObject(runtime).isFunction(runtime);
|
||||
bool hasFailureCallback = failureCallbackValue.isObject() &&
|
||||
failureCallbackValue.getObject(runtime).isFunction(runtime);
|
||||
LayoutAnimationCallbackWrapper successCallback = hasSuccessCallback
|
||||
? LayoutAnimationCallbackWrapper(
|
||||
successCallbackValue.getObject(runtime).getFunction(runtime))
|
||||
: LayoutAnimationCallbackWrapper();
|
||||
LayoutAnimationCallbackWrapper failureCallback = hasFailureCallback
|
||||
? LayoutAnimationCallbackWrapper(
|
||||
failureCallbackValue.getObject(runtime).getFunction(runtime))
|
||||
: LayoutAnimationCallbackWrapper();
|
||||
|
||||
auto layoutAnimationConfig =
|
||||
parseLayoutAnimationConfig((folly::dynamic)config);
|
||||
|
||||
if (layoutAnimationConfig) {
|
||||
std::lock_guard<std::mutex> lock(currentAnimationMutex_);
|
||||
|
||||
currentAnimation_ = better::optional<LayoutAnimation>{
|
||||
LayoutAnimation{-1,
|
||||
0,
|
||||
false,
|
||||
*layoutAnimationConfig,
|
||||
successCallback,
|
||||
errorCallback,
|
||||
failureCallback,
|
||||
{}}};
|
||||
} else {
|
||||
// TODO: call errorCallback
|
||||
LOG(ERROR) << "Parsing LayoutAnimationConfig failed: "
|
||||
<< (folly::dynamic)config;
|
||||
|
||||
callCallback(failureCallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,13 +489,14 @@ LayoutAnimationKeyFrameManager::pullTransaction(
|
|||
{
|
||||
std::lock_guard<std::mutex> lock(currentAnimationMutex_);
|
||||
if (currentAnimation_) {
|
||||
currentAnimation = currentAnimation_;
|
||||
currentAnimation = std::move(currentAnimation_);
|
||||
currentAnimation_ = {};
|
||||
}
|
||||
}
|
||||
|
||||
if (currentAnimation) {
|
||||
LayoutAnimation animation = currentAnimation.value();
|
||||
LayoutAnimation animation = std::move(currentAnimation.value());
|
||||
currentAnimation = {};
|
||||
animation.surfaceId = surfaceId;
|
||||
animation.startTime = now;
|
||||
|
||||
|
@ -909,7 +927,7 @@ LayoutAnimationKeyFrameManager::pullTransaction(
|
|||
#endif
|
||||
|
||||
animation.keyFrames = keyFramesToAnimate;
|
||||
inflightAnimations_.push_back(animation);
|
||||
inflightAnimations_.push_back(std::move(animation));
|
||||
|
||||
// These will be executed immediately.
|
||||
mutations = immediateMutations;
|
||||
|
@ -1069,5 +1087,41 @@ ShadowView LayoutAnimationKeyFrameManager::createInterpolatedShadowView(
|
|||
return mutatedShadowView;
|
||||
}
|
||||
|
||||
void LayoutAnimationKeyFrameManager::callCallback(
|
||||
const LayoutAnimationCallbackWrapper &callback) const {
|
||||
if (callback.readyForCleanup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Callbacks can only be called once. Replace the callsite with an empty
|
||||
// CallbackWrapper. We use a unique_ptr to avoid copying into the vector.
|
||||
std::unique_ptr<LayoutAnimationCallbackWrapper> copiedCallback(
|
||||
std::make_unique<LayoutAnimationCallbackWrapper>(callback));
|
||||
|
||||
// Call the callback that is being retained in the vector
|
||||
copiedCallback->call(runtimeExecutor_);
|
||||
|
||||
// Protect with a mutex: this can be called on failure callbacks in the JS
|
||||
// thread and success callbacks on the UI thread
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(callbackWrappersPendingMutex_);
|
||||
|
||||
// Clean any stale data in the retention vector
|
||||
callbackWrappersPending_.erase(
|
||||
std::remove_if(
|
||||
callbackWrappersPending_.begin(),
|
||||
callbackWrappersPending_.end(),
|
||||
[](const std::unique_ptr<LayoutAnimationCallbackWrapper> &wrapper) {
|
||||
return wrapper->readyForCleanup();
|
||||
}),
|
||||
callbackWrappersPending_.end());
|
||||
|
||||
// Hold onto a reference to the callback, only while
|
||||
// LayoutAnimationKeyFrameManager is alive and the callback hasn't completed
|
||||
// yet.
|
||||
callbackWrappersPending_.push_back(std::move(copiedCallback));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <ReactCommon/RuntimeExecutor.h>
|
||||
#include <better/optional.h>
|
||||
#include <react/renderer/core/EventTarget.h>
|
||||
#include <react/renderer/core/RawValue.h>
|
||||
#include <react/renderer/mounting/Differentiator.h>
|
||||
|
@ -87,30 +89,72 @@ struct AnimationKeyFrame {
|
|||
double initialProgress;
|
||||
};
|
||||
|
||||
class LayoutAnimationCallbackWrapper {
|
||||
public:
|
||||
LayoutAnimationCallbackWrapper(jsi::Function &&callback)
|
||||
: callback_(std::make_shared<jsi::Function>(std::move(callback))) {}
|
||||
LayoutAnimationCallbackWrapper() : callback_(nullptr) {}
|
||||
~LayoutAnimationCallbackWrapper() {}
|
||||
|
||||
// Copy and assignment-copy constructors should copy callback_, and not
|
||||
// std::move it. Copying is desirable, otherwise the shared_ptr and
|
||||
// jsi::Function will be deallocated too early.
|
||||
|
||||
bool readyForCleanup() const {
|
||||
return callback_ == nullptr || *callComplete_;
|
||||
}
|
||||
|
||||
void call(const RuntimeExecutor &runtimeExecutor) const {
|
||||
if (readyForCleanup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::weak_ptr<jsi::Function> callable = callback_;
|
||||
std::shared_ptr<bool> callComplete = callComplete_;
|
||||
|
||||
runtimeExecutor(
|
||||
[=, callComplete = std::move(callComplete)](jsi::Runtime &runtime) {
|
||||
auto fn = callable.lock();
|
||||
|
||||
if (!fn || *callComplete) {
|
||||
return;
|
||||
}
|
||||
|
||||
fn->call(runtime);
|
||||
*callComplete = true;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<bool> callComplete_ = std::make_shared<bool>(false);
|
||||
std::shared_ptr<jsi::Function> callback_;
|
||||
};
|
||||
|
||||
struct LayoutAnimation {
|
||||
SurfaceId surfaceId;
|
||||
uint64_t startTime;
|
||||
bool completed = false;
|
||||
LayoutAnimationConfig layoutAnimationConfig;
|
||||
std::shared_ptr<const EventTarget> successCallback;
|
||||
std::shared_ptr<const EventTarget> errorCallback;
|
||||
LayoutAnimationCallbackWrapper successCallback;
|
||||
LayoutAnimationCallbackWrapper failureCallback;
|
||||
std::vector<AnimationKeyFrame> keyFrames;
|
||||
};
|
||||
|
||||
class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate,
|
||||
public MountingOverrideDelegate {
|
||||
public:
|
||||
LayoutAnimationKeyFrameManager(LayoutAnimationStatusDelegate *delegate)
|
||||
: layoutAnimationStatusDelegate_(delegate) {
|
||||
// This is the ONLY place where we set or access
|
||||
// layoutAnimationStatusDelegate_ without a mutex.
|
||||
}
|
||||
LayoutAnimationKeyFrameManager(
|
||||
RuntimeExecutor runtimeExecutor,
|
||||
LayoutAnimationStatusDelegate *delegate)
|
||||
: runtimeExecutor_(runtimeExecutor),
|
||||
layoutAnimationStatusDelegate_(delegate) {}
|
||||
~LayoutAnimationKeyFrameManager() {}
|
||||
|
||||
void uiManagerDidConfigureNextLayoutAnimation(
|
||||
jsi::Runtime &runtime,
|
||||
RawValue const &config,
|
||||
std::shared_ptr<EventTarget const> successCallback,
|
||||
std::shared_ptr<EventTarget const> errorCallback) const override;
|
||||
const jsi::Value &successCallbackValue,
|
||||
const jsi::Value &failureCallbackValue) const override;
|
||||
void setComponentDescriptorRegistry(SharedComponentDescriptorRegistry const &
|
||||
componentDescriptorRegistry) override;
|
||||
|
||||
|
@ -138,6 +182,7 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate,
|
|||
LayoutAnimationStatusDelegate *delegate) const;
|
||||
|
||||
private:
|
||||
RuntimeExecutor runtimeExecutor_;
|
||||
mutable std::mutex layoutAnimationStatusDelegateMutex_;
|
||||
mutable LayoutAnimationStatusDelegate *layoutAnimationStatusDelegate_{};
|
||||
|
||||
|
@ -162,6 +207,8 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate,
|
|||
ShadowView startingView,
|
||||
ShadowView finalView) const;
|
||||
|
||||
void callCallback(const LayoutAnimationCallbackWrapper &callback) const;
|
||||
|
||||
virtual void animationMutationsForFrame(
|
||||
SurfaceId surfaceId,
|
||||
ShadowViewMutation::List &mutationsList,
|
||||
|
@ -183,6 +230,13 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate,
|
|||
* by the MountingCoordinator's mutex.
|
||||
*/
|
||||
mutable std::vector<LayoutAnimation> inflightAnimations_{};
|
||||
|
||||
private:
|
||||
// A vector of callable function wrappers that are in the process of being
|
||||
// called
|
||||
mutable std::mutex callbackWrappersPendingMutex_;
|
||||
mutable std::vector<std::unique_ptr<LayoutAnimationCallbackWrapper>>
|
||||
callbackWrappersPending_{};
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
|
|
|
@ -268,12 +268,16 @@ void UIManager::dispatchCommand(
|
|||
}
|
||||
|
||||
void UIManager::configureNextLayoutAnimation(
|
||||
jsi::Runtime &runtime,
|
||||
RawValue const &config,
|
||||
SharedEventTarget successCallback,
|
||||
SharedEventTarget errorCallback) const {
|
||||
const jsi::Value &successCallback,
|
||||
const jsi::Value &failureCallback) const {
|
||||
if (animationDelegate_) {
|
||||
animationDelegate_->uiManagerDidConfigureNextLayoutAnimation(
|
||||
config, successCallback, errorCallback);
|
||||
runtime,
|
||||
config,
|
||||
std::move(successCallback),
|
||||
std::move(failureCallback));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <folly/Optional.h>
|
||||
#include <folly/dynamic.h>
|
||||
#include <jsi/jsi.h>
|
||||
|
||||
|
@ -135,9 +134,10 @@ class UIManager final : public ShadowTreeDelegate {
|
|||
* This API configures a global LayoutAnimation starting from the root node.
|
||||
*/
|
||||
void configureNextLayoutAnimation(
|
||||
jsi::Runtime &runtime,
|
||||
RawValue const &config,
|
||||
SharedEventTarget successCallback,
|
||||
SharedEventTarget errorCallback) const;
|
||||
const jsi::Value &successCallback,
|
||||
const jsi::Value &failureCallback) const;
|
||||
|
||||
ShadowTreeRegistry const &getShadowTreeRegistry() const;
|
||||
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <jsi/jsi.h>
|
||||
|
||||
#include <react/renderer/componentregistry/ComponentDescriptorFactory.h>
|
||||
#include <react/renderer/core/EventTarget.h>
|
||||
#include <react/renderer/core/RawValue.h>
|
||||
|
||||
namespace facebook {
|
||||
|
@ -23,9 +24,10 @@ class UIManagerAnimationDelegate {
|
|||
* TODO: need SurfaceId here
|
||||
*/
|
||||
virtual void uiManagerDidConfigureNextLayoutAnimation(
|
||||
jsi::Runtime &runtime,
|
||||
RawValue const &config,
|
||||
SharedEventTarget successCallback,
|
||||
SharedEventTarget errorCallback) const = 0;
|
||||
const jsi::Value &successCallback,
|
||||
const jsi::Value &failureCallback) const = 0;
|
||||
|
||||
/**
|
||||
* Set ComponentDescriptor registry.
|
||||
|
|
|
@ -651,10 +651,11 @@ jsi::Value UIManagerBinding::get(
|
|||
const jsi::Value *arguments,
|
||||
size_t count) -> jsi::Value {
|
||||
uiManager->configureNextLayoutAnimation(
|
||||
runtime,
|
||||
// TODO: pass in JSI value instead of folly::dynamic to RawValue
|
||||
RawValue(commandArgsFromValue(runtime, arguments[0])),
|
||||
eventTargetFromValue(runtime, arguments[1], -1),
|
||||
eventTargetFromValue(runtime, arguments[2], -1));
|
||||
arguments[1],
|
||||
arguments[2]);
|
||||
return jsi::Value::undefined();
|
||||
});
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче