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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
61
core/java/android/content/res/ConstantState.java
Normal file
61
core/java/android/content/res/ConstantState.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
37
core/java/android/view/animation/BaseInterpolator.java
Normal file
37
core/java/android/view/animation/BaseInterpolator.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
22
core/tests/coretests/res/anim/test_animator.xml
Normal file
22
core/tests/coretests/res/anim/test_animator.xml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
18
core/tests/coretests/res/values-land/dimens.xml
Normal file
18
core/tests/coretests/res/values-land/dimens.xml
Normal 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>
|
||||
19
core/tests/coretests/res/values/dimens.xml
Normal file
19
core/tests/coretests/res/values/dimens.xml
Normal 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>
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user