From f0c595b57c8ebe5e8972e8eff69ba390cd4752ab Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Wed, 20 May 2020 14:11:18 -0700 Subject: [PATCH] LayoutAnimations: implement LayoutAnimationStatusDelegate for platform-specific integrations Summary: The LayoutAnimationStatusDelegate exists so that platforms can get a signal when animations are starting or have all completed. This signal is meant to be used ONLY for driving animations at 60fps, or stopping that process, on the platform side. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21583109 fbshipit-source-id: 234496841bde226fcd6623c74c1a500e5cd00d99 --- .../fabric/animations/LayoutAnimationDriver.h | 3 ++ .../LayoutAnimationKeyFrameManager.cpp | 23 ++++++++++++++ .../LayoutAnimationKeyFrameManager.h | 21 ++++++++++++- .../uimanager/LayoutAnimationStatusDelegate.h | 30 +++++++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 ReactCommon/fabric/uimanager/LayoutAnimationStatusDelegate.h diff --git a/ReactCommon/fabric/animations/LayoutAnimationDriver.h b/ReactCommon/fabric/animations/LayoutAnimationDriver.h index 2a221577529..3aa04f99163 100644 --- a/ReactCommon/fabric/animations/LayoutAnimationDriver.h +++ b/ReactCommon/fabric/animations/LayoutAnimationDriver.h @@ -23,6 +23,9 @@ namespace react { class LayoutAnimationDriver : public LayoutAnimationKeyFrameManager { public: + LayoutAnimationDriver(LayoutAnimationStatusDelegate *delegate) + : LayoutAnimationKeyFrameManager(delegate) {} + virtual ~LayoutAnimationDriver() {} protected: diff --git a/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.cpp b/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.cpp index 034d871388b..12478e15569 100644 --- a/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.cpp +++ b/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.cpp @@ -212,6 +212,12 @@ void LayoutAnimationKeyFrameManager::uiManagerDidConfigureNextLayoutAnimation( } } +void LayoutAnimationKeyFrameManager::setLayoutAnimationStatusDelegate( + LayoutAnimationStatusDelegate *delegate) const { + std::lock_guard lock(layoutAnimationStatusDelegateMutex_); + layoutAnimationStatusDelegate_ = delegate; +} + bool LayoutAnimationKeyFrameManager::shouldOverridePullTransaction() const { return shouldAnimateFrame(); } @@ -341,6 +347,8 @@ LayoutAnimationKeyFrameManager::pullTransaction( std::chrono::high_resolution_clock::now().time_since_epoch()) .count(); + bool inflightAnimationsExistInitially = !inflightAnimations_.empty(); + if (!mutations.empty()) { #ifdef RN_SHADOW_TREE_INTROSPECTION { @@ -817,6 +825,21 @@ LayoutAnimationKeyFrameManager::pullTransaction( mutationsForAnimation.begin(), mutationsForAnimation.end()); + // Signal to delegate if all animations are complete, or if we were not + // animating anything and now some animation exists. + if (inflightAnimationsExistInitially && inflightAnimations_.empty()) { + std::lock_guard lock(layoutAnimationStatusDelegateMutex_); + if (layoutAnimationStatusDelegate_ != nullptr) { + layoutAnimationStatusDelegate_->onAllAnimationsComplete(); + } + } else if ( + !inflightAnimationsExistInitially && !inflightAnimations_.empty()) { + std::lock_guard lock(layoutAnimationStatusDelegateMutex_); + if (layoutAnimationStatusDelegate_ != nullptr) { + layoutAnimationStatusDelegate_->onAnimationStarted(); + } + } + // TODO: fill in telemetry return MountingTransaction{ surfaceId, transactionNumber, std::move(mutations), {}}; diff --git a/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.h b/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.h index f286bd27a38..4f2a766113a 100644 --- a/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.h +++ b/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.h @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace facebook { @@ -37,7 +38,7 @@ enum class AnimationProperty { }; enum class AnimationConfigurationType { Noop, // for animation placeholders that are not animated, and should be - // executed once other animations have completed + // executed once other animations have completed Create, Update, Delete @@ -97,6 +98,12 @@ struct LayoutAnimation { 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. + } + void uiManagerDidConfigureNextLayoutAnimation( RawValue const &config, std::shared_ptr successCallback, @@ -118,7 +125,19 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate, MountingTelemetry const &telemetry, ShadowViewMutationList mutations) const override; + // LayoutAnimationStatusDelegate - this is for the platform to get + // signal when animations start and complete. Setting and resetting this + // delegate is protected by a mutex; ALL method calls into this delegate are + // also protected by the mutex! The only way to set this without a mutex is + // via a constructor. + public: + void setLayoutAnimationStatusDelegate( + LayoutAnimationStatusDelegate *delegate) const; + private: + mutable std::mutex layoutAnimationStatusDelegateMutex_; + mutable LayoutAnimationStatusDelegate *layoutAnimationStatusDelegate_{}; + void adjustDelayedMutationIndicesForMutation( SurfaceId surfaceId, ShadowViewMutation const &mutation) const; diff --git a/ReactCommon/fabric/uimanager/LayoutAnimationStatusDelegate.h b/ReactCommon/fabric/uimanager/LayoutAnimationStatusDelegate.h new file mode 100644 index 00000000000..b87b96f68f7 --- /dev/null +++ b/ReactCommon/fabric/uimanager/LayoutAnimationStatusDelegate.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +namespace facebook { +namespace react { + +class LayoutAnimationStatusDelegate { + public: + /** + * Called when the LayoutAnimation engine state changes from animation nothing + * to animating something. This will only be called when you go from 0 to N>0 + * active animations, N to N+1 animations will not result in this being + * called. + */ + virtual void onAnimationStarted() = 0; + + /** + * Called when the LayoutAnimation engine completes all pending animations. + */ + virtual void onAllAnimationsComplete() = 0; +}; + +} // namespace react +} // namespace facebook