am fa9ed8ca: Merge "Added a generic configuration and theme based cache" into lmp-mr1-dev

* commit 'fa9ed8ca0a08be0b32b2ccc33563a47df1f6d3da':
  Added a generic configuration and theme based cache
This commit is contained in:
Yigit Boyar
2014-10-23 21:16:06 +00:00
committed by Android Git Automerger
37 changed files with 1201 additions and 195 deletions

View File

@@ -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();
}
@@ -35428,13 +35429,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);
@@ -35536,14 +35537,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);
@@ -35551,19 +35552,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);
@@ -35638,20 +35643,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);

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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()

View File

@@ -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();

View File

@@ -19,6 +19,7 @@ package android.animation;
import android.animation.Keyframe.IntKeyframe;
import java.util.ArrayList;
import java.util.List;
/**
* This class holds a collection of IntKeyframe objects and is called by ValueAnimator to calculate
@@ -47,7 +48,7 @@ class IntKeyframeSet extends KeyframeSet implements Keyframes.IntKeyframes {
@Override
public IntKeyframeSet clone() {
ArrayList<Keyframe> keyframes = mKeyframes;
List<Keyframe> keyframes = mKeyframes;
int numKeyframes = mKeyframes.size();
IntKeyframe[] newKeyframes = new IntKeyframe[numKeyframes];
for (int i = 0; i < numKeyframes; ++i) {

View File

@@ -18,6 +18,8 @@ package android.animation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.animation.Keyframe.IntKeyframe;
import android.animation.Keyframe.FloatKeyframe;
import android.animation.Keyframe.ObjectKeyframe;
@@ -36,16 +38,16 @@ class KeyframeSet implements Keyframes {
Keyframe mFirstKeyframe;
Keyframe mLastKeyframe;
TimeInterpolator mInterpolator; // only used in the 2-keyframe case
ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes
List<Keyframe> mKeyframes; // only used when there are not 2 keyframes
TypeEvaluator mEvaluator;
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
mKeyframes = new ArrayList<Keyframe>();
mKeyframes.addAll(Arrays.asList(keyframes));
mFirstKeyframe = mKeyframes.get(0);
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
// immutable list
mKeyframes = Arrays.asList(keyframes);
mFirstKeyframe = keyframes[0];
mLastKeyframe = keyframes[mNumKeyframes - 1];
mInterpolator = mLastKeyframe.getInterpolator();
}
@@ -57,7 +59,7 @@ class KeyframeSet implements Keyframes {
public void invalidateCache() {
}
public ArrayList<Keyframe> getKeyframes() {
public List<Keyframe> getKeyframes() {
return mKeyframes;
}
@@ -177,9 +179,9 @@ class KeyframeSet implements Keyframes {
@Override
public KeyframeSet clone() {
ArrayList<Keyframe> keyframes = mKeyframes;
List<Keyframe> keyframes = mKeyframes;
int numKeyframes = mKeyframes.size();
Keyframe[] newKeyframes = new Keyframe[numKeyframes];
final Keyframe[] newKeyframes = new Keyframe[numKeyframes];
for (int i = 0; i < numKeyframes; ++i) {
newKeyframes[i] = keyframes.get(i).clone();
}

View File

@@ -16,6 +16,7 @@
package android.animation;
import java.util.ArrayList;
import java.util.List;
/**
* This interface abstracts a collection of Keyframe objects and is called by
@@ -62,7 +63,7 @@ interface Keyframes extends Cloneable {
* @return A list of all Keyframes contained by this. This may return null if this is
* not made up of Keyframes.
*/
ArrayList<Keyframe> getKeyframes();
List<Keyframe> getKeyframes();
Keyframes clone();

View File

@@ -27,6 +27,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
@@ -791,7 +792,7 @@ public class PropertyValuesHolder implements Cloneable {
// check to make sure that mProperty is on the class of target
try {
Object testValue = null;
ArrayList<Keyframe> keyframes = mKeyframes.getKeyframes();
List<Keyframe> keyframes = mKeyframes.getKeyframes();
int keyframeCount = keyframes == null ? 0 : keyframes.size();
for (int i = 0; i < keyframeCount; i++) {
Keyframe kf = keyframes.get(i);
@@ -814,7 +815,7 @@ public class PropertyValuesHolder implements Cloneable {
if (mSetter == null) {
setupSetter(targetClass);
}
ArrayList<Keyframe> keyframes = mKeyframes.getKeyframes();
List<Keyframe> keyframes = mKeyframes.getKeyframes();
int keyframeCount = keyframes == null ? 0 : keyframes.size();
for (int i = 0; i < keyframeCount; i++) {
Keyframe kf = keyframes.get(i);
@@ -890,7 +891,7 @@ public class PropertyValuesHolder implements Cloneable {
* @param target The object which holds the start values that should be set.
*/
void setupStartValue(Object target) {
ArrayList<Keyframe> keyframes = mKeyframes.getKeyframes();
List<Keyframe> keyframes = mKeyframes.getKeyframes();
if (!keyframes.isEmpty()) {
setupValue(target, keyframes.get(0));
}
@@ -905,7 +906,7 @@ public class PropertyValuesHolder implements Cloneable {
* @param target The object which holds the start values that should be set.
*/
void setupEndValue(Object target) {
ArrayList<Keyframe> keyframes = mKeyframes.getKeyframes();
List<Keyframe> keyframes = mKeyframes.getKeyframes();
if (!keyframes.isEmpty()) {
setupValue(target, keyframes.get(keyframes.size() - 1));
}

View File

@@ -16,6 +16,7 @@
package android.animation;
import android.content.res.ConstantState;
import android.util.StateSet;
import android.view.View;
@@ -44,25 +45,31 @@ import java.util.ArrayList;
* @attr ref android.R.styleable#DrawableStates_state_pressed
* @attr ref android.R.styleable#StateListAnimatorItem_animation
*/
public class StateListAnimator {
private final ArrayList<Tuple> mTuples = new ArrayList<Tuple>();
public class StateListAnimator implements Cloneable {
private ArrayList<Tuple> mTuples = new ArrayList<Tuple>();
private Tuple mLastMatch = null;
private Animator mRunningAnimator = null;
private WeakReference<View> mViewRef;
private StateListAnimatorConstantState mConstantState;
private AnimatorListenerAdapter mAnimatorListener;
private int mChangingConfigurations;
private AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
animation.setTarget(null);
if (mRunningAnimator == animation) {
mRunningAnimator = null;
public StateListAnimator() {
initAnimatorListener();
}
private void initAnimatorListener() {
mAnimatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
animation.setTarget(null);
if (mRunningAnimator == animation) {
mRunningAnimator = null;
}
}
}
};
};
}
/**
* Associates the given animator with the provided drawable state specs so that it will be run
@@ -75,6 +82,7 @@ public class StateListAnimator {
Tuple tuple = new Tuple(specs, animator);
tuple.mAnimator.addListener(mAnimatorListener);
mTuples.add(tuple);
mChangingConfigurations |= animator.getChangingConfigurations();
}
/**
@@ -118,12 +126,35 @@ public class StateListAnimator {
for (int i = 0; i < size; i++) {
mTuples.get(i).mAnimator.setTarget(null);
}
mViewRef = null;
mLastMatch = null;
mRunningAnimator = null;
}
@Override
public StateListAnimator clone() {
try {
StateListAnimator clone = (StateListAnimator) super.clone();
clone.mTuples = new ArrayList<Tuple>(mTuples.size());
clone.mLastMatch = null;
clone.mRunningAnimator = null;
clone.mViewRef = null;
clone.mAnimatorListener = null;
clone.initAnimatorListener();
final int tupleSize = mTuples.size();
for (int i = 0; i < tupleSize; i++) {
final Tuple tuple = mTuples.get(i);
final Animator animatorClone = tuple.mAnimator.clone();
animatorClone.removeListener(mAnimatorListener);
clone.addState(tuple.mSpecs, animatorClone);
}
clone.setChangingConfigurations(getChangingConfigurations());
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError("cannot clone state list animator", e);
}
}
/**
* Called by View
* @hide
@@ -181,6 +212,63 @@ public class StateListAnimator {
}
}
/**
* Return a mask of the configuration parameters for which this animator may change, requiring
* that it be re-created. The default implementation returns whatever was provided through
* {@link #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 should be recreated from resources instead of being cloned.
*
* @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. Default
* implementation creates a new {@link StateListAnimatorConstantState}. 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 {@link android.content.res.ConstantState} associated to this Animator.
* @see android.content.res.ConstantState
* @see #clone()
* @hide
*/
public ConstantState<StateListAnimator> createConstantState() {
return new StateListAnimatorConstantState(this);
}
/**
* @hide
*/
@@ -209,4 +297,36 @@ public class StateListAnimator {
return mAnimator;
}
}
/**
* Creates a constant state which holds changing configurations information associated with the
* given Animator.
* <p>
* When new instance is called, default implementation clones the Animator.
*/
private static class StateListAnimatorConstantState
extends ConstantState<StateListAnimator> {
final StateListAnimator mAnimator;
int mChangingConf;
public StateListAnimatorConstantState(StateListAnimator animator) {
mAnimator = animator;
mAnimator.mConstantState = this;
mChangingConf = mAnimator.getChangingConfigurations();
}
@Override
public int getChangingConfigurations() {
return mChangingConf;
}
@Override
public StateListAnimator newInstance() {
final StateListAnimator clone = mAnimator.clone();
clone.mConstantState = this;
return clone;
}
}
}

View File

@@ -16,6 +16,7 @@
package android.animation;
import android.content.res.ConfigurationBoundResourceCache;
import android.os.Looper;
import android.os.Trace;
import android.util.AndroidRuntimeException;
@@ -1289,12 +1290,7 @@ public class ValueAnimator extends Animator {
public ValueAnimator clone() {
final ValueAnimator anim = (ValueAnimator) super.clone();
if (mUpdateListeners != null) {
ArrayList<AnimatorUpdateListener> oldListeners = mUpdateListeners;
anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
int numListeners = oldListeners.size();
for (int i = 0; i < numListeners; ++i) {
anim.mUpdateListeners.add(oldListeners.get(i));
}
anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>(mUpdateListeners);
}
anim.mSeekTime = -1;
anim.mPlayingBackwards = false;

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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 android.content.res;
import android.util.ArrayMap;
import android.util.LongSparseArray;
import java.lang.ref.WeakReference;
/**
* A Cache class which can be used to cache resource objects that are easy to clone but more
* expensive to inflate.
* @hide
*/
public class ConfigurationBoundResourceCache<T> {
private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>> mCache =
new ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>>();
final Resources mResources;
/**
* Creates a Resource cache for the given Resources instance.
*
* @param resources The Resource which can be used when creating new instances.
*/
public ConfigurationBoundResourceCache(Resources resources) {
mResources = resources;
}
/**
* Adds a new item to the cache.
*
* @param key A custom key that uniquely identifies the resource.
* @param theme The Theme instance where this resource was loaded.
* @param constantState The constant state that can create new instances of the resource.
*
*/
public void put(long key, Resources.Theme theme, ConstantState<T> constantState) {
if (constantState == null) {
return;
}
final String themeKey = theme == null ? "" : theme.getKey();
LongSparseArray<WeakReference<ConstantState<T>>> themedCache;
synchronized (this) {
themedCache = mCache.get(themeKey);
if (themedCache == null) {
themedCache = new LongSparseArray<WeakReference<ConstantState<T>>>(1);
mCache.put(themeKey, themedCache);
}
themedCache.put(key, new WeakReference<ConstantState<T>>(constantState));
}
}
/**
* If the resource is cached, creates a new instance of it and returns.
*
* @param key The long key which can be used to uniquely identify the resource.
* @param theme The The Theme instance where we want to load this resource.
*
* @return If this resources was loaded before, returns a new instance of it. Otherwise, returns
* null.
*/
public T get(long key, Resources.Theme theme) {
final String themeKey = theme != null ? theme.getKey() : "";
final LongSparseArray<WeakReference<ConstantState<T>>> themedCache;
final WeakReference<ConstantState<T>> wr;
synchronized (this) {
themedCache = mCache.get(themeKey);
if (themedCache == null) {
return null;
}
wr = themedCache.get(key);
}
if (wr == null) {
return null;
}
final ConstantState entry = wr.get();
if (entry != null) {
return (T) entry.newInstance(mResources, theme);
} else { // our entry has been purged
synchronized (this) {
// there is a potential race condition here where this entry may be put in
// another thread. But we prefer it to minimize lock duration
themedCache.delete(key);
}
}
return null;
}
/**
* Users of ConfigurationBoundResourceCache must call this method whenever a configuration
* change happens. On this callback, the cache invalidates all resources that are not valid
* anymore.
*
* @param configChanges The configuration changes
*/
public void onConfigurationChange(final int configChanges) {
synchronized (this) {
final int size = mCache.size();
for (int i = size - 1; i >= 0; i--) {
final LongSparseArray<WeakReference<ConstantState<T>>>
themeCache = mCache.valueAt(i);
onConfigurationChangeInt(themeCache, configChanges);
if (themeCache.size() == 0) {
mCache.removeAt(i);
}
}
}
}
private void onConfigurationChangeInt(
final LongSparseArray<WeakReference<ConstantState<T>>> themeCache,
final int configChanges) {
final int size = themeCache.size();
for (int i = size - 1; i >= 0; i--) {
final WeakReference<ConstantState<T>> wr = themeCache.valueAt(i);
final ConstantState<T> constantState = wr.get();
if (constantState == null || Configuration.needNewResources(
configChanges, constantState.getChangingConfigurations())) {
themeCache.removeAt(i);
}
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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 android.content.res;
/**
* A cache class that can provide new instances of a particular resource which may change
* depending on the current {@link Resources.Theme} or {@link Configuration}.
* <p>
* A constant state should be able to return a bitmask of changing configurations, which
* identifies the type of configuration changes that may invalidate this resource. These
* configuration changes can be obtained from {@link android.util.TypedValue}. Entities such as
* {@link android.animation.Animator} also provide a changing configuration method to include
* their dependencies (e.g. An AnimatorSet's changing configuration is the union of the
* changing configurations of each Animator in the set)
* @hide
*/
abstract public class ConstantState<T> {
/**
* Return a bit mask of configuration changes that will impact
* this resource (and thus require completely reloading it).
*/
abstract public int getChangingConfigurations();
/**
* Create a new instance without supplying resources the caller
* is running in.
*/
public abstract T newInstance();
/**
* Create a new instance from its constant state. This
* must be implemented for resources that change based on the target
* density of their caller (that is depending on whether it is
* in compatibility mode).
*/
public T newInstance(Resources res) {
return newInstance();
}
/**
* Create a new instance from its constant state. This must be
* implemented for resources that can have a theme applied.
*/
public T newInstance(Resources res, Resources.Theme theme) {
return newInstance(res);
}
}

View File

@@ -16,6 +16,8 @@
package android.content.res;
import android.animation.Animator;
import android.animation.StateListAnimator;
import android.util.Pools.SynchronizedPool;
import android.view.ViewDebug;
import com.android.internal.util.XmlUtils;
@@ -115,6 +117,10 @@ public class Resources {
new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
private final LongSparseArray<WeakReference<ColorStateList>> mColorStateListCache =
new LongSparseArray<WeakReference<ColorStateList>>();
private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
new ConfigurationBoundResourceCache<Animator>(this);
private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
new ConfigurationBoundResourceCache<StateListAnimator>(this);
private TypedValue mTmpValue = new TypedValue();
private boolean mPreloading;
@@ -182,6 +188,24 @@ public class Resources {
return deviceDefault;
}
/**
* Used by AnimatorInflater.
*
* @hide
*/
public ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
return mAnimatorCache;
}
/**
* Used by AnimatorInflater.
*
* @hide
*/
public ConfigurationBoundResourceCache<StateListAnimator> getStateListAnimatorCache() {
return mStateListAnimatorCache;
}
/**
* This exception is thrown by the resource APIs when a requested resource
* can not be found.
@@ -1761,23 +1785,7 @@ public class Resources {
// the framework.
mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
int configChanges = 0xfffffff;
if (config != null) {
mTmpConfig.setTo(config);
int density = config.densityDpi;
if (density == Configuration.DENSITY_DPI_UNDEFINED) {
density = mMetrics.noncompatDensityDpi;
}
mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
if (mTmpConfig.locale == null) {
mTmpConfig.locale = Locale.getDefault();
mTmpConfig.setLayoutDirection(mTmpConfig.locale);
}
configChanges = mConfiguration.updateFrom(mTmpConfig);
configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
}
int configChanges = calcConfigChanges(config);
if (mConfiguration.locale == null) {
mConfiguration.locale = Locale.getDefault();
mConfiguration.setLayoutDirection(mConfiguration.locale);
@@ -1825,6 +1833,8 @@ public class Resources {
clearDrawableCachesLocked(mDrawableCache, configChanges);
clearDrawableCachesLocked(mColorDrawableCache, configChanges);
mAnimatorCache.onConfigurationChange(configChanges);
mStateListAnimatorCache.onConfigurationChange(configChanges);
mColorStateListCache.clear();
@@ -1837,6 +1847,30 @@ public class Resources {
}
}
/**
* Called by ConfigurationBoundResourceCacheTest via reflection.
*/
private int calcConfigChanges(Configuration config) {
int configChanges = 0xfffffff;
if (config != null) {
mTmpConfig.setTo(config);
int density = config.densityDpi;
if (density == Configuration.DENSITY_DPI_UNDEFINED) {
density = mMetrics.noncompatDensityDpi;
}
mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
if (mTmpConfig.locale == null) {
mTmpConfig.locale = Locale.getDefault();
mTmpConfig.setLayoutDirection(mTmpConfig.locale);
}
configChanges = mConfiguration.updateFrom(mTmpConfig);
configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
}
return configChanges;
}
private void clearDrawableCachesLocked(
ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
int configChanges) {

View File

@@ -26,17 +26,17 @@ import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the rate of change starts and ends slowly but
* accelerates through the middle.
*
*/
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
public class AccelerateDecelerateInterpolator extends BaseInterpolator
implements NativeInterpolatorFactory {
public AccelerateDecelerateInterpolator() {
}
@SuppressWarnings({"UnusedDeclaration"})
public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

View File

@@ -33,7 +33,7 @@ import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
*
*/
@HasNativeInterpolator
public class AccelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
private final float mFactor;
private final double mDoubleFactor;
@@ -70,7 +70,7 @@ public class AccelerateInterpolator implements Interpolator, NativeInterpolatorF
mFactor = a.getFloat(R.styleable.AccelerateInterpolator_factor, 1.0f);
mDoubleFactor = 2 * mFactor;
setChangingConfiguration(a.getChangingConfigurations());
a.recycle();
}

View File

@@ -321,7 +321,7 @@ public class AnimationUtils {
private static Interpolator createInterpolatorFromXml(Resources res, Theme theme, XmlPullParser parser)
throws XmlPullParserException, IOException {
Interpolator interpolator = null;
BaseInterpolator interpolator = null;
// Make sure we are on a start tag.
int type;
@@ -361,10 +361,7 @@ public class AnimationUtils {
} else {
throw new RuntimeException("Unknown interpolator name: " + parser.getName());
}
}
return interpolator;
}
}

View File

@@ -31,7 +31,7 @@ import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
* An interpolator where the change starts backward then flings forward.
*/
@HasNativeInterpolator
public class AnticipateInterpolator implements Interpolator, NativeInterpolatorFactory {
public class AnticipateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
private final float mTension;
public AnticipateInterpolator() {
@@ -60,9 +60,8 @@ public class AnticipateInterpolator implements Interpolator, NativeInterpolatorF
a = res.obtainAttributes(attrs, R.styleable.AnticipateInterpolator);
}
mTension =
a.getFloat(R.styleable.AnticipateInterpolator_tension, 2.0f);
mTension = a.getFloat(R.styleable.AnticipateInterpolator_tension, 2.0f);
setChangingConfiguration(a.getChangingConfigurations());
a.recycle();
}

View File

@@ -35,7 +35,8 @@ import static com.android.internal.R.styleable.AnticipateOvershootInterpolator;
* the target value and finally goes back to the final value.
*/
@HasNativeInterpolator
public class AnticipateOvershootInterpolator implements Interpolator, NativeInterpolatorFactory {
public class AnticipateOvershootInterpolator extends BaseInterpolator
implements NativeInterpolatorFactory {
private final float mTension;
public AnticipateOvershootInterpolator() {
@@ -78,7 +79,7 @@ public class AnticipateOvershootInterpolator implements Interpolator, NativeInte
mTension = a.getFloat(AnticipateOvershootInterpolator_tension, 2.0f) *
a.getFloat(AnticipateOvershootInterpolator_extraTension, 1.5f);
setChangingConfiguration(a.getChangingConfigurations());
a.recycle();
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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 android.view.animation;
/**
* An abstract class which is extended by default interpolators.
*/
abstract public class BaseInterpolator implements Interpolator {
private int mChangingConfiguration;
/**
* @hide
*/
public int getChangingConfiguration() {
return mChangingConfiguration;
}
/**
* @hide
*/
void setChangingConfiguration(int changingConfiguration) {
mChangingConfiguration = changingConfiguration;
}
}

View File

@@ -27,7 +27,7 @@ import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
* An interpolator where the change bounces at the end.
*/
@HasNativeInterpolator
public class BounceInterpolator implements Interpolator, NativeInterpolatorFactory {
public class BounceInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public BounceInterpolator() {
}

View File

@@ -33,7 +33,7 @@ import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
*
*/
@HasNativeInterpolator
public class CycleInterpolator implements Interpolator, NativeInterpolatorFactory {
public class CycleInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public CycleInterpolator(float cycles) {
mCycles = cycles;
}
@@ -52,7 +52,7 @@ public class CycleInterpolator implements Interpolator, NativeInterpolatorFactor
}
mCycles = a.getFloat(R.styleable.CycleInterpolator_cycles, 1.0f);
setChangingConfiguration(a.getChangingConfigurations());
a.recycle();
}

View File

@@ -33,7 +33,7 @@ import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
*
*/
@HasNativeInterpolator
public class DecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
public class DecelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public DecelerateInterpolator() {
}
@@ -62,7 +62,7 @@ public class DecelerateInterpolator implements Interpolator, NativeInterpolatorF
}
mFactor = a.getFloat(R.styleable.DecelerateInterpolator_factor, 1.0f);
setChangingConfiguration(a.getChangingConfigurations());
a.recycle();
}

View File

@@ -25,17 +25,16 @@ import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the rate of change is constant
*
*/
@HasNativeInterpolator
public class LinearInterpolator implements Interpolator, NativeInterpolatorFactory {
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return input;
}

View File

@@ -32,7 +32,7 @@ import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
* then comes back.
*/
@HasNativeInterpolator
public class OvershootInterpolator implements Interpolator, NativeInterpolatorFactory {
public class OvershootInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
private final float mTension;
public OvershootInterpolator() {
@@ -61,9 +61,8 @@ public class OvershootInterpolator implements Interpolator, NativeInterpolatorFa
a = res.obtainAttributes(attrs, R.styleable.OvershootInterpolator);
}
mTension =
a.getFloat(R.styleable.OvershootInterpolator_tension, 2.0f);
mTension = a.getFloat(R.styleable.OvershootInterpolator_tension, 2.0f);
setChangingConfiguration(a.getChangingConfigurations());
a.recycle();
}

View File

@@ -42,7 +42,7 @@ import com.android.internal.R;
* path.lineTo(1f, 1f);
* </pre></blockquote></p>
*/
public class PathInterpolator implements Interpolator {
public class PathInterpolator extends BaseInterpolator {
// This governs how accurate the approximation of the Path is.
private static final float PRECISION = 0.002f;
@@ -98,7 +98,7 @@ public class PathInterpolator implements Interpolator {
a = res.obtainAttributes(attrs, R.styleable.PathInterpolator);
}
parseInterpolatorFromTypeArray(a);
setChangingConfiguration(a.getChangingConfigurations());
a.recycle();
}

View File

@@ -1252,6 +1252,13 @@
</intent-filter>
</activity>
<activity android:name="android.content.res.ResourceCacheActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
</intent-filter>
</activity>
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"

View File

@@ -1,4 +1,18 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2014 The Android Open Source Project
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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
<objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2014 The Android Open Source Project
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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
<objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
<objectAnimator android:propertyName="y" android:duration="100" android:valueTo="1" android:valueType="floatType"/>
<objectAnimator android:propertyName="left" android:duration="100" android:valueTo="2" android:valueType="intType"/>
</set>

View File

@@ -1,4 +1,18 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2014 The Android Open Source Project
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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<set>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
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.
-->
<resources>
<dimen name="resource_cache_test_orientation_dependent">3dp</dimen>
</resources>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
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.
-->
<resources>
<dimen name="resource_cache_test_generic">10dp</dimen>
<dimen name="resource_cache_test_orientation_dependent">20dp</dimen>
</resources>

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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 android.animation;
import android.test.ActivityInstrumentationTestCase2;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import com.android.frameworks.coretests.R;
public class AnimatorInflaterTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
Set<Integer> identityHashes = new HashSet<Integer>();
public AnimatorInflaterTest() {
super(BasicAnimatorActivity.class);
}
private void assertUnique(Object object) {
assertUnique(object, "");
}
private void assertUnique(Object object, String msg) {
final int code = System.identityHashCode(object);
assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code));
}
public void testLoadStateListAnimator() {
StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(getActivity(),
R.anim.test_state_anim);
sla1.setTarget(getActivity().mAnimatingButton);
StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(getActivity(),
R.anim.test_state_anim);
assertNull(sla2.getTarget());
for (StateListAnimator sla : new StateListAnimator[]{sla1, sla2}) {
assertUnique(sla);
assertEquals(3, sla.getTuples().size());
for (StateListAnimator.Tuple tuple : sla.getTuples()) {
assertUnique(tuple);
assertUnique(tuple.getAnimator());
}
}
}
}

View File

@@ -19,11 +19,14 @@ import com.android.frameworks.coretests.R;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
public class BasicAnimatorActivity extends Activity {
public Button mAnimatingButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.animator_basic);
mAnimatingButton = (Button) findViewById(R.id.animatingButton);
}
}

View File

@@ -0,0 +1,224 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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 android.content.res;
import android.test.ActivityInstrumentationTestCase2;
import android.util.TypedValue;
import com.android.frameworks.coretests.R;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ConfigurationBoundResourceCacheTest
extends ActivityInstrumentationTestCase2<ResourceCacheActivity> {
ConfigurationBoundResourceCache<Float> mCache;
Method mCalcConfigChanges;
public ConfigurationBoundResourceCacheTest() {
super(ResourceCacheActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
mCache = new ConfigurationBoundResourceCache<Float>(getActivity().getResources());
}
public void testGetEmpty() {
assertNull(mCache.get(-1, null));
}
public void testSetGet() {
mCache.put(1, null, new DummyFloatConstantState(5f));
assertEquals(5f, mCache.get(1, null));
assertNotSame(5f, mCache.get(1, null));
assertEquals(null, mCache.get(1, getActivity().getTheme()));
}
public void testSetGetThemed() {
mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f));
assertEquals(null, mCache.get(1, null));
assertEquals(5f, mCache.get(1, getActivity().getTheme()));
assertNotSame(5f, mCache.get(1, getActivity().getTheme()));
}
public void testMultiThreadPutGet() {
mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f));
mCache.put(1, null, new DummyFloatConstantState(10f));
assertEquals(10f, mCache.get(1, null));
assertNotSame(10f, mCache.get(1, null));
assertEquals(5f, mCache.get(1, getActivity().getTheme()));
assertNotSame(5f, mCache.get(1, getActivity().getTheme()));
}
public void testVoidConfigChange()
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
TypedValue staticValue = new TypedValue();
long key = 3L;
final Resources res = getActivity().getResources();
res.getValue(R.dimen.resource_cache_test_generic, staticValue, true);
float staticDim = TypedValue.complexToDimension(staticValue.data, res.getDisplayMetrics());
mCache.put(key, getActivity().getTheme(),
new DummyFloatConstantState(staticDim, staticValue.changingConfigurations));
final Configuration cfg = res.getConfiguration();
Configuration newCnf = new Configuration(cfg);
newCnf.orientation = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE ?
Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
int changes = calcConfigChanges(res, newCnf);
assertEquals(staticDim, mCache.get(key, getActivity().getTheme()));
mCache.onConfigurationChange(changes);
assertEquals(staticDim, mCache.get(key, getActivity().getTheme()));
}
public void testEffectiveConfigChange()
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
TypedValue changingValue = new TypedValue();
long key = 4L;
final Resources res = getActivity().getResources();
res.getValue(R.dimen.resource_cache_test_orientation_dependent, changingValue, true);
float changingDim = TypedValue.complexToDimension(changingValue.data,
res.getDisplayMetrics());
mCache.put(key, getActivity().getTheme(),
new DummyFloatConstantState(changingDim, changingValue.changingConfigurations));
final Configuration cfg = res.getConfiguration();
Configuration newCnf = new Configuration(cfg);
newCnf.orientation = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE ?
Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
int changes = calcConfigChanges(res, newCnf);
assertEquals(changingDim, mCache.get(key, getActivity().getTheme()));
mCache.onConfigurationChange(changes);
assertNull(mCache.get(key, getActivity().getTheme()));
}
public void testConfigChangeMultipleResources()
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
TypedValue staticValue = new TypedValue();
TypedValue changingValue = new TypedValue();
final Resources res = getActivity().getResources();
res.getValue(R.dimen.resource_cache_test_generic, staticValue, true);
res.getValue(R.dimen.resource_cache_test_orientation_dependent, changingValue, true);
float staticDim = TypedValue.complexToDimension(staticValue.data, res.getDisplayMetrics());
float changingDim = TypedValue.complexToDimension(changingValue.data,
res.getDisplayMetrics());
mCache.put(R.dimen.resource_cache_test_generic, getActivity().getTheme(),
new DummyFloatConstantState(staticDim, staticValue.changingConfigurations));
mCache.put(R.dimen.resource_cache_test_orientation_dependent, getActivity().getTheme(),
new DummyFloatConstantState(changingDim, changingValue.changingConfigurations));
final Configuration cfg = res.getConfiguration();
Configuration newCnf = new Configuration(cfg);
newCnf.orientation = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE ?
Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
int changes = calcConfigChanges(res, newCnf);
assertEquals(staticDim, mCache.get(R.dimen.resource_cache_test_generic,
getActivity().getTheme()));
assertEquals(changingDim, mCache.get(R.dimen.resource_cache_test_orientation_dependent,
getActivity().getTheme()));
mCache.onConfigurationChange(changes);
assertEquals(staticDim, mCache.get(R.dimen.resource_cache_test_generic,
getActivity().getTheme()));
assertNull(mCache.get(R.dimen.resource_cache_test_orientation_dependent,
getActivity().getTheme()));
}
public void testConfigChangeMultipleThemes()
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
TypedValue[] staticValues = new TypedValue[]{new TypedValue(), new TypedValue()};
TypedValue[] changingValues = new TypedValue[]{new TypedValue(), new TypedValue()};
float staticDim = 0;
float changingDim = 0;
final Resources res = getActivity().getResources();
for (int i = 0; i < 2; i++) {
res.getValue(R.dimen.resource_cache_test_generic, staticValues[i], true);
staticDim = TypedValue
.complexToDimension(staticValues[i].data, res.getDisplayMetrics());
res.getValue(R.dimen.resource_cache_test_orientation_dependent, changingValues[i],
true);
changingDim = TypedValue.complexToDimension(changingValues[i].data,
res.getDisplayMetrics());
final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null;
mCache.put(R.dimen.resource_cache_test_generic, theme,
new DummyFloatConstantState(staticDim, staticValues[i].changingConfigurations));
mCache.put(R.dimen.resource_cache_test_orientation_dependent, theme,
new DummyFloatConstantState(changingDim,
changingValues[i].changingConfigurations));
}
final Configuration cfg = res.getConfiguration();
Configuration newCnf = new Configuration(cfg);
newCnf.orientation = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE ?
Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
int changes = calcConfigChanges(res, newCnf);
for (int i = 0; i < 2; i++) {
final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null;
assertEquals(staticDim, mCache.get(R.dimen.resource_cache_test_generic, theme));
assertEquals(changingDim,
mCache.get(R.dimen.resource_cache_test_orientation_dependent, theme));
}
mCache.onConfigurationChange(changes);
for (int i = 0; i < 2; i++) {
final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null;
assertEquals(staticDim, mCache.get(R.dimen.resource_cache_test_generic, theme));
assertNull(mCache.get(R.dimen.resource_cache_test_orientation_dependent, theme));
}
}
private int calcConfigChanges(Resources resources, Configuration configuration)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
if (mCalcConfigChanges == null) {
mCalcConfigChanges = Resources.class.getDeclaredMethod("calcConfigChanges",
Configuration.class);
mCalcConfigChanges.setAccessible(true);
}
return (Integer) mCalcConfigChanges.invoke(resources, configuration);
}
static class DummyFloatConstantState extends
ConstantState<Float> {
final Float mObj;
int mChangingConf = 0;
DummyFloatConstantState(Float obj) {
mObj = obj;
}
DummyFloatConstantState(Float obj, int changingConf) {
mObj = obj;
mChangingConf = changingConf;
}
@Override
public int getChangingConfigurations() {
return mChangingConf;
}
@Override
public Float newInstance() {
return new Float(mObj);
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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 android.content.res;
import android.annotation.Nullable;
import android.app.Activity;
import android.os.Bundle;
import java.lang.ref.WeakReference;
public class ResourceCacheActivity extends Activity {
static WeakReference<ResourceCacheActivity> lastCreatedInstance;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lastCreatedInstance = new WeakReference<ResourceCacheActivity>(this);
}
public static ResourceCacheActivity getLastCreatedInstance() {
return lastCreatedInstance == null ? null : lastCreatedInstance.get();
}
}

View File

@@ -1058,54 +1058,72 @@ public abstract class Drawable {
final Drawable drawable;
final String name = parser.getName();
if (name.equals("selector")) {
drawable = new StateListDrawable();
} else if (name.equals("animated-selector")) {
drawable = new AnimatedStateListDrawable();
} else if (name.equals("level-list")) {
drawable = new LevelListDrawable();
} else if (name.equals("layer-list")) {
drawable = new LayerDrawable();
} else if (name.equals("transition")) {
drawable = new TransitionDrawable();
} else if (name.equals("ripple")) {
drawable = new RippleDrawable();
} else if (name.equals("color")) {
drawable = new ColorDrawable();
} else if (name.equals("shape")) {
drawable = new GradientDrawable();
} else if (name.equals("vector")) {
drawable = new VectorDrawable();
} else if (name.equals("animated-vector")) {
drawable = new AnimatedVectorDrawable();
} else if (name.equals("scale")) {
drawable = new ScaleDrawable();
} else if (name.equals("clip")) {
drawable = new ClipDrawable();
} else if (name.equals("rotate")) {
drawable = new RotateDrawable();
} else if (name.equals("animated-rotate")) {
drawable = new AnimatedRotateDrawable();
} else if (name.equals("animation-list")) {
drawable = new AnimationDrawable();
} else if (name.equals("inset")) {
drawable = new InsetDrawable();
} else if (name.equals("bitmap")) {
//noinspection deprecation
drawable = new BitmapDrawable(r);
if (r != null) {
((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
}
} else if (name.equals("nine-patch")) {
drawable = new NinePatchDrawable();
if (r != null) {
((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
}
} else {
throw new XmlPullParserException(parser.getPositionDescription() +
": invalid drawable tag " + name);
}
switch (name) {
case "selector":
drawable = new StateListDrawable();
break;
case "animated-selector":
drawable = new AnimatedStateListDrawable();
break;
case "level-list":
drawable = new LevelListDrawable();
break;
case "layer-list":
drawable = new LayerDrawable();
break;
case "transition":
drawable = new TransitionDrawable();
break;
case "ripple":
drawable = new RippleDrawable();
break;
case "color":
drawable = new ColorDrawable();
break;
case "shape":
drawable = new GradientDrawable();
break;
case "vector":
drawable = new VectorDrawable();
break;
case "animated-vector":
drawable = new AnimatedVectorDrawable();
break;
case "scale":
drawable = new ScaleDrawable();
break;
case "clip":
drawable = new ClipDrawable();
break;
case "rotate":
drawable = new RotateDrawable();
break;
case "animated-rotate":
drawable = new AnimatedRotateDrawable();
break;
case "animation-list":
drawable = new AnimationDrawable();
break;
case "inset":
drawable = new InsetDrawable();
break;
case "bitmap":
drawable = new BitmapDrawable(r);
if (r != null) {
((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
}
break;
case "nine-patch":
drawable = new NinePatchDrawable();
if (r != null) {
((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
}
break;
default:
throw new XmlPullParserException(parser.getPositionDescription() +
": invalid drawable tag " + name);
}
drawable.inflate(r, parser, attrs, theme);
return drawable;
}