Allow use of theme attributes in color state lists

BUG: 17384842
Change-Id: Ibdc413acbd00e37b908432abd55f6521c22b8fc9
This commit is contained in:
Alan Viverette
2015-01-05 14:59:19 -08:00
parent 4ae0d9045f
commit 45c4bbbbce
16 changed files with 788 additions and 301 deletions

View File

@@ -2407,6 +2407,7 @@ package android {
field public static final int Widget_Material_Button_Borderless = 16974425; // 0x1030259
field public static final int Widget_Material_Button_Borderless_Colored = 16974426; // 0x103025a
field public static final int Widget_Material_Button_Borderless_Small = 16974427; // 0x103025b
field public static final int Widget_Material_Button_Colored = 16974547; // 0x10302d3
field public static final int Widget_Material_Button_Inset = 16974428; // 0x103025c
field public static final int Widget_Material_Button_Small = 16974429; // 0x103025d
field public static final int Widget_Material_Button_Toggle = 16974430; // 0x103025e
@@ -7148,6 +7149,8 @@ package android.content {
method public abstract java.io.File getCacheDir();
method public abstract java.lang.ClassLoader getClassLoader();
method public abstract java.io.File getCodeCacheDir();
method public final int getColor(int);
method public final android.content.res.ColorStateList getColorStateList(int);
method public abstract android.content.ContentResolver getContentResolver();
method public abstract java.io.File getDatabasePath(java.lang.String);
method public abstract java.io.File getDir(java.lang.String, int);
@@ -9076,7 +9079,10 @@ package android.content.res {
public class ColorStateList implements android.os.Parcelable {
ctor public ColorStateList(int[][], int[]);
method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public void applyTheme(android.content.res.Resources.Theme);
method public boolean canApplyTheme();
method public static deprecated android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public int describeContents();
method public int getColorForState(int[], int);
method public int getDefaultColor();
@@ -9206,8 +9212,10 @@ package android.content.res {
method public android.content.res.XmlResourceParser getAnimation(int) throws android.content.res.Resources.NotFoundException;
method public final android.content.res.AssetManager getAssets();
method public boolean getBoolean(int) throws android.content.res.Resources.NotFoundException;
method public int getColor(int) throws android.content.res.Resources.NotFoundException;
method public android.content.res.ColorStateList getColorStateList(int) throws android.content.res.Resources.NotFoundException;
method public deprecated int getColor(int) throws android.content.res.Resources.NotFoundException;
method public int getColor(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
method public deprecated android.content.res.ColorStateList getColorStateList(int) throws android.content.res.Resources.NotFoundException;
method public android.content.res.ColorStateList getColorStateList(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
method public android.content.res.Configuration getConfiguration();
method public float getDimension(int) throws android.content.res.Resources.NotFoundException;
method public int getDimensionPixelOffset(int) throws android.content.res.Resources.NotFoundException;
@@ -41764,7 +41772,7 @@ package java.lang {
method public static double nextUp(double);
method public static float nextUp(float);
method public static double pow(double, double);
method public static synchronized double random();
method public static double random();
method public static double rint(double);
method public static long round(double);
method public static int round(float);

View File

@@ -24,6 +24,7 @@ import android.annotation.SystemApi;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -392,18 +393,55 @@ public abstract class Context {
}
/**
* Return a drawable object associated with a particular resource ID and
* Returns a color associated with a particular resource ID and styled for
* the current theme.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return A single color value in the form 0xAARRGGBB.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
@Nullable
public final int getColor(int id) {
return getResources().getColor(id, getTheme());
}
/**
* Returns a drawable object associated with a particular resource ID and
* styled for the current theme.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return Drawable An object that can be used to draw this resource.
* @return An object that can be used to draw this resource, or
* {@code null} if the resource could not be resolved.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
@Nullable
public final Drawable getDrawable(int id) {
return getResources().getDrawable(id, getTheme());
}
/**
* Returns a color state list associated with a particular resource ID and
* styled for the current theme.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return A color state list, or {@code null} if the resource could not be
* resolved.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
@Nullable
public final ColorStateList getColorStateList(int id) {
return getResources().getColorStateList(id, getTheme());
}
/**
* Set the base theme for this context. Note that this should be called
* before any views are instantiated in the Context (for example before

View File

@@ -16,8 +16,12 @@
package android.content.res;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources.Theme;
import android.graphics.Color;
import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
@@ -25,6 +29,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
import android.util.SparseArray;
import android.util.StateSet;
@@ -64,15 +69,23 @@ import java.util.Arrays;
* List Resource</a>.</p>
*/
public class ColorStateList implements Parcelable {
private int[][] mStateSpecs; // must be parallel to mColors
private int[] mColors; // must be parallel to mStateSpecs
private int mDefaultColor = 0xffff0000;
private static final String TAG = "ColorStateList";
private static final int DEFAULT_COLOR = Color.RED;
private static final int[][] EMPTY = new int[][] { new int[0] };
private static final SparseArray<WeakReference<ColorStateList>> sCache =
new SparseArray<WeakReference<ColorStateList>>();
private ColorStateList() { }
private int[][] mThemeAttrs;
private int mChangingConfigurations;
private int[][] mStateSpecs;
private int[] mColors;
private int mDefaultColor;
private boolean mIsOpaque;
private ColorStateList() {
// Not publicly instantiable.
}
/**
* Creates a ColorStateList that returns the specified mapping from
@@ -82,53 +95,102 @@ public class ColorStateList implements Parcelable {
mStateSpecs = states;
mColors = colors;
if (states.length > 0) {
mDefaultColor = colors[0];
for (int i = 0; i < states.length; i++) {
if (states[i].length == 0) {
mDefaultColor = colors[i];
}
}
}
onColorsChanged();
}
/**
* Creates or retrieves a ColorStateList that always returns a single color.
* @return A ColorStateList containing a single color.
*/
@NonNull
public static ColorStateList valueOf(int color) {
// TODO: should we collect these eventually?
synchronized (sCache) {
final WeakReference<ColorStateList> ref = sCache.get(color);
final int index = sCache.indexOfKey(color);
if (index >= 0) {
final ColorStateList cached = sCache.valueAt(index).get();
if (cached != null) {
return cached;
}
ColorStateList csl = ref != null ? ref.get() : null;
if (csl != null) {
return csl;
// Prune missing entry.
sCache.removeAt(index);
}
csl = new ColorStateList(EMPTY, new int[] { color });
// Prune the cache before adding new items.
final int N = sCache.size();
for (int i = N - 1; i >= 0; i--) {
if (sCache.valueAt(i).get() == null) {
sCache.removeAt(i);
}
}
final ColorStateList csl = new ColorStateList(EMPTY, new int[] { color });
sCache.put(color, new WeakReference<ColorStateList>(csl));
return csl;
}
}
/**
* Create a ColorStateList from an XML document, given a set of {@link Resources}.
* Creates a ColorStateList with the same properties as another
* ColorStateList.
* <p>
* The properties of the new ColorStateList can be modified without
* affecting the source ColorStateList.
*
* @param orig the source color state list
*/
private ColorStateList(ColorStateList orig) {
if (orig != null) {
mStateSpecs = orig.mStateSpecs;
mDefaultColor = orig.mDefaultColor;
mIsOpaque = orig.mIsOpaque;
// Deep copy, this may change due to theming.
mColors = orig.mColors.clone();
}
}
/**
* Creates a ColorStateList from an XML document.
*
* @param r Resources against which the ColorStateList should be inflated.
* @param parser Parser for the XML document defining the ColorStateList.
* @return A new color state list.
*
* @deprecated Use #createFromXml(Resources, XmlPullParser parser, Theme)
*/
@NonNull
@Deprecated
public static ColorStateList createFromXml(Resources r, XmlPullParser parser)
throws XmlPullParserException, IOException {
return createFromXml(r, parser, null);
}
/**
* Creates a ColorStateList from an XML document using given a set of
* {@link Resources} and a {@link Theme}.
*
* @param r Resources against which the ColorStateList should be inflated.
* @param parser Parser for the XML document defining the ColorStateList.
* @param theme Optional theme to apply to the color state list, may be
* {@code null}.
* @return A new color state list.
*/
@NonNull
public static ColorStateList createFromXml(@NonNull Resources r, @NonNull XmlPullParser parser,
@Nullable Theme theme) throws XmlPullParserException, IOException {
final AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
while ((type=parser.next()) != XmlPullParser.START_TAG
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
// Seek parser to start tag.
}
if (type != XmlPullParser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
return createFromXmlInner(r, parser, attrs);
return createFromXmlInner(r, parser, attrs, theme);
}
/**
@@ -136,28 +198,31 @@ public class ColorStateList implements Parcelable {
* tag in an XML document, tries to create a ColorStateList from that tag.
*
* @throws XmlPullParserException if the current tag is not &lt;selector>
* @return A color state list for the current tag.
* @return A new color state list for the current tag.
*/
private static ColorStateList createFromXmlInner(Resources r, XmlPullParser parser,
AttributeSet attrs) throws XmlPullParserException, IOException {
final ColorStateList colorStateList;
@NonNull
private static ColorStateList createFromXmlInner(@NonNull Resources r,
@NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final String name = parser.getName();
if (name.equals("selector")) {
colorStateList = new ColorStateList();
} else {
if (!name.equals("selector")) {
throw new XmlPullParserException(
parser.getPositionDescription() + ": invalid drawable tag " + name);
parser.getPositionDescription() + ": invalid color state list tag " + name);
}
colorStateList.inflate(r, parser, attrs);
final ColorStateList colorStateList = new ColorStateList();
colorStateList.inflate(r, parser, attrs, theme);
return colorStateList;
}
/**
* Creates a new ColorStateList that has the same states and
* colors as this one but where each color has the specified alpha value
* (0-255).
* Creates a new ColorStateList that has the same states and colors as this
* one but where each color has the specified alpha value (0-255).
*
* @param alpha The new alpha channel value (0-255).
* @return A new color state list.
*/
@NonNull
public ColorStateList withAlpha(int alpha) {
final int[] colors = new int[mColors.length];
final int len = colors.length;
@@ -171,88 +236,154 @@ public class ColorStateList implements Parcelable {
/**
* Fill in this object based on the contents of an XML "selector" element.
*/
private void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
private void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
int type;
final int innerDepth = parser.getDepth()+1;
int depth;
int type;
int changingConfigurations = 0;
int defaultColor = DEFAULT_COLOR;
boolean hasUnresolvedAttrs = false;
int[][] stateSpecList = ArrayUtils.newUnpaddedArray(int[].class, 20);
int[][] themeAttrsList = new int[stateSpecList.length][];
int[] colorList = new int[stateSpecList.length];
int listSize = 0;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& ((depth=parser.getDepth()) >= innerDepth
|| type != XmlPullParser.END_TAG)) {
if (type != XmlPullParser.START_TAG) {
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
if (type != XmlPullParser.START_TAG || depth > innerDepth
|| !parser.getName().equals("item")) {
continue;
}
if (depth > innerDepth || !parser.getName().equals("item")) {
continue;
}
final TypedArray a = Resources.obtainAttributes(r, theme, attrs,
R.styleable.ColorStateListItem);
final int[] themeAttrs = a.extractThemeAttrs();
final int baseColor = a.getColor(R.styleable.ColorStateListItem_color, 0);
final float alphaMod = a.getFloat(R.styleable.ColorStateListItem_alpha, 1.0f);
int alphaRes = 0;
float alpha = 1.0f;
int colorRes = 0;
int color = 0xffff0000;
boolean haveColor = false;
changingConfigurations |= a.getChangingConfigurations();
int i;
a.recycle();
// Parse all unrecognized attributes as state specifiers.
int j = 0;
final int numAttrs = attrs.getAttributeCount();
int[] stateSpec = new int[numAttrs];
for (i = 0; i < numAttrs; i++) {
for (int i = 0; i < numAttrs; i++) {
final int stateResId = attrs.getAttributeNameResource(i);
if (stateResId == 0) break;
if (stateResId == com.android.internal.R.attr.alpha) {
alphaRes = attrs.getAttributeResourceValue(i, 0);
if (alphaRes == 0) {
alpha = attrs.getAttributeFloatValue(i, 1.0f);
}
} else if (stateResId == com.android.internal.R.attr.color) {
colorRes = attrs.getAttributeResourceValue(i, 0);
if (colorRes == 0) {
color = attrs.getAttributeIntValue(i, color);
haveColor = true;
}
} else {
stateSpec[j++] = attrs.getAttributeBooleanValue(i, false)
? stateResId : -stateResId;
switch (stateResId) {
case R.attr.color:
case R.attr.alpha:
// Recognized attribute, ignore.
break;
default:
stateSpec[j++] = attrs.getAttributeBooleanValue(i, false)
? stateResId : -stateResId;
}
}
stateSpec = StateSet.trimStateSet(stateSpec, j);
if (colorRes != 0) {
color = r.getColor(colorRes);
} else if (!haveColor) {
throw new XmlPullParserException(
parser.getPositionDescription()
+ ": <item> tag requires a 'android:color' attribute.");
}
if (alphaRes != 0) {
alpha = r.getFloat(alphaRes);
}
// Apply alpha modulation.
final int alphaMod = MathUtils.constrain((int) (Color.alpha(color) * alpha), 0, 255);
color = (color & 0xFFFFFF) | (alphaMod << 24);
final int color = modulateColorAlpha(baseColor, alphaMod);
if (listSize == 0 || stateSpec.length == 0) {
mDefaultColor = color;
defaultColor = color;
}
if (themeAttrs != null) {
hasUnresolvedAttrs = true;
}
colorList = GrowingArrayUtils.append(colorList, listSize, color);
themeAttrsList = GrowingArrayUtils.append(themeAttrsList, listSize, themeAttrs);
stateSpecList = GrowingArrayUtils.append(stateSpecList, listSize, stateSpec);
listSize++;
}
mChangingConfigurations = changingConfigurations;
mDefaultColor = defaultColor;
if (hasUnresolvedAttrs) {
mThemeAttrs = new int[listSize][];
System.arraycopy(themeAttrsList, 0, mThemeAttrs, 0, listSize);
} else {
mThemeAttrs = null;
}
mColors = new int[listSize];
mStateSpecs = new int[listSize][];
System.arraycopy(colorList, 0, mColors, 0, listSize);
System.arraycopy(stateSpecList, 0, mStateSpecs, 0, listSize);
onColorsChanged();
}
/**
* Returns whether a theme can be applied to this color state list, which
* usually indicates that the color state list has unresolved theme
* attributes.
*
* @return whether a theme can be applied to this color state list
*/
public boolean canApplyTheme() {
return mThemeAttrs != null;
}
/**
* Applies a theme to this color state list.
*
* @param t the theme to apply
*/
public void applyTheme(Theme t) {
if (mThemeAttrs == null) {
return;
}
boolean hasUnresolvedAttrs = false;
final int[][] themeAttrsList = mThemeAttrs;
final int N = themeAttrsList.length;
for (int i = 0; i < N; i++) {
if (themeAttrsList[i] != null) {
final TypedArray a = t.resolveAttributes(themeAttrsList[i],
R.styleable.ColorStateListItem);
final int baseColor = a.getColor(
R.styleable.ColorStateListItem_color, mColors[i]);
final float alphaMod = a.getFloat(
R.styleable.ColorStateListItem_alpha, 1.0f);
mColors[i] = modulateColorAlpha(baseColor, alphaMod);
mChangingConfigurations |= a.getChangingConfigurations();
themeAttrsList[i] = a.extractThemeAttrs(themeAttrsList[i]);
if (themeAttrsList[i] != null) {
hasUnresolvedAttrs = true;
}
a.recycle();
}
}
if (!hasUnresolvedAttrs) {
mThemeAttrs = null;
}
onColorsChanged();
}
private int modulateColorAlpha(int baseColor, float alphaMod) {
if (alphaMod == 1.0f) {
return baseColor;
}
final int baseAlpha = Color.alpha(baseColor);
final int alpha = MathUtils.constrain((int) (baseAlpha * alphaMod + 0.5f), 0, 255);
final int color = (baseColor & 0xFFFFFF) | (alpha << 24);
return color;
}
/**
@@ -275,28 +406,24 @@ public class ColorStateList implements Parcelable {
* @return True if this color state list is opaque.
*/
public boolean isOpaque() {
final int n = mColors.length;
for (int i = 0; i < n; i++) {
if (Color.alpha(mColors[i]) != 0xFF) {
return false;
}
}
return true;
return mIsOpaque;
}
/**
* Return the color associated with the given set of {@link android.view.View} states.
* Return the color associated with the given set of
* {@link android.view.View} states.
*
* @param stateSet an array of {@link android.view.View} states
* @param defaultColor the color to return if there's not state spec in this
* {@link ColorStateList} that matches the stateSet.
* @param defaultColor the color to return if there's no matching state
* spec in this {@link ColorStateList} that matches the
* stateSet.
*
* @return the color associated with that set of states in this {@link ColorStateList}.
*/
public int getColorForState(int[] stateSet, int defaultColor) {
public int getColorForState(@Nullable int[] stateSet, int defaultColor) {
final int setLength = mStateSpecs.length;
for (int i = 0; i < setLength; i++) {
int[] stateSpec = mStateSpecs[i];
final int[] stateSpec = mStateSpecs[i];
if (StateSet.stateSetMatches(stateSpec, stateSet)) {
return mColors[i];
}
@@ -314,7 +441,9 @@ public class ColorStateList implements Parcelable {
}
/**
* Return the states in this {@link ColorStateList}.
* Return the states in this {@link ColorStateList}. The returned array
* should not be modified.
*
* @return the states in this {@link ColorStateList}
* @hide
*/
@@ -323,7 +452,9 @@ public class ColorStateList implements Parcelable {
}
/**
* Return the colors in this {@link ColorStateList}.
* Return the colors in this {@link ColorStateList}. The returned array
* should not be modified.
*
* @return the colors in this {@link ColorStateList}
* @hide
*/
@@ -374,11 +505,81 @@ public class ColorStateList implements Parcelable {
@Override
public String toString() {
return "ColorStateList{" +
"mThemeAttrs=" + Arrays.deepToString(mThemeAttrs) +
"mChangingConfigurations=" + mChangingConfigurations +
"mStateSpecs=" + Arrays.deepToString(mStateSpecs) +
"mColors=" + Arrays.toString(mColors) +
"mDefaultColor=" + mDefaultColor + '}';
}
/**
* Updates the default color and opacity.
*/
private void onColorsChanged() {
int defaultColor = DEFAULT_COLOR;
boolean isOpaque = true;
final int[][] states = mStateSpecs;
final int[] colors = mColors;
final int N = states.length;
if (N > 0) {
defaultColor = colors[0];
for (int i = N - 1; i > 0; i--) {
if (states[i].length == 0) {
defaultColor = colors[i];
break;
}
}
for (int i = 0; i < N; i++) {
if (Color.alpha(colors[i]) != 0xFF) {
isOpaque = false;
break;
}
}
}
mDefaultColor = defaultColor;
mIsOpaque = isOpaque;
}
/**
* @return A factory that can create new instances of this ColorStateList.
*/
ColorStateListFactory getFactory() {
return new ColorStateListFactory(this);
}
static class ColorStateListFactory extends ConstantState<ColorStateList> {
final ColorStateList mSrc;
public ColorStateListFactory(ColorStateList src) {
mSrc = src;
}
@Override
public int getChangingConfigurations() {
return mSrc.mChangingConfigurations;
}
@Override
public ColorStateList newInstance() {
return mSrc;
}
@Override
public ColorStateList newInstance(Resources res, Theme theme) {
if (theme == null || !mSrc.canApplyTheme()) {
return mSrc;
}
final ColorStateList clone = new ColorStateList(mSrc);
clone.applyTheme(theme);
return clone;
}
}
@Override
public int describeContents() {
return 0;
@@ -386,6 +587,9 @@ public class ColorStateList implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
if (canApplyTheme()) {
Log.w(TAG, "Wrote partially-resolved ColorStateList to parcel!");
}
final int N = mStateSpecs.length;
dest.writeInt(N);
for (int i = 0; i < N; i++) {

View File

@@ -16,21 +16,20 @@
package android.content.res;
import android.animation.Animator;
import android.animation.StateListAnimator;
import android.annotation.NonNull;
import android.util.Pools.SynchronizedPool;
import android.view.ViewDebug;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.animation.Animator;
import android.animation.StateListAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
import android.content.res.ColorStateList.ColorStateListFactory;
import android.graphics.Movie;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable.ConstantState;
import android.os.Build;
import android.os.Bundle;
@@ -40,9 +39,11 @@ import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pools.SynchronizedPool;
import android.util.Slog;
import android.util.TypedValue;
import android.util.LongSparseArray;
import android.view.ViewDebug;
import java.io.IOException;
import java.io.InputStream;
@@ -97,8 +98,8 @@ public class Resources {
private static final LongSparseArray<ConstantState>[] sPreloadedDrawables;
private static final LongSparseArray<ConstantState> sPreloadedColorDrawables
= new LongSparseArray<ConstantState>();
private static final LongSparseArray<ColorStateList> sPreloadedColorStateLists
= new LongSparseArray<ColorStateList>();
private static final LongSparseArray<ColorStateListFactory> sPreloadedColorStateLists
= new LongSparseArray<ColorStateListFactory>();
// Pool of TypedArrays targeted to this Resources object.
final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<TypedArray>(5);
@@ -116,8 +117,8 @@ public class Resources {
new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache =
new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
private final LongSparseArray<WeakReference<ColorStateList>> mColorStateListCache =
new LongSparseArray<WeakReference<ColorStateList>>();
private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache =
new ConfigurationBoundResourceCache<ColorStateList>(this);
private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
new ConfigurationBoundResourceCache<Animator>(this);
private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
@@ -897,20 +898,41 @@ public class Resources {
}
/**
* Return a color integer associated with a particular resource ID.
* If the resource holds a complex
* {@link android.content.res.ColorStateList}, then the default color from
* the set is returned.
* Returns a color integer associated with a particular resource ID. If the
* resource holds a complex {@link ColorStateList}, then the default color
* from the set is returned.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
* @throws NotFoundException Throws NotFoundException if the given ID does
* not exist.
*
* @return Returns a single color value in the form 0xAARRGGBB.
* @return A single color value in the form 0xAARRGGBB.
* @deprecated Use {@link #getColor(int, Theme)} instead.
*/
public int getColor(int id) throws NotFoundException {
return getColor(id, null);
}
/**
* Returns a themed color integer associated with a particular resource ID.
* If the resource holds a complex {@link ColorStateList}, then the default
* color from the set is returned.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @param theme The theme used to style the color attributes, may be
* {@code null}.
*
* @throws NotFoundException Throws NotFoundException if the given ID does
* not exist.
*
* @return A single color value in the form 0xAARRGGBB.
*/
public int getColor(int id, @Nullable Theme theme) throws NotFoundException {
TypedValue value;
synchronized (mAccessLock) {
value = mTmpValue;
@@ -919,40 +941,77 @@ public class Resources {
}
getValue(id, value, true);
if (value.type >= TypedValue.TYPE_FIRST_INT
&& value.type <= TypedValue.TYPE_LAST_INT) {
&& value.type <= TypedValue.TYPE_LAST_INT) {
mTmpValue = value;
return value.data;
} else if (value.type != TypedValue.TYPE_STRING) {
throw new NotFoundException(
"Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ Integer.toHexString(value.type) + " is not valid");
"Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ Integer.toHexString(value.type) + " is not valid");
}
mTmpValue = null;
}
ColorStateList csl = loadColorStateList(value, id);
final ColorStateList csl = loadColorStateList(value, id, theme);
synchronized (mAccessLock) {
if (mTmpValue == null) {
mTmpValue = value;
}
}
return csl.getDefaultColor();
}
/**
* Return a color state list associated with a particular resource ID. The
* resource may contain either a single raw color value, or a complex
* {@link android.content.res.ColorStateList} holding multiple possible colors.
* Returns a color state list associated with a particular resource ID. The
* resource may contain either a single raw color value or a complex
* {@link ColorStateList} holding multiple possible colors.
*
* @param id The desired resource identifier of a {@link ColorStateList},
* as generated by the aapt tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* as generated by the aapt tool. This integer encodes the
* package, type, and resource entry. The value 0 is an invalid
* identifier.
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
* @throws NotFoundException Throws NotFoundException if the given ID does
* not exist.
*
* @return Returns a ColorStateList object containing either a single
* solid color or multiple colors that can be selected based on a state.
* @return A ColorStateList object containing either a single solid color
* or multiple colors that can be selected based on a state.
* @deprecated Use {@link #getColorStateList(int, Theme)} instead.
*/
@Nullable
public ColorStateList getColorStateList(int id) throws NotFoundException {
final ColorStateList csl = getColorStateList(id, null);
if (csl != null && csl.canApplyTheme()) {
Log.w(TAG, "ColorStateList " + getResourceName(id) + " has "
+ "unresolved theme attributes! Consider using "
+ "Resources.getColorStateList(int, Theme) or "
+ "Context.getColorStateList(int).", new RuntimeException());
}
return csl;
}
/**
* Returns a themed color state list associated with a particular resource
* ID. The resource may contain either a single raw color value or a
* complex {@link ColorStateList} holding multiple possible colors.
*
* @param id The desired resource identifier of a {@link ColorStateList},
* as generated by the aapt tool. This integer encodes the
* package, type, and resource entry. The value 0 is an invalid
* identifier.
* @param theme The theme used to style the color attributes, may be
* {@code null}.
*
* @throws NotFoundException Throws NotFoundException if the given ID does
* not exist.
*
* @return A themed ColorStateList object containing either a single solid
* color or multiple colors that can be selected based on a state.
*/
@Nullable
public ColorStateList getColorStateList(int id, @Nullable Theme theme)
throws NotFoundException {
TypedValue value;
synchronized (mAccessLock) {
value = mTmpValue;
@@ -963,15 +1022,19 @@ public class Resources {
}
getValue(id, value, true);
}
ColorStateList res = loadColorStateList(value, id);
final ColorStateList res = loadColorStateList(value, id, theme);
synchronized (mAccessLock) {
if (mTmpValue == null) {
mTmpValue = value;
}
}
return res;
}
/**
* Return a boolean associated with a particular resource ID. This can be
* used with any integral resource value, and will return true if it is
@@ -1843,11 +1906,10 @@ public class Resources {
clearDrawableCachesLocked(mDrawableCache, configChanges);
clearDrawableCachesLocked(mColorDrawableCache, configChanges);
mColorStateListCache.onConfigurationChange(configChanges);
mAnimatorCache.onConfigurationChange(configChanges);
mStateListAnimatorCache.onConfigurationChange(configChanges);
mColorStateListCache.clear();
flushLayoutCache();
}
synchronized (sSync) {
@@ -2439,7 +2501,7 @@ public class Resources {
private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {
if (value.string == null) {
throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+ Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
+ Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
}
final String file = value.string.toString();
@@ -2530,7 +2592,8 @@ public class Resources {
return null;
}
/*package*/ ColorStateList loadColorStateList(TypedValue value, int id)
@Nullable
ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)
throws NotFoundException {
if (TRACE_FOR_PRELOAD) {
// Log only framework resources
@@ -2544,99 +2607,105 @@ public class Resources {
ColorStateList csl;
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
csl = sPreloadedColorStateLists.get(key);
if (csl != null) {
return csl;
// Handle inline color definitions.
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
&& value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
final ColorStateListFactory factory = sPreloadedColorStateLists.get(key);
if (factory != null) {
return factory.newInstance();
}
csl = ColorStateList.valueOf(value.data);
if (mPreloading) {
if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
"color")) {
sPreloadedColorStateLists.put(key, csl);
sPreloadedColorStateLists.put(key, csl.getFactory());
}
}
return csl;
}
csl = getCachedColorStateList(key);
final ConfigurationBoundResourceCache<ColorStateList> cache = mColorStateListCache;
csl = cache.get(key, theme);
if (csl != null) {
return csl;
}
csl = sPreloadedColorStateLists.get(key);
if (csl != null) {
return csl;
final ColorStateListFactory factory = sPreloadedColorStateLists.get(key);
if (factory != null) {
csl = factory.newInstance(this, theme);
}
if (value.string == null) {
throw new NotFoundException(
"Resource is not a ColorStateList (color or path): " + value);
}
final String file = value.string.toString();
if (file.endsWith(".xml")) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
try {
final XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "colorstatelist");
csl = ColorStateList.createFromXml(this, rp);
rp.close();
} catch (Exception e) {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
NotFoundException rnf = new NotFoundException(
"File " + file + " from color state list resource ID #0x"
+ Integer.toHexString(id));
rnf.initCause(e);
throw rnf;
}
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
} else {
throw new NotFoundException(
"File " + file + " from drawable resource ID #0x"
+ Integer.toHexString(id) + ": .xml extension required");
if (csl == null) {
csl = loadColorStateListForCookie(value, id, theme);
}
if (csl != null) {
if (mPreloading) {
if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
"color")) {
sPreloadedColorStateLists.put(key, csl);
sPreloadedColorStateLists.put(key, csl.getFactory());
}
} else {
synchronized (mAccessLock) {
//Log.i(TAG, "Saving cached color state list @ #" +
// Integer.toHexString(key.intValue())
// + " in " + this + ": " + csl);
mColorStateListCache.put(key, new WeakReference<ColorStateList>(csl));
}
cache.put(key, theme, csl.getFactory());
}
}
return csl;
}
private ColorStateList getCachedColorStateList(long key) {
synchronized (mAccessLock) {
WeakReference<ColorStateList> wr = mColorStateListCache.get(key);
if (wr != null) { // we have the key
ColorStateList entry = wr.get();
if (entry != null) {
//Log.i(TAG, "Returning cached color state list @ #" +
// Integer.toHexString(((Integer)key).intValue())
// + " in " + this + ": " + entry);
return entry;
} else { // our entry has been purged
mColorStateListCache.delete(key);
private ColorStateList loadColorStateListForCookie(TypedValue value, int id, Theme theme) {
if (value.string == null) {
throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+ Integer.toHexString(id) + ") is not a ColorStateList: " + value);
}
final String file = value.string.toString();
if (TRACE_FOR_MISS_PRELOAD) {
// Log only framework resources
if ((id >>> 24) == 0x1) {
final String name = getResourceName(id);
if (name != null) {
Log.d(TAG, "Loading framework color state list #" + Integer.toHexString(id)
+ ": " + name + " at " + file);
}
}
}
return null;
if (DEBUG_LOAD) {
Log.v(TAG, "Loading color state list for cookie " + value.assetCookie + ": " + file);
}
final ColorStateList csl;
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
if (file.endsWith(".xml")) {
try {
final XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "colorstatelist");
csl = ColorStateList.createFromXml(this, rp, theme);
rp.close();
} catch (Exception e) {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
final NotFoundException rnf = new NotFoundException(
"File " + file + " from color state list resource ID #0x"
+ Integer.toHexString(id));
rnf.initCause(e);
throw rnf;
}
} else {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
throw new NotFoundException(
"File " + file + " from drawable resource ID #0x"
+ Integer.toHexString(id) + ": .xml extension required");
}
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
return csl;
}
/*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
@@ -2715,6 +2784,20 @@ public class Resources {
}
}
/**
* Obtains styled attributes from the theme, if available, or unstyled
* resources if the theme is null.
*
* @hide
*/
public static TypedArray obtainAttributes(
Resources res, Theme theme, AttributeSet set, int[] attrs) {
if (theme == null) {
return res.obtainAttributes(set, attrs);
}
return theme.obtainStyledAttributes(set, attrs, 0, 0);
}
private Resources() {
mAssets = AssetManager.getSystem();
// NOTE: Intentionally leaving this uninitialized (all values set

View File

@@ -392,8 +392,8 @@ public class TypedArray {
} else if (type == TypedValue.TYPE_STRING) {
final TypedValue value = mValue;
if (getValueAt(index, value)) {
ColorStateList csl = mResources.loadColorStateList(
value, value.resourceId);
final ColorStateList csl = mResources.loadColorStateList(
value, value.resourceId, mTheme);
return csl.getDefaultColor();
}
return defValue;
@@ -424,7 +424,7 @@ public class TypedArray {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new RuntimeException("Failed to resolve attribute at index " + index);
}
return mResources.loadColorStateList(value, value.resourceId);
return mResources.loadColorStateList(value, value.resourceId, mTheme);
}
return null;
}
@@ -905,12 +905,21 @@ public class TypedArray {
* Removes the entries from the typed array so that subsequent calls to typed
* getters will return the default value without crashing.
*
* @return an array of length {@link #getIndexCount()} populated with theme
* attributes, or null if there are no theme attributes in the typed
* array
* @return An array of length {@link #getIndexCount()} populated with theme
* attributes, or {@code null} if there are no theme attributes in
* the typed array.
* @hide
*/
@Nullable
public int[] extractThemeAttrs() {
return extractThemeAttrs(null);
}
/**
* @hide
*/
@Nullable
public int[] extractThemeAttrs(@Nullable int[] scrap) {
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
@@ -922,6 +931,7 @@ public class TypedArray {
for (int i = 0; i < N; i++) {
final int index = i * AssetManager.STYLE_NUM_ENTRIES;
if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
// Not an attribute, ignore.
continue;
}
@@ -930,13 +940,20 @@ public class TypedArray {
final int attr = data[index + AssetManager.STYLE_DATA];
if (attr == 0) {
// This attribute is useless!
// Useless data, ignore.
continue;
}
// Ensure we have a usable attribute array.
if (attrs == null) {
attrs = new int[N];
if (scrap != null && scrap.length == N) {
attrs = scrap;
Arrays.fill(attrs, 0);
} else {
attrs = new int[N];
}
}
attrs[i] = attr;
}

View File

@@ -0,0 +1,22 @@
<?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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
android:color="@color/button_material_dark" />
<item android:color="?attr/colorAccent" />
</selector>

View File

@@ -4882,6 +4882,14 @@
<attr name="animation"/>
</declare-styleable>
<!-- Attributes that can be assigned to a ColorStateList item. -->
<declare-styleable name="ColorStateListItem">
<!-- Base color for this state. -->
<attr name="color" />
<!-- Alpha multiplier applied to the base color. -->
<attr name="alpha" />
</declare-styleable>
<!-- Drawable used to render a geometric shape, with a gradient or a solid color. -->
<declare-styleable name="GradientDrawable">
<!-- Indicates whether the drawable should intially be visible. -->

View File

@@ -2598,4 +2598,11 @@
<public type="style" name="Theme.DeviceDefault.Dialog.Alert" />
<public type="style" name="Theme.DeviceDefault.Light.Dialog.Alert" />
<!-- ===============================================================
Resources added in version MNC of the platform
=============================================================== -->
<eat-comment />
<public type="style" name="Widget.Material.Button.Colored" />
</resources>

View File

@@ -462,6 +462,11 @@ please see styles_device_defaults.xml.
<item name="gravity">center_vertical|center_horizontal</item>
</style>
<!-- Colored bordered ink button -->
<style name="Widget.Material.Button.Colored">
<item name="backgroundTint">@color/btn_material_accent</item>
</style>
<!-- Small bordered ink button -->
<style name="Widget.Material.Button.Small">
<item name="minHeight">48dip</item>

View File

@@ -705,8 +705,8 @@ public class BitmapDrawable extends Drawable {
@Override
public boolean isStateful() {
final BitmapState s = mBitmapState;
return super.isStateful() || (s.mTint != null && s.mTint.isStateful());
return (mBitmapState.mTint != null && mBitmapState.mTint.isStateful())
|| super.isStateful();
}
@Override
@@ -718,6 +718,9 @@ public class BitmapDrawable extends Drawable {
updateStateFromTypedArray(a);
verifyState(a);
a.recycle();
// Update local properties.
updateLocalState(r);
}
/**
@@ -800,9 +803,6 @@ public class BitmapDrawable extends Drawable {
if (tileModeY != TILE_MODE_UNDEFINED) {
setTileModeY(parseTileMode(tileModeY));
}
// Update local properties.
initializeWithState(state, r);
}
@Override
@@ -810,18 +810,28 @@ public class BitmapDrawable extends Drawable {
super.applyTheme(t);
final BitmapState state = mBitmapState;
if (state == null || state.mThemeAttrs == null) {
if (state == null) {
return;
}
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.BitmapDrawable);
try {
updateStateFromTypedArray(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.BitmapDrawable);
try {
updateStateFromTypedArray(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
}
}
// Apply theme to contained color state list.
if (state.mTint != null && state.mTint.canApplyTheme()) {
state.mTint.applyTheme(t);
}
// Update local properties.
updateLocalState(t.getResources());
}
private static Shader.TileMode parseTileMode(int tileMode) {
@@ -839,7 +849,7 @@ public class BitmapDrawable extends Drawable {
@Override
public boolean canApplyTheme() {
return mBitmapState != null && mBitmapState.mThemeAttrs != null;
return mBitmapState != null && mBitmapState.canApplyTheme();
}
@Override
@@ -910,7 +920,7 @@ public class BitmapDrawable extends Drawable {
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null;
return mThemeAttrs != null || mTint != null && mTint.canApplyTheme();
}
@Override
@@ -944,7 +954,7 @@ public class BitmapDrawable extends Drawable {
private BitmapDrawable(BitmapState state, Resources res) {
mBitmapState = state;
initializeWithState(mBitmapState, res);
updateLocalState(res);
}
/**
@@ -952,14 +962,14 @@ public class BitmapDrawable extends Drawable {
* after significant state changes, e.g. from the One True Constructor and
* after inflating or applying a theme.
*/
private void initializeWithState(BitmapState state, Resources res) {
private void updateLocalState(Resources res) {
if (res != null) {
mTargetDensity = res.getDisplayMetrics().densityDpi;
} else {
mTargetDensity = state.mTargetDensity;
mTargetDensity = mBitmapState.mTargetDensity;
}
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
mTintFilter = updateTintFilter(mTintFilter, mBitmapState.mTint, mBitmapState.mTintMode);
computeBitmapSize();
}
}

View File

@@ -70,7 +70,7 @@ public class ColorDrawable extends Drawable {
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations() | mColorState.mChangingConfigurations;
return super.getChangingConfigurations() | mColorState.getChangingConfigurations();
}
/**
@@ -233,6 +233,8 @@ public class ColorDrawable extends Drawable {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorDrawable);
updateStateFromTypedArray(a);
a.recycle();
updateLocalState(r);
}
/**
@@ -261,13 +263,21 @@ public class ColorDrawable extends Drawable {
super.applyTheme(t);
final ColorState state = mColorState;
if (state == null || state.mThemeAttrs == null) {
if (state == null) {
return;
}
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ColorDrawable);
updateStateFromTypedArray(a);
a.recycle();
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ColorDrawable);
updateStateFromTypedArray(a);
a.recycle();
}
if (state.mTint != null && state.mTint.canApplyTheme()) {
state.mTint.applyTheme(t);
}
updateLocalState(t.getResources());
}
@Override
@@ -299,17 +309,18 @@ public class ColorDrawable extends Drawable {
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null;
return mThemeAttrs != null
|| (mTint != null && mTint.canApplyTheme());
}
@Override
public Drawable newDrawable() {
return new ColorDrawable(this);
return new ColorDrawable(this, null);
}
@Override
public Drawable newDrawable(Resources res) {
return new ColorDrawable(this);
return new ColorDrawable(this, res);
}
@Override
@@ -318,8 +329,18 @@ public class ColorDrawable extends Drawable {
}
}
private ColorDrawable(ColorState state) {
private ColorDrawable(ColorState state, Resources res) {
mColorState = state;
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
updateLocalState(res);
}
/**
* Initializes local dynamic properties from state. This should be called
* after significant state changes, e.g. from the One True Constructor and
* after inflating or applying a theme.
*/
private void updateLocalState(Resources r) {
mTintFilter = updateTintFilter(mTintFilter, mColorState.mTint, mColorState.mTintMode);
}
}

View File

@@ -143,7 +143,7 @@ public class GradientDrawable extends Drawable {
private final RectF mRect = new RectF();
private Paint mLayerPaint; // internal, used if we use saveLayer()
private boolean mGradientIsDirty; // internal state
private boolean mGradientIsDirty;
private boolean mMutated;
private Path mRingPath;
private boolean mPathIsDirty = true;
@@ -174,7 +174,7 @@ public class GradientDrawable extends Drawable {
}
public GradientDrawable() {
this(new GradientState(Orientation.TOP_BOTTOM, null));
this(new GradientState(Orientation.TOP_BOTTOM, null), null);
}
/**
@@ -182,7 +182,7 @@ public class GradientDrawable extends Drawable {
* of colors for the gradient.
*/
public GradientDrawable(Orientation orientation, int[] colors) {
this(new GradientState(orientation, colors));
this(new GradientState(orientation, colors), null);
}
@Override
@@ -1018,7 +1018,7 @@ public class GradientDrawable extends Drawable {
inflateChildElements(r, parser, attrs, theme);
mGradientState.computeOpacity();
updateLocalState(r);
}
@Override
@@ -1037,9 +1037,21 @@ public class GradientDrawable extends Drawable {
a.recycle();
}
if (state.mTint != null && state.mTint.canApplyTheme()) {
state.mTint.applyTheme(t);
}
if (state.mColorStateList != null && state.mColorStateList.canApplyTheme()) {
state.mColorStateList.applyTheme(t);
}
if (state.mStrokeColorStateList != null && state.mStrokeColorStateList.canApplyTheme()) {
state.mStrokeColorStateList.applyTheme(t);
}
applyThemeChildElements(t);
state.computeOpacity();
updateLocalState(t.getResources());
}
/**
@@ -1087,8 +1099,6 @@ public class GradientDrawable extends Drawable {
if (tint != null) {
state.mTint = tint;
}
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
}
@Override
@@ -1516,7 +1526,7 @@ public class GradientDrawable extends Drawable {
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mGradientState = new GradientState(mGradientState);
initializeWithState(mGradientState);
updateLocalState(null);
mMutated = true;
}
return this;
@@ -1577,7 +1587,7 @@ public class GradientDrawable extends Drawable {
int[] mAttrCorners;
int[] mAttrPadding;
GradientState(Orientation orientation, int[] colors) {
public GradientState(Orientation orientation, int[] colors) {
mOrientation = orientation;
setColors(colors);
}
@@ -1634,19 +1644,24 @@ public class GradientDrawable extends Drawable {
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null || mAttrSize != null || mAttrGradient != null
|| mAttrSolid != null || mAttrStroke != null || mAttrCorners != null
|| mAttrPadding != null || super.canApplyTheme();
return mThemeAttrs != null
|| mAttrSize != null || mAttrGradient != null
|| mAttrSolid != null || mAttrStroke != null
|| mAttrCorners != null || mAttrPadding != null
|| (mTint != null && mTint.canApplyTheme())
|| (mStrokeColorStateList != null && mStrokeColorStateList.canApplyTheme())
|| (mColorStateList != null && mColorStateList.canApplyTheme())
|| super.canApplyTheme();
}
@Override
public Drawable newDrawable() {
return new GradientDrawable(this);
return new GradientDrawable(this, null);
}
@Override
public Drawable newDrawable(Resources res) {
return new GradientDrawable(this);
return new GradientDrawable(this, res);
}
@Override
@@ -1751,16 +1766,15 @@ public class GradientDrawable extends Drawable {
*
* @param state Constant state from which the drawable inherits
*/
private GradientDrawable(GradientState state) {
private GradientDrawable(GradientState state, Resources res) {
mGradientState = state;
initializeWithState(mGradientState);
mGradientIsDirty = true;
mMutated = false;
updateLocalState(res);
}
private void initializeWithState(GradientState state) {
private void updateLocalState(Resources res) {
final GradientState state = mGradientState;
if (state.mColorStateList != null) {
final int[] currentState = getState();
final int stateColor = state.mColorStateList.getColorForState(currentState, 0);
@@ -1797,5 +1811,8 @@ public class GradientDrawable extends Drawable {
}
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
mGradientIsDirty = true;
state.computeOpacity();
}
}

View File

@@ -401,6 +401,8 @@ public class NinePatchDrawable extends Drawable {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.NinePatchDrawable);
updateStateFromTypedArray(a);
a.recycle();
updateLocalState(r);
}
/**
@@ -467,12 +469,6 @@ public class NinePatchDrawable extends Drawable {
if (tint != null) {
state.mTint = tint;
}
// Update local properties.
initializeWithState(state, r);
// Push density applied by setNinePatchState into state.
state.mTargetDensity = mTargetDensity;
}
@Override
@@ -480,23 +476,32 @@ public class NinePatchDrawable extends Drawable {
super.applyTheme(t);
final NinePatchState state = mNinePatchState;
if (state == null || state.mThemeAttrs == null) {
if (state == null) {
return;
}
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.NinePatchDrawable);
try {
updateStateFromTypedArray(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(
state.mThemeAttrs, R.styleable.NinePatchDrawable);
try {
updateStateFromTypedArray(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
}
}
if (state.mTint != null && state.mTint.canApplyTheme()) {
state.mTint.applyTheme(t);
}
updateLocalState(t.getResources());
}
@Override
public boolean canApplyTheme() {
return mNinePatchState != null && mNinePatchState.mThemeAttrs != null;
return mNinePatchState != null && mNinePatchState.canApplyTheme();
}
public Paint getPaint() {
@@ -645,7 +650,8 @@ public class NinePatchDrawable extends Drawable {
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null;
return mThemeAttrs != null
|| (mTint != null && mTint.canApplyTheme());
}
@Override
@@ -680,19 +686,25 @@ public class NinePatchDrawable extends Drawable {
private NinePatchDrawable(NinePatchState state, Resources res) {
mNinePatchState = state;
initializeWithState(mNinePatchState, res);
updateLocalState(res);
// Push density applied by setNinePatchState into state.
mNinePatchState.mTargetDensity = mTargetDensity;
}
/**
* Initializes local dynamic properties from state.
*/
private void initializeWithState(NinePatchState state, Resources res) {
private void updateLocalState(Resources res) {
final NinePatchState state = mNinePatchState;
if (res != null) {
mTargetDensity = res.getDisplayMetrics().densityDpi;
} else {
mTargetDensity = state.mTargetDensity;
}
// If we can, avoid calling any methods that initialize Paint.
if (state.mDither != DEFAULT_DITHER) {
setDither(state.mDither);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 The Android Open Source Project
* 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.
@@ -198,7 +198,7 @@ public class RippleDrawable extends LayerDrawable {
setColor(color);
ensurePadding();
initializeFromState();
updateLocalState();
}
@Override
@@ -352,6 +352,11 @@ public class RippleDrawable extends LayerDrawable {
return true;
}
/**
* Sets the ripple color.
*
* @param color Ripple color as a color state list.
*/
public void setColor(ColorStateList color) {
mState.mColor = color;
invalidateSelf();
@@ -370,7 +375,8 @@ public class RippleDrawable extends LayerDrawable {
super.inflate(r, parser, attrs, theme);
setTargetDensity(r.getDisplayMetrics());
initializeFromState();
updateLocalState();
}
@Override
@@ -450,21 +456,27 @@ public class RippleDrawable extends LayerDrawable {
super.applyTheme(t);
final RippleState state = mState;
if (state == null || state.mTouchThemeAttrs == null) {
if (state == null) {
return;
}
final TypedArray a = t.resolveAttributes(state.mTouchThemeAttrs,
R.styleable.RippleDrawable);
try {
updateStateFromTypedArray(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
if (state.mTouchThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(state.mTouchThemeAttrs,
R.styleable.RippleDrawable);
try {
updateStateFromTypedArray(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
}
}
initializeFromState();
if (state.mColor != null && state.mColor.canApplyTheme()) {
state.mColor.applyTheme(t);
}
updateLocalState();
}
@Override
@@ -931,7 +943,9 @@ public class RippleDrawable extends LayerDrawable {
@Override
public boolean canApplyTheme() {
return mTouchThemeAttrs != null || super.canApplyTheme();
return mTouchThemeAttrs != null
|| (mColor != null && mColor.canApplyTheme())
|| super.canApplyTheme();
}
@Override
@@ -987,10 +1001,10 @@ public class RippleDrawable extends LayerDrawable {
mDensity = res.getDisplayMetrics().density;
}
initializeFromState();
updateLocalState();
}
private void initializeFromState() {
private void updateLocalState() {
// Initialize from constant state.
mMask = findDrawableByLayerId(R.id.mask);
}

View File

@@ -402,7 +402,7 @@ public class ShapeDrawable extends Drawable {
}
// Update local properties.
initializeWithState(mShapeState, r);
updateLocalState(r);
}
@Override
@@ -410,16 +410,23 @@ public class ShapeDrawable extends Drawable {
super.applyTheme(t);
final ShapeState state = mShapeState;
if (state == null || state.mThemeAttrs == null) {
if (state == null) {
return;
}
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ShapeDrawable);
updateStateFromTypedArray(a);
a.recycle();
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ShapeDrawable);
updateStateFromTypedArray(a);
a.recycle();
}
// Apply theme to contained color state list.
if (state.mTint != null && state.mTint.canApplyTheme()) {
state.mTint.applyTheme(t);
}
// Update local properties.
initializeWithState(state, t.getResources());
updateLocalState(t.getResources());
}
private void updateStateFromTypedArray(TypedArray a) {
@@ -550,7 +557,8 @@ public class ShapeDrawable extends Drawable {
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null;
return mThemeAttrs != null
|| (mTint != null && mTint.canApplyTheme());
}
@Override
@@ -576,7 +584,7 @@ public class ShapeDrawable extends Drawable {
private ShapeDrawable(ShapeState state, Resources res) {
mShapeState = state;
initializeWithState(state, res);
updateLocalState(res);
}
/**
@@ -584,8 +592,8 @@ public class ShapeDrawable extends Drawable {
* after significant state changes, e.g. from the One True Constructor and
* after inflating or applying a theme.
*/
private void initializeWithState(ShapeState state, Resources res) {
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
private void updateLocalState(Resources res) {
mTintFilter = updateTintFilter(mTintFilter, mShapeState.mTint, mShapeState.mTintMode);
}
/**

View File

@@ -369,8 +369,13 @@ public class VectorDrawable extends Drawable {
super.applyTheme(t);
final VectorDrawableState state = mVectorState;
if (state != null && state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.VectorDrawable);
if (state == null) {
return;
}
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(
state.mThemeAttrs, R.styleable.VectorDrawable);
try {
state.mCacheDirty = true;
updateStateFromTypedArray(a);
@@ -379,14 +384,20 @@ public class VectorDrawable extends Drawable {
} finally {
a.recycle();
}
}
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
// Apply theme to contained color state list.
if (state.mTint != null && state.mTint.canApplyTheme()) {
state.mTint.applyTheme(t);
}
final VPathRenderer path = state.mVPathRenderer;
if (path != null && path.canApplyTheme()) {
path.applyTheme(t);
}
// Update local state.
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
}
/**
@@ -750,7 +761,9 @@ public class VectorDrawable extends Drawable {
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null || (mVPathRenderer != null && mVPathRenderer.canApplyTheme())
return mThemeAttrs != null
|| (mVPathRenderer != null && mVPathRenderer.canApplyTheme())
|| (mTint != null && mTint.canApplyTheme())
|| super.canApplyTheme();
}