Implement APIs for obtaining, caching themed Drawables
When Drawables are inflated during preload (or otherwise without a theme)
they cache their themeable attributes in their constant state as an array
keyed on attribute index. Drawables inflated with a theme will simply
resolve theme attributes as part of normal inflation, and they will not
cache any themeable attributes.
Drawables obtained from Resources are pulled from theme-specific cache
when possible. If an unthemed Drawable exists in the preload cache, a
new constant state will be obtained for the Drawable and the theme will
be applied by resolving the cached themeable attributes and overwriting
their respective constant state properties. If no cached version exists,
a new Drawable is inflated against the desired theme.
Constant states from themed drawables may be cached if the applied theme
is "pure" and was loaded from a style resource without any subsequent
modifications.
This CL does not handle applying themes to several Drawable types, but it
fully supports BitmapDrawable, GradientDrawable, NinePatchDrawable,
ColorDrawable, and TouchFeedbackDrawable.
BUG: 12611005
Change-Id: I4e794fbb62f7a371715f4ebdf946ee5f9a5ad1c9
diff --git a/api/current.txt b/api/current.txt
index ec1900e..9fd6dad 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8125,6 +8125,7 @@
method public void applyStyle(int, boolean);
method public void dump(int, java.lang.String, java.lang.String);
method public android.graphics.drawable.Drawable getDrawable(int) throws android.content.res.Resources.NotFoundException;
+ method public android.content.res.Resources getResources();
method public android.content.res.TypedArray obtainStyledAttributes(int[]);
method public android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
method public android.content.res.TypedArray obtainStyledAttributes(android.util.AttributeSet, int[], int, int);
@@ -8155,6 +8156,7 @@
method public java.lang.String getString(int);
method public java.lang.CharSequence getText(int);
method public java.lang.CharSequence[] getTextArray(int);
+ method public int getType(int);
method public boolean getValue(int, android.util.TypedValue);
method public boolean hasValue(int);
method public int length();
@@ -10492,15 +10494,22 @@
public abstract class Drawable {
ctor public Drawable();
+ method public void applyTheme(android.content.res.Resources.Theme);
+ method public boolean canApplyTheme();
method public void clearColorFilter();
method public final void copyBounds(android.graphics.Rect);
method public final android.graphics.Rect copyBounds();
method public static android.graphics.drawable.Drawable createFromPath(java.lang.String);
method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String);
method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.graphics.BitmapFactory.Options);
+ method public static android.graphics.drawable.Drawable createFromResourceStreamThemed(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.content.res.Resources.Theme);
+ method public static android.graphics.drawable.Drawable createFromResourceStreamThemed(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.graphics.BitmapFactory.Options, android.content.res.Resources.Theme);
method public static android.graphics.drawable.Drawable createFromStream(java.io.InputStream, java.lang.String);
+ method public static android.graphics.drawable.Drawable createFromStreamThemed(java.io.InputStream, java.lang.String, android.content.res.Resources.Theme);
method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static android.graphics.drawable.Drawable createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static android.graphics.drawable.Drawable createFromXmlInnerThemed(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static android.graphics.drawable.Drawable createFromXmlThemed(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public abstract void draw(android.graphics.Canvas);
method public int getAlpha();
method public final android.graphics.Rect getBounds();
@@ -10520,6 +10529,7 @@
method public int[] getState();
method public android.graphics.Region getTransparentRegion();
method public void inflate(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public void inflate(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public void invalidateSelf();
method public boolean isAutoMirrored();
method public boolean isStateful();
@@ -10555,9 +10565,11 @@
public static abstract class Drawable.ConstantState {
ctor public Drawable.ConstantState();
+ method public boolean canApplyTheme();
method public abstract int getChangingConfigurations();
method public abstract android.graphics.drawable.Drawable newDrawable();
method public android.graphics.drawable.Drawable newDrawable(android.content.res.Resources);
+ method public android.graphics.drawable.Drawable newDrawable(android.content.res.Resources, android.content.res.Resources.Theme);
}
public class DrawableContainer extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index a41b4f9..2f8dd53 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -722,6 +722,9 @@
/*package*/ native static final boolean applyStyle(long theme,
int defStyleAttr, int defStyleRes, long xmlParser,
int[] inAttrs, int[] outValues, int[] outIndices);
+ /*package*/ native static final boolean resolveAttrs(long theme,
+ int defStyleAttr, int defStyleRes, int[] inValues,
+ int[] inAttrs, int[] outValues, int[] outIndices);
/*package*/ native final boolean retrieveAttributes(
long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices);
/*package*/ native final int getArraySize(int resource);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 5c27072..affc784 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -34,6 +34,7 @@
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TypedValue;
import android.util.LongSparseArray;
@@ -74,7 +75,6 @@
private static final boolean DEBUG_LOAD = false;
private static final boolean DEBUG_CONFIG = false;
- private static final boolean DEBUG_ATTRIBUTES_CACHE = false;
private static final boolean TRACE_FOR_PRELOAD = false;
private static final boolean TRACE_FOR_MISS_PRELOAD = false;
@@ -88,9 +88,9 @@
// Information about preloaded resources. Note that they are not
// protected by a lock, because while preloading in zygote we are all
// single-threaded, and after that these are immutable.
- private static final LongSparseArray<Drawable.ConstantState>[] sPreloadedDrawables;
- private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables
- = new LongSparseArray<Drawable.ConstantState>();
+ private static final LongSparseArray<ConstantState>[] sPreloadedDrawables;
+ private static final LongSparseArray<ConstantState> sPreloadedColorDrawables
+ = new LongSparseArray<ConstantState>();
private static final LongSparseArray<ColorStateList> sPreloadedColorStateLists
= new LongSparseArray<ColorStateList>();
@@ -103,12 +103,12 @@
// These are protected by mAccessLock.
private final Object mAccessLock = new Object();
private final Configuration mTmpConfig = new Configuration();
- private final LongSparseArray<WeakReference<Drawable.ConstantState>> mDrawableCache
- = new LongSparseArray<WeakReference<Drawable.ConstantState>>(0);
- private final LongSparseArray<WeakReference<ColorStateList>> mColorStateListCache
- = new LongSparseArray<WeakReference<ColorStateList>>(0);
- private final LongSparseArray<WeakReference<Drawable.ConstantState>> mColorDrawableCache
- = new LongSparseArray<WeakReference<Drawable.ConstantState>>(0);
+ private final ThemedCaches<ConstantState> mDrawableCache =
+ new ThemedCaches<ConstantState>();
+ private final ThemedCaches<ConstantState> mColorDrawableCache =
+ new ThemedCaches<ConstantState>();
+ private final LongSparseArray<WeakReference<ColorStateList>> mColorStateListCache =
+ new LongSparseArray<WeakReference<ColorStateList>>();
private TypedValue mTmpValue = new TypedValue();
private boolean mPreloading;
@@ -126,12 +126,14 @@
private NativePluralRules mPluralRule;
private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+
+ @SuppressWarnings("unused")
private WeakReference<IBinder> mToken;
static {
sPreloadedDrawables = new LongSparseArray[2];
- sPreloadedDrawables[0] = new LongSparseArray<Drawable.ConstantState>();
- sPreloadedDrawables[1] = new LongSparseArray<Drawable.ConstantState>();
+ sPreloadedDrawables[0] = new LongSparseArray<ConstantState>();
+ sPreloadedDrawables[1] = new LongSparseArray<ConstantState>();
}
/** @hide */
@@ -518,7 +520,7 @@
+ Integer.toHexString(id));
}
- TypedArray array = getCachedStyledAttributes(len);
+ TypedArray array = TypedArray.obtain(this, len);
array.mLength = mAssets.retrieveArray(id, array.mData);
array.mIndices[0] = 0;
@@ -1250,6 +1252,13 @@
*/
public void applyStyle(int resid, boolean force) {
AssetManager.applyThemeStyle(mTheme, resid, force);
+
+ if (!mHasStyle) {
+ mHasStyle = true;
+ mThemeResId = resid;
+ } else if (resid != mThemeResId) {
+ mThemeResId = 0;
+ }
}
/**
@@ -1263,6 +1272,9 @@
*/
public void setTo(Theme other) {
AssetManager.copyTheme(mTheme, other.mTheme);
+
+ mHasStyle = other.mHasStyle;
+ mThemeResId = other.mThemeResId;
}
/**
@@ -1286,11 +1298,9 @@
*/
public TypedArray obtainStyledAttributes(int[] attrs) {
final int len = attrs.length;
- final TypedArray array = getCachedStyledAttributes(len);
+ final TypedArray array = TypedArray.obtain(Resources.this, len);
array.mTheme = this;
- array.mRsrcs = attrs;
- AssetManager.applyStyle(mTheme, 0, 0, 0, attrs,
- array.mData, array.mIndices);
+ AssetManager.applyStyle(mTheme, 0, 0, 0, attrs, array.mData, array.mIndices);
return array;
}
@@ -1316,12 +1326,8 @@
*/
public TypedArray obtainStyledAttributes(int resid, int[] attrs) throws NotFoundException {
final int len = attrs.length;
- final TypedArray array = getCachedStyledAttributes(len);
+ final TypedArray array = TypedArray.obtain(Resources.this, len);
array.mTheme = this;
- array.mRsrcs = attrs;
-
- AssetManager.applyStyle(mTheme, 0, resid, 0, attrs,
- array.mData, array.mIndices);
if (false) {
int[] data = array.mData;
@@ -1348,6 +1354,7 @@
}
System.out.println(s);
}
+ AssetManager.applyStyle(mTheme, 0, resid, 0, attrs, array.mData, array.mIndices);
return array;
}
@@ -1402,7 +1409,7 @@
public TypedArray obtainStyledAttributes(AttributeSet set,
int[] attrs, int defStyleAttr, int defStyleRes) {
final int len = attrs.length;
- final TypedArray array = getCachedStyledAttributes(len);
+ final TypedArray array = TypedArray.obtain(Resources.this, len);
// XXX note that for now we only work with compiled XML files.
// To support generic XML files we will need to manually parse
@@ -1413,7 +1420,6 @@
parser != null ? parser.mParseState : 0, attrs, array.mData, array.mIndices);
array.mTheme = this;
- array.mRsrcs = attrs;
array.mXml = parser;
if (false) {
@@ -1449,6 +1455,45 @@
}
/**
+ * Retrieve the values for a set of attributes in the Theme. The
+ * contents of the typed array are ultimately filled in by
+ * {@link Resources#getValue}.
+ *
+ * @param values The base set of attribute values, must be equal
+ * in length to {@code attrs} or {@code null}. All values
+ * must be of type {@link TypedValue#TYPE_ATTRIBUTE}.
+ * @param attrs The desired attributes to be retrieved.
+ * @param defStyleAttr An attribute in the current theme that contains a
+ * reference to a style resource that supplies
+ * defaults values for the TypedArray. Can be
+ * 0 to not look for defaults.
+ * @param defStyleRes A resource identifier of a style resource that
+ * supplies default values for the TypedArray,
+ * used only if defStyleAttr is 0 or can not be found
+ * in the theme. Can be 0 to not look for defaults.
+ * @return Returns a TypedArray holding an array of the attribute
+ * values. Be sure to call {@link TypedArray#recycle()}
+ * when done with it.
+ * @hide
+ */
+ public TypedArray resolveAttributes(int[] values, int[] attrs,
+ int defStyleAttr, int defStyleRes) {
+ final int len = attrs.length;
+ if (values != null && len != values.length) {
+ throw new IllegalArgumentException(
+ "Base attribute values must be null or the same length as attrs");
+ }
+
+ final TypedArray array = TypedArray.obtain(Resources.this, len);
+ AssetManager.resolveAttrs(mTheme, defStyleAttr, defStyleRes,
+ values, attrs, array.mData, array.mIndices);
+ array.mTheme = this;
+ array.mXml = null;
+
+ return array;
+ }
+
+ /**
* Retrieve the value of an attribute in the Theme. The contents of
* <var>outValue</var> are ultimately filled in by
* {@link Resources#getValue}.
@@ -1465,8 +1510,7 @@
* @return boolean Returns true if the attribute was found and
* <var>outValue</var> is valid, else false.
*/
- public boolean resolveAttribute(int resid, TypedValue outValue,
- boolean resolveRefs) {
+ public boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
boolean got = mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
if (false) {
System.out.println(
@@ -1478,6 +1522,15 @@
}
/**
+ * Returns the resources to which this theme belongs.
+ *
+ * @return Resources to which this theme belongs.
+ */
+ public Resources getResources() {
+ return Resources.this;
+ }
+
+ /**
* Return a drawable object associated with a particular resource ID
* and styled for the Theme.
*
@@ -1509,6 +1562,10 @@
mAssets.releaseTheme(mTheme);
}
+ /*package*/ boolean canCacheDrawables() {
+ return mHasStyle && mThemeResId != 0;
+ }
+
/*package*/ Theme() {
mAssets = Resources.this.mAssets;
mTheme = mAssets.createTheme();
@@ -1517,6 +1574,13 @@
@SuppressWarnings("hiding")
private final AssetManager mAssets;
private final long mTheme;
+
+ /**
+ * Resource identifier for the theme. If multiple styles have been
+ * applied to this theme, this value will be 0 (invalid).
+ */
+ private int mThemeResId = 0;
+ private boolean mHasStyle = false;
}
/**
@@ -1543,7 +1607,7 @@
*/
public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
int len = attrs.length;
- TypedArray array = getCachedStyledAttributes(len);
+ TypedArray array = TypedArray.obtain(this, len);
// XXX note that for now we only work with compiled XML files.
// To support generic XML files we will need to manually parse
@@ -1553,7 +1617,6 @@
mAssets.retrieveAttributes(parser.mParseState, attrs,
array.mData, array.mIndices);
- array.mRsrcs = attrs;
array.mXml = parser;
return array;
@@ -1658,8 +1721,8 @@
+ " final compat is " + mCompatibilityInfo);
}
- clearDrawableCacheLocked(mDrawableCache, configChanges);
- clearDrawableCacheLocked(mColorDrawableCache, configChanges);
+ clearDrawableCachesLocked(mDrawableCache, configChanges);
+ clearDrawableCachesLocked(mColorDrawableCache, configChanges);
mColorStateListCache.clear();
@@ -1672,18 +1735,25 @@
}
}
+ private void clearDrawableCachesLocked(
+ ThemedCaches<ConstantState> caches, int configChanges) {
+ final int N = caches.size();
+ for (int i = 0; i < N; i++) {
+ clearDrawableCacheLocked(caches.valueAt(i), configChanges);
+ }
+ }
+
private void clearDrawableCacheLocked(
- LongSparseArray<WeakReference<ConstantState>> cache,
- int configChanges) {
- int N = cache.size();
+ LongSparseArray<WeakReference<ConstantState>> cache, int configChanges) {
if (DEBUG_CONFIG) {
Log.d(TAG, "Cleaning up drawables config changes: 0x"
+ Integer.toHexString(configChanges));
}
- for (int i=0; i<N; i++) {
- WeakReference<Drawable.ConstantState> ref = cache.valueAt(i);
+ final int N = cache.size();
+ for (int i = 0; i < N; i++) {
+ final WeakReference<ConstantState> ref = cache.valueAt(i);
if (ref != null) {
- Drawable.ConstantState cs = ref.get();
+ final ConstantState cs = ref.get();
if (cs != null) {
if (Configuration.needNewResources(
configChanges, cs.getChangingConfigurations())) {
@@ -2080,7 +2150,7 @@
/**
* @hide
*/
- public LongSparseArray<Drawable.ConstantState> getPreloadedDrawables() {
+ public LongSparseArray<ConstantState> getPreloadedDrawables() {
return sPreloadedDrawables[0];
}
@@ -2098,6 +2168,8 @@
} catch (NotFoundException e) {
resName = "?";
}
+ // This should never happen in production, so we should log a
+ // warning even if we're not debugging.
Log.w(TAG, "Preloaded " + name + " resource #0x"
+ Integer.toHexString(resourceId)
+ " (" + resName + ") that varies with configuration!!");
@@ -2128,155 +2200,211 @@
}
}
- boolean isColorDrawable = false;
- if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
- value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+ final boolean isColorDrawable;
+ final ThemedCaches<ConstantState> caches;
+ final long key;
+ if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+ && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
isColorDrawable = true;
+ caches = mColorDrawableCache;
+ key = value.data;
+ } else {
+ isColorDrawable = false;
+ caches = mDrawableCache;
+ key = (((long) value.assetCookie) << 32) | value.data;
}
- final long key = isColorDrawable ? value.data :
- (((long) value.assetCookie) << 32) | value.data;
- Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);
-
- if (dr != null) {
- return dr;
+ // First, check whether we have a cached version of this drawable
+ // that's valid for the specified theme. This may apply a theme to a
+ // cached drawable that has themeable attributes but was not previously
+ // themed.
+ if (!mPreloading) {
+ final Drawable cachedDrawable = getCachedDrawable(caches, key, theme);
+ if (cachedDrawable != null) {
+ return cachedDrawable;
+ }
}
- Drawable.ConstantState cs;
+
+ // Next, check preloaded drawables. These are unthemed but may have
+ // themeable attributes.
+ final ConstantState cs;
if (isColorDrawable) {
cs = sPreloadedColorDrawables.get(key);
} else {
cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
}
+
+ final Drawable dr;
if (cs != null) {
- dr = cs.newDrawable(this);
+ dr = cs.newDrawable(this, theme);
+ } else if (isColorDrawable) {
+ dr = new ColorDrawable(value.data);
} else {
- if (isColorDrawable) {
- dr = new ColorDrawable(value.data);
- }
-
- if (dr == null) {
- if (value.string == null) {
- throw new NotFoundException(
- "Resource is not a Drawable (color or path): " + value);
- }
-
- 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) android.util.Log.d(TAG, "Loading framework drawable #"
- + Integer.toHexString(id) + ": " + name
- + " at " + file);
- }
- }
-
- if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie "
- + value.assetCookie + ": " + file);
-
- if (file.endsWith(".xml")) {
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
- try {
- XmlResourceParser rp = loadXmlResourceParser(
- file, id, value.assetCookie, "drawable");
- dr = Drawable.createFromXml(this, rp);
- rp.close();
- } catch (Exception e) {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
- NotFoundException rnf = new NotFoundException(
- "File " + file + " from drawable resource ID #0x"
- + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
- }
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
-
- } else {
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
- try {
- InputStream is = mAssets.openNonAsset(
- value.assetCookie, file, AssetManager.ACCESS_STREAMING);
- // System.out.println("Opened file " + file + ": " + is);
- dr = Drawable.createFromResourceStream(this, value, is,
- file, null);
- is.close();
- // System.out.println("Created stream: " + dr);
- } catch (Exception e) {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
- NotFoundException rnf = new NotFoundException(
- "File " + file + " from drawable resource ID #0x"
- + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
- }
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
- }
- }
+ dr = loadDrawableForCookie(value, id, theme);
}
+ // If we were able to obtain a drawable, attempt to place it in the
+ // appropriate cache (e.g. no theme, themed, themeable).
if (dr != null) {
dr.setChangingConfigurations(value.changingConfigurations);
- cs = dr.getConstantState();
- if (cs != null) {
- if (mPreloading) {
- final int changingConfigs = cs.getChangingConfigurations();
- if (isColorDrawable) {
- if (verifyPreloadConfig(changingConfigs, 0, value.resourceId,
- "drawable")) {
- sPreloadedColorDrawables.put(key, cs);
- }
- } else {
- if (verifyPreloadConfig(changingConfigs,
- LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) {
- if ((changingConfigs&LAYOUT_DIR_CONFIG) == 0) {
- // If this resource does not vary based on layout direction,
- // we can put it in all of the preload maps.
- sPreloadedDrawables[0].put(key, cs);
- sPreloadedDrawables[1].put(key, cs);
- } else {
- // Otherwise, only in the layout dir we loaded it for.
- final LongSparseArray<Drawable.ConstantState> preloads
- = sPreloadedDrawables[mConfiguration.getLayoutDirection()];
- preloads.put(key, cs);
- }
- }
- }
- } else {
- synchronized (mAccessLock) {
- //Log.i(TAG, "Saving cached drawable @ #" +
- // Integer.toHexString(key.intValue())
- // + " in " + this + ": " + cs);
- if (isColorDrawable) {
- mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
- } else {
- mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
- }
- }
- }
- }
+ cacheDrawable(value, theme, isColorDrawable, caches, key, dr);
}
return dr;
}
- private Drawable getCachedDrawable(
- LongSparseArray<WeakReference<ConstantState>> drawableCache,
- long key) {
- synchronized (mAccessLock) {
- WeakReference<Drawable.ConstantState> wr = drawableCache.get(key);
- if (wr != null) { // we have the key
- Drawable.ConstantState entry = wr.get();
- if (entry != null) {
- //Log.i(TAG, "Returning cached drawable @ #" +
- // Integer.toHexString(((Integer)key).intValue())
- // + " in " + this + ": " + entry);
- return entry.newDrawable(this);
+ private void cacheDrawable(TypedValue value, Theme theme, boolean isColorDrawable,
+ ThemedCaches<ConstantState> caches, long key, Drawable dr) {
+ final ConstantState cs = dr.getConstantState();
+ if (cs == null) {
+ return;
+ }
+
+ // Abort if the drawable is themed, but the theme cannot be cached.
+ if (dr.canApplyTheme() && theme != null && !theme.canCacheDrawables()) {
+ return;
+ }
+
+ if (mPreloading) {
+ // Preloaded drawables never have a theme, but may be themeable.
+ final int changingConfigs = cs.getChangingConfigurations();
+ if (isColorDrawable) {
+ if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) {
+ sPreloadedColorDrawables.put(key, cs);
}
- else { // our entry has been purged
- drawableCache.delete(key);
+ } else {
+ if (verifyPreloadConfig(
+ changingConfigs, LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) {
+ if ((changingConfigs & LAYOUT_DIR_CONFIG) == 0) {
+ // If this resource does not vary based on layout direction,
+ // we can put it in all of the preload maps.
+ sPreloadedDrawables[0].put(key, cs);
+ sPreloadedDrawables[1].put(key, cs);
+ } else {
+ // Otherwise, only in the layout dir we loaded it for.
+ sPreloadedDrawables[mConfiguration.getLayoutDirection()].put(key, cs);
+ }
}
}
+ } else {
+ synchronized (mAccessLock) {
+ final LongSparseArray<WeakReference<ConstantState>> themedCache;
+ if (!dr.canApplyTheme()) {
+ themedCache = caches.getUnthemed(true);
+ } else {
+ themedCache = caches.getOrCreate(theme == null ? 0 : theme.mThemeResId);
+ }
+ themedCache.put(key, new WeakReference<ConstantState>(cs));
+ }
+ }
+ }
+
+ /**
+ * Loads a drawable from XML or resources stream.
+ */
+ private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {
+ if (value.string == null) {
+ throw new NotFoundException(
+ "Resource is not a Drawable (color or path): " + 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 drawable #" + Integer.toHexString(id)
+ + ": " + name + " at " + file);
+ }
+ }
+ }
+
+ if (DEBUG_LOAD) {
+ Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
+ }
+
+ final Drawable dr;
+
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+ try {
+ if (file.endsWith(".xml")) {
+ final XmlResourceParser rp = loadXmlResourceParser(
+ file, id, value.assetCookie, "drawable");
+ dr = Drawable.createFromXmlThemed(this, rp, theme);
+ rp.close();
+ } else {
+ final InputStream is = mAssets.openNonAsset(
+ value.assetCookie, file, AssetManager.ACCESS_STREAMING);
+ dr = Drawable.createFromResourceStreamThemed(this, value, is, file, null, theme);
+ is.close();
+ }
+ } catch (Exception e) {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ final NotFoundException rnf = new NotFoundException(
+ "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+
+ return dr;
+ }
+
+ private Drawable getCachedDrawable(ThemedCaches<ConstantState> caches, long key, Theme theme) {
+ synchronized (mAccessLock) {
+ // First, check for a matching unthemed drawable.
+ final LongSparseArray<WeakReference<ConstantState>> unthemed = caches.getUnthemed(false);
+ if (unthemed != null) {
+ final Drawable unthemedDrawable = getCachedDrawableLocked(unthemed, key);
+ if (unthemedDrawable != null) {
+ return unthemedDrawable;
+ }
+ }
+
+ final boolean themeCannotCache = theme != null && !theme.canCacheDrawables();
+ if (themeCannotCache) {
+ return null;
+ }
+
+ // Next, check for a matching themed drawable.
+ final int themeKey = theme != null ? theme.mThemeResId : 0;
+ final LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey);
+ if (themedCache != null) {
+ final Drawable themedDrawable = getCachedDrawableLocked(themedCache, key);
+ if (themedDrawable != null) {
+ return themedDrawable;
+ }
+ }
+
+ // No cached drawable, we'll need to create a new one.
+ return null;
+ }
+ }
+
+ private ConstantState getConstantStateLocked(
+ LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) {
+ final WeakReference<ConstantState> wr = drawableCache.get(key);
+ if (wr != null) { // we have the key
+ final ConstantState entry = wr.get();
+ if (entry != null) {
+ //Log.i(TAG, "Returning cached drawable @ #" +
+ // Integer.toHexString(((Integer)key).intValue())
+ // + " in " + this + ": " + entry);
+ return entry;
+ } else { // our entry has been purged
+ drawableCache.delete(key);
+ }
+ }
+ return null;
+ }
+
+ private Drawable getCachedDrawableLocked(
+ LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) {
+ final ConstantState entry = getConstantStateLocked(drawableCache, key);
+ if (entry != null) {
+ return entry.newDrawable(this);
}
return null;
}
@@ -2466,40 +2594,6 @@
}
}
- private TypedArray getCachedStyledAttributes(int len) {
- synchronized (mAccessLock) {
- TypedArray attrs = mCachedStyledAttributes;
- if (attrs != null) {
- mCachedStyledAttributes = null;
- if (DEBUG_ATTRIBUTES_CACHE) {
- mLastRetrievedAttrs = new RuntimeException("here");
- mLastRetrievedAttrs.fillInStackTrace();
- }
-
- attrs.mLength = len;
- int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;
- if (attrs.mData.length >= fullLen) {
- return attrs;
- }
- attrs.mData = new int[fullLen];
- attrs.mIndices = new int[1+len];
- return attrs;
- }
- if (DEBUG_ATTRIBUTES_CACHE) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- if (mLastRetrievedAttrs != null) {
- Log.i(TAG, "Allocated new TypedArray of " + len + " in " + this, here);
- Log.i(TAG, "Last retrieved attributes here", mLastRetrievedAttrs);
- }
- mLastRetrievedAttrs = here;
- }
- return new TypedArray(this,
- new int[len*AssetManager.STYLE_NUM_ENTRIES],
- new int[1+len], len);
- }
- }
-
private Resources() {
mAssets = AssetManager.getSystem();
// NOTE: Intentionally leaving this uninitialized (all values set
@@ -2510,4 +2604,33 @@
updateConfiguration(null, null);
mAssets.ensureStringBlocks();
}
+
+ static class ThemedCaches<T> extends SparseArray<LongSparseArray<WeakReference<T>>> {
+ private LongSparseArray<WeakReference<T>> mUnthemed = null;
+
+ /**
+ * Returns the cache of drawables with no themeable attributes.
+ */
+ public LongSparseArray<WeakReference<T>> getUnthemed(boolean autoCreate) {
+ if (mUnthemed == null && autoCreate) {
+ mUnthemed = new LongSparseArray<WeakReference<T>>(1);
+ }
+ return mUnthemed;
+ }
+
+ /**
+ * Returns the cache of drawables styled for the specified theme.
+ * <p>
+ * Drawables that have themeable attributes but were loaded without
+ * specifying a theme are cached at themeResId = 0.
+ */
+ public LongSparseArray<WeakReference<T>> getOrCreate(int themeResId) {
+ LongSparseArray<WeakReference<T>> result = get(themeResId);
+ if (result == null) {
+ result = new LongSparseArray<WeakReference<T>>(1);
+ put(themeResId, result);
+ }
+ return result;
+ }
+ }
}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 4858d08..baf887e 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -20,6 +20,7 @@
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.Pools.SynchronizedPool;
import android.util.TypedValue;
import com.android.internal.util.XmlUtils;
@@ -36,16 +37,40 @@
* the positions of the attributes given to obtainStyledAttributes.
*/
public class TypedArray {
- private final Resources mResources;
- private final DisplayMetrics mMetrics;
- private final AssetManager mAssets;
+ private static final SynchronizedPool<TypedArray> mPool = new SynchronizedPool<TypedArray>(5);
+
+ static TypedArray obtain(Resources res, int len) {
+ final TypedArray attrs = mPool.acquire();
+ if (attrs != null) {
+ attrs.mLength = len;
+ attrs.mResources = res;
+ attrs.mMetrics = res.getDisplayMetrics();
+ attrs.mAssets = res.getAssets();
+
+ final int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;
+ if (attrs.mData.length >= fullLen) {
+ return attrs;
+ }
+
+ attrs.mData = new int[fullLen];
+ attrs.mIndices = new int[1 + len];
+ return attrs;
+ }
+
+ return new TypedArray(res,
+ new int[len*AssetManager.STYLE_NUM_ENTRIES],
+ new int[1+len], len);
+ }
+
+ private Resources mResources;
+ private DisplayMetrics mMetrics;
+ private AssetManager mAssets;
/*package*/ XmlBlock.Parser mXml;
- /*package*/ int[] mRsrcs;
+ /*package*/ Resources.Theme mTheme;
/*package*/ int[] mData;
/*package*/ int[] mIndices;
/*package*/ int mLength;
/*package*/ TypedValue mValue = new TypedValue();
- /*package*/ Resources.Theme mTheme;
/**
* Return the number of values in this array.
@@ -580,6 +605,25 @@
}
/**
+ * Retrieve the theme attribute resource identifier for the attribute at
+ * <var>index</var>.
+ *
+ * @param index Index of attribute to retrieve.
+ * @param defValue Value to return if the attribute is not defined or not a
+ * resource.
+ * @return Theme attribute resource identifier, or defValue if not defined.
+ * @hide
+ */
+ public int getThemeAttributeId(int index, int defValue) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
+ return data[index + AssetManager.STYLE_DATA];
+ }
+ return defValue;
+ }
+
+ /**
* Retrieve the Drawable for the attribute at <var>index</var>. This
* gets the resource ID of the selected attribute, and uses
* {@link Resources#getDrawable Resources.getDrawable} of the owning
@@ -647,6 +691,38 @@
}
/**
+ * Determines whether this TypedArray contains an attribute of the specified
+ * type.
+ *
+ * @param type Type of data, e.g. {@link TypedValue#TYPE_ATTRIBUTE}
+ * @return True if the TypedArray contains an attribute of the specified
+ * type.
+ * @hide
+ */
+ public boolean hasType(int type) {
+ final int[] data = mData;
+ final int N = getIndexCount();
+ for (int i = 0; i < N; i++) {
+ final int index = getIndex(i) * AssetManager.STYLE_NUM_ENTRIES;
+ if (data[index + AssetManager.STYLE_TYPE] == type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the type of attribute at the specified index.
+ *
+ * @param index Index of attribute whose type to retrieve.
+ * @return Attribute type.
+ */
+ public int getType(int index) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ return mData[index + AssetManager.STYLE_TYPE];
+ }
+
+ /**
* Determines whether there is an attribute at <var>index</var>.
*
* @param index Index of attribute to retrieve.
@@ -690,11 +766,46 @@
* Give back a previously retrieved array, for later re-use.
*/
public void recycle() {
- mResources.recycleCachedStyledAttributes(this);
+ mResources = null;
+ mMetrics = null;
+ mAssets = null;
+ // These may have been set by the client.
mXml = null;
- mRsrcs = null;
mTheme = null;
+
+ synchronized (mPool) {
+ mPool.release(this);
+ }
+ }
+
+ /**
+ * Extracts theme attributes from a typed array for later resolution using
+ * {@link Theme#resolveAttributes(int[], int[], int, int)}.
+ *
+ * @param array An array to populate with theme attributes. If the array is
+ * null or not large enough, a new array will be returned.
+ * @return an array of length {@link #getIndexCount()} populated with theme
+ * attributes, or null if there are no theme attributes in the
+ * typed array
+ * @hide
+ */
+ public int[] extractThemeAttrs() {
+ int[] attrs = null;
+
+ final int N = getIndexCount();
+ for (int i = 0; i < N; i++) {
+ final int index = getIndex(i);
+ final int attrId = getThemeAttributeId(index, 0);
+ if (attrId != 0) {
+ if (attrs == null) {
+ attrs = new int[N];
+ }
+ attrs[i] = attrId;
+ }
+ }
+
+ return attrs;
}
private boolean getValueAt(int index, TypedValue outValue) {
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 572302a4..eedacb5 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -664,7 +664,7 @@
InputStream stream = null;
try {
stream = mContext.getContentResolver().openInputStream(mUri);
- d = Drawable.createFromStream(stream, null);
+ d = Drawable.createFromStreamThemed(stream, null, mContext.getTheme());
} catch (Exception e) {
Log.w("ImageView", "Unable to open content: " + mUri, e);
} finally {
diff --git a/core/java/android/widget/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java
index c8917e0..02033012 100644
--- a/core/java/android/widget/SuggestionsAdapter.java
+++ b/core/java/android/widget/SuggestionsAdapter.java
@@ -574,7 +574,7 @@
throw new FileNotFoundException("Failed to open " + uri);
}
try {
- return Drawable.createFromStream(stream, null);
+ return Drawable.createFromStreamThemed(stream, null, mContext.getTheme());
} finally {
try {
stream.close();
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 7162a1c..9dde701a 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -972,6 +972,200 @@
theme->dumpToLog();
}
+static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz,
+ jlong themeToken,
+ jint defStyleAttr,
+ jint defStyleRes,
+ jintArray inValues,
+ jintArray attrs,
+ jintArray outValues,
+ jintArray outIndices)
+{
+ if (themeToken == 0) {
+ jniThrowNullPointerException(env, "theme token");
+ return JNI_FALSE;
+ }
+ if (attrs == NULL) {
+ jniThrowNullPointerException(env, "attrs");
+ return JNI_FALSE;
+ }
+ if (outValues == NULL) {
+ jniThrowNullPointerException(env, "out values");
+ return JNI_FALSE;
+ }
+
+ DEBUG_STYLES(LOGI("APPLY STYLE: theme=0x%x defStyleAttr=0x%x defStyleRes=0x%x",
+ themeToken, defStyleAttr, defStyleRes));
+
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
+ const ResTable& res = theme->getResTable();
+ ResTable_config config;
+ Res_value value;
+
+ const jsize NI = env->GetArrayLength(attrs);
+ const jsize NV = env->GetArrayLength(outValues);
+ if (NV < (NI*STYLE_NUM_ENTRIES)) {
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
+ return JNI_FALSE;
+ }
+
+ jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
+ if (src == NULL) {
+ return JNI_FALSE;
+ }
+
+ jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0);
+ const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues);
+
+ jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
+ jint* dest = baseDest;
+ if (dest == NULL) {
+ env->ReleasePrimitiveArrayCritical(attrs, src, 0);
+ return JNI_FALSE;
+ }
+
+ jint* indices = NULL;
+ int indicesIdx = 0;
+ if (outIndices != NULL) {
+ if (env->GetArrayLength(outIndices) > NI) {
+ indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
+ }
+ }
+
+ // Load default style from attribute, if specified...
+ uint32_t defStyleBagTypeSetFlags = 0;
+ if (defStyleAttr != 0) {
+ Res_value value;
+ if (theme->getAttribute(defStyleAttr, &value, &defStyleBagTypeSetFlags) >= 0) {
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ defStyleRes = value.data;
+ }
+ }
+ }
+
+ // Now lock down the resource object and start pulling stuff from it.
+ res.lock();
+
+ // Retrieve the default style bag, if requested.
+ const ResTable::bag_entry* defStyleEnt = NULL;
+ uint32_t defStyleTypeSetFlags = 0;
+ ssize_t bagOff = defStyleRes != 0
+ ? res.getBagLocked(defStyleRes, &defStyleEnt, &defStyleTypeSetFlags) : -1;
+ defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
+ const ResTable::bag_entry* endDefStyleEnt = defStyleEnt +
+ (bagOff >= 0 ? bagOff : 0);;
+
+ // Now iterate through all of the attributes that the client has requested,
+ // filling in each with whatever data we can find.
+ ssize_t block = 0;
+ uint32_t typeSetFlags;
+ for (jsize ii=0; ii<NI; ii++) {
+ const uint32_t curIdent = (uint32_t)src[ii];
+
+ DEBUG_STYLES(LOGI("RETRIEVING ATTR 0x%08x...", curIdent));
+
+ // Try to find a value for this attribute... we prioritize values
+ // coming from, first XML attributes, then XML style, then default
+ // style, and finally the theme.
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = 0;
+ typeSetFlags = 0;
+ config.density = 0;
+
+ // Retrieve the current input value if available.
+ if (NSV > 0 && srcValues[ii] != 0) {
+ block = -1;
+ value.dataType = Res_value::TYPE_ATTRIBUTE;
+ value.data = srcValues[ii];
+ DEBUG_STYLES(LOGI("-> From values: type=0x%x, data=0x%08x",
+ value.dataType, value.data));
+ }
+
+ // Skip through the default style values until the end or the next possible match.
+ while (defStyleEnt < endDefStyleEnt && curIdent > defStyleEnt->map.name.ident) {
+ defStyleEnt++;
+ }
+ // Retrieve the current default style attribute if it matches, and step to next.
+ if (defStyleEnt < endDefStyleEnt && curIdent == defStyleEnt->map.name.ident) {
+ if (value.dataType == Res_value::TYPE_NULL) {
+ block = defStyleEnt->stringBlock;
+ typeSetFlags = defStyleTypeSetFlags;
+ value = defStyleEnt->map.value;
+ DEBUG_STYLES(LOGI("-> From def style: type=0x%x, data=0x%08x",
+ value.dataType, value.data));
+ }
+ defStyleEnt++;
+ }
+
+ uint32_t resid = 0;
+ if (value.dataType != Res_value::TYPE_NULL) {
+ // Take care of resolving the found resource to its final value.
+ ssize_t newBlock = theme->resolveAttributeReference(&value, block,
+ &resid, &typeSetFlags, &config);
+ if (newBlock >= 0) block = newBlock;
+ DEBUG_STYLES(LOGI("-> Resolved attr: type=0x%x, data=0x%08x",
+ value.dataType, value.data));
+ } else {
+ // If we still don't have a value for this attribute, try to find
+ // it in the theme!
+ ssize_t newBlock = theme->getAttribute(curIdent, &value, &typeSetFlags);
+ if (newBlock >= 0) {
+ DEBUG_STYLES(LOGI("-> From theme: type=0x%x, data=0x%08x",
+ value.dataType, value.data));
+ newBlock = res.resolveReference(&value, block, &resid,
+ &typeSetFlags, &config);
+#if THROW_ON_BAD_ID
+ if (newBlock == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return JNI_FALSE;
+ }
+#endif
+ if (newBlock >= 0) block = newBlock;
+ DEBUG_STYLES(LOGI("-> Resolved theme: type=0x%x, data=0x%08x",
+ value.dataType, value.data));
+ }
+ }
+
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ DEBUG_STYLES(LOGI("-> Setting to @null!"));
+ value.dataType = Res_value::TYPE_NULL;
+ block = -1;
+ }
+
+ DEBUG_STYLES(LOGI("Attribute 0x%08x: type=0x%x, data=0x%08x",
+ curIdent, value.dataType, value.data));
+
+ // Write the final value back to Java.
+ dest[STYLE_TYPE] = value.dataType;
+ dest[STYLE_DATA] = value.data;
+ dest[STYLE_ASSET_COOKIE] =
+ block != -1 ? reinterpret_cast<jint>(res.getTableCookie(block)) : (jint)-1;
+ dest[STYLE_RESOURCE_ID] = resid;
+ dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
+ dest[STYLE_DENSITY] = config.density;
+
+ if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
+ indicesIdx++;
+ indices[indicesIdx] = ii;
+ }
+
+ dest += STYLE_NUM_ENTRIES;
+ }
+
+ res.unlock();
+
+ if (indices != NULL) {
+ indices[0] = indicesIdx;
+ env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
+ }
+ env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
+ env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0);
+ env->ReleasePrimitiveArrayCritical(attrs, src, 0);
+
+ return JNI_TRUE;
+}
+
static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject clazz,
jlong themeToken,
jint defStyleAttr,
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 9accbbc..a37ceef 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -21,10 +21,12 @@
import android.graphics.ColorFilter;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.util.Log;
import android.os.SystemClock;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -223,7 +225,7 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedRotateDrawable);
@@ -258,7 +260,7 @@
continue;
}
- if ((drawable = Drawable.createFromXmlInner(r, parser, attrs)) == null) {
+ if ((drawable = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme)) == null) {
Log.w("drawable", "Bad element under <animated-rotate>: "
+ parser .getName());
}
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 02b2588..3f94e26 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -23,6 +23,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.os.SystemClock;
import android.util.AttributeSet;
@@ -236,7 +237,7 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
TypedArray a = r.obtainAttributes(attrs,
@@ -292,7 +293,7 @@
": <item> tag requires a 'drawable' attribute or child tag" +
" defining a drawable");
}
- dr = Drawable.createFromXmlInner(r, parser, attrs);
+ dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
}
mAnimationState.addFrame(dr, duration);
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index fe08f4b..19131f2 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -18,6 +18,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -37,6 +38,8 @@
import android.util.LayoutDirection;
import android.view.Gravity;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -114,7 +117,7 @@
*/
@Deprecated
public BitmapDrawable(Bitmap bitmap) {
- this(new BitmapState(bitmap), null);
+ this(new BitmapState(bitmap), null, null);
}
/**
@@ -122,7 +125,7 @@
* the display metrics of the resources.
*/
public BitmapDrawable(Resources res, Bitmap bitmap) {
- this(new BitmapState(bitmap), res);
+ this(new BitmapState(bitmap), res, null);
mBitmapState.mTargetDensity = mTargetDensity;
}
@@ -133,7 +136,7 @@
*/
@Deprecated
public BitmapDrawable(String filepath) {
- this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
+ this(new BitmapState(BitmapFactory.decodeFile(filepath)), null, null);
if (mBitmap == null) {
android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
}
@@ -144,7 +147,7 @@
*/
@SuppressWarnings({"UnusedParameters"})
public BitmapDrawable(Resources res, String filepath) {
- this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
+ this(new BitmapState(BitmapFactory.decodeFile(filepath)), null, null);
mBitmapState.mTargetDensity = mTargetDensity;
if (mBitmap == null) {
android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
@@ -158,7 +161,7 @@
*/
@Deprecated
public BitmapDrawable(java.io.InputStream is) {
- this(new BitmapState(BitmapFactory.decodeStream(is)), null);
+ this(new BitmapState(BitmapFactory.decodeStream(is)), null, null);
if (mBitmap == null) {
android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
}
@@ -169,7 +172,7 @@
*/
@SuppressWarnings({"UnusedParameters"})
public BitmapDrawable(Resources res, java.io.InputStream is) {
- this(new BitmapState(BitmapFactory.decodeStream(is)), null);
+ this(new BitmapState(BitmapFactory.decodeStream(is)), null, null);
mBitmapState.mTargetDensity = mTargetDensity;
if (mBitmap == null) {
android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
@@ -633,6 +636,7 @@
/**
* @hide Candidate for future API inclusion
*/
+ @Override
public void setXfermode(Xfermode xfermode) {
mBitmapState.mPaint.setXfermode(xfermode);
invalidateSelf();
@@ -676,64 +680,228 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
- super.inflate(r, parser, attrs);
+ super.inflate(r, parser, attrs, theme);
+ final TypedArray a = obtainAttributes(
+ r, theme, attrs, R.styleable.BitmapDrawable);
+ inflateStateFromTypedArray(a);
+ a.recycle();
+ }
+
+ /**
+ * Initializes the constant state from the values in the typed array.
+ */
+ private void inflateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
+ final Resources r = a.getResources();
final BitmapState state = mBitmapState;
- final TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.BitmapDrawable);
- final int id = a.getResourceId(com.android.internal.R.styleable.BitmapDrawable_src, 0);
- if (id == 0) {
- throw new XmlPullParserException(parser.getPositionDescription() +
- ": <bitmap> requires a valid src attribute");
+ // Extract the theme attributes, if any.
+ final int[] themeAttrs = a.extractThemeAttrs();
+ state.mThemeAttrs = themeAttrs;
+
+ if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_src] == 0) {
+ final int id = a.getResourceId(R.styleable.BitmapDrawable_src, 0);
+ if (id == 0) {
+ throw new XmlPullParserException(a.getPositionDescription() +
+ ": <bitmap> requires a valid src attribute");
+ }
+
+ final Bitmap bitmap = BitmapFactory.decodeResource(r, id);
+ if (bitmap == null) {
+ throw new XmlPullParserException(a.getPositionDescription() +
+ ": <bitmap> requires a valid src attribute");
+ }
+ state.mBitmap = bitmap;
+ setBitmap(bitmap);
}
- final Bitmap bitmap = BitmapFactory.decodeResource(r, id);
- if (bitmap == null) {
- throw new XmlPullParserException(parser.getPositionDescription() +
- ": <bitmap> requires a valid src attribute");
- }
- state.mBitmap = bitmap;
- setBitmap(bitmap);
+
setTargetDensity(r.getDisplayMetrics());
- setMipMap(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_mipMap,
- bitmap.hasMipMap()));
- setAutoMirrored(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_autoMirrored,
- false));
- final int tintModeValue = a.getInt(
- com.android.internal.R.styleable.BitmapDrawable_tintMode, -1);
- state.mTintMode = Drawable.parseTintMode(tintModeValue, Mode.SRC_IN);
- state.mTint = a.getColorStateList(com.android.internal.R.styleable.BitmapDrawable_tint);
- if (state.mTint != null) {
- final int color = state.mTint.getColorForState(getState(), 0);
- mTintFilter = new PorterDuffColorFilter(color, mBitmapState.mTintMode);
+ if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_mipMap] == 0) {
+ final boolean defMipMap = state.mBitmap != null ? state.mBitmap.hasMipMap() : false;
+ final boolean mipMap = a.getBoolean(
+ R.styleable.BitmapDrawable_mipMap, defMipMap);
+ setMipMap(mipMap);
}
- final Paint paint = state.mPaint;
- paint.setAntiAlias(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_antialias,
- paint.isAntiAlias()));
- paint.setFilterBitmap(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_filter,
- paint.isFilterBitmap()));
- paint.setDither(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_dither,
- paint.isDither()));
- setGravity(a.getInt(com.android.internal.R.styleable.BitmapDrawable_gravity, Gravity.FILL));
- int tileMode = a.getInt(com.android.internal.R.styleable.BitmapDrawable_tileMode, -1);
- if (tileMode != -1) {
- switch (tileMode) {
- case 0:
- setTileModeXY(Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
- break;
- case 1:
- setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
- break;
- case 2:
- setTileModeXY(Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
- break;
+ if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_autoMirrored] == 0) {
+ final boolean autoMirrored = a.getBoolean(
+ R.styleable.BitmapDrawable_autoMirrored, false);
+ setAutoMirrored(autoMirrored);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_tintMode] == 0) {
+ final int tintModeValue = a.getInt(
+ R.styleable.BitmapDrawable_tintMode, -1);
+ state.mTintMode = Drawable.parseTintMode(tintModeValue, Mode.SRC_IN);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_tint] == 0) {
+ state.mTint = a.getColorStateList(R.styleable.BitmapDrawable_tint);
+ if (state.mTint != null) {
+ final int color = state.mTint.getColorForState(getState(), 0);
+ mTintFilter = new PorterDuffColorFilter(color, mBitmapState.mTintMode);
}
}
- a.recycle();
+ final Paint paint = mBitmapState.mPaint;
+
+ if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_antialias] == 0) {
+ final boolean antiAlias = a.getBoolean(
+ R.styleable.BitmapDrawable_antialias, paint.isAntiAlias());
+ paint.setAntiAlias(antiAlias);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_filter] == 0) {
+ final boolean filter = a.getBoolean(
+ R.styleable.BitmapDrawable_filter, paint.isFilterBitmap());
+ paint.setFilterBitmap(filter);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_dither] == 0) {
+ final boolean dither = a.getBoolean(
+ R.styleable.BitmapDrawable_dither, paint.isDither());
+ paint.setDither(dither);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_gravity] == 0) {
+ final int gravity = a.getInt(
+ R.styleable.BitmapDrawable_gravity, Gravity.FILL);
+ setGravity(gravity);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_tileMode] == 0) {
+ final int tileMode = a.getInt(
+ R.styleable.BitmapDrawable_tileMode, -1);
+ setTileModeInternal(tileMode);
+ }
+ }
+
+ @Override
+ public void applyTheme(Theme t) {
+ super.applyTheme(t);
+
+ final BitmapState state = mBitmapState;
+ if (state == null) {
+ throw new RuntimeException("Can't apply theme to <bitmap> with no constant state");
+ }
+
+ final int[] themeAttrs = state.mThemeAttrs;
+ if (themeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.BitmapDrawable, 0, 0);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+ }
+
+ /**
+ * Updates the constant state from the values in the typed array.
+ */
+ private void updateStateFromTypedArray(TypedArray a) {
+ final Resources r = a.getResources();
+ final BitmapState state = mBitmapState;
+ final Paint paint = mBitmapState.mPaint;
+
+ if (a.hasValue(R.styleable.BitmapDrawable_antialias)) {
+ final boolean antiAlias = a.getBoolean(
+ R.styleable.BitmapDrawable_antialias, paint.isAntiAlias());
+ paint.setAntiAlias(antiAlias);
+ }
+
+ if (a.hasValue(R.styleable.BitmapDrawable_filter)) {
+ final boolean filter = a.getBoolean(
+ R.styleable.BitmapDrawable_filter, paint.isFilterBitmap());
+ paint.setFilterBitmap(filter);
+ }
+
+ if (a.hasValue(R.styleable.BitmapDrawable_dither)) {
+ final boolean dither = a.getBoolean(
+ R.styleable.BitmapDrawable_dither, paint.isDither());
+ paint.setDither(dither);
+ }
+
+ if (a.hasValue(R.styleable.BitmapDrawable_gravity)) {
+ final int gravity = a.getInt(
+ R.styleable.BitmapDrawable_gravity, Gravity.FILL);
+ setGravity(gravity);
+ }
+
+ if (a.hasValue(R.styleable.BitmapDrawable_tileMode)) {
+ final int tileMode = a.getInt(
+ R.styleable.BitmapDrawable_tileMode, -1);
+ setTileModeInternal(tileMode);
+ }
+
+ if (a.hasValue(R.styleable.BitmapDrawable_src)) {
+ final int id = a.getResourceId(R.styleable.BitmapDrawable_src, 0);
+ if (id == 0) {
+ throw new RuntimeException(a.getPositionDescription() +
+ ": <bitmap> requires a valid src attribute");
+ }
+
+ final Bitmap bitmap = BitmapFactory.decodeResource(r, id);
+ if (bitmap == null) {
+ throw new RuntimeException(a.getPositionDescription() +
+ ": <bitmap> requires a valid src attribute");
+ }
+
+ setBitmap(bitmap);
+ }
+
+ setTargetDensity(r.getDisplayMetrics());
+
+ if (a.hasValue(R.styleable.BitmapDrawable_mipMap)) {
+ final boolean mipMap = a.getBoolean(
+ R.styleable.BitmapDrawable_mipMap,
+ state.mBitmap.hasMipMap());
+ setMipMap(mipMap);
+ }
+
+ if (a.hasValue(R.styleable.BitmapDrawable_autoMirrored)) {
+ final boolean autoMirrored = a.getBoolean(
+ R.styleable.BitmapDrawable_autoMirrored, false);
+ setAutoMirrored(autoMirrored);
+ }
+
+ if (a.hasValue(R.styleable.BitmapDrawable_tintMode)) {
+ final int modeValue = a.getInt(
+ R.styleable.BitmapDrawable_tintMode, -1);
+ state.mTintMode = Drawable.parseTintMode(modeValue, Mode.SRC_IN);
+ }
+
+ if (a.hasValue(R.styleable.BitmapDrawable_tint)) {
+ final ColorStateList tint = a.getColorStateList(
+ R.styleable.BitmapDrawable_tint);
+ if (tint != null) {
+ state.mTint = tint;
+ final int color = tint.getColorForState(getState(), 0);
+ mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
+ }
+ }
+ }
+
+ private void setTileModeInternal(final int tileMode) {
+ switch (tileMode) {
+ case -1:
+ // Do nothing.
+ break;
+ case 0:
+ setTileModeXY(Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+ break;
+ case 1:
+ setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
+ break;
+ case 2:
+ setTileModeXY(Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
+ break;
+ }
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mBitmapState != null && mBitmapState.mThemeAttrs != null;
}
@Override
@@ -766,6 +934,7 @@
Bitmap mBitmap;
ColorStateList mTint;
Mode mTintMode = Mode.SRC_IN;
+ int[] mThemeAttrs;
int mChangingConfigurations;
int mGravity = Gravity.FILL;
Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS);
@@ -783,6 +952,7 @@
mBitmap = bitmapState.mBitmap;
mTint = bitmapState.mTint;
mTintMode = bitmapState.mTintMode;
+ mThemeAttrs = bitmapState.mThemeAttrs;
mChangingConfigurations = bitmapState.mChangingConfigurations;
mGravity = bitmapState.mGravity;
mTileModeX = bitmapState.mTileModeX;
@@ -794,18 +964,28 @@
}
@Override
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null;
+ }
+
+ @Override
public Bitmap getBitmap() {
return mBitmap;
}
@Override
public Drawable newDrawable() {
- return new BitmapDrawable(this, null);
+ return new BitmapDrawable(this, null, null);
}
@Override
public Drawable newDrawable(Resources res) {
- return new BitmapDrawable(this, res);
+ return new BitmapDrawable(this, res, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return new BitmapDrawable(this, res, theme);
}
@Override
@@ -814,9 +994,21 @@
}
}
- private BitmapDrawable(BitmapState state, Resources res) {
- mBitmapState = state;
+ private BitmapDrawable(BitmapState state, Resources res, Theme theme) {
+ if (theme != null && state.canApplyTheme()) {
+ mBitmapState = new BitmapState(state);
+ applyTheme(theme);
+ } else {
+ mBitmapState = state;
+ }
+ initializeWithState(state, res);
+ }
+
+ /**
+ * Initializes local dynamic properties from state.
+ */
+ private void initializeWithState(BitmapState state, Resources res) {
if (res != null) {
mTargetDensity = res.getDisplayMetrics().densityDpi;
} else {
@@ -828,6 +1020,6 @@
mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
}
- setBitmap(state != null ? state.mBitmap : null);
+ setBitmap(state.mBitmap);
}
}
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index 2a9a14b..3dbd235 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -21,6 +21,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.graphics.*;
import android.view.Gravity;
import android.util.AttributeSet;
@@ -72,9 +73,9 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
- super.inflate(r, parser, attrs);
+ super.inflate(r, parser, attrs, theme);
int type;
@@ -94,7 +95,7 @@
if (type != XmlPullParser.START_TAG) {
continue;
}
- dr = Drawable.createFromXmlInner(r, parser, attrs);
+ dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
}
if (dr == null) {
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index 61dd675..8243b7c 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -18,9 +18,13 @@
import android.graphics.*;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.ViewDebug;
+
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -36,7 +40,7 @@
*/
public class ColorDrawable extends Drawable {
@ViewDebug.ExportedProperty(deepExport = true, prefix = "state_")
- private ColorState mState;
+ private ColorState mColorState;
private final Paint mPaint = new Paint();
private boolean mMutated;
@@ -44,7 +48,7 @@
* Creates a new black ColorDrawable.
*/
public ColorDrawable() {
- this(null);
+ mColorState = new ColorState();
}
/**
@@ -53,17 +57,14 @@
* @param color The color to draw.
*/
public ColorDrawable(int color) {
- this(null);
- setColor(color);
- }
+ mColorState = new ColorState();
- private ColorDrawable(ColorState state) {
- mState = new ColorState(state);
+ setColor(color);
}
@Override
public int getChangingConfigurations() {
- return super.getChangingConfigurations() | mState.mChangingConfigurations;
+ return super.getChangingConfigurations() | mColorState.mChangingConfigurations;
}
/**
@@ -75,7 +76,7 @@
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
- mState = new ColorState(mState);
+ mColorState = new ColorState(mColorState);
mMutated = true;
}
return this;
@@ -83,8 +84,8 @@
@Override
public void draw(Canvas canvas) {
- if ((mState.mUseColor >>> 24) != 0) {
- mPaint.setColor(mState.mUseColor);
+ if ((mColorState.mUseColor >>> 24) != 0) {
+ mPaint.setColor(mColorState.mUseColor);
canvas.drawRect(getBounds(), mPaint);
}
}
@@ -95,19 +96,20 @@
* @return int The color to draw.
*/
public int getColor() {
- return mState.mUseColor;
+ return mColorState.mUseColor;
}
/**
- * Sets the drawable's color value. This action will clobber the results of prior calls to
- * {@link #setAlpha(int)} on this object, which side-affected the underlying color.
+ * Sets the drawable's color value. This action will clobber the results of
+ * prior calls to {@link #setAlpha(int)} on this object, which side-affected
+ * the underlying color.
*
* @param color The color to draw.
*/
public void setColor(int color) {
- if (mState.mBaseColor != color || mState.mUseColor != color) {
+ if (mColorState.mBaseColor != color || mColorState.mUseColor != color) {
+ mColorState.mBaseColor = mColorState.mUseColor = color;
invalidateSelf();
- mState.mBaseColor = mState.mUseColor = color;
}
}
@@ -118,7 +120,7 @@
*/
@Override
public int getAlpha() {
- return mState.mUseColor >>> 24;
+ return mColorState.mUseColor >>> 24;
}
/**
@@ -126,13 +128,14 @@
*
* @param alpha The alpha value to set, between 0 and 255.
*/
+ @Override
public void setAlpha(int alpha) {
alpha += alpha >> 7; // make it 0..256
- int baseAlpha = mState.mBaseColor >>> 24;
- int useAlpha = baseAlpha * alpha >> 8;
- int oldUseColor = mState.mUseColor;
- mState.mUseColor = (mState.mBaseColor << 8 >>> 8) | (useAlpha << 24);
- if (oldUseColor != mState.mUseColor) {
+ final int baseAlpha = mColorState.mBaseColor >>> 24;
+ final int useAlpha = baseAlpha * alpha >> 8;
+ final int useColor = (mColorState.mBaseColor << 8 >>> 8) | (useAlpha << 24);
+ if (mColorState.mUseColor != useColor) {
+ mColorState.mUseColor = useColor;
invalidateSelf();
}
}
@@ -142,11 +145,13 @@
*
* @param colorFilter Ignore.
*/
+ @Override
public void setColorFilter(ColorFilter colorFilter) {
}
+ @Override
public int getOpacity() {
- switch (mState.mUseColor >>> 24) {
+ switch (mColorState.mUseColor >>> 24) {
case 255:
return PixelFormat.OPAQUE;
case 0:
@@ -156,23 +161,67 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
- super.inflate(r, parser, attrs);
+ super.inflate(r, parser, attrs, theme);
- TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ColorDrawable);
-
- int color = mState.mBaseColor;
- color = a.getColor(com.android.internal.R.styleable.ColorDrawable_color, color);
- mState.mBaseColor = mState.mUseColor = color;
-
+ final TypedArray a = obtainAttributes(
+ r, theme, attrs, R.styleable.ColorDrawable);
+ inflateStateFromTypedArray(a);
a.recycle();
}
+ /**
+ * Initializes the constant state from the values in the typed array.
+ */
+ private void inflateStateFromTypedArray(TypedArray a) {
+ final ColorState state = mColorState;
+
+ // Extract the theme attributes, if any.
+ final int[] themeAttrs = a.extractThemeAttrs();
+ state.mThemeAttrs = themeAttrs;
+
+ if (themeAttrs == null || themeAttrs[R.styleable.ColorDrawable_color] == 0) {
+ final int color = a.getColor(R.styleable.ColorDrawable_color, 0);
+ state.mBaseColor = color;
+ state.mUseColor = color;
+ }
+ }
+
+ @Override
+ public void applyTheme(Theme t) {
+ super.applyTheme(t);
+
+ final ColorState state = mColorState;
+ if (state == null) {
+ throw new RuntimeException("Can't apply theme to <color> with no constant state");
+ }
+
+ final int[] themeAttrs = state.mThemeAttrs;
+ if (themeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.ColorDrawable, 0, 0);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+ }
+
+ /**
+ * Updates the constant state from the values in the typed array.
+ */
+ private void updateStateFromTypedArray(TypedArray a) {
+ final ColorState state = mColorState;
+
+ if (a.hasValue(R.styleable.ColorDrawable_color)) {
+ final int color = a.getColor(R.styleable.ColorDrawable_color, 0);
+ state.mBaseColor = color;
+ state.mUseColor = color;
+ }
+ }
+
@Override
public ConstantState getConstantState() {
- mState.mChangingConfigurations = getChangingConfigurations();
- return mState;
+ mColorState.mChangingConfigurations = getChangingConfigurations();
+ return mColorState;
}
final static class ColorState extends ConstantState {
@@ -180,23 +229,37 @@
@ViewDebug.ExportedProperty
int mUseColor; // basecolor modulated by setAlpha()
int mChangingConfigurations;
+ int[] mThemeAttrs;
+
+ ColorState() {
+ // Empty constructor.
+ }
ColorState(ColorState state) {
- if (state != null) {
- mBaseColor = state.mBaseColor;
- mUseColor = state.mUseColor;
- mChangingConfigurations = state.mChangingConfigurations;
- }
+ mBaseColor = state.mBaseColor;
+ mUseColor = state.mUseColor;
+ mChangingConfigurations = state.mChangingConfigurations;
+ mThemeAttrs = state.mThemeAttrs;
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null;
}
@Override
public Drawable newDrawable() {
- return new ColorDrawable(this);
+ return new ColorDrawable(this, null, null);
}
@Override
public Drawable newDrawable(Resources res) {
- return new ColorDrawable(this);
+ return new ColorDrawable(this, res, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return new ColorDrawable(this, res, theme);
}
@Override
@@ -204,4 +267,15 @@
return mChangingConfigurations;
}
}
+
+ private ColorDrawable(ColorState state, Resources res, Theme theme) {
+ if (theme != null && state.canApplyTheme()) {
+ mColorState = new ColorState(state);
+ applyTheme(theme);
+ } else {
+ mColorState = state;
+ }
+
+ // No local properties to initialize.
+ }
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index eb6b536..de2b68f 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -24,6 +24,7 @@
import org.xmlpull.v1.XmlPullParserException;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -697,6 +698,16 @@
}
/**
+ * Applies the specified theme to this Drawable and its children.
+ */
+ public void applyTheme(@SuppressWarnings("unused") Theme t) {
+ }
+
+ public boolean canApplyTheme() {
+ return false;
+ }
+
+ /**
* Return the opacity/transparency of this Drawable. The returned value is
* one of the abstract format constants in
* {@link android.graphics.PixelFormat}:
@@ -900,9 +911,13 @@
* Create a drawable from an inputstream
*/
public static Drawable createFromStream(InputStream is, String srcName) {
+ return createFromStreamThemed(is, srcName, null);
+ }
+
+ public static Drawable createFromStreamThemed(InputStream is, String srcName, Theme theme) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
try {
- return createFromResourceStream(null, null, is, srcName, null);
+ return createFromResourceStreamThemed(null, null, is, srcName, theme);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
@@ -914,9 +929,14 @@
*/
public static Drawable createFromResourceStream(Resources res, TypedValue value,
InputStream is, String srcName) {
+ return createFromResourceStreamThemed(res, value, is, srcName, null);
+ }
+
+ public static Drawable createFromResourceStreamThemed(Resources res, TypedValue value,
+ InputStream is, String srcName, Theme theme) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
try {
- return createFromResourceStream(res, value, is, srcName, null);
+ return createFromResourceStreamThemed(res, value, is, srcName, null, theme);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
@@ -928,7 +948,11 @@
*/
public static Drawable createFromResourceStream(Resources res, TypedValue value,
InputStream is, String srcName, BitmapFactory.Options opts) {
+ return createFromResourceStreamThemed(res, value, is, srcName, opts, null);
+ }
+ public static Drawable createFromResourceStreamThemed(Resources res, TypedValue value,
+ InputStream is, String srcName, BitmapFactory.Options opts, Theme theme) {
if (is == null) {
return null;
}
@@ -976,6 +1000,16 @@
*/
public static Drawable createFromXml(Resources r, XmlPullParser parser)
throws XmlPullParserException, IOException {
+ return createFromXmlThemed(r, parser, null);
+ }
+
+ /**
+ * Create a themed drawable from an XML document. For more information on
+ * how to create resources in XML, see
+ * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
+ */
+ public static Drawable createFromXmlThemed(Resources r, XmlPullParser parser, Theme theme)
+ throws XmlPullParserException, IOException {
AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
@@ -988,7 +1022,7 @@
throw new XmlPullParserException("No start tag found");
}
- Drawable drawable = createFromXmlInner(r, parser, attrs);
+ Drawable drawable = createFromXmlInnerThemed(r, parser, attrs, theme);
if (drawable == null) {
throw new RuntimeException("Unknown initial tag: " + parser.getName());
@@ -1003,11 +1037,15 @@
* Returns null if the tag is not a valid drawable.
*/
public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)
- throws XmlPullParserException, IOException {
- Drawable drawable;
+ throws XmlPullParserException, IOException {
+ return createFromXmlInnerThemed(r, parser, attrs, null);
+ }
+
+ public static Drawable createFromXmlInnerThemed(Resources r, XmlPullParser parser,
+ AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException {
+ final Drawable drawable;
final String name = parser.getName();
-
if (name.equals("selector")) {
drawable = new StateListDrawable();
} else if (name.equals("level-list")) {
@@ -1052,7 +1090,7 @@
": invalid drawable tag " + name);
}
- drawable.inflate(r, parser, attrs);
+ drawable.inflate(r, parser, attrs, theme);
return drawable;
}
@@ -1079,12 +1117,35 @@
}
/**
- * Inflate this Drawable from an XML resource.
+ * Inflate this Drawable from an XML resource. Does not apply a theme.
+ *
+ * @see #inflate(Resources, XmlPullParser, AttributeSet, Theme)
*/
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
throws XmlPullParserException, IOException {
+ inflate(r, parser, attrs, null);
+ }
- TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.Drawable);
+ /**
+ * Inflate this Drawable from an XML resource optionally styled by a theme.
+ *
+ * @param r Resources used to resolve attribute values
+ * @param parser XML parser from which to inflate this Drawable
+ * @param attrs Base set of attribute values
+ * @param theme Theme to apply, may be null
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
+ final TypedArray a;
+ if (theme != null) {
+ a = theme.obtainStyledAttributes(
+ attrs, com.android.internal.R.styleable.Drawable, 0, 0);
+ } else {
+ a = r.obtainAttributes(attrs, com.android.internal.R.styleable.Drawable);
+ }
+
inflateWithAttributes(r, parser, a, com.android.internal.R.styleable.Drawable_visible);
a.recycle();
}
@@ -1095,10 +1156,8 @@
* @throws XmlPullParserException
* @throws IOException
*/
- void inflateWithAttributes(Resources r, XmlPullParser parser,
- TypedArray attrs, int visibleAttr)
+ void inflateWithAttributes(Resources r, XmlPullParser parser, TypedArray attrs, int visibleAttr)
throws XmlPullParserException, IOException {
-
mVisible = attrs.getBoolean(visibleAttr, mVisible);
}
@@ -1125,6 +1184,7 @@
* instead to provide a resource.
*/
public abstract Drawable newDrawable();
+
/**
* Create a new Drawable instance from its constant state. This
* must be implemented for drawables that change based on the target
@@ -1134,6 +1194,15 @@
public Drawable newDrawable(Resources res) {
return newDrawable();
}
+
+ /**
+ * Create a new Drawable instance from its constant state. This must be
+ * implemented for drawables that can have a theme applied.
+ */
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return newDrawable();
+ }
+
/**
* Return a bit mask of configuration changes that will impact
* this drawable (and thus require completely reloading it).
@@ -1146,6 +1215,13 @@
public Bitmap getBitmap() {
return null;
}
+
+ /**
+ * Return whether this constant state can have a theme applied.
+ */
+ public boolean canApplyTheme() {
+ return false;
+ }
}
/**
@@ -1170,6 +1246,18 @@
}
/**
+ * Obtains styled attributes from the theme, if available, or unstyled
+ * resources if the theme is null.
+ */
+ 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);
+ }
+
+ /**
* Parses a {@link android.graphics.PorterDuff.Mode} from a tintMode
* attribute's enum value.
*/
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 6144e69..05df3bc 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -17,6 +17,7 @@
package android.graphics.drawable;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Insets;
@@ -456,6 +457,16 @@
}
@Override
+ public void applyTheme(Theme theme) {
+ mDrawableContainerState.applyTheme(theme);
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mDrawableContainerState.canApplyTheme();
+ }
+
+ @Override
public ConstantState getConstantState() {
if (mDrawableContainerState.canConstantState()) {
mDrawableContainerState.mChangingConfigurations = getChangingConfigurations();
@@ -483,6 +494,8 @@
final DrawableContainer mOwner;
final Resources mRes;
+ Theme mTheme;
+
SparseArray<ConstantStateFuture> mDrawableFutures;
int mChangingConfigurations;
@@ -680,6 +693,41 @@
mLayoutDirection = layoutDirection;
}
+ final void applyTheme(Theme theme) {
+ // No need to call createAllFutures, since future drawables will
+ // apply the theme when they are prepared.
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
+ for (int i = 0; i < N; i++) {
+ if (drawables[i] != null) {
+ drawables[i].applyTheme(theme);
+ }
+ }
+
+ mTheme = theme;
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
+ for (int i = 0; i < N; i++) {
+ final Drawable d = drawables[i];
+ if (d != null) {
+ if (d.canApplyTheme()) {
+ return true;
+ }
+ } else {
+ final ConstantStateFuture future = mDrawableFutures.get(i);
+ if (future != null && future.canApplyTheme()) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
final void mutate() {
// No need to call createAllFutures, since future drawables will
// mutate when they are prepared.
@@ -898,8 +946,14 @@
* @return a prepared Drawable
*/
public Drawable get(DrawableContainerState state) {
- final Drawable result = (state.mRes == null) ?
- mConstantState.newDrawable() : mConstantState.newDrawable(state.mRes);
+ final Drawable result;
+ if (state.mRes == null) {
+ result = mConstantState.newDrawable();
+ } else if (state.mTheme == null) {
+ result = mConstantState.newDrawable(state.mRes);
+ } else {
+ result = mConstantState.newDrawable(state.mRes, state.mTheme);
+ }
result.setLayoutDirection(state.mLayoutDirection);
result.setCallback(state.mOwner);
@@ -909,6 +963,14 @@
return result;
}
+
+ /**
+ * Whether the constant state wrapped by this future can apply a
+ * theme.
+ */
+ public boolean canApplyTheme() {
+ return mConstantState.canApplyTheme();
+ }
}
}
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 8f22add..1568e99 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -19,6 +19,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -33,11 +34,12 @@
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
-import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -123,6 +125,9 @@
/** Radius is a fraction of the bounds size. */
private static final int RADIUS_TYPE_FRACTION_PARENT = 2;
+ private static final float DEFAULT_INNER_RADIUS_RATIO = 3.0f;
+ private static final float DEFAULT_THICKNESS_RATIO = 9.0f;
+
private GradientState mGradientState;
private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -168,7 +173,7 @@
}
public GradientDrawable() {
- this(new GradientState(Orientation.TOP_BOTTOM, null));
+ this(new GradientState(Orientation.TOP_BOTTOM, null), null);
}
/**
@@ -176,7 +181,7 @@
* of colors for the gradient.
*/
public GradientDrawable(Orientation orientation, int[] colors) {
- this(new GradientState(orientation, colors));
+ this(new GradientState(orientation, colors), null);
}
@Override
@@ -857,7 +862,7 @@
float x0, x1, y0, y1;
if (st.mGradient == LINEAR_GRADIENT) {
- final float level = st.mUseLevel ? (float) getLevel() / 10000.0f : 1.0f;
+ final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
switch (st.mOrientation) {
case TOP_BOTTOM:
x0 = r.left; y0 = r.top;
@@ -937,12 +942,12 @@
tempColors[length] = colors[length - 1];
tempPositions = st.mTempPositions;
- final float fraction = 1.0f / (float) (length - 1);
+ final float fraction = 1.0f / (length - 1);
if (tempPositions == null || tempPositions.length != length + 1) {
tempPositions = st.mTempPositions = new float[length + 1];
}
- final float level = (float) getLevel() / 10000.0f;
+ final float level = getLevel() / 10000.0f;
for (int i = 0; i < length; i++) {
tempPositions[i] = i * fraction * level;
}
@@ -963,45 +968,196 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser,
- AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
-
- final GradientState st = mGradientState;
-
- TypedArray a = r.obtainAttributes(attrs,
- com.android.internal.R.styleable.GradientDrawable);
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.GradientDrawable);
+ super.inflateWithAttributes(r, parser, a, R.styleable.GradientDrawable_visible);
- super.inflateWithAttributes(r, parser, a,
- com.android.internal.R.styleable.GradientDrawable_visible);
-
- int shapeType = a.getInt(
- com.android.internal.R.styleable.GradientDrawable_shape, RECTANGLE);
- boolean dither = a.getBoolean(
- com.android.internal.R.styleable.GradientDrawable_dither, false);
-
- if (shapeType == RING) {
- st.mInnerRadius = a.getDimensionPixelSize(
- com.android.internal.R.styleable.GradientDrawable_innerRadius, -1);
- if (st.mInnerRadius == -1) {
- st.mInnerRadiusRatio = a.getFloat(
- com.android.internal.R.styleable.GradientDrawable_innerRadiusRatio, 3.0f);
- }
- st.mThickness = a.getDimensionPixelSize(
- com.android.internal.R.styleable.GradientDrawable_thickness, -1);
- if (st.mThickness == -1) {
- st.mThicknessRatio = a.getFloat(
- com.android.internal.R.styleable.GradientDrawable_thicknessRatio, 9.0f);
- }
- st.mUseLevelForShape = a.getBoolean(
- com.android.internal.R.styleable.GradientDrawable_useLevel, true);
- }
-
+ inflateStateFromTypedArray(a);
a.recycle();
-
- setShape(shapeType);
- setDither(dither);
+ inflateChildElements(r, parser, attrs, theme);
+
+ mGradientState.computeOpacity();
+ }
+
+ /**
+ * Initializes the constant state from the values in the typed array.
+ */
+ private void inflateStateFromTypedArray(TypedArray a) {
+ final GradientState state = mGradientState;
+
+ // Extract the theme attributes, if any.
+ final int[] themeAttrs = a.extractThemeAttrs();
+ state.mThemeAttrs = themeAttrs;
+
+ final boolean needsRingAttrs;
+ if (themeAttrs == null || themeAttrs[R.styleable.GradientDrawable_shape] == 0) {
+ final int shapeType = a.getInt(R.styleable.GradientDrawable_shape, RECTANGLE);
+ setShape(shapeType);
+ needsRingAttrs = shapeType == RING;
+ } else {
+ needsRingAttrs = true;
+ }
+
+ // We only need to load ring attributes if the shape type is a theme
+ // attribute (e.g. unknown) or defined in XML as RING.
+ if (needsRingAttrs) {
+ if (themeAttrs == null || themeAttrs[R.styleable.GradientDrawable_innerRadius] == 0) {
+ state.mInnerRadius = a.getDimensionPixelSize(
+ R.styleable.GradientDrawable_innerRadius, -1);
+ }
+
+ if (state.mInnerRadius == -1
+ && themeAttrs == null || themeAttrs[R.styleable.GradientDrawable_thicknessRatio] == 0) {
+ state.mInnerRadiusRatio = a.getFloat(
+ R.styleable.GradientDrawable_innerRadiusRatio, DEFAULT_INNER_RADIUS_RATIO);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.GradientDrawable_thickness] == 0) {
+ state.mThickness = a.getDimensionPixelSize(
+ R.styleable.GradientDrawable_thickness, -1);
+ }
+
+ if (state.mThickness == -1
+ && themeAttrs == null || themeAttrs[R.styleable.GradientDrawable_thicknessRatio] == 0) {
+ state.mThicknessRatio = a.getFloat(
+ R.styleable.GradientDrawable_thicknessRatio, DEFAULT_THICKNESS_RATIO);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.GradientDrawable_useLevel] == 0) {
+ state.mUseLevelForShape = a.getBoolean(
+ R.styleable.GradientDrawable_useLevel, true);
+ }
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.GradientDrawable_dither] == 0) {
+ final boolean dither = a.getBoolean(R.styleable.GradientDrawable_dither, false);
+ setDither(dither);
+ }
+ }
+
+ @Override
+ public void applyTheme(Theme t) {
+ super.applyTheme(t);
+
+ final GradientState state = mGradientState;
+ if (state == null) {
+ throw new RuntimeException("Can't apply theme to <shape> with no constant state");
+ }
+
+ final int[] themeAttrs = state.mThemeAttrs;
+ if (themeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(
+ themeAttrs, R.styleable.GradientDrawable, 0, 0);
+ updateStateFromTypedArray(a);
+ a.recycle();
+
+ applyThemeChildElements(t);
+
+ mGradientState.computeOpacity();
+ }
+ }
+
+ /**
+ * Updates the constant state from the values in the typed array.
+ */
+ private void updateStateFromTypedArray(TypedArray a) {
+ final GradientState state = mGradientState;
+
+ if (a.hasValue(R.styleable.GradientDrawable_shape)) {
+ final int shapeType = a.getInt(R.styleable.GradientDrawable_shape, RECTANGLE);
+ setShape(shapeType);
+ }
+
+ if (a.hasValue(R.styleable.GradientDrawable_dither)) {
+ final boolean dither = a.getBoolean(R.styleable.GradientDrawable_dither, false);
+ setDither(dither);
+ }
+
+ if (state.mShape == RING) {
+ if (a.hasValue(R.styleable.GradientDrawable_innerRadius)) {
+ state.mInnerRadius = a.getDimensionPixelSize(
+ R.styleable.GradientDrawable_innerRadius, -1);
+ }
+
+ if (state.mInnerRadius == -1 && a.hasValue(
+ R.styleable.GradientDrawable_innerRadiusRatio)) {
+ state.mInnerRadiusRatio = a.getFloat(
+ R.styleable.GradientDrawable_innerRadiusRatio, DEFAULT_INNER_RADIUS_RATIO);
+ }
+
+ if (a.hasValue(R.styleable.GradientDrawable_thickness)) {
+ state.mThickness = a.getDimensionPixelSize(
+ R.styleable.GradientDrawable_thickness, -1);
+ }
+
+ if (state.mThickness == -1 && a.hasValue(
+ R.styleable.GradientDrawable_thicknessRatio)) {
+ state.mThicknessRatio = a.getFloat(
+ R.styleable.GradientDrawable_thicknessRatio, DEFAULT_THICKNESS_RATIO);
+ }
+
+ if (a.hasValue(R.styleable.GradientDrawable_useLevel)) {
+ state.mUseLevelForShape = a.getBoolean(
+ R.styleable.GradientDrawable_useLevel, true);
+ }
+ }
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ final GradientState state = mGradientState;
+ return state != null && (state.mThemeAttrs != null || state.mAttrSize != null
+ || state.mAttrGradient != null || state.mAttrSolid != null
+ || state.mAttrStroke != null || state.mAttrCorners != null
+ || state.mAttrPadding != null);
+ }
+
+ private void applyThemeChildElements(Theme t) {
+ final GradientState state = mGradientState;
+ TypedArray a;
+
+ if (state.mAttrSize != null) {
+ a = t.resolveAttributes(state.mAttrSize, R.styleable.GradientDrawableSize, 0, 0);
+ // TODO: updateGradientDrawableSize(a);
+ a.recycle();
+ }
+
+ if (state.mAttrGradient != null) {
+ a = t.resolveAttributes(state.mAttrGradient, R.styleable.GradientDrawableGradient, 0, 0);
+ // TODO: updateGradientDrawableGradient(a);
+ a.recycle();
+ }
+
+ if (state.mAttrSolid != null) {
+ a = t.resolveAttributes(state.mAttrSolid, R.styleable.GradientDrawableSolid, 0, 0);
+ // TODO: updateGradientDrawableSolid(a);
+ a.recycle();
+ }
+
+ if (state.mAttrStroke != null) {
+ a = t.resolveAttributes(state.mAttrStroke, R.styleable.GradientDrawableStroke, 0, 0);
+ // TODO: updateGradientDrawableStroke(a);
+ a.recycle();
+ }
+
+ if (state.mAttrCorners != null) {
+ a = t.resolveAttributes(state.mAttrCorners, R.styleable.DrawableCorners, 0, 0);
+ // TODO: updateDrawableCorners(a);
+ a.recycle();
+ }
+
+ if (state.mAttrPadding != null) {
+ a = t.resolveAttributes(state.mAttrPadding, R.styleable.GradientDrawablePadding, 0, 0);
+ // TODO: updateGradientDrawablePadding(a);
+ a.recycle();
+ }
+ }
+
+ private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
+ Theme theme) throws XmlPullParserException, IOException {
+ TypedArray a;
int type;
final int innerDepth = parser.getDepth() + 1;
@@ -1016,203 +1172,233 @@
if (depth > innerDepth) {
continue;
}
-
+
String name = parser.getName();
if (name.equals("size")) {
- a = r.obtainAttributes(attrs,
- com.android.internal.R.styleable.GradientDrawableSize);
- int width = a.getDimensionPixelSize(
- com.android.internal.R.styleable.GradientDrawableSize_width, -1);
- int height = a.getDimensionPixelSize(
- com.android.internal.R.styleable.GradientDrawableSize_height, -1);
+ a = obtainAttributes(
+ r, theme, attrs, R.styleable.GradientDrawableSize);
+ applyGradientDrawableSize(a);
a.recycle();
- setSize(width, height);
} else if (name.equals("gradient")) {
- a = r.obtainAttributes(attrs,
- com.android.internal.R.styleable.GradientDrawableGradient);
- int startColor = a.getColor(
- com.android.internal.R.styleable.GradientDrawableGradient_startColor, 0);
- boolean hasCenterColor = a
- .hasValue(com.android.internal.R.styleable.GradientDrawableGradient_centerColor);
- int centerColor = a.getColor(
- com.android.internal.R.styleable.GradientDrawableGradient_centerColor, 0);
- int endColor = a.getColor(
- com.android.internal.R.styleable.GradientDrawableGradient_endColor, 0);
- int gradientType = a.getInt(
- com.android.internal.R.styleable.GradientDrawableGradient_type,
- LINEAR_GRADIENT);
-
- st.mCenterX = getFloatOrFraction(
- a,
- com.android.internal.R.styleable.GradientDrawableGradient_centerX,
- 0.5f);
-
- st.mCenterY = getFloatOrFraction(
- a,
- com.android.internal.R.styleable.GradientDrawableGradient_centerY,
- 0.5f);
-
- st.mUseLevel = a.getBoolean(
- com.android.internal.R.styleable.GradientDrawableGradient_useLevel, false);
- st.mGradient = gradientType;
-
- if (gradientType == LINEAR_GRADIENT) {
- int angle = (int)a.getFloat(
- com.android.internal.R.styleable.GradientDrawableGradient_angle, 0);
- angle %= 360;
- if (angle % 45 != 0) {
- throw new XmlPullParserException(a.getPositionDescription()
- + "<gradient> tag requires 'angle' attribute to "
- + "be a multiple of 45");
- }
-
- switch (angle) {
- case 0:
- st.mOrientation = Orientation.LEFT_RIGHT;
- break;
- case 45:
- st.mOrientation = Orientation.BL_TR;
- break;
- case 90:
- st.mOrientation = Orientation.BOTTOM_TOP;
- break;
- case 135:
- st.mOrientation = Orientation.BR_TL;
- break;
- case 180:
- st.mOrientation = Orientation.RIGHT_LEFT;
- break;
- case 225:
- st.mOrientation = Orientation.TR_BL;
- break;
- case 270:
- st.mOrientation = Orientation.TOP_BOTTOM;
- break;
- case 315:
- st.mOrientation = Orientation.TL_BR;
- break;
- }
- } else {
- final TypedValue tv = a.peekValue(
- com.android.internal.R.styleable.GradientDrawableGradient_gradientRadius);
- if (tv != null) {
- final float radius;
- final int radiusType;
- if (tv.type == TypedValue.TYPE_FRACTION) {
- radius = tv.getFraction(1.0f, 1.0f);
-
- final int unit = (tv.data >> TypedValue.COMPLEX_UNIT_SHIFT)
- & TypedValue.COMPLEX_UNIT_MASK;
- if (unit == TypedValue.COMPLEX_UNIT_FRACTION_PARENT) {
- radiusType = RADIUS_TYPE_FRACTION_PARENT;
- } else {
- radiusType = RADIUS_TYPE_FRACTION;
- }
- } else {
- radius = tv.getDimension(r.getDisplayMetrics());
- radiusType = RADIUS_TYPE_PIXELS;
- }
-
- st.mGradientRadius = radius;
- st.mGradientRadiusType = radiusType;
- } else if (gradientType == RADIAL_GRADIENT) {
- throw new XmlPullParserException(
- a.getPositionDescription()
- + "<gradient> tag requires 'gradientRadius' "
- + "attribute with radial type");
- }
- }
-
+ a = obtainAttributes(
+ r, theme, attrs, R.styleable.GradientDrawableGradient);
+ applyGradientDrawableGradient(r, a);
a.recycle();
-
- if (hasCenterColor) {
- st.mColors = new int[3];
- st.mColors[0] = startColor;
- st.mColors[1] = centerColor;
- st.mColors[2] = endColor;
-
- st.mPositions = new float[3];
- st.mPositions[0] = 0.0f;
- // Since 0.5f is default value, try to take the one that isn't 0.5f
- st.mPositions[1] = st.mCenterX != 0.5f ? st.mCenterX : st.mCenterY;
- st.mPositions[2] = 1f;
- } else {
- st.mColors = new int[2];
- st.mColors[0] = startColor;
- st.mColors[1] = endColor;
- }
-
} else if (name.equals("solid")) {
- a = r.obtainAttributes(attrs,
- com.android.internal.R.styleable.GradientDrawableSolid);
- final ColorStateList colorStateList = a.getColorStateList(
- com.android.internal.R.styleable.GradientDrawableSolid_color);
+ a = obtainAttributes(
+ r, theme, attrs, R.styleable.GradientDrawableSolid);
+ applyGradientDrawableSolid(a);
a.recycle();
- setColor(colorStateList);
} else if (name.equals("stroke")) {
- a = r.obtainAttributes(attrs,
- com.android.internal.R.styleable.GradientDrawableStroke);
- final int width = a.getDimensionPixelSize(
- com.android.internal.R.styleable.GradientDrawableStroke_width, 0);
- final ColorStateList colorStateList = a.getColorStateList(
- com.android.internal.R.styleable.GradientDrawableStroke_color);
- final float dashWidth = a.getDimension(
- com.android.internal.R.styleable.GradientDrawableStroke_dashWidth, 0);
- if (dashWidth != 0.0f) {
- final float dashGap = a.getDimension(
- com.android.internal.R.styleable.GradientDrawableStroke_dashGap, 0);
- setStroke(width, colorStateList, dashWidth, dashGap);
- } else {
- setStroke(width, colorStateList);
- }
+ a = obtainAttributes(
+ r, theme, attrs, R.styleable.GradientDrawableStroke);
+ applyGradientDrawableStroke(a);
a.recycle();
} else if (name.equals("corners")) {
- a = r.obtainAttributes(attrs,
- com.android.internal.R.styleable.DrawableCorners);
- int radius = a.getDimensionPixelSize(
- com.android.internal.R.styleable.DrawableCorners_radius, 0);
- setCornerRadius(radius);
- int topLeftRadius = a.getDimensionPixelSize(
- com.android.internal.R.styleable.DrawableCorners_topLeftRadius, radius);
- int topRightRadius = a.getDimensionPixelSize(
- com.android.internal.R.styleable.DrawableCorners_topRightRadius, radius);
- int bottomLeftRadius = a.getDimensionPixelSize(
- com.android.internal.R.styleable.DrawableCorners_bottomLeftRadius, radius);
- int bottomRightRadius = a.getDimensionPixelSize(
- com.android.internal.R.styleable.DrawableCorners_bottomRightRadius, radius);
- if (topLeftRadius != radius || topRightRadius != radius ||
- bottomLeftRadius != radius || bottomRightRadius != radius) {
- // The corner radii are specified in clockwise order (see Path.addRoundRect())
- setCornerRadii(new float[] {
- topLeftRadius, topLeftRadius,
- topRightRadius, topRightRadius,
- bottomRightRadius, bottomRightRadius,
- bottomLeftRadius, bottomLeftRadius
- });
- }
+ a = obtainAttributes(r
+ , theme, attrs, R.styleable.DrawableCorners);
+ applyDrawableCorners(a);
a.recycle();
} else if (name.equals("padding")) {
- a = r.obtainAttributes(attrs,
- com.android.internal.R.styleable.GradientDrawablePadding);
- mPadding = new Rect(
- a.getDimensionPixelOffset(
- com.android.internal.R.styleable.GradientDrawablePadding_left, 0),
- a.getDimensionPixelOffset(
- com.android.internal.R.styleable.GradientDrawablePadding_top, 0),
- a.getDimensionPixelOffset(
- com.android.internal.R.styleable.GradientDrawablePadding_right, 0),
- a.getDimensionPixelOffset(
- com.android.internal.R.styleable.GradientDrawablePadding_bottom, 0));
+ a = obtainAttributes(
+ r, theme, attrs, R.styleable.GradientDrawablePadding);
+ applyGradientDrawablePadding(a);
a.recycle();
- mGradientState.mPadding = mPadding;
} else {
Log.w("drawable", "Bad element under <shape>: " + name);
}
+ }
+ }
+ private void applyGradientDrawablePadding(TypedArray a) {
+ mPadding = new Rect(
+ a.getDimensionPixelOffset(
+ R.styleable.GradientDrawablePadding_left, 0),
+ a.getDimensionPixelOffset(
+ R.styleable.GradientDrawablePadding_top, 0),
+ a.getDimensionPixelOffset(
+ R.styleable.GradientDrawablePadding_right, 0),
+ a.getDimensionPixelOffset(
+ R.styleable.GradientDrawablePadding_bottom, 0));
+ mGradientState.mPadding = mPadding;
+
+ // Extract the theme attributes, if any.
+ mGradientState.mAttrPadding = a.extractThemeAttrs();
+ }
+
+ private void applyDrawableCorners(TypedArray a) {
+ int radius = a.getDimensionPixelSize(
+ R.styleable.DrawableCorners_radius, 0);
+ setCornerRadius(radius);
+ int topLeftRadius = a.getDimensionPixelSize(
+ R.styleable.DrawableCorners_topLeftRadius, radius);
+ int topRightRadius = a.getDimensionPixelSize(
+ R.styleable.DrawableCorners_topRightRadius, radius);
+ int bottomLeftRadius = a.getDimensionPixelSize(
+ R.styleable.DrawableCorners_bottomLeftRadius, radius);
+ int bottomRightRadius = a.getDimensionPixelSize(
+ R.styleable.DrawableCorners_bottomRightRadius, radius);
+ if (topLeftRadius != radius || topRightRadius != radius ||
+ bottomLeftRadius != radius || bottomRightRadius != radius) {
+ // The corner radii are specified in clockwise order (see Path.addRoundRect())
+ setCornerRadii(new float[] {
+ topLeftRadius, topLeftRadius,
+ topRightRadius, topRightRadius,
+ bottomRightRadius, bottomRightRadius,
+ bottomLeftRadius, bottomLeftRadius
+ });
}
- mGradientState.computeOpacity();
+ // Extract the theme attributes, if any.
+ mGradientState.mAttrCorners = a.extractThemeAttrs();
+ }
+
+ private void applyGradientDrawableStroke(TypedArray a) {
+ final int width = a.getDimensionPixelSize(
+ R.styleable.GradientDrawableStroke_width, 0);
+ final ColorStateList colorStateList = a.getColorStateList(
+ R.styleable.GradientDrawableStroke_color);
+ final float dashWidth = a.getDimension(
+ R.styleable.GradientDrawableStroke_dashWidth, 0);
+ if (dashWidth != 0.0f) {
+ final float dashGap = a.getDimension(
+ R.styleable.GradientDrawableStroke_dashGap, 0);
+ setStroke(width, colorStateList, dashWidth, dashGap);
+ } else {
+ setStroke(width, colorStateList);
+ }
+
+ // Extract the theme attributes, if any.
+ mGradientState.mAttrStroke = a.extractThemeAttrs();
+ }
+
+ private void applyGradientDrawableSolid(TypedArray a) {
+ final ColorStateList colorStateList = a.getColorStateList(
+ R.styleable.GradientDrawableSolid_color);
+ setColor(colorStateList);
+
+ // Extract the theme attributes, if any.
+ mGradientState.mAttrSolid = a.extractThemeAttrs();
+ }
+
+ private void applyGradientDrawableGradient(Resources r, TypedArray a)
+ throws XmlPullParserException {
+ final GradientState st = mGradientState;
+ final int startColor = a.getColor(
+ R.styleable.GradientDrawableGradient_startColor, 0);
+ final boolean hasCenterColor = a.hasValue(
+ R.styleable.GradientDrawableGradient_centerColor);
+ final int centerColor = a.getColor(
+ R.styleable.GradientDrawableGradient_centerColor, 0);
+ final int endColor = a.getColor(
+ R.styleable.GradientDrawableGradient_endColor, 0);
+
+ if (hasCenterColor) {
+ st.mColors = new int[3];
+ st.mColors[0] = startColor;
+ st.mColors[1] = centerColor;
+ st.mColors[2] = endColor;
+
+ st.mPositions = new float[3];
+ st.mPositions[0] = 0.0f;
+ // Since 0.5f is default value, try to take the one that isn't 0.5f
+ st.mPositions[1] = st.mCenterX != 0.5f ? st.mCenterX : st.mCenterY;
+ st.mPositions[2] = 1f;
+ } else {
+ st.mColors = new int[2];
+ st.mColors[0] = startColor;
+ st.mColors[1] = endColor;
+ }
+
+ st.mCenterX = getFloatOrFraction(
+ a, R.styleable.GradientDrawableGradient_centerX, 0.5f);
+ st.mCenterY = getFloatOrFraction(
+ a, R.styleable.GradientDrawableGradient_centerY, 0.5f);
+ st.mUseLevel = a.getBoolean(
+ R.styleable.GradientDrawableGradient_useLevel, false);
+ st.mGradient = a.getInt(
+ R.styleable.GradientDrawableGradient_type, LINEAR_GRADIENT);
+
+ if (st.mGradient == LINEAR_GRADIENT) {
+ int angle = (int) a.getFloat(
+ R.styleable.GradientDrawableGradient_angle, 0);
+ angle %= 360;
+
+ if (angle % 45 != 0) {
+ throw new XmlPullParserException(a.getPositionDescription()
+ + "<gradient> tag requires 'angle' attribute to "
+ + "be a multiple of 45");
+ }
+
+ switch (angle) {
+ case 0:
+ st.mOrientation = Orientation.LEFT_RIGHT;
+ break;
+ case 45:
+ st.mOrientation = Orientation.BL_TR;
+ break;
+ case 90:
+ st.mOrientation = Orientation.BOTTOM_TOP;
+ break;
+ case 135:
+ st.mOrientation = Orientation.BR_TL;
+ break;
+ case 180:
+ st.mOrientation = Orientation.RIGHT_LEFT;
+ break;
+ case 225:
+ st.mOrientation = Orientation.TR_BL;
+ break;
+ case 270:
+ st.mOrientation = Orientation.TOP_BOTTOM;
+ break;
+ case 315:
+ st.mOrientation = Orientation.TL_BR;
+ break;
+ }
+ } else {
+ final TypedValue tv = a.peekValue(
+ R.styleable.GradientDrawableGradient_gradientRadius);
+ if (tv != null) {
+ final float radius;
+ final int radiusType;
+ if (tv.type == TypedValue.TYPE_FRACTION) {
+ radius = tv.getFraction(1.0f, 1.0f);
+
+ final int unit = (tv.data >> TypedValue.COMPLEX_UNIT_SHIFT)
+ & TypedValue.COMPLEX_UNIT_MASK;
+ if (unit == TypedValue.COMPLEX_UNIT_FRACTION_PARENT) {
+ radiusType = RADIUS_TYPE_FRACTION_PARENT;
+ } else {
+ radiusType = RADIUS_TYPE_FRACTION;
+ }
+ } else {
+ radius = tv.getDimension(r.getDisplayMetrics());
+ radiusType = RADIUS_TYPE_PIXELS;
+ }
+
+ st.mGradientRadius = radius;
+ st.mGradientRadiusType = radiusType;
+ } else if (st.mGradient == RADIAL_GRADIENT) {
+ throw new XmlPullParserException(
+ a.getPositionDescription()
+ + "<gradient> tag requires 'gradientRadius' "
+ + "attribute with radial type");
+ }
+ }
+
+ // Extract the theme attributes, if any.
+ mGradientState.mAttrGradient = a.extractThemeAttrs();
+ }
+
+ private void applyGradientDrawableSize(TypedArray a) {
+ int width = a.getDimensionPixelSize(R.styleable.GradientDrawableSize_width, -1);
+ int height = a.getDimensionPixelSize(R.styleable.GradientDrawableSize_height, -1);
+ setSize(width, height);
+
+ // Extract the theme attributes, if any.
+ mGradientState.mAttrSize = a.extractThemeAttrs();
}
private static float getFloatOrFraction(TypedArray a, int index, float defaultValue) {
@@ -1322,6 +1508,14 @@
private boolean mUseLevelForShape;
private boolean mOpaque;
+ int[] mThemeAttrs;
+ int[] mAttrSize;
+ int[] mAttrGradient;
+ int[] mAttrSolid;
+ int[] mAttrStroke;
+ int[] mAttrCorners;
+ int[] mAttrPadding;
+
GradientState(Orientation orientation, int[] colors) {
mOrientation = orientation;
setColors(colors);
@@ -1363,16 +1557,33 @@
mUseLevel = state.mUseLevel;
mUseLevelForShape = state.mUseLevelForShape;
mOpaque = state.mOpaque;
+ mThemeAttrs = state.mThemeAttrs;
+ mAttrSize = state.mAttrSize;
+ mAttrGradient = state.mAttrGradient;
+ mAttrSolid = state.mAttrSolid;
+ mAttrStroke = state.mAttrStroke;
+ mAttrCorners = state.mAttrCorners;
+ mAttrPadding = state.mAttrPadding;
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null;
}
@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, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return new GradientDrawable(this, theme);
}
@Override
@@ -1482,8 +1693,20 @@
}
}
- private GradientDrawable(GradientState state) {
- mGradientState = state;
+ /**
+ * Creates a new themed GradientDrawable based on the specified constant state.
+ * <p>
+ * The resulting drawable is guaranteed to have a new constant state.
+ *
+ * @param state Constant state from which the drawable inherits
+ * @param theme Theme to apply to the drawable
+ */
+ private GradientDrawable(GradientState state, Theme theme) {
+ mGradientState = new GradientState(state);
+ if (theme != null && state.canApplyTheme()) {
+ applyTheme(theme);
+ }
+
initializeWithState(state);
mRectIsDirty = true;
mMutated = false;
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index f841d6a..59a0c93 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -21,6 +21,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.graphics.*;
import android.util.AttributeSet;
import android.util.Log;
@@ -72,10 +73,10 @@
drawable.setCallback(this);
}
}
-
- @Override public void inflate(Resources r, XmlPullParser parser,
- AttributeSet attrs)
- throws XmlPullParserException, IOException {
+
+ @Override
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
int type;
TypedArray a = r.obtainAttributes(attrs,
@@ -110,7 +111,7 @@
+ ": <inset> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
- dr = Drawable.createFromXmlInner(r, parser, attrs);
+ dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
}
if (dr == null) {
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 2e098a0..3d48cda 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -16,10 +16,8 @@
package android.graphics.drawable;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
@@ -28,6 +26,11 @@
import android.util.AttributeSet;
import android.view.View;
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
/**
@@ -84,7 +87,7 @@
* @param state The constant drawable state.
*/
LayerDrawable(Drawable[] layers, LayerState state) {
- this(state, null);
+ this(state, null, null);
int length = layers.length;
ChildDrawable[] r = new ChildDrawable[length];
@@ -101,15 +104,18 @@
}
LayerDrawable() {
- this((LayerState) null, null);
+ this((LayerState) null, null, null);
}
- LayerDrawable(LayerState state, Resources res) {
- LayerState as = createConstantState(state, res);
+ LayerDrawable(LayerState state, Resources res, Theme theme) {
+ final LayerState as = createConstantState(state, res);
mLayerState = as;
if (as.mNum > 0) {
ensurePadding();
}
+ if (theme != null && canApplyTheme()) {
+ applyTheme(theme);
+ }
}
LayerState createConstantState(LayerState state, Resources res) {
@@ -117,26 +123,53 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
- super.inflate(r, parser, attrs);
+ super.inflate(r, parser, attrs, theme);
- int type;
-
- TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.LayerDrawable);
-
- mOpacityOverride = a.getInt(com.android.internal.R.styleable.LayerDrawable_opacity,
- PixelFormat.UNKNOWN);
-
- setAutoMirrored(a.getBoolean(com.android.internal.R.styleable.LayerDrawable_autoMirrored,
- false));
-
- mLayerState.mPaddingMode = a.getInteger(
- com.android.internal.R.styleable.LayerDrawableItem_drawable, PADDING_MODE_NEST);
-
+ final TypedArray a = obtainAttributes(
+ r, theme, attrs, R.styleable.LayerDrawable);
+ inflateStateFromTypedArray(a);
a.recycle();
+ inflateLayers(r, parser, attrs, theme);
+
+ ensurePadding();
+ onStateChange(getState());
+ }
+
+ /**
+ * Initializes the constant state from the values in the typed array.
+ */
+ private void inflateStateFromTypedArray(TypedArray a) {
+ final LayerState state = mLayerState;
+
+ // Extract the theme attributes, if any.
+ final int[] themeAttrs = a.extractThemeAttrs();
+ state.mThemeAttrs = themeAttrs;
+
+ if (themeAttrs == null || themeAttrs[R.styleable.LayerDrawable_opacity] == 0) {
+ mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, PixelFormat.UNKNOWN);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.LayerDrawable_autoMirrored] == 0) {
+ state.mAutoMirrored = a.getBoolean(R.styleable.LayerDrawable_autoMirrored, false);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.LayerDrawableItem_drawable] == 0) {
+ state.mPaddingMode = a.getInteger(
+ R.styleable.LayerDrawableItem_drawable, PADDING_MODE_NEST);
+ }
+ }
+
+ /**
+ * Inflates child layers using the specified parser.
+ */
+ private void inflateLayers(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
+ TypedArray a;
final int innerDepth = parser.getDepth() + 1;
+ int type;
int depth;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
@@ -148,27 +181,28 @@
continue;
}
- a = r.obtainAttributes(attrs,
- com.android.internal.R.styleable.LayerDrawableItem);
+ a = obtainAttributes(
+ r, theme, attrs, R.styleable.LayerDrawableItem);
- int left = a.getDimensionPixelOffset(
- com.android.internal.R.styleable.LayerDrawableItem_left, 0);
- int top = a.getDimensionPixelOffset(
- com.android.internal.R.styleable.LayerDrawableItem_top, 0);
- int right = a.getDimensionPixelOffset(
- com.android.internal.R.styleable.LayerDrawableItem_right, 0);
- int bottom = a.getDimensionPixelOffset(
- com.android.internal.R.styleable.LayerDrawableItem_bottom, 0);
- int drawableRes = a.getResourceId(
- com.android.internal.R.styleable.LayerDrawableItem_drawable, 0);
- int id = a.getResourceId(com.android.internal.R.styleable.LayerDrawableItem_id,
- View.NO_ID);
+ final int left = a.getDimensionPixelOffset(
+ R.styleable.LayerDrawableItem_left, 0);
+ final int top = a.getDimensionPixelOffset(
+ R.styleable.LayerDrawableItem_top, 0);
+ final int right = a.getDimensionPixelOffset(
+ R.styleable.LayerDrawableItem_right, 0);
+ final int bottom = a.getDimensionPixelOffset(
+ R.styleable.LayerDrawableItem_bottom, 0);
+ final int drawableRes = a.getResourceId(
+ R.styleable.LayerDrawableItem_drawable, 0);
+ final int id = a.getResourceId(
+ R.styleable.LayerDrawableItem_id, View.NO_ID);
+ // TODO: Cache typed array, if necessary.
a.recycle();
- Drawable dr;
+ final Drawable dr;
if (drawableRes != 0) {
- dr = r.getDrawable(drawableRes);
+ dr = r.getDrawable(drawableRes, theme);
} else {
while ((type = parser.next()) == XmlPullParser.TEXT) {
}
@@ -177,17 +211,87 @@
+ ": <item> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
- dr = Drawable.createFromXmlInner(r, parser, attrs);
+ dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
}
addLayer(dr, id, left, top, right, bottom);
}
+ }
+
+ @Override
+ public void applyTheme(Theme t) {
+ super.applyTheme(t);
+
+ final LayerState state = mLayerState;
+ if (state == null) {
+ throw new RuntimeException("Can't apply theme to <layer-list> with no constant state");
+ }
+
+ final int[] themeAttrs = state.mThemeAttrs;
+ if (themeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.LayerDrawable, 0, 0);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+
+ // TODO: Update layer positions from cached typed arrays.
+
+ final ChildDrawable[] array = mLayerState.mChildren;
+ final int N = mLayerState.mNum;
+ for (int i = 0; i < N; i++) {
+ final Drawable layer = array[i].mDrawable;
+ if (layer.canApplyTheme()) {
+ layer.applyTheme(t);
+ }
+ }
ensurePadding();
onStateChange(getState());
}
/**
+ * Updates the constant state from the values in the typed array.
+ */
+ private void updateStateFromTypedArray(TypedArray a) {
+ final LayerState state = mLayerState;
+
+ if (a.hasValue(R.styleable.LayerDrawable_opacity)) {
+ mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, PixelFormat.UNKNOWN);
+ }
+
+ if (a.hasValue(R.styleable.LayerDrawable_autoMirrored)) {
+ state.mAutoMirrored = a.getBoolean(R.styleable.LayerDrawable_autoMirrored, false);
+ }
+
+ if (a.hasValue(R.styleable.LayerDrawableItem_drawable)) {
+ state.mPaddingMode = a.getInteger(
+ R.styleable.LayerDrawableItem_drawable, PADDING_MODE_NEST);
+ }
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ final LayerState state = mLayerState;
+ if (state == null) {
+ return false;
+ }
+
+ if (state.mThemeAttrs != null) {
+ return true;
+ }
+
+ final ChildDrawable[] array = state.mChildren;
+ final int N = state.mNum;
+ for (int i = 0; i < N; i++) {
+ if (array[i].mDrawable.canApplyTheme()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Add a new layer to this drawable. The new layer is identified by an id.
*
* @param layer The drawable to add as a layer.
@@ -783,13 +887,35 @@
static class ChildDrawable {
public Drawable mDrawable;
+ public int[] mThemeAttrs;
public int mInsetL, mInsetT, mInsetR, mInsetB;
public int mId;
+
+ ChildDrawable() {
+ // Default empty constructor.
+ }
+
+ ChildDrawable(ChildDrawable or, LayerDrawable owner, Resources res) {
+ if (res != null) {
+ mDrawable = or.mDrawable.getConstantState().newDrawable(res);
+ } else {
+ mDrawable = or.mDrawable.getConstantState().newDrawable();
+ }
+ mDrawable.setCallback(owner);
+ mDrawable.setLayoutDirection(or.mDrawable.getLayoutDirection());
+ mThemeAttrs = or.mThemeAttrs;
+ mInsetL = or.mInsetL;
+ mInsetT = or.mInsetT;
+ mInsetR = or.mInsetR;
+ mInsetB = or.mInsetB;
+ mId = or.mId;
+ }
}
static class LayerState extends ConstantState {
int mNum;
ChildDrawable[] mChildren;
+ int[] mThemeAttrs;
int mChangingConfigurations;
int mChildrenChangingConfigurations;
@@ -819,20 +945,8 @@
mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
for (int i = 0; i < N; i++) {
- final ChildDrawable r = mChildren[i] = new ChildDrawable();
final ChildDrawable or = origChildDrawable[i];
- if (res != null) {
- r.mDrawable = or.mDrawable.getConstantState().newDrawable(res);
- } else {
- r.mDrawable = or.mDrawable.getConstantState().newDrawable();
- }
- r.mDrawable.setCallback(owner);
- r.mDrawable.setLayoutDirection(or.mDrawable.getLayoutDirection());
- r.mInsetL = or.mInsetL;
- r.mInsetT = or.mInsetT;
- r.mInsetR = or.mInsetR;
- r.mInsetB = or.mInsetB;
- r.mId = or.mId;
+ mChildren[i] = new ChildDrawable(or, owner, res);
}
mHaveOpacity = orig.mHaveOpacity;
@@ -842,6 +956,7 @@
mCheckedConstantState = mCanConstantState = true;
mAutoMirrored = orig.mAutoMirrored;
mPaddingMode = orig.mPaddingMode;
+ mThemeAttrs = orig.mThemeAttrs;
} else {
mNum = 0;
mChildren = null;
@@ -849,13 +964,23 @@
}
@Override
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null;
+ }
+
+ @Override
public Drawable newDrawable() {
- return new LayerDrawable(this, null);
+ return new LayerDrawable(this, null, null);
}
@Override
public Drawable newDrawable(Resources res) {
- return new LayerDrawable(this, res);
+ return new LayerDrawable(this, res, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return new LayerDrawable(this, res, theme);
}
@Override
diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java
index 872fdce..9f6c0ad 100644
--- a/graphics/java/android/graphics/drawable/LevelListDrawable.java
+++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java
@@ -23,6 +23,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.util.AttributeSet;
/**
@@ -83,10 +84,9 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
-
- super.inflate(r, parser, attrs);
+ super.inflate(r, parser, attrs, theme);
int type;
@@ -124,7 +124,7 @@
Drawable dr;
if (drawableRes != 0) {
- dr = r.getDrawable(drawableRes);
+ dr = r.getDrawable(drawableRes, theme);
} else {
while ((type = parser.next()) == XmlPullParser.TEXT) {
}
@@ -134,7 +134,7 @@
+ ": <item> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
- dr = Drawable.createFromXmlInner(r, parser, attrs);
+ dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
}
mLevelListState.addLevel(low, high, dr);
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 44584a7..66193a5 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -18,6 +18,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -36,6 +37,8 @@
import android.util.LayoutDirection;
import android.util.TypedValue;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -73,6 +76,7 @@
private int mBitmapHeight;
NinePatchDrawable() {
+ mNinePatchState = new NinePatchState();
}
/**
@@ -82,7 +86,7 @@
*/
@Deprecated
public NinePatchDrawable(Bitmap bitmap, byte[] chunk, Rect padding, String srcName) {
- this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), null);
+ this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), null, null);
}
/**
@@ -91,7 +95,7 @@
*/
public NinePatchDrawable(Resources res, Bitmap bitmap, byte[] chunk,
Rect padding, String srcName) {
- this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), res);
+ this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), res, null);
mNinePatchState.mTargetDensity = mTargetDensity;
}
@@ -103,7 +107,8 @@
*/
public NinePatchDrawable(Resources res, Bitmap bitmap, byte[] chunk,
Rect padding, Rect opticalInsets, String srcName) {
- this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding, opticalInsets), res);
+ this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding, opticalInsets),
+ res, null);
mNinePatchState.mTargetDensity = mTargetDensity;
}
@@ -114,7 +119,7 @@
*/
@Deprecated
public NinePatchDrawable(NinePatch patch) {
- this(new NinePatchState(patch, new Rect()), null);
+ this(new NinePatchState(patch, new Rect()), null, null);
}
/**
@@ -122,35 +127,10 @@
* based on the display metrics of the resources.
*/
public NinePatchDrawable(Resources res, NinePatch patch) {
- this(new NinePatchState(patch, new Rect()), res);
+ this(new NinePatchState(patch, new Rect()), res, null);
mNinePatchState.mTargetDensity = mTargetDensity;
}
- private void setNinePatchState(NinePatchState state, Resources res) {
- mNinePatchState = state;
- mNinePatch = state.mNinePatch;
- mPadding = state.mPadding;
- mTargetDensity = res != null ? res.getDisplayMetrics().densityDpi
- : state.mTargetDensity;
- //noinspection PointlessBooleanExpression
- if (state.mDither != DEFAULT_DITHER) {
- // avoid calling the setter unless we need to, since it does a
- // lazy allocation of a paint
- setDither(state.mDither);
- }
-
- if (state.mTint != null) {
- final int color = state.mTint.getColorForState(getState(), 0);
- mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
- }
-
- setAutoMirrored(state.mAutoMirrored);
-
- if (mNinePatch != null) {
- computeBitmapSize();
- }
- }
-
/**
* Set the density scale at which this drawable will be rendered. This
* method assumes the drawable will be rendered at the same density as the
@@ -228,6 +208,19 @@
}
}
+ private void setNinePatch(NinePatch ninePatch) {
+ if (ninePatch != mNinePatch) {
+ mNinePatch = ninePatch;
+ if (ninePatch != null) {
+ computeBitmapSize();
+ } else {
+ mBitmapWidth = mBitmapHeight = -1;
+ mOpticalInsets = Insets.NONE;
+ }
+ invalidateSelf();
+ }
+ }
+
@Override
public void draw(Canvas canvas) {
final Rect bounds = getBounds();
@@ -266,12 +259,17 @@
@Override
public boolean getPadding(Rect padding) {
- if (needsMirroring()) {
- padding.set(mPadding.right, mPadding.top, mPadding.left, mPadding.bottom);
- } else {
- padding.set(mPadding);
+ final Rect scaledPadding = mPadding;
+ if (scaledPadding != null) {
+ if (needsMirroring()) {
+ padding.set(scaledPadding.right, scaledPadding.top,
+ scaledPadding.left, scaledPadding.bottom);
+ } else {
+ padding.set(scaledPadding);
+ }
+ return (padding.left | padding.top | padding.right | padding.bottom) != 0;
}
- return (padding.left | padding.top | padding.right | padding.bottom) != 0;
+ return false;
}
/**
@@ -408,72 +406,197 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
- super.inflate(r, parser, attrs);
+ super.inflate(r, parser, attrs, theme);
- TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.NinePatchDrawable);
-
- final int id = a.getResourceId(com.android.internal.R.styleable.NinePatchDrawable_src, 0);
- if (id == 0) {
- throw new XmlPullParserException(parser.getPositionDescription() +
- ": <nine-patch> requires a valid src attribute");
- }
-
- final boolean dither = a.getBoolean(
- com.android.internal.R.styleable.NinePatchDrawable_dither, DEFAULT_DITHER);
- final BitmapFactory.Options options = new BitmapFactory.Options();
- if (dither) {
- options.inDither = false;
- }
- options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
-
- final Rect padding = new Rect();
- final Rect opticalInsets = new Rect();
- Bitmap bitmap = null;
-
- try {
- final TypedValue value = new TypedValue();
- final InputStream is = r.openRawResource(id, value);
-
- bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
-
- is.close();
- } catch (IOException e) {
- // Ignore
- }
-
- if (bitmap == null) {
- throw new XmlPullParserException(parser.getPositionDescription() +
- ": <nine-patch> requires a valid src attribute");
- } else if (bitmap.getNinePatchChunk() == null) {
- throw new XmlPullParserException(parser.getPositionDescription() +
- ": <nine-patch> requires a valid 9-patch source image");
- }
-
- final boolean automirrored = a.getBoolean(
- com.android.internal.R.styleable.NinePatchDrawable_autoMirrored, false);
- final NinePatchState ninePatchState = new NinePatchState(
- new NinePatch(bitmap, bitmap.getNinePatchChunk()), padding, opticalInsets, dither,
- automirrored);
-
- final int tintModeValue = a.getInt(
- com.android.internal.R.styleable.NinePatchDrawable_tintMode, -1);
- ninePatchState.mTintMode = Drawable.parseTintMode(tintModeValue, Mode.SRC_IN);
- ninePatchState.mTint = a.getColorStateList(
- com.android.internal.R.styleable.NinePatchDrawable_tint);
- if (ninePatchState.mTint != null) {
- final int color = ninePatchState.mTint.getColorForState(getState(), 0);
- mTintFilter = new PorterDuffColorFilter(color, ninePatchState.mTintMode);
- }
-
- setNinePatchState(ninePatchState, r);
-
- mNinePatchState.mTargetDensity = mTargetDensity;
-
+ final TypedArray a = obtainAttributes(
+ r, theme, attrs, R.styleable.NinePatchDrawable);
+ inflateStateFromTypedArray(a);
a.recycle();
}
+ /**
+ * Initializes the constant state from the values in the typed array.
+ */
+ private void inflateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
+ final Resources r = a.getResources();
+ final NinePatchState ninePatchState = mNinePatchState;
+
+ // Extract the theme attributes, if any.
+ final int[] themeAttrs = a.extractThemeAttrs();
+ ninePatchState.mThemeAttrs = themeAttrs;
+
+ if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_dither] == 0) {
+ final boolean dither = a.getBoolean(
+ R.styleable.NinePatchDrawable_dither, DEFAULT_DITHER);
+ ninePatchState.mDither = dither;
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_src] == 0) {
+ final int id = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
+ if (id == 0) {
+ throw new XmlPullParserException(a.getPositionDescription() +
+ ": <nine-patch> requires a valid src attribute");
+ }
+
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inDither = !ninePatchState.mDither;
+ options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
+
+ final Rect padding = new Rect();
+ final Rect opticalInsets = new Rect();
+ Bitmap bitmap = null;
+
+ try {
+ final TypedValue value = new TypedValue();
+ final InputStream is = r.openRawResource(id, value);
+
+ bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
+
+ is.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+
+ if (bitmap == null) {
+ throw new XmlPullParserException(a.getPositionDescription() +
+ ": <nine-patch> requires a valid src attribute");
+ } else if (bitmap.getNinePatchChunk() == null) {
+ throw new XmlPullParserException(a.getPositionDescription() +
+ ": <nine-patch> requires a valid 9-patch source image");
+ }
+
+ final NinePatch ninePatch = new NinePatch(bitmap, bitmap.getNinePatchChunk());
+ ninePatchState.mNinePatch = ninePatch;
+ ninePatchState.mPadding = padding;
+ ninePatchState.mOpticalInsets = Insets.of(opticalInsets);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_autoMirrored] == 0) {
+ final boolean autoMirrored = a.getBoolean(
+ R.styleable.NinePatchDrawable_autoMirrored, false);
+ ninePatchState.mAutoMirrored = autoMirrored;
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_tintMode] == 0) {
+ final int tintModeValue = a.getInt(R.styleable.NinePatchDrawable_tintMode, -1);
+ ninePatchState.mTintMode = Drawable.parseTintMode(tintModeValue, Mode.SRC_IN);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_tint] == 0) {
+ ninePatchState.mTint = a.getColorStateList(R.styleable.NinePatchDrawable_tint);
+ if (ninePatchState.mTint != null) {
+ final int color = ninePatchState.mTint.getColorForState(getState(), 0);
+ mTintFilter = new PorterDuffColorFilter(color, ninePatchState.mTintMode);
+ }
+ }
+
+ // Apply the constant state to the paint.
+ initializeWithState(ninePatchState, r);
+
+ // Push density applied by setNinePatchState into state.
+ ninePatchState.mTargetDensity = mTargetDensity;
+ }
+
+ @Override
+ public void applyTheme(Theme t) {
+ super.applyTheme(t);
+
+ final NinePatchState state = mNinePatchState;
+ if (state == null) {
+ throw new RuntimeException("Can't apply theme to <nine-patch> with no constant state");
+ }
+
+ final int[] themeAttrs = state.mThemeAttrs;
+ if (themeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(
+ themeAttrs, R.styleable.NinePatchDrawable, 0, 0);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+ }
+
+ /**
+ * Updates the constant state from the values in the typed array.
+ */
+ private void updateStateFromTypedArray(TypedArray a) {
+ final Resources r = a.getResources();
+ final NinePatchState state = mNinePatchState;
+
+ if (a.hasValue(R.styleable.NinePatchDrawable_dither)) {
+ state.mDither = a.getBoolean(R.styleable.NinePatchDrawable_dither, DEFAULT_DITHER);
+ }
+
+ if (a.hasValue(R.styleable.NinePatchDrawable_autoMirrored)) {
+ state.mAutoMirrored = a.getBoolean(R.styleable.NinePatchDrawable_autoMirrored, false);
+ }
+
+ if (a.hasValue(R.styleable.NinePatchDrawable_src)) {
+ final int id = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
+ if (id == 0) {
+ throw new RuntimeException(a.getPositionDescription() +
+ ": <nine-patch> requires a valid src attribute");
+ }
+
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inDither = !state.mDither;
+ options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
+
+ final Rect padding = new Rect();
+ final Rect opticalInsets = new Rect();
+ Bitmap bitmap = null;
+
+ try {
+ final TypedValue value = new TypedValue();
+ final InputStream is = r.openRawResource(id, value);
+
+ bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
+
+ is.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+
+ if (bitmap == null) {
+ throw new RuntimeException(a.getPositionDescription() +
+ ": <nine-patch> requires a valid src attribute");
+ } else if (bitmap.getNinePatchChunk() == null) {
+ throw new RuntimeException(a.getPositionDescription() +
+ ": <nine-patch> requires a valid 9-patch source image");
+ }
+
+ state.mNinePatch = new NinePatch(bitmap, bitmap.getNinePatchChunk());
+ state.mPadding = padding;
+ state.mOpticalInsets = Insets.of(opticalInsets);
+ }
+
+ if (a.hasValue(R.styleable.NinePatchDrawable_tintMode)) {
+ final int modeValue = a.getInt(R.styleable.NinePatchDrawable_tintMode, -1);
+ state.mTintMode = Drawable.parseTintMode(modeValue, Mode.SRC_IN);
+ }
+
+ if (a.hasValue(R.styleable.NinePatchDrawable_tint)) {
+ final ColorStateList tint = a.getColorStateList(R.styleable.NinePatchDrawable_tint);
+ if (tint != null) {
+ state.mTint = tint;
+ final int color = tint.getColorForState(getState(), 0);
+ mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
+ }
+ }
+
+ // Apply the constant state to the paint.
+ initializeWithState(state, r);
+
+ // Push density applied by setNinePatchState into state.
+ state.mTargetDensity = mTargetDensity;
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mNinePatchState != null && mNinePatchState.mThemeAttrs != null;
+ }
+
public Paint getPaint() {
if (mPaint == null) {
mPaint = new Paint();
@@ -568,9 +691,14 @@
Rect mPadding;
Insets mOpticalInsets;
boolean mDither;
+ int[] mThemeAttrs;
int mChangingConfigurations;
int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
boolean mAutoMirrored;
+
+ NinePatchState() {
+ // Empty constructor.
+ }
NinePatchState(NinePatch ninePatch, Rect padding) {
this(ninePatch, padding, new Rect(), DEFAULT_DITHER, false);
@@ -581,7 +709,7 @@
}
NinePatchState(NinePatch ninePatch, Rect rect, Rect opticalInsets, boolean dither,
- boolean autoMirror) {
+ boolean autoMirror) {
mNinePatch = ninePatch;
mPadding = rect;
mOpticalInsets = Insets.of(opticalInsets);
@@ -596,6 +724,7 @@
mNinePatch = state.mNinePatch;
mTint = state.mTint;
mTintMode = state.mTintMode;
+ mThemeAttrs = state.mThemeAttrs;
mPadding = state.mPadding;
mOpticalInsets = state.mOpticalInsets;
mDither = state.mDither;
@@ -605,18 +734,28 @@
}
@Override
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null;
+ }
+
+ @Override
public Bitmap getBitmap() {
return mNinePatch.getBitmap();
}
@Override
public Drawable newDrawable() {
- return new NinePatchDrawable(this, null);
+ return new NinePatchDrawable(this, null, null);
}
@Override
public Drawable newDrawable(Resources res) {
- return new NinePatchDrawable(this, res);
+ return new NinePatchDrawable(this, res, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return new NinePatchDrawable(this, res, theme);
}
@Override
@@ -625,7 +764,40 @@
}
}
- private NinePatchDrawable(NinePatchState state, Resources res) {
- setNinePatchState(state, res);
+ private NinePatchDrawable(NinePatchState state, Resources res, Theme theme) {
+ if (theme != null && state.canApplyTheme()) {
+ mNinePatchState = new NinePatchState(state);
+ applyTheme(theme);
+ } else {
+ mNinePatchState = state;
+ }
+
+ initializeWithState(state, res);
+ }
+
+ /**
+ * Initializes local dynamic properties from state.
+ */
+ private void initializeWithState(NinePatchState state, Resources res) {
+ 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);
+ }
+
+ if (state.mTint != null) {
+ final int color = state.mTint.getColorForState(getState(), 0);
+ mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
+ }
+
+ final Rect statePadding = state.mPadding;
+ mPadding = statePadding != null ? new Rect(statePadding) : null;
+
+ setNinePatch(state.mNinePatch);
}
}
diff --git a/graphics/java/android/graphics/drawable/RevealDrawable.java b/graphics/java/android/graphics/drawable/RevealDrawable.java
index 91de638..2f96fe4 100644
--- a/graphics/java/android/graphics/drawable/RevealDrawable.java
+++ b/graphics/java/android/graphics/drawable/RevealDrawable.java
@@ -17,6 +17,7 @@
package android.graphics.drawable;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
@@ -88,11 +89,15 @@
* @see #RevealDrawable(Drawable[])
*/
RevealDrawable() {
- this(new RevealState(null, null, null), (Resources) null);
+ this(new RevealState(null, null, null), (Resources) null, null);
}
private RevealDrawable(RevealState state, Resources res) {
- super(state, res);
+ super(state, res, null);
+ }
+
+ private RevealDrawable(RevealState state, Resources res, Theme theme) {
+ super(state, res, theme);
}
private RevealDrawable(RevealState state, Drawable[] layers) {
@@ -100,9 +105,9 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
- super.inflate(r, parser, attrs);
+ super.inflate(r, parser, attrs, theme);
setTargetDensity(r.getDisplayMetrics());
setPaddingMode(PADDING_MODE_STACK);
@@ -303,5 +308,10 @@
public Drawable newDrawable(Resources res) {
return new RevealDrawable(this, res);
}
+
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return new RevealDrawable(this, res, theme);
+ }
}
}
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 630dc2e..edf1091 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -24,6 +24,7 @@
import android.graphics.Rect;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.util.TypedValue;
import android.util.AttributeSet;
import android.util.Log;
@@ -358,7 +359,7 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = r.obtainAttributes(attrs,
com.android.internal.R.styleable.RotateDrawable);
@@ -411,7 +412,7 @@
continue;
}
- if ((drawable = Drawable.createFromXmlInner(r, parser, attrs)) == null) {
+ if ((drawable = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme)) == null) {
Log.w("drawable", "Bad element under <rotate>: "
+ parser .getName());
}
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index ec6b2c16..4c4d9af 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -21,6 +21,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.graphics.*;
import android.view.Gravity;
import android.util.AttributeSet;
@@ -84,9 +85,9 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
- super.inflate(r, parser, attrs);
+ super.inflate(r, parser, attrs, theme);
int type;
@@ -107,7 +108,7 @@
if (type != XmlPullParser.START_TAG) {
continue;
}
- dr = Drawable.createFromXmlInner(r, parser, attrs);
+ dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
}
if (dr == null) {
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 16de9f3..96309f9 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -28,6 +28,7 @@
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.shapes.Shape;
+import android.content.res.Resources.Theme;
import android.util.AttributeSet;
import org.xmlpull.v1.XmlPullParser;
@@ -443,9 +444,9 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
- super.inflate(r, parser, attrs);
+ super.inflate(r, parser, attrs, theme);
TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ShapeDrawable);
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 48d66b7..271af2b 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -24,6 +24,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.util.AttributeSet;
import android.util.StateSet;
@@ -110,8 +111,7 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser,
- AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
TypedArray a = r.obtainAttributes(attrs,
@@ -183,7 +183,7 @@
+ ": <item> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
- dr = Drawable.createFromXmlInner(r, parser, attrs);
+ dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
}
mStateListState.addStateSet(states, dr);
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index f4f545c..ef91494 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -18,6 +18,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -78,20 +79,10 @@
/** The drawable to use as the mask. */
private Drawable mMask;
- /* package */TouchFeedbackDrawable() {
- this(null, null);
+ TouchFeedbackDrawable() {
+ this(new TouchFeedbackState(null), null, null);
}
- TouchFeedbackDrawable(TouchFeedbackState state, Resources res) {
- mState = new TouchFeedbackState(state);
-
- setConstantState(mState, res);
-
- if (res != null) {
- mDensity = res.getDisplayMetrics().density;
- }
- }
-
private void setConstantState(TouchFeedbackState wrapperState, Resources res) {
super.setConstantState(wrapperState, res);
@@ -103,6 +94,10 @@
} else {
mMask = wrapperState.mMaskState.newDrawable();
}
+
+ if (res != null) {
+ mDensity = res.getDisplayMetrics().density;
+ }
}
@Override
@@ -152,61 +147,32 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
- super.inflate(r, parser, attrs);
+ super.inflate(r, parser, attrs, theme);
- final TypedArray a = r.obtainAttributes(attrs, R.styleable.TouchFeedbackDrawable);
-
- mState.mTint = a.getColorStateList(R.styleable.TouchFeedbackDrawable_tint);
- mState.mTintMode = Drawable.parseTintMode(
- a.getInt(R.styleable.TouchFeedbackDrawable_tintMode, -1), Mode.SRC_ATOP);
- mState.mPinned = a.getBoolean(R.styleable.TouchFeedbackDrawable_pinned, false);
-
- if (mState.mTint == null) {
- throw new XmlPullParserException(parser.getPositionDescription()
- + ": <touch-feedback> tag requires a 'tint' attribute");
- }
-
- Drawable mask = a.getDrawable(R.styleable.TouchFeedbackDrawable_mask);
- final int drawableRes = a.getResourceId(R.styleable.TouchFeedbackDrawable_drawable, 0);
+ final TypedArray a = obtainAttributes(
+ r, theme, attrs, R.styleable.TouchFeedbackDrawable);
+ inflateStateFromTypedArray(r, a);
a.recycle();
+
+ inflateChildElements(r, parser, attrs, theme);
- final Drawable dr;
- if (drawableRes != 0) {
- dr = r.getDrawable(drawableRes);
- } else {
- int type;
- while ((type = parser.next()) == XmlPullParser.TEXT) {
- // Find the next non-text element.
- }
-
- if (type == XmlPullParser.START_TAG) {
- dr = Drawable.createFromXmlInner(r, parser, attrs);
- } else {
- dr = null;
- }
- }
-
- // If no mask is set, implicitly use the lower drawable.
- if (mask == null) {
- mask = dr;
- }
-
- // If neither a mask not a bottom layer was specified, assume we're
- // projecting onto a parent surface.
- mState.mProjected = mask == null && dr == null;
-
- if (dr != null) {
- setDrawable(dr, r);
- } else {
- // For now at least, we MUST have a wrapped drawable.
- setDrawable(new ColorDrawable(Color.TRANSPARENT), r);
- }
-
- setMaskDrawable(mask, r);
setTargetDensity(r.getDisplayMetrics());
}
+
+ private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
+ Theme theme) throws XmlPullParserException, IOException {
+ int type;
+ while ((type = parser.next()) == XmlPullParser.TEXT) {
+ // Find the next non-text element.
+ }
+
+ if (type == XmlPullParser.START_TAG) {
+ final Drawable dr = Drawable.createFromXmlInner(r, parser, attrs);
+ setDrawable(dr, r);
+ }
+ }
/**
* Sets the wrapped drawable and update the constant state.
@@ -228,6 +194,62 @@
}
/**
+ * Initializes the constant state from the values in the typed array.
+ */
+ private void inflateStateFromTypedArray(Resources r, TypedArray a) {
+ final TouchFeedbackState state = mState;
+
+ // Extract the theme attributes, if any.
+ final int[] themeAttrs = a.extractThemeAttrs();
+ state.mThemeAttrs = themeAttrs;
+
+ if (themeAttrs == null || themeAttrs[R.styleable.TouchFeedbackDrawable_tint] == 0) {
+ mState.mTint = a.getColorStateList(R.styleable.TouchFeedbackDrawable_tint);
+
+ if (mState.mTint == null) {
+ throw new RuntimeException("<touch-feedback> tag requires a 'tint' attribute");
+ }
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.TouchFeedbackDrawable_tintMode] == 0) {
+ mState.mTintMode = Drawable.parseTintMode(
+ a.getInt(R.styleable.TouchFeedbackDrawable_tintMode, -1), Mode.SRC_ATOP);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.TouchFeedbackDrawable_pinned] == 0) {
+ mState.mPinned = a.getBoolean(R.styleable.TouchFeedbackDrawable_pinned, false);
+ }
+
+ Drawable mask = mMask;
+ if (themeAttrs == null || themeAttrs[R.styleable.TouchFeedbackDrawable_mask] == 0) {
+ mask = a.getDrawable(R.styleable.TouchFeedbackDrawable_mask);
+ }
+
+ Drawable dr = super.getDrawable();
+ if (themeAttrs == null || themeAttrs[R.styleable.TouchFeedbackDrawable_drawable] == 0) {
+ final int drawableRes = a.getResourceId(R.styleable.TouchFeedbackDrawable_drawable, 0);
+ if (drawableRes != 0) {
+ dr = r.getDrawable(drawableRes);
+ }
+ }
+
+ // If neither a mask not a bottom layer was specified, assume we're
+ // projecting onto a parent surface.
+ mState.mProjected = mask == null && dr == null;
+
+ if (dr != null) {
+ setDrawable(dr, r);
+ } else {
+ // For now at least, we MUST have a wrapped drawable.
+ setDrawable(new ColorDrawable(Color.TRANSPARENT), r);
+ }
+
+ if (mask != null) {
+ setMaskDrawable(mask, r);
+ }
+ }
+
+ /**
* Set the density at which this drawable will be rendered.
*
* @param metrics The display metrics for this drawable.
@@ -239,6 +261,78 @@
}
}
+ @Override
+ public void applyTheme(Theme t) {
+ super.applyTheme(t);
+
+ final TouchFeedbackState state = mState;
+ if (state == null) {
+ throw new RuntimeException(
+ "Can't apply theme to <touch-feedback> with no constant state");
+ }
+
+ final int[] themeAttrs = state.mThemeAttrs;
+ if (themeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(
+ themeAttrs, R.styleable.TouchFeedbackDrawable, 0, 0);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+ }
+
+ /**
+ * Updates the constant state from the values in the typed array.
+ */
+ private void updateStateFromTypedArray(TypedArray a) {
+ final TouchFeedbackState state = mState;
+
+ if (a.hasValue(R.styleable.TouchFeedbackDrawable_tint)) {
+ state.mTint = a.getColorStateList(R.styleable.TouchFeedbackDrawable_tint);
+ }
+
+ if (a.hasValue(R.styleable.TouchFeedbackDrawable_tintMode)) {
+ mState.mTintMode = Drawable.parseTintMode(
+ a.getInt(R.styleable.TouchFeedbackDrawable_tintMode, -1), Mode.SRC_ATOP);
+ }
+
+ if (a.hasValue(R.styleable.TouchFeedbackDrawable_pinned)) {
+ mState.mPinned = a.getBoolean(R.styleable.TouchFeedbackDrawable_pinned, false);
+ }
+
+ Drawable mask = mMask;
+ if (a.hasValue(R.styleable.TouchFeedbackDrawable_mask)) {
+ mask = a.getDrawable(R.styleable.TouchFeedbackDrawable_mask);
+ }
+
+ Drawable dr = super.getDrawable();
+ if (a.hasValue(R.styleable.TouchFeedbackDrawable_drawable)) {
+ final int drawableRes = a.getResourceId(R.styleable.TouchFeedbackDrawable_drawable, 0);
+ if (drawableRes != 0) {
+ dr = a.getResources().getDrawable(drawableRes);
+ }
+ }
+
+ // If neither a mask not a bottom layer was specified, assume we're
+ // projecting onto a parent surface.
+ mState.mProjected = mask == null && dr == null;
+
+ if (dr != null) {
+ setDrawable(dr, a.getResources());
+ } else {
+ // For now at least, we MUST have a wrapped drawable.
+ setDrawable(new ColorDrawable(Color.TRANSPARENT), a.getResources());
+ }
+
+ if (mask != null) {
+ setMaskDrawable(mask, a.getResources());
+ }
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mState != null && mState.mThemeAttrs != null;
+ }
+
/**
* @hide until hotspot APIs are finalized
*/
@@ -333,7 +427,7 @@
if (mAnimationRunnable == null) {
mAnimationRunnable = new Runnable() {
- @Override
+ @Override
public void run() {
mAnimating = false;
scheduleAnimation();
@@ -357,7 +451,7 @@
}
final ArrayList<Ripple> activeRipples = mActiveRipples;
- final Drawable mask = mMask;
+ final Drawable mask = mMask == null && !mState.mProjected ? getDrawable() : null;
final Rect bounds = mask == null ? null : mask.getBounds();
// Draw ripples into a layer that merges using SRC_IN.
@@ -450,6 +544,7 @@
}
static class TouchFeedbackState extends WrapperState {
+ int[] mThemeAttrs;
ConstantState mMaskState;
ColorStateList mTint;
Mode mTintMode;
@@ -460,6 +555,7 @@
super(orig);
if (orig != null) {
+ mThemeAttrs = orig.mThemeAttrs;
mTint = orig.mTint;
mTintMode = orig.mTintMode;
mMaskState = orig.mMaskState;
@@ -469,13 +565,34 @@
}
@Override
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null;
+ }
+
+ @Override
public Drawable newDrawable() {
- return new TouchFeedbackDrawable(this, null);
+ return new TouchFeedbackDrawable(this, null, null);
}
@Override
public Drawable newDrawable(Resources res) {
- return new TouchFeedbackDrawable(this, res);
+ return new TouchFeedbackDrawable(this, res, null);
}
+
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return new TouchFeedbackDrawable(this, res, theme);
+ }
+ }
+
+ private TouchFeedbackDrawable(TouchFeedbackState state, Resources res, Theme theme) {
+ if (theme != null && state.canApplyTheme()) {
+ mState = new TouchFeedbackState(state);
+ applyTheme(theme);
+ } else {
+ mState = state;
+ }
+
+ setConstantState(state, res);
}
}
diff --git a/graphics/java/android/graphics/drawable/TransitionDrawable.java b/graphics/java/android/graphics/drawable/TransitionDrawable.java
index 483fa56..622e90b 100644
--- a/graphics/java/android/graphics/drawable/TransitionDrawable.java
+++ b/graphics/java/android/graphics/drawable/TransitionDrawable.java
@@ -17,6 +17,7 @@
package android.graphics.drawable;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.graphics.Canvas;
import android.os.SystemClock;
@@ -85,11 +86,11 @@
* @see #TransitionDrawable(Drawable[])
*/
TransitionDrawable() {
- this(new TransitionState(null, null, null), (Resources)null);
+ this(new TransitionState(null, null, null), null, null);
}
- private TransitionDrawable(TransitionState state, Resources res) {
- super(state, res);
+ private TransitionDrawable(TransitionState state, Resources res, Theme theme) {
+ super(state, res, theme);
}
private TransitionDrawable(TransitionState state, Drawable[] layers) {
@@ -251,12 +252,17 @@
@Override
public Drawable newDrawable() {
- return new TransitionDrawable(this, (Resources)null);
+ return new TransitionDrawable(this, null, null);
}
@Override
public Drawable newDrawable(Resources res) {
- return new TransitionDrawable(this, res);
+ return new TransitionDrawable(this, res, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return new TransitionDrawable(this, res, theme);
}
@Override
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 977c2e7..a65f677 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -3307,8 +3307,9 @@
private Drawable loadImageURI(Uri uri) {
try {
- return Drawable.createFromStream(
- getContext().getContentResolver().openInputStream(uri), null);
+ final Context context = getContext();
+ return Drawable.createFromStreamThemed(
+ context.getContentResolver().openInputStream(uri), null, context.getTheme());
} catch (Exception e) {
Log.w(TAG, "Unable to open content: " + uri);
}