summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Viverette <alanv@google.com>2015-01-06 18:47:25 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2015-01-06 18:47:29 +0000
commit304ea5aac77cae03b4e8440a7c73c18086d96a20 (patch)
tree6411b0eb6ee19f4334988bee2f13e6c3712ed864
parent2b2ad7ce9f008cc383394cc91389d667575ec45d (diff)
parent45c4bbbbce6bbad50a033efcba7948a23f1f117a (diff)
downloadframeworks_base-304ea5aac77cae03b4e8440a7c73c18086d96a20.zip
frameworks_base-304ea5aac77cae03b4e8440a7c73c18086d96a20.tar.gz
frameworks_base-304ea5aac77cae03b4e8440a7c73c18086d96a20.tar.bz2
Merge "Allow use of theme attributes in color state lists"
-rw-r--r--api/current.txt14
-rw-r--r--core/java/android/content/Context.java42
-rw-r--r--core/java/android/content/res/ColorStateList.java400
-rw-r--r--core/java/android/content/res/Resources.java261
-rw-r--r--core/java/android/content/res/TypedArray.java33
-rw-r--r--core/res/res/color/btn_material_accent.xml22
-rw-r--r--core/res/res/values/attrs.xml8
-rw-r--r--core/res/res/values/public.xml7
-rw-r--r--core/res/res/values/styles_material.xml5
-rw-r--r--graphics/java/android/graphics/drawable/BitmapDrawable.java48
-rw-r--r--graphics/java/android/graphics/drawable/ColorDrawable.java41
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java57
-rw-r--r--graphics/java/android/graphics/drawable/NinePatchDrawable.java48
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java46
-rw-r--r--graphics/java/android/graphics/drawable/ShapeDrawable.java28
-rw-r--r--graphics/java/android/graphics/drawable/VectorDrawable.java21
16 files changed, 784 insertions, 297 deletions
diff --git a/api/current.txt b/api/current.txt
index 5707e54..31d45f2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -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;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b329e1f..dec2524 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -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
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 68a39d3..b42d8bc 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -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);
+
+ changingConfigurations |= a.getChangingConfigurations();
- int alphaRes = 0;
- float alpha = 1.0f;
- int colorRes = 0;
- int color = 0xffff0000;
- boolean haveColor = false;
+ a.recycle();
- int i;
+ // 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++) {
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 2e2c5d9..c5c8d75 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -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) {
@@ -2476,7 +2538,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();
@@ -2567,7 +2629,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
@@ -2581,101 +2644,107 @@ 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);
+ final ColorStateListFactory factory = sPreloadedColorStateLists.get(key);
+ if (factory != null) {
+ csl = factory.newInstance(this, theme);
+ }
+
+ if (csl == null) {
+ csl = loadColorStateListForCookie(value, id, theme);
+ }
+
if (csl != null) {
- return csl;
+ if (mPreloading) {
+ if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
+ "color")) {
+ sPreloadedColorStateLists.put(key, csl.getFactory());
+ }
+ } else {
+ cache.put(key, theme, csl.getFactory());
+ }
}
+ return csl;
+ }
+
+ private ColorStateList loadColorStateListForCookie(TypedValue value, int id, Theme theme) {
if (value.string == null) {
- throw new NotFoundException(
- "Resource is not a ColorStateList (color or path): " + value);
+ 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);
+ }
+ }
+ }
+
+ 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")) {
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
try {
final XmlResourceParser rp = loadXmlResourceParser(
- file, id, value.assetCookie, "colorstatelist");
- csl = ColorStateList.createFromXml(this, rp);
+ file, id, value.assetCookie, "colorstatelist");
+ csl = ColorStateList.createFromXml(this, rp, theme);
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));
+ final 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 {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
throw new NotFoundException(
"File " + file + " from drawable resource ID #0x"
- + Integer.toHexString(id) + ": .xml extension required");
- }
-
- if (csl != null) {
- if (mPreloading) {
- if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
- "color")) {
- sPreloadedColorStateLists.put(key, csl);
- }
- } 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));
- }
- }
+ + Integer.toHexString(id) + ": .xml extension required");
}
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
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);
- }
- }
- }
- return null;
- }
-
/*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
throws NotFoundException {
synchronized (mAccessLock) {
@@ -2752,6 +2821,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
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 02602fb..06c701a 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -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;
}
diff --git a/core/res/res/color/btn_material_accent.xml b/core/res/res/color/btn_material_accent.xml
new file mode 100644
index 0000000..71469b6
--- /dev/null
+++ b/core/res/res/color/btn_material_accent.xml
@@ -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>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e6e76b8..1ef68a4 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4890,6 +4890,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. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2bb9aa8..c7b5615 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -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>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 6220a1b..4f8c31d 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -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>
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 9be296a..f2b635a 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -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();
}
}
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index e3b50ea..85e02b7 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -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);
}
}
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 72707e6..7882705 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -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();
}
}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index b87ae92..a6299be 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -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);
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 9809606..c7b506e 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -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();
+ }
+ }
+
+ if (state.mColor != null && state.mColor.canApplyTheme()) {
+ state.mColor.applyTheme(t);
}
- initializeFromState();
+ 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);
}
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index a3d8c92..c49bc8c 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -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);
}
/**
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 8b0f635..21dba43 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -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();
}