com.flipkart.android.proteus.toolbox.AnimationUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.flipkart.android.proteus.toolbox.AnimationUtils.java

Source

/*
 * Copyright 2016 Flipkart Internet Pvt. Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *          http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.flipkart.android.proteus.toolbox;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.util.Log;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnticipateInterpolator;
import android.view.animation.AnticipateOvershootInterpolator;
import android.view.animation.BounceInterpolator;
import android.view.animation.CycleInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;
import android.view.animation.PathInterpolator;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;

import com.flipkart.android.proteus.parser.ParseHelper;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.annotations.SerializedName;

/**
 * Defines common utilities for working with animations.
 */
public class AnimationUtils {

    private static final String TAG = "AnimationUtils";

    private static final String LINEAR_INTERPOLATOR = "linearInterpolator";
    private static final String ACCELERATE_INTERPOLATOR = "accelerateInterpolator";
    private static final String DECELERATE_INTERPOLATOR = "decelerateInterpolator";
    private static final String ACCELERATE_DECELERATE_INTERPOLATOR = "accelerateDecelerateInterpolator";
    private static final String CYCLE_INTERPOLATOR = "cycleInterpolator";
    private static final String ANTICIPATE_INTERPOLATOR = "anticipateInterpolator";
    private static final String OVERSHOOT_INTERPOLATOR = "overshootInterpolator";
    private static final String ANTICIPATE_OVERSHOOT_INTERPOLATOR = "anticipateOvershootInterpolator";
    private static final String BOUNCE_INTERPOLATOR = "bounceInterpolator";
    private static final String PATH_INTERPOLATOR = "pathInterpolator";

    private static final String TYPE = "type";
    private static final String SET = "set";
    private static final String ALPHA = "alpha";
    private static final String SCALE = "scale";
    private static final String ROTATE = "rotate";
    private static final String TRANSLATE = "translate";

    private static final String PERCENT_SELF = "%";
    private static final String PERCENT_RELATIVE_PARENT = "%p";

    private static Gson sGson = new Gson();

    /**
     * Loads an {@link Animation} object from a resource
     *
     * @param context Application context used to access resources
     * @param value   JSON representation of the Animation
     * @return The animation object reference by the specified id
     * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
     */
    public static Animation loadAnimation(Context context, JsonElement value) throws Resources.NotFoundException {
        Animation anim = null;
        if (value.isJsonPrimitive()) {
            anim = handleString(context, value.getAsString());
        } else if (value.isJsonObject()) {
            anim = handleElement(context, value.getAsJsonObject());
        } else {
            if (ProteusConstants.isLoggingEnabled()) {
                Log.e(TAG, "Could not load animation for : " + value.toString());
            }
        }
        return anim;
    }

    private static Animation handleString(Context c, String value) {
        Animation anim = null;
        if (ParseHelper.isTweenAnimationResource(value)) {
            try {
                Resources r = c.getResources();
                int animationId = r.getIdentifier(value, "anim", c.getPackageName());
                anim = android.view.animation.AnimationUtils.loadAnimation(c, animationId);
            } catch (Exception ex) {
                System.out.println("Could not load local resource " + value);
            }
        }
        return anim;
    }

    private static Animation handleElement(Context c, JsonObject value) {
        Animation anim = null;
        JsonElement type = value.get(TYPE);
        String animationType = type.getAsString();
        AnimationProperties animationProperties = null;
        if (SET.equalsIgnoreCase(animationType)) {
            animationProperties = sGson.fromJson(value, AnimationSetProperties.class);
        } else if (ALPHA.equalsIgnoreCase(animationType)) {
            animationProperties = sGson.fromJson(value, AlphaAnimProperties.class);
        } else if (SCALE.equalsIgnoreCase(animationType)) {
            animationProperties = sGson.fromJson(value, ScaleAnimProperties.class);
        } else if (ROTATE.equalsIgnoreCase(animationType)) {
            animationProperties = sGson.fromJson(value, RotateAnimProperties.class);
        } else if (TRANSLATE.equalsIgnoreCase(animationType)) {
            animationProperties = sGson.fromJson(value, TranslateAnimProperties.class);
        }

        if (null != animationProperties) {
            anim = animationProperties.instantiate(c);
        }

        return anim;
    }

    /**
     * Loads an {@link Interpolator} object from a resource
     *
     * @param context Application context used to access resources
     * @param value   Json representation of the Interpolator
     * @return The animation object reference by the specified id
     * @throws android.content.res.Resources.NotFoundException
     */
    public static Interpolator loadInterpolator(Context context, JsonElement value)
            throws Resources.NotFoundException {
        Interpolator interpolator = null;
        if (value.isJsonPrimitive()) {
            interpolator = handleStringInterpolator(context, value.getAsString());
        } else if (value.isJsonObject()) {
            interpolator = handleElementInterpolator(context, value.getAsJsonObject());
        } else {
            if (ProteusConstants.isLoggingEnabled()) {
                Log.e(TAG, "Could not load interpolator for : " + value.toString());
            }
        }
        return interpolator;
    }

    private static Interpolator handleStringInterpolator(Context c, String value) {
        Interpolator interpolator = null;
        if (ParseHelper.isTweenAnimationResource(value)) {
            try {
                Resources r = c.getResources();
                int interpolatorID = r.getIdentifier(value, "anim", c.getPackageName());
                interpolator = android.view.animation.AnimationUtils.loadInterpolator(c, interpolatorID);
            } catch (Exception ex) {
                System.out.println("Could not load local resource " + value);
            }
        }
        return interpolator;
    }

    private static Interpolator handleElementInterpolator(Context c, JsonObject value) {

        Interpolator interpolator = null;
        JsonElement type = value.get("type");
        String interpolatorType = type.getAsString();
        InterpolatorProperties interpolatorProperties = null;
        if (LINEAR_INTERPOLATOR.equalsIgnoreCase(interpolatorType)) {
            interpolator = new LinearInterpolator();
        } else if (ACCELERATE_INTERPOLATOR.equalsIgnoreCase(interpolatorType)) {
            interpolator = new AccelerateInterpolator();
        } else if (DECELERATE_INTERPOLATOR.equalsIgnoreCase(interpolatorType)) {
            interpolator = new DecelerateInterpolator();
        } else if (ACCELERATE_DECELERATE_INTERPOLATOR.equalsIgnoreCase(interpolatorType)) {
            interpolator = new AccelerateDecelerateInterpolator();
        } else if (CYCLE_INTERPOLATOR.equalsIgnoreCase(interpolatorType)) {
            interpolatorProperties = sGson.fromJson(value, CycleInterpolatorProperties.class);
        } else if (ANTICIPATE_INTERPOLATOR.equalsIgnoreCase(interpolatorType)) {
            interpolatorProperties = sGson.fromJson(value, AnticipateInterpolatorProperties.class);
        } else if (OVERSHOOT_INTERPOLATOR.equalsIgnoreCase(interpolatorType)) {
            interpolatorProperties = sGson.fromJson(value, OvershootInterpolatorProperties.class);
        } else if (ANTICIPATE_OVERSHOOT_INTERPOLATOR.equalsIgnoreCase(interpolatorType)) {
            interpolatorProperties = sGson.fromJson(value, AnticipateOvershootInterpolatorProperties.class);
        } else if (BOUNCE_INTERPOLATOR.equalsIgnoreCase(interpolatorType)) {
            interpolator = new BounceInterpolator();
        } else if (PATH_INTERPOLATOR.equalsIgnoreCase(interpolatorType)) {
            interpolatorProperties = sGson.fromJson(value, PathInterpolatorProperties.class);
        } else {
            if (ProteusConstants.isLoggingEnabled()) {
                Log.e(TAG, "Unknown interpolator name: " + interpolatorType);
            }
            throw new RuntimeException("Unknown interpolator name: " + interpolatorType);
        }

        if (null != interpolatorProperties) {
            interpolator = interpolatorProperties.createInterpolator(c);
        }

        return interpolator;
    }

    /**
     * Utility class to parse a string description of a size.
     */
    private static class Description {
        /**
         * One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
         * Animation.RELATIVE_TO_PARENT.
         */
        public int type;

        /**
         * The absolute or relative dimension for this Description.
         */
        public float value;

        /**
         * Size descriptions can appear in three forms:
         * <ol>
         * <li>An absolute size. This is represented by a number.</li>
         * <li>A size relative to the size of the object being animated. This
         * is represented by a number followed by "%".</li> *
         * <li>A size relative to the size of the parent of object being
         * animated. This is represented by a number followed by "%p".</li>
         * </ol>
         *
         * @param value The Json value to parse
         * @return The parsed version of the description
         */
        static Description parseValue(JsonPrimitive value) {
            Description d = new Description();
            d.type = Animation.ABSOLUTE;
            d.value = 0;
            if (value != null && (value.isNumber() || value.isString())) {
                if (value.isNumber()) {
                    d.type = Animation.ABSOLUTE;
                    d.value = value.getAsNumber().floatValue();
                } else {
                    String stringValue = value.getAsString();
                    if (stringValue.endsWith(PERCENT_SELF)) {
                        stringValue = stringValue.substring(0, stringValue.length() - PERCENT_SELF.length());
                        d.value = Float.parseFloat(stringValue) / 100;
                        d.type = Animation.RELATIVE_TO_SELF;
                    } else if (stringValue.endsWith(PERCENT_RELATIVE_PARENT)) {
                        stringValue = stringValue.substring(0,
                                stringValue.length() - PERCENT_RELATIVE_PARENT.length());
                        d.value = Float.parseFloat(stringValue) / 100;
                        d.type = Animation.RELATIVE_TO_PARENT;
                    } else {
                        d.type = Animation.ABSOLUTE;
                        d.value = value.getAsNumber().floatValue();
                    }
                }
            }

            return d;
        }
    }

    private abstract static class AnimationProperties {

        @SerializedName("detachWallpaper")
        Boolean detachWallpaper;
        @SerializedName("duration")
        Long duration;
        @SerializedName("fillAfter")
        Boolean fillAfter;
        @SerializedName("fillBefore")
        Boolean fillBefore;
        @SerializedName("fillEnabled")
        Boolean fillEnabled;
        @SerializedName("interpolator")
        JsonElement interpolator;
        @SerializedName("repeatCount")
        Integer repeatCount;
        @SerializedName("repeatMode")
        Integer repeatMode;
        @SerializedName("startOffset")
        Long startOffset;
        @SerializedName("zAdjustment")
        Integer zAdjustment;

        public Animation instantiate(Context c) {
            Animation anim = createAnimation(c);
            if (null != anim) {
                if (null != detachWallpaper) {
                    anim.setDetachWallpaper(detachWallpaper);
                }

                if (null != duration) {
                    anim.setDuration(duration);
                }

                if (null != fillAfter) {
                    anim.setFillAfter(fillAfter);
                }

                if (null != fillBefore) {
                    anim.setFillBefore(fillBefore);
                }

                if (null != fillEnabled) {
                    anim.setFillEnabled(fillEnabled);
                }

                if (null != interpolator) {
                    Interpolator i = loadInterpolator(c, interpolator);
                    if (null != i) {
                        anim.setInterpolator(i);
                    }
                }

                if (null != repeatCount) {
                    anim.setRepeatCount(repeatCount);
                }

                if (null != repeatMode) {
                    anim.setRepeatMode(repeatMode);
                }

                if (null != startOffset) {
                    anim.setStartOffset(startOffset);
                }

                if (null != zAdjustment) {
                    anim.setZAdjustment(zAdjustment);
                }
            }
            return anim;
        }

        abstract Animation createAnimation(Context c);
    }

    private static class AnimationSetProperties extends AnimationProperties {

        @SerializedName("shareInterpolator")
        Boolean shareInterpolator;
        @SerializedName("children")
        JsonElement children;

        @Override
        Animation createAnimation(Context c) {
            AnimationSet animationSet = new AnimationSet(shareInterpolator == null ? true : shareInterpolator);

            if (null != children) {
                if (children.isJsonArray()) {
                    for (JsonElement element : children.getAsJsonArray()) {
                        Animation animation = loadAnimation(c, element);
                        if (null != animation) {
                            animationSet.addAnimation(animation);
                        }
                    }
                } else if (children.isJsonObject() || children.isJsonPrimitive()) {
                    Animation animation = loadAnimation(c, children);
                    if (null != animation) {
                        animationSet.addAnimation(animation);
                    }
                }
            }
            return animationSet;
        }
    }

    private static class AlphaAnimProperties extends AnimationProperties {

        @SerializedName("fromAlpha")
        public Float fromAlpha;
        @SerializedName("toAlpha")
        public Float toAlpha;

        @Override
        Animation createAnimation(Context c) {
            return null == fromAlpha || null == toAlpha ? null : new AlphaAnimation(fromAlpha, toAlpha);
        }
    }

    private static class ScaleAnimProperties extends AnimationProperties {

        @SerializedName("fromXScale")
        public Float fromXScale;
        @SerializedName("toXScale")
        public Float toXScale;
        @SerializedName("fromYScale")
        public Float fromYScale;
        @SerializedName("toYScale")
        public Float toYScale;
        @SerializedName("pivotX")
        public JsonPrimitive pivotX;
        @SerializedName("pivotY")
        public JsonPrimitive pivotY;

        @Override
        Animation createAnimation(Context c) {
            if (pivotX != null && pivotY != null) {
                Description pivotXDesc = Description.parseValue(pivotX);
                Description pivotYDesc = Description.parseValue(pivotY);
                return new ScaleAnimation(fromXScale, toXScale, fromYScale, toYScale, pivotXDesc.type,
                        pivotXDesc.value, pivotYDesc.type, pivotYDesc.value);
            } else {
                return new ScaleAnimation(fromXScale, toXScale, fromYScale, toYScale);
            }
        }
    }

    private static class TranslateAnimProperties extends AnimationProperties {

        @SerializedName("fromXDelta")
        public JsonPrimitive fromXDelta;
        @SerializedName("toXDelta")
        public JsonPrimitive toXDelta;
        @SerializedName("fromYDelta")
        public JsonPrimitive fromYDelta;
        @SerializedName("toYDelta")
        public JsonPrimitive toYDelta;

        @Override
        Animation createAnimation(Context c) {
            Description fromXDeltaDescription = Description.parseValue(fromXDelta);
            Description toXDeltaDescription = Description.parseValue(toXDelta);
            Description fromYDeltaDescription = Description.parseValue(fromYDelta);
            Description toYDeltaDescription = Description.parseValue(toYDelta);

            return new TranslateAnimation(fromXDeltaDescription.type, fromXDeltaDescription.value,
                    toXDeltaDescription.type, toXDeltaDescription.value, fromYDeltaDescription.type,
                    fromYDeltaDescription.value, toYDeltaDescription.type, toYDeltaDescription.value);
        }
    }

    private static class RotateAnimProperties extends AnimationProperties {

        @SerializedName("fromDegrees")
        public Float fromDegrees;
        @SerializedName("toDegrees")
        public Float toDegrees;
        @SerializedName("pivotX")
        public JsonPrimitive pivotX;
        @SerializedName("pivotY")
        public JsonPrimitive pivotY;

        @Override
        Animation createAnimation(Context c) {
            if (null != pivotX && null != pivotY) {
                Description pivotXDesc = Description.parseValue(pivotX);
                Description pivotYDesc = Description.parseValue(pivotY);
                return new RotateAnimation(fromDegrees, toDegrees, pivotXDesc.type, pivotXDesc.value,
                        pivotYDesc.type, pivotYDesc.value);
            } else {
                return new RotateAnimation(fromDegrees, toDegrees);
            }
        }
    }

    private abstract static class InterpolatorProperties {
        abstract Interpolator createInterpolator(Context c);
    }

    private static class PathInterpolatorProperties extends InterpolatorProperties {

        @SerializedName("controlX1")
        public Float controlX1;
        @SerializedName("controlY1")
        public Float controlY1;
        @SerializedName("controlX2")
        public Float controlX2;
        @SerializedName("controlY2")
        public Float controlY2;

        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        Interpolator createInterpolator(Context c) {
            if (null != controlX2 && null != controlY2) {
                return new PathInterpolator(controlX1, controlY1, controlX2, controlY2);
            } else {
                return new PathInterpolator(controlX1, controlY1);
            }
        }
    }

    private static class AnticipateInterpolatorProperties extends InterpolatorProperties {

        @SerializedName("tension")
        public Float tension;

        Interpolator createInterpolator(Context c) {
            return new AnticipateInterpolator(tension);
        }
    }

    private static class OvershootInterpolatorProperties extends InterpolatorProperties {

        @SerializedName("tension")
        public Float tension;

        Interpolator createInterpolator(Context c) {
            return tension == null ? new OvershootInterpolator() : new OvershootInterpolator(tension);
        }
    }

    private static class AnticipateOvershootInterpolatorProperties extends InterpolatorProperties {

        @SerializedName("tension")
        public Float tension;
        @SerializedName("extraTension")
        public Float extraTension;

        Interpolator createInterpolator(Context c) {
            return null == tension ? new AnticipateOvershootInterpolator()
                    : (null == extraTension ? new AnticipateOvershootInterpolator(tension)
                            : new AnticipateOvershootInterpolator(tension, extraTension));
        }
    }

    private static class CycleInterpolatorProperties extends InterpolatorProperties {

        @SerializedName("cycles")
        public Float cycles;

        Interpolator createInterpolator(Context c) {
            return new CycleInterpolator(cycles);
        }
    }
}