/**
* 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;
}
}