// Copyright 2004-present Facebook. All Rights Reserved. package com.facebook.react.uimanager.layoutanimation; import javax.annotation.Nullable; import java.util.Map; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.MapBuilder; import com.facebook.react.uimanager.IllegalViewOperationException; /** * Class responsible for parsing and converting layout animation data into native {@link Animation} * in order to animate layout when a valid configuration has been supplied by the application. */ /* package */ abstract class AbstractLayoutAnimation { // Forces animation to be playing 10x slower, used for debug purposes. private static final boolean SLOWDOWN_ANIMATION_MODE = false; abstract boolean isValid(); /** * Create an animation object for the current animation type, based on the view and final screen * coordinates. If the application-supplied configuraiton does not specify an animation definition * for this types, or if the animation definition is invalid, returns null. */ abstract @Nullable Animation createAnimationImpl(View view, int x, int y, int width, int height); private static final Map<InterpolatorType, Interpolator> INTERPOLATOR = MapBuilder.of( InterpolatorType.LINEAR, new LinearInterpolator(), InterpolatorType.EASE_IN, new AccelerateInterpolator(), InterpolatorType.EASE_OUT, new DecelerateInterpolator(), InterpolatorType.EASE_IN_EASE_OUT, new AccelerateDecelerateInterpolator(), InterpolatorType.SPRING, new SimpleSpringInterpolator()); private @Nullable Interpolator mInterpolator; private int mDelayMs; protected @Nullable AnimatedPropertyType mAnimatedProperty; protected int mDurationMs; public void reset() { mAnimatedProperty = null; mDurationMs = 0; mDelayMs = 0; mInterpolator = null; } public void initializeFromConfig(ReadableMap data, int globalDuration) { mAnimatedProperty = data.hasKey("property") ? AnimatedPropertyType.fromString(data.getString("property")) : null; mDurationMs = data.hasKey("duration") ? data.getInt("duration") : globalDuration; mDelayMs = data.hasKey("delay") ? data.getInt("delay") : 0; if (!data.hasKey("type")) { throw new IllegalArgumentException("Missing interpolation type."); } mInterpolator = getInterpolator(InterpolatorType.fromString(data.getString("type"))); if (!isValid()) { throw new IllegalViewOperationException("Invalid layout animation : " + data); } } /** * Create an animation object to be used to animate the view, based on the animation config * supplied at initialization time and the new view position and size. * * @param view the view to create the animation for * @param x the new X position for the view * @param y the new Y position for the view * @param width the new width value for the view * @param height the new height value for the view */ public final @Nullable Animation createAnimation( View view, int x, int y, int width, int height) { if (!isValid()) { return null; } Animation animation = createAnimationImpl(view, x, y, width, height); if (animation != null) { int slowdownFactor = SLOWDOWN_ANIMATION_MODE ? 10 : 1; animation.setDuration(mDurationMs * slowdownFactor); animation.setStartOffset(mDelayMs * slowdownFactor); animation.setInterpolator(mInterpolator); } return animation; } private static Interpolator getInterpolator(InterpolatorType type) { Interpolator interpolator = INTERPOLATOR.get(type); if (interpolator == null) { throw new IllegalArgumentException("Missing interpolator for type : " + type); } return interpolator; } }