/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.react.animation; import javax.annotation.Nullable; import android.view.View; import com.facebook.infer.annotation.Assertions; /** * Base class for various catalyst animation engines. Subclasses of this class should implement * {@link #run} method which should bootstrap the animation. Then in each animation frame we expect * animation engine to call {@link #onUpdate} with a float progress which then will be transferred * to the underlying {@link AnimationPropertyUpdater} instance. * * Animation engine should support animation cancelling by monitoring the returned value of * {@link #onUpdate}. In case of returning false, animation should be considered cancelled and * engine should not attempt to call {@link #onUpdate} again. */ public abstract class Animation { private final int mAnimationID; private final AnimationPropertyUpdater mPropertyUpdater; private volatile boolean mCancelled = false; private volatile boolean mIsFinished = false; private @Nullable AnimationListener mAnimationListener; private @Nullable View mAnimatedView; public Animation(int animationID, AnimationPropertyUpdater propertyUpdater) { mAnimationID = animationID; mPropertyUpdater = propertyUpdater; } public void setAnimationListener(AnimationListener animationListener) { mAnimationListener = animationListener; } public final void start(View view) { mAnimatedView = view; mPropertyUpdater.prepare(view); run(); } public abstract void run(); /** * Animation engine should call this method for every animation frame passing animation progress * value as a parameter. Animation progress should be within the range 0..1 (the exception here * would be a spring animation engine which may slightly exceed start and end progress values). * * This method will return false if the animation has been cancelled. In that case animation * engine should not attempt to call this method again. Otherwise this method will return true */ protected final boolean onUpdate(float value) { Assertions.assertCondition(!mIsFinished, "Animation must not already be finished!"); if (!mCancelled) { mPropertyUpdater.onUpdate(Assertions.assertNotNull(mAnimatedView), value); } return !mCancelled; } /** * Animation engine should call this method when the animation is finished. Should be called only * once */ protected final void finish() { Assertions.assertCondition(!mIsFinished, "Animation must not already be finished!"); mIsFinished = true; if (!mCancelled) { if (mAnimatedView != null) { mPropertyUpdater.onFinish(mAnimatedView); } if (mAnimationListener != null) { mAnimationListener.onFinished(); } } } /** * Cancels the animation. * * It is possible for this to be called after finish() and should handle that gracefully. */ public final void cancel() { if (mIsFinished || mCancelled) { // If we were already finished, ignore return; } mCancelled = true; if (mAnimationListener != null) { mAnimationListener.onCancel(); } } public int getAnimationID() { return mAnimationID; } }