Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d422dc35 authored by Yigit Boyar's avatar Yigit Boyar
Browse files

Added a generic configuration and theme based cache

For now, only animators use it but we can consider migrating
drawable cache to it as well.

Bug: 17456416
Change-Id: I571b96856805edb171f0fc52e6bff5a365f46b70
parent d0aae5a2
Loading
Loading
Loading
Loading
+16 −11
Original line number Diff line number Diff line
@@ -3052,9 +3052,10 @@ package android.animation {
    method public android.graphics.Rect evaluate(float, android.graphics.Rect, android.graphics.Rect);
  }
  public class StateListAnimator {
  public class StateListAnimator implements java.lang.Cloneable {
    ctor public StateListAnimator();
    method public void addState(int[], android.animation.Animator);
    method public android.animation.StateListAnimator clone();
    method public void jumpToCurrentState();
  }
@@ -35424,13 +35425,13 @@ package android.view.accessibility {
package android.view.animation {
  public class AccelerateDecelerateInterpolator implements android.view.animation.Interpolator {
  public class AccelerateDecelerateInterpolator extends android.view.animation.BaseInterpolator {
    ctor public AccelerateDecelerateInterpolator();
    ctor public AccelerateDecelerateInterpolator(android.content.Context, android.util.AttributeSet);
    method public float getInterpolation(float);
  }
  public class AccelerateInterpolator implements android.view.animation.Interpolator {
  public class AccelerateInterpolator extends android.view.animation.BaseInterpolator {
    ctor public AccelerateInterpolator();
    ctor public AccelerateInterpolator(float);
    ctor public AccelerateInterpolator(android.content.Context, android.util.AttributeSet);
@@ -35532,14 +35533,14 @@ package android.view.animation {
    method public static android.view.animation.Animation makeOutAnimation(android.content.Context, boolean);
  }
  public class AnticipateInterpolator implements android.view.animation.Interpolator {
  public class AnticipateInterpolator extends android.view.animation.BaseInterpolator {
    ctor public AnticipateInterpolator();
    ctor public AnticipateInterpolator(float);
    ctor public AnticipateInterpolator(android.content.Context, android.util.AttributeSet);
    method public float getInterpolation(float);
  }
  public class AnticipateOvershootInterpolator implements android.view.animation.Interpolator {
  public class AnticipateOvershootInterpolator extends android.view.animation.BaseInterpolator {
    ctor public AnticipateOvershootInterpolator();
    ctor public AnticipateOvershootInterpolator(float);
    ctor public AnticipateOvershootInterpolator(float, float);
@@ -35547,19 +35548,23 @@ package android.view.animation {
    method public float getInterpolation(float);
  }
  public class BounceInterpolator implements android.view.animation.Interpolator {
  public abstract class BaseInterpolator implements android.view.animation.Interpolator {
    ctor public BaseInterpolator();
  }
  public class BounceInterpolator extends android.view.animation.BaseInterpolator {
    ctor public BounceInterpolator();
    ctor public BounceInterpolator(android.content.Context, android.util.AttributeSet);
    method public float getInterpolation(float);
  }
  public class CycleInterpolator implements android.view.animation.Interpolator {
  public class CycleInterpolator extends android.view.animation.BaseInterpolator {
    ctor public CycleInterpolator(float);
    ctor public CycleInterpolator(android.content.Context, android.util.AttributeSet);
    method public float getInterpolation(float);
  }
  public class DecelerateInterpolator implements android.view.animation.Interpolator {
  public class DecelerateInterpolator extends android.view.animation.BaseInterpolator {
    ctor public DecelerateInterpolator();
    ctor public DecelerateInterpolator(float);
    ctor public DecelerateInterpolator(android.content.Context, android.util.AttributeSet);
@@ -35634,20 +35639,20 @@ package android.view.animation {
    field public int index;
  }
  public class LinearInterpolator implements android.view.animation.Interpolator {
  public class LinearInterpolator extends android.view.animation.BaseInterpolator {
    ctor public LinearInterpolator();
    ctor public LinearInterpolator(android.content.Context, android.util.AttributeSet);
    method public float getInterpolation(float);
  }
  public class OvershootInterpolator implements android.view.animation.Interpolator {
  public class OvershootInterpolator extends android.view.animation.BaseInterpolator {
    ctor public OvershootInterpolator();
    ctor public OvershootInterpolator(float);
    ctor public OvershootInterpolator(android.content.Context, android.util.AttributeSet);
    method public float getInterpolation(float);
  }
  public class PathInterpolator implements android.view.animation.Interpolator {
  public class PathInterpolator extends android.view.animation.BaseInterpolator {
    ctor public PathInterpolator(android.graphics.Path);
    ctor public PathInterpolator(float, float);
    ctor public PathInterpolator(float, float, float, float);
+103 −12
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.animation;

import android.content.res.ConstantState;

import java.util.ArrayList;

/**
@@ -40,6 +42,18 @@ public abstract class Animator implements Cloneable {
     */
    boolean mPaused = false;

    /**
     * A set of flags which identify the type of configuration changes that can affect this
     * Animator. Used by the Animator cache.
     */
    int mChangingConfigurations = 0;

    /**
     * If this animator is inflated from a constant state, keep a reference to it so that
     * ConstantState will not be garbage collected until this animator is collected
     */
    private AnimatorConstantState mConstantState;

    /**
     * Starts this animation. If the animation has a nonzero startDelay, the animation will start
     * running after that delay elapses. A non-delayed animation will have its initial
@@ -295,25 +309,71 @@ public abstract class Animator implements Cloneable {
        }
    }

    /**
     * Return a mask of the configuration parameters for which this animator may change, requiring
     * that it should be re-created from Resources. The default implementation returns whatever
     * value was provided through setChangingConfigurations(int) or 0 by default.
     *
     * @return Returns a mask of the changing configuration parameters, as defined by
     * {@link android.content.pm.ActivityInfo}.
     * @see android.content.pm.ActivityInfo
     * @hide
     */
    public int getChangingConfigurations() {
        return mChangingConfigurations;
    }

    /**
     * Set a mask of the configuration parameters for which this animator may change, requiring
     * that it be re-created from resource.
     *
     * @param configs A mask of the changing configuration parameters, as
     * defined by {@link android.content.pm.ActivityInfo}.
     *
     * @see android.content.pm.ActivityInfo
     * @hide
     */
    public void setChangingConfigurations(int configs) {
        mChangingConfigurations = configs;
    }

    /**
     * Sets the changing configurations value to the union of the current changing configurations
     * and the provided configs.
     * This method is called while loading the animator.
     * @hide
     */
    public void appendChangingConfigurations(int configs) {
        mChangingConfigurations |= configs;
    }

    /**
     * Return a {@link android.content.res.ConstantState} instance that holds the shared state of
     * this Animator.
     * <p>
     * This constant state is used to create new instances of this animator when needed, instead
     * of re-loading it from resources. Default implementation creates a new
     * {@link AnimatorConstantState}. You can override this method to provide your custom logic or
     * return null if you don't want this animator to be cached.
     *
     * @return The ConfigurationBoundResourceCache.BaseConstantState associated to this Animator.
     * @see android.content.res.ConstantState
     * @see #clone()
     * @hide
     */
    public ConstantState<Animator> createConstantState() {
        return new AnimatorConstantState(this);
    }

    @Override
    public Animator clone() {
        try {
            final Animator anim = (Animator) super.clone();
            if (mListeners != null) {
                ArrayList<AnimatorListener> oldListeners = mListeners;
                anim.mListeners = new ArrayList<AnimatorListener>();
                int numListeners = oldListeners.size();
                for (int i = 0; i < numListeners; ++i) {
                    anim.mListeners.add(oldListeners.get(i));
                }
                anim.mListeners = new ArrayList<AnimatorListener>(mListeners);
            }
            if (mPauseListeners != null) {
                ArrayList<AnimatorPauseListener> oldListeners = mPauseListeners;
                anim.mPauseListeners = new ArrayList<AnimatorPauseListener>();
                int numListeners = oldListeners.size();
                for (int i = 0; i < numListeners; ++i) {
                    anim.mPauseListeners.add(oldListeners.get(i));
                }
                anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners);
            }
            return anim;
        } catch (CloneNotSupportedException e) {
@@ -469,4 +529,35 @@ public abstract class Animator implements Cloneable {
    public void setAllowRunningAsynchronously(boolean mayRunAsync) {
        // It is up to subclasses to support this, if they can.
    }

    /**
     * Creates a {@link ConstantState} which holds changing configurations information associated
     * with the given Animator.
     * <p>
     * When {@link #newInstance()} is called, default implementation clones the Animator.
     */
    private static class AnimatorConstantState extends ConstantState<Animator> {

        final Animator mAnimator;
        int mChangingConf;

        public AnimatorConstantState(Animator animator) {
            mAnimator = animator;
            // ensure a reference back to here so that constante state is not gc'ed.
            mAnimator.mConstantState = this;
            mChangingConf = mAnimator.getChangingConfigurations();
        }

        @Override
        public int getChangingConfigurations() {
            return mChangingConf;
        }

        @Override
        public Animator newInstance() {
            final Animator clone = mAnimator.clone();
            clone.mConstantState = this;
            return clone;
        }
    }
}
+73 −17
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
package android.animation;

import android.content.Context;
import android.content.res.ConfigurationBoundResourceCache;
import android.content.res.ConstantState;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
@@ -30,6 +32,8 @@ import android.util.TypedValue;
import android.util.Xml;
import android.view.InflateException;
import android.view.animation.AnimationUtils;
import android.view.animation.BaseInterpolator;
import android.view.animation.Interpolator;

import com.android.internal.R;

@@ -67,6 +71,9 @@ public class AnimatorInflater {

    private static final boolean DBG_ANIMATOR_INFLATER = false;

    // used to calculate changing configs for resource references
    private static final TypedValue sTmpTypedValue = new TypedValue();

    /**
     * Loads an {@link Animator} object from a resource
     *
@@ -98,11 +105,34 @@ public class AnimatorInflater {
    /** @hide */
    public static Animator loadAnimator(Resources resources, Theme theme, int id,
            float pathErrorScale) throws NotFoundException {

        final ConfigurationBoundResourceCache<Animator> animatorCache = resources
                .getAnimatorCache();
        Animator animator = animatorCache.get(id, theme);
        if (animator != null) {
            if (DBG_ANIMATOR_INFLATER) {
                Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id));
            }
            return animator;
        } else if (DBG_ANIMATOR_INFLATER) {
            Log.d(TAG, "cache miss for animator " + resources.getResourceName(id));
        }
        XmlResourceParser parser = null;
        try {
            parser = resources.getAnimation(id);
            return createAnimatorFromXml(resources, theme, parser, pathErrorScale);
            animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale);
            if (animator != null) {
                animator.appendChangingConfigurations(getChangingConfigs(resources, id));
                final ConstantState<Animator> constantState = animator.createConstantState();
                if (constantState != null) {
                    if (DBG_ANIMATOR_INFLATER) {
                        Log.d(TAG, "caching animator for res " + resources.getResourceName(id));
                    }
                    animatorCache.put(id, theme, constantState);
                    // create a new animator so that cached version is never used by the user
                    animator = constantState.newInstance(resources, theme);
                }
            }
            return animator;
        } catch (XmlPullParserException ex) {
            Resources.NotFoundException rnf =
                    new Resources.NotFoundException("Can't load animation resource ID #0x" +
@@ -122,10 +152,29 @@ public class AnimatorInflater {

    public static StateListAnimator loadStateListAnimator(Context context, int id)
            throws NotFoundException {
        final Resources resources = context.getResources();
        final ConfigurationBoundResourceCache<StateListAnimator> cache = resources
                .getStateListAnimatorCache();
        final Theme theme = context.getTheme();
        StateListAnimator animator = cache.get(id, theme);
        if (animator != null) {
            return animator;
        }
        XmlResourceParser parser = null;
        try {
            parser = context.getResources().getAnimation(id);
            return createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser));
            parser = resources.getAnimation(id);
            animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser));
            if (animator != null) {
                animator.appendChangingConfigurations(getChangingConfigs(resources, id));
                final ConstantState<StateListAnimator> constantState = animator
                        .createConstantState();
                if (constantState != null) {
                    cache.put(id, theme, constantState);
                    // return a clone so that the animator in constant state is never used.
                    animator = constantState.newInstance(resources, theme);
                }
            }
            return animator;
        } catch (XmlPullParserException ex) {
            Resources.NotFoundException rnf =
                    new Resources.NotFoundException(
@@ -172,14 +221,13 @@ public class AnimatorInflater {
                        for (int i = 0; i < attributeCount; i++) {
                            int attrName = attributeSet.getAttributeNameResource(i);
                            if (attrName == R.attr.animation) {
                                animator = loadAnimator(context,
                                        attributeSet.getAttributeResourceValue(i, 0));
                                final int animId = attributeSet.getAttributeResourceValue(i, 0);
                                animator = loadAnimator(context, animId);
                            } else {
                                states[stateIndex++] =
                                        attributeSet.getAttributeBooleanValue(i, false) ?
                                                attrName : -attrName;
                            }

                        }
                        if (animator == null) {
                            animator = createAnimatorFromXml(context.getResources(),
@@ -192,7 +240,6 @@ public class AnimatorInflater {
                        }
                        stateListAnimator
                                .addState(StateSet.trimStateSet(states, stateIndex), animator);

                    }
                    break;
            }
@@ -508,7 +555,6 @@ public class AnimatorInflater {
    private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser,
            AttributeSet attrs, AnimatorSet parent, int sequenceOrdering, float pixelSize)
            throws XmlPullParserException, IOException {

        Animator anim = null;
        ArrayList<Animator> childAnims = null;

@@ -537,8 +583,8 @@ public class AnimatorInflater {
                } else {
                    a = res.obtainAttributes(attrs, R.styleable.AnimatorSet);
                }
                int ordering = a.getInt(R.styleable.AnimatorSet_ordering,
                        TOGETHER);
                anim.appendChangingConfigurations(a.getChangingConfigurations());
                int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER);
                createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering,
                        pixelSize);
                a.recycle();
@@ -565,7 +611,6 @@ public class AnimatorInflater {
                parent.playSequentially(animsArray);
            }
        }

        return anim;

    }
@@ -591,7 +636,6 @@ public class AnimatorInflater {
    private static ValueAnimator loadAnimator(Resources res, Theme theme,
            AttributeSet attrs, ValueAnimator anim, float pathErrorScale)
            throws NotFoundException {

        TypedArray arrayAnimator = null;
        TypedArray arrayObjectAnimator = null;

@@ -609,25 +653,37 @@ public class AnimatorInflater {
            } else {
                arrayObjectAnimator = res.obtainAttributes(attrs, R.styleable.PropertyAnimator);
            }
            anim.appendChangingConfigurations(arrayObjectAnimator.getChangingConfigurations());
        }

        if (anim == null) {
            anim = new ValueAnimator();
        }
        anim.appendChangingConfigurations(arrayAnimator.getChangingConfigurations());

        parseAnimatorFromTypeArray(anim, arrayAnimator, arrayObjectAnimator, pathErrorScale);

        final int resID =
                arrayAnimator.getResourceId(R.styleable.Animator_interpolator, 0);
        final int resID = arrayAnimator.getResourceId(R.styleable.Animator_interpolator, 0);
        if (resID > 0) {
            anim.setInterpolator(AnimationUtils.loadInterpolator(res, theme, resID));
            final Interpolator interpolator = AnimationUtils.loadInterpolator(res, theme, resID);
            if (interpolator instanceof BaseInterpolator) {
                anim.appendChangingConfigurations(
                        ((BaseInterpolator) interpolator).getChangingConfiguration());
            }
            anim.setInterpolator(interpolator);
        }

        arrayAnimator.recycle();
        if (arrayObjectAnimator != null) {
            arrayObjectAnimator.recycle();
        }

        return anim;
    }

    private static int getChangingConfigs(Resources resources, int id) {
        synchronized (sTmpTypedValue) {
            resources.getValue(id, sTmpTypedValue, true);
            return sTmpTypedValue.changingConfigurations;
        }
    }
}
+54 −24
Original line number Diff line number Diff line
@@ -240,6 +240,19 @@ public final class AnimatorSet extends Animator {
        }
    }

    /**
     * @hide
     */
    @Override
    public int getChangingConfigurations() {
        int conf = super.getChangingConfigurations();
        final int nodeCount = mNodes.size();
        for (int i = 0; i < nodeCount; i ++) {
            conf |= mNodes.get(i).animation.getChangingConfigurations();
        }
        return conf;
    }

    /**
     * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
     * of this AnimatorSet. The default value is null, which means that no interpolator
@@ -628,23 +641,25 @@ public final class AnimatorSet extends Animator {
         * manually, as we clone each Node (and its animation). The clone will then be sorted,
         * and will populate any appropriate lists, when it is started.
         */
        final int nodeCount = mNodes.size();
        anim.mNeedsSort = true;
        anim.mTerminated = false;
        anim.mStarted = false;
        anim.mPlayingSet = new ArrayList<Animator>();
        anim.mNodeMap = new HashMap<Animator, Node>();
        anim.mNodes = new ArrayList<Node>();
        anim.mSortedNodes = new ArrayList<Node>();
        anim.mNodes = new ArrayList<Node>(nodeCount);
        anim.mSortedNodes = new ArrayList<Node>(nodeCount);
        anim.mReversible = mReversible;
        anim.mSetListener = null;

        // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
        // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
        // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
        HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new>
        for (Node node : mNodes) {

        for (int n = 0; n < nodeCount; n++) {
            final Node node = mNodes.get(n);
            Node nodeClone = node.clone();
            nodeCloneMap.put(node, nodeClone);
            node.mTmpClone = nodeClone;
            anim.mNodes.add(nodeClone);
            anim.mNodeMap.put(nodeClone.animation, nodeClone);
            // Clear out the dependencies in the clone; we'll set these up manually later
@@ -652,40 +667,50 @@ public final class AnimatorSet extends Animator {
            nodeClone.tmpDependencies = null;
            nodeClone.nodeDependents = null;
            nodeClone.nodeDependencies = null;

            // clear out any listeners that were set up by the AnimatorSet; these will
            // be set up when the clone's nodes are sorted
            ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
            final ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
            if (cloneListeners != null) {
                ArrayList<AnimatorListener> listenersToRemove = null;
                for (AnimatorListener listener : cloneListeners) {
                for (int i = cloneListeners.size() - 1; i >= 0; i--) {
                    final AnimatorListener listener = cloneListeners.get(i);
                    if (listener instanceof AnimatorSetListener) {
                        if (listenersToRemove == null) {
                            listenersToRemove = new ArrayList<AnimatorListener>();
                        }
                        listenersToRemove.add(listener);
                    }
                }
                if (listenersToRemove != null) {
                    for (AnimatorListener listener : listenersToRemove) {
                        cloneListeners.remove(listener);
                        cloneListeners.remove(i);
                    }
                }
            }
        }
        // Now that we've cloned all of the nodes, we're ready to walk through their
        // dependencies, mapping the old dependencies to the new nodes
        for (Node node : mNodes) {
            Node nodeClone = nodeCloneMap.get(node);
        for (int n = 0; n < nodeCount; n++) {
            final Node node = mNodes.get(n);
            final Node clone = node.mTmpClone;
            if (node.dependencies != null) {
                for (Dependency dependency : node.dependencies) {
                    Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
                    Dependency cloneDependency = new Dependency(clonedDependencyNode,
                clone.dependencies = new ArrayList<Dependency>(node.dependencies.size());
                final int depSize = node.dependencies.size();
                for (int i = 0; i < depSize; i ++) {
                    final Dependency dependency = node.dependencies.get(i);
                    Dependency cloneDependency = new Dependency(dependency.node.mTmpClone,
                            dependency.rule);
                    nodeClone.addDependency(cloneDependency);
                    clone.dependencies.add(cloneDependency);
                }
            }
            if (node.nodeDependents != null) {
                clone.nodeDependents = new ArrayList<Node>(node.nodeDependents.size());
                for (Node dep : node.nodeDependents) {
                    clone.nodeDependents.add(dep.mTmpClone);
                }
            }
            if (node.nodeDependencies != null) {
                clone.nodeDependencies = new ArrayList<Node>(node.nodeDependencies.size());
                for (Node dep : node.nodeDependencies) {
                    clone.nodeDependencies.add(dep.mTmpClone);
                }
            }
        }
        for (int n = 0; n < nodeCount; n++) {
            mNodes.get(n).mTmpClone = null;
        }

        return anim;
    }

@@ -1016,6 +1041,11 @@ public final class AnimatorSet extends Animator {
         */
        public boolean done = false;

        /**
         * Temporary field to hold the clone in AnimatorSet#clone. Cleaned after clone is complete
         */
        private Node mTmpClone = null;

        /**
         * Constructs the Node with the animation that it encapsulates. A Node has no
         * dependencies by default; dependencies are added via the addDependency()
+3 −2
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.animation;
import android.animation.Keyframe.FloatKeyframe;

import java.util.ArrayList;
import java.util.List;

/**
 * This class holds a collection of FloatKeyframe objects and is called by ValueAnimator to calculate
@@ -47,8 +48,8 @@ class FloatKeyframeSet extends KeyframeSet implements Keyframes.FloatKeyframes {

    @Override
    public FloatKeyframeSet clone() {
        ArrayList<Keyframe> keyframes = mKeyframes;
        int numKeyframes = mKeyframes.size();
        final List<Keyframe> keyframes = mKeyframes;
        final int numKeyframes = mKeyframes.size();
        FloatKeyframe[] newKeyframes = new FloatKeyframe[numKeyframes];
        for (int i = 0; i < numKeyframes; ++i) {
            newKeyframes[i] = (FloatKeyframe) keyframes.get(i).clone();
Loading