/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace facebook::react { #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING void PrintMutationInstruction(std::string message, const ShadowViewMutation &mutation); void PrintMutationInstructionRelative( std::string message, const ShadowViewMutation &mutation, const ShadowViewMutation &relativeMutation); #else #define PrintMutationInstruction(a, b) #define PrintMutationInstructionRelative(a, b, c) #endif class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate, public MountingOverrideDelegate { public: LayoutAnimationKeyFrameManager( RuntimeExecutor runtimeExecutor, std::shared_ptr &contextContainer, LayoutAnimationStatusDelegate *delegate); #pragma mark - UIManagerAnimationDelegate methods void uiManagerDidConfigureNextLayoutAnimation( jsi::Runtime &runtime, const RawValue &config, const jsi::Value &successCallbackValue, const jsi::Value &failureCallbackValue) const override; void setComponentDescriptorRegistry(const SharedComponentDescriptorRegistry &componentDescriptorRegistry) override; // TODO: add SurfaceId to this API as well bool shouldAnimateFrame() const override; void stopSurface(SurfaceId surfaceId) override; #pragma mark - MountingOverrideDelegate methods bool shouldOverridePullTransaction() const override; // This is used to "hijack" the diffing process to figure out which mutations // should be animated. The mutations returned by this function will be // executed immediately. std::optional pullTransaction( SurfaceId surfaceId, MountingTransaction::Number number, const TransactionTelemetry &telemetry, ShadowViewMutationList mutations) const override; // Exposed for testing. void uiManagerDidConfigureNextLayoutAnimation(LayoutAnimation layoutAnimation) const; // 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. void setLayoutAnimationStatusDelegate(LayoutAnimationStatusDelegate *delegate) const; void setClockNow(std::function now); protected: SharedComponentDescriptorRegistry componentDescriptorRegistry_; mutable std::optional currentAnimation_{}; mutable std::mutex currentAnimationMutex_; /** * All mutations of inflightAnimations_ are thread-safe as long as * we keep the contract of: only mutate it within the context of * `pullTransaction`. If that contract is held, this is implicitly protected * by the MountingCoordinator's mutex. */ mutable std::vector inflightAnimations_{}; bool hasComponentDescriptorForShadowView(const ShadowView &shadowView) const; const ComponentDescriptor &getComponentDescriptorForShadowView(const ShadowView &shadowView) const; /** * Given a `progress` between 0 and 1, a mutation and LayoutAnimation config, * return a ShadowView with mutated props and/or LayoutMetrics. * * @param progress the current progress for the animation * @param startingView the initial configuration of the ShadowView * @param finalView the final configuration of the ShadowView * @return the current ShadowView */ ShadowView createInterpolatedShadowView(Float progress, const ShadowView &startingView, const ShadowView &finalView) const; void callCallback(const LayoutAnimationCallbackWrapper &callback) const; virtual void animationMutationsForFrame(SurfaceId surfaceId, ShadowViewMutation::List &mutationsList, uint64_t now) const = 0; /** * Queue (and potentially synthesize) final mutations for a finished keyframe. * Keyframe animation may have timed-out, or be canceled due to a conflict. */ void queueFinalMutationsForCompletedKeyFrame( const AnimationKeyFrame &keyframe, ShadowViewMutation::List &mutationsList, bool interrupted, const std::string &logPrefix) const; private: RuntimeExecutor runtimeExecutor_; std::shared_ptr contextContainer_; mutable std::mutex layoutAnimationStatusDelegateMutex_; mutable LayoutAnimationStatusDelegate *layoutAnimationStatusDelegate_{}; mutable std::mutex surfaceIdsToStopMutex_; mutable std::unordered_set surfaceIdsToStop_{}; // Function that returns current time in milliseconds std::function now_; void adjustImmediateMutationIndicesForDelayedMutations( SurfaceId surfaceId, ShadowViewMutation &mutation, bool skipLastAnimation = false, bool lastAnimationOnly = false) const; void adjustDelayedMutationIndicesForMutation( SurfaceId surfaceId, const ShadowViewMutation &mutation, bool skipLastAnimation = false) const; void getAndEraseConflictingAnimations( SurfaceId surfaceId, const ShadowViewMutationList &mutations, std::vector &conflictingAnimations) const; /* * Removes animations from `inflightAnimations_` for stopped surfaces. */ void deleteAnimationsForStoppedSurfaces() const; void simulateImagePropsMemoryAccess(const ShadowViewMutationList &mutations) const; /** * Interpolates the props values. */ Props::Shared interpolateProps( const ComponentDescriptor &componentDescriptor, const PropsParserContext &context, Float animationProgress, const Props::Shared &props, const Props::Shared &newProps, const Size &size) const; }; } // namespace facebook::react