summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt12
-rw-r--r--core/java/android/content/res/AssetManager.java3
-rw-r--r--core/java/android/content/res/Resources.java499
-rw-r--r--core/java/android/content/res/TypedArray.java125
-rw-r--r--core/java/android/widget/ImageView.java2
-rw-r--r--core/java/android/widget/SuggestionsAdapter.java2
-rw-r--r--core/jni/android_util_AssetManager.cpp194
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java6
-rw-r--r--graphics/java/android/graphics/drawable/AnimationDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/BitmapDrawable.java306
-rw-r--r--graphics/java/android/graphics/drawable/ClipDrawable.java7
-rw-r--r--graphics/java/android/graphics/drawable/ColorDrawable.java152
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java112
-rw-r--r--graphics/java/android/graphics/drawable/DrawableContainer.java66
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java663
-rw-r--r--graphics/java/android/graphics/drawable/InsetDrawable.java11
-rw-r--r--graphics/java/android/graphics/drawable/LayerDrawable.java229
-rw-r--r--graphics/java/android/graphics/drawable/LevelListDrawable.java10
-rw-r--r--graphics/java/android/graphics/drawable/NinePatchDrawable.java342
-rw-r--r--graphics/java/android/graphics/drawable/RevealDrawable.java18
-rw-r--r--graphics/java/android/graphics/drawable/RotateDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/ScaleDrawable.java7
-rw-r--r--graphics/java/android/graphics/drawable/ShapeDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/StateListDrawable.java6
-rw-r--r--graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java241
-rw-r--r--graphics/java/android/graphics/drawable/TransitionDrawable.java16
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindow.java5
27 files changed, 2285 insertions, 764 deletions
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 @@ package android.content.res {
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 @@ package android.content.res {
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 @@ package android.graphics.drawable {
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 @@ package android.graphics.drawable {
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 @@ package android.graphics.drawable {
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 @@ public final class AssetManager {
/*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.AttributeSet;
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 @@ public class Resources {
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 @@ public class Resources {
// 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 @@ public class Resources {
// 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 @@ public class Resources {
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 @@ public class Resources {
+ 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 class Resources {
*/
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 class Resources {
*/
public void setTo(Theme other) {
AssetManager.copyTheme(mTheme, other.mTheme);
+
+ mHasStyle = other.mHasStyle;
+ mThemeResId = other.mThemeResId;
}
/**
@@ -1286,11 +1298,9 @@ public class Resources {
*/
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 class Resources {
*/
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 @@ public class Resources {
}
System.out.println(s);
}
+ AssetManager.applyStyle(mTheme, 0, resid, 0, attrs, array.mData, array.mIndices);
return array;
}
@@ -1402,7 +1409,7 @@ public class Resources {
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 @@ public class Resources {
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 @@ public class Resources {
}
/**
+ * 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 @@ public class Resources {
* @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 @@ public class Resources {
}
/**
+ * 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 @@ public class Resources {
mAssets.releaseTheme(mTheme);
}
+ /*package*/ boolean canCacheDrawables() {
+ return mHasStyle && mThemeResId != 0;
+ }
+
/*package*/ Theme() {
mAssets = Resources.this.mAssets;
mTheme = mAssets.createTheme();
@@ -1517,6 +1574,13 @@ public class Resources {
@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 class Resources {
*/
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 @@ public class Resources {
mAssets.retrieveAttributes(parser.mParseState, attrs,
array.mData, array.mIndices);
- array.mRsrcs = attrs;
array.mXml = parser;
return array;
@@ -1658,8 +1721,8 @@ public class Resources {
+ " final compat is " + mCompatibilityInfo);
}
- clearDrawableCacheLocked(mDrawableCache, configChanges);
- clearDrawableCacheLocked(mColorDrawableCache, configChanges);
+ clearDrawableCachesLocked(mDrawableCache, configChanges);
+ clearDrawableCachesLocked(mColorDrawableCache, configChanges);
mColorStateListCache.clear();
@@ -1672,18 +1735,25 @@ public class Resources {
}
}
+ 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 @@ public class Resources {
/**
* @hide
*/
- public LongSparseArray<Drawable.ConstantState> getPreloadedDrawables() {
+ public LongSparseArray<ConstantState> getPreloadedDrawables() {
return sPreloadedDrawables[0];
}
@@ -2098,6 +2168,8 @@ public class Resources {
} 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 @@ public class Resources {
}
}
- 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);
- }
+ dr = loadDrawableForCookie(value, id, theme);
+ }
- if (dr == null) {
- if (value.string == null) {
- throw new NotFoundException(
- "Resource is not a Drawable (color or path): " + value);
- }
+ // 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);
+ cacheDrawable(value, theme, isColorDrawable, caches, key, dr);
+ }
- String file = value.string.toString();
+ return dr;
+ }
- 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);
- }
- }
+ 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;
+ }
- 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);
+ // Abort if the drawable is themed, but the theme cannot be cached.
+ if (dr.canApplyTheme() && theme != null && !theme.canCacheDrawables()) {
+ return;
+ }
- } 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;
+ 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 {
+ 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);
}
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
+ } 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));
+ }
}
+ }
- 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));
- }
- }
+ /**
+ * 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(
- LongSparseArray<WeakReference<ConstantState>> drawableCache,
- long key) {
+ private Drawable getCachedDrawable(ThemedCaches<ConstantState> caches, long key, Theme theme) {
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);
+ // 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;
}
- else { // our entry has been purged
- drawableCache.delete(key);
+ }
+
+ 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 @@ public class Resources {
}
}
- 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 @@ public class Resources {
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.graphics.drawable.Drawable;
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 @@ import java.util.Arrays;
* 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 @@ public class TypedArray {
}
/**
+ * 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 @@ public class TypedArray {
}
/**
+ * 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 @@ public class TypedArray {
* 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 572302a..eedacb5 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -664,7 +664,7 @@ public class ImageView extends View {
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..0203301 100644
--- a/core/java/android/widget/SuggestionsAdapter.java
+++ b/core/java/android/widget/SuggestionsAdapter.java
@@ -574,7 +574,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene
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..9dde701 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -972,6 +972,200 @@ static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
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.Rect;
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 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
}
@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 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
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 org.xmlpull.v1.XmlPullParserException;
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 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
}
@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 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
": <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 @@ package android.graphics.drawable;
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.DisplayMetrics;
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 @@ public class BitmapDrawable extends Drawable {
*/
@Deprecated
public BitmapDrawable(Bitmap bitmap) {
- this(new BitmapState(bitmap), null);
+ this(new BitmapState(bitmap), null, null);
}
/**
@@ -122,7 +125,7 @@ public class BitmapDrawable extends Drawable {
* 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 @@ public class BitmapDrawable extends Drawable {
*/
@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 @@ public class BitmapDrawable extends Drawable {
*/
@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 @@ public class BitmapDrawable extends Drawable {
*/
@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 @@ public class BitmapDrawable extends Drawable {
*/
@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 @@ public class BitmapDrawable extends Drawable {
/**
* @hide Candidate for future API inclusion
*/
+ @Override
public void setXfermode(Xfermode xfermode) {
mBitmapState.mPaint.setXfermode(xfermode);
invalidateSelf();
@@ -676,64 +680,228 @@ public class BitmapDrawable extends Drawable {
}
@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");
+
+ setTargetDensity(r.getDisplayMetrics());
+
+ 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);
}
- state.mBitmap = bitmap;
- setBitmap(bitmap);
+
+ 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);
+ }
+ }
+
+ 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());
- 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 (a.hasValue(R.styleable.BitmapDrawable_mipMap)) {
+ final boolean mipMap = a.getBoolean(
+ R.styleable.BitmapDrawable_mipMap,
+ state.mBitmap.hasMipMap());
+ 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 (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);
}
}
+ }
- a.recycle();
+ 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 @@ public class BitmapDrawable extends Drawable {
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 @@ public class BitmapDrawable extends Drawable {
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 @@ public class BitmapDrawable extends Drawable {
}
@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 @@ public class BitmapDrawable extends Drawable {
}
}
- 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 @@ public class BitmapDrawable extends Drawable {
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 org.xmlpull.v1.XmlPullParserException;
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 @@ public class ClipDrawable extends Drawable implements Drawable.Callback {
}
@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 @@ public class ClipDrawable extends Drawable implements Drawable.Callback {
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 @@ package android.graphics.drawable;
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 @@ import java.io.IOException;
*/
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 @@ public class ColorDrawable extends Drawable {
* Creates a new black ColorDrawable.
*/
public ColorDrawable() {
- this(null);
+ mColorState = new ColorState();
}
/**
@@ -53,17 +57,14 @@ public class ColorDrawable extends Drawable {
* @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 @@ public class ColorDrawable extends Drawable {
@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 @@ public class ColorDrawable extends Drawable {
@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 @@ public class ColorDrawable extends Drawable {
* @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 @@ public class ColorDrawable extends Drawable {
*/
@Override
public int getAlpha() {
- return mState.mUseColor >>> 24;
+ return mColorState.mUseColor >>> 24;
}
/**
@@ -126,13 +128,14 @@ public class ColorDrawable extends Drawable {
*
* @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 @@ public class ColorDrawable extends Drawable {
*
* @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 @@ public class ColorDrawable extends Drawable {
}
@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.ColorDrawable);
+ inflateStateFromTypedArray(a);
+ a.recycle();
+ }
- TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ColorDrawable);
+ /**
+ * Initializes the constant state from the values in the typed array.
+ */
+ private void inflateStateFromTypedArray(TypedArray a) {
+ final ColorState state = mColorState;
- int color = mState.mBaseColor;
- color = a.getColor(com.android.internal.R.styleable.ColorDrawable_color, color);
- mState.mBaseColor = mState.mUseColor = color;
+ // Extract the theme attributes, if any.
+ final int[] themeAttrs = a.extractThemeAttrs();
+ state.mThemeAttrs = themeAttrs;
- a.recycle();
+ 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 @@ public class ColorDrawable extends Drawable {
@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 @@ public class ColorDrawable extends Drawable {
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.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.Bitmap;
import android.graphics.BitmapFactory;
@@ -697,6 +698,16 @@ public abstract class Drawable {
}
/**
+ * 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 @@ public abstract class Drawable {
* 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 abstract class Drawable {
*/
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 abstract class Drawable {
*/
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 abstract class Drawable {
*/
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 @@ public abstract class Drawable {
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 @@ public abstract class Drawable {
* 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);
+ }
- final String name = parser.getName();
+ 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 @@ public abstract class Drawable {
": invalid drawable tag " + name);
}
- drawable.inflate(r, parser, attrs);
+ drawable.inflate(r, parser, attrs, theme);
return drawable;
}
@@ -1079,12 +1117,35 @@ public abstract class Drawable {
}
/**
- * 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);
+ }
+
+ /**
+ * 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);
+ }
- TypedArray 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 @@ public abstract class Drawable {
* @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 @@ public abstract class Drawable {
* 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 abstract class Drawable {
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 abstract class Drawable {
public Bitmap getBitmap() {
return null;
}
+
+ /**
+ * Return whether this constant state can have a theme applied.
+ */
+ public boolean canApplyTheme() {
+ return false;
+ }
}
/**
@@ -1170,6 +1246,18 @@ public abstract class Drawable {
}
/**
+ * 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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
@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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
final DrawableContainer mOwner;
final Resources mRes;
+ Theme mTheme;
+
SparseArray<ConstantStateFuture> mDrawableFutures;
int mChangingConfigurations;
@@ -680,6 +693,41 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
* @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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
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 @@ package android.graphics.drawable;
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.Rect;
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 @@ public class GradientDrawable extends Drawable {
/** 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 class GradientDrawable extends Drawable {
}
public GradientDrawable() {
- this(new GradientState(Orientation.TOP_BOTTOM, null));
+ this(new GradientState(Orientation.TOP_BOTTOM, null), null);
}
/**
@@ -176,7 +181,7 @@ public class GradientDrawable extends Drawable {
* of colors for the gradient.
*/
public GradientDrawable(Orientation orientation, int[] colors) {
- this(new GradientState(orientation, colors));
+ this(new GradientState(orientation, colors), null);
}
@Override
@@ -857,7 +862,7 @@ public class GradientDrawable extends Drawable {
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 @@ public class GradientDrawable extends Drawable {
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 @@ public class GradientDrawable extends Drawable {
}
@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);
+ inflateStateFromTypedArray(a);
+ a.recycle();
+
+ 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);
}
- 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);
+
+ 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);
}
- st.mUseLevelForShape = a.getBoolean(
- com.android.internal.R.styleable.GradientDrawable_useLevel, true);
}
-
- a.recycle();
-
- setShape(shapeType);
- setDither(dither);
+ 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 @@ public class GradientDrawable extends Drawable {
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 @@ public class GradientDrawable extends Drawable {
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 @@ public class GradientDrawable extends Drawable {
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 @@ public class GradientDrawable extends Drawable {
}
}
- 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 org.xmlpull.v1.XmlPullParserException;
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 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
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 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
+ ": <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.graphics.Rect;
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 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
* @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 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
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 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
@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;
+ final TypedArray a = obtainAttributes(
+ r, theme, attrs, R.styleable.LayerDrawable);
+ inflateStateFromTypedArray(a);
+ a.recycle();
- TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.LayerDrawable);
+ inflateLayers(r, parser, attrs, theme);
- mOpacityOverride = a.getInt(com.android.internal.R.styleable.LayerDrawable_opacity,
- PixelFormat.UNKNOWN);
+ ensurePadding();
+ onStateChange(getState());
+ }
+
+ /**
+ * Initializes the constant state from the values in the typed array.
+ */
+ private void inflateStateFromTypedArray(TypedArray a) {
+ final LayerState state = mLayerState;
- setAutoMirrored(a.getBoolean(com.android.internal.R.styleable.LayerDrawable_autoMirrored,
- false));
+ // Extract the theme attributes, if any.
+ final int[] themeAttrs = a.extractThemeAttrs();
+ state.mThemeAttrs = themeAttrs;
- mLayerState.mPaddingMode = a.getInteger(
- com.android.internal.R.styleable.LayerDrawableItem_drawable, PADDING_MODE_NEST);
+ if (themeAttrs == null || themeAttrs[R.styleable.LayerDrawable_opacity] == 0) {
+ mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, PixelFormat.UNKNOWN);
+ }
- a.recycle();
+ 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 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
continue;
}
- a = r.obtainAttributes(attrs,
- com.android.internal.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);
-
+ a = obtainAttributes(
+ r, theme, attrs, R.styleable.LayerDrawableItem);
+
+ 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 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
+ ": <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 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
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 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
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 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
mCheckedConstantState = mCanConstantState = true;
mAutoMirrored = orig.mAutoMirrored;
mPaddingMode = orig.mPaddingMode;
+ mThemeAttrs = orig.mThemeAttrs;
} else {
mNum = 0;
mChildren = null;
@@ -849,13 +964,23 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
@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 org.xmlpull.v1.XmlPullParserException;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.util.AttributeSet;
/**
@@ -83,10 +84,9 @@ public class LevelListDrawable extends DrawableContainer {
}
@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 @@ public class LevelListDrawable extends DrawableContainer {
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 @@ public class LevelListDrawable extends DrawableContainer {
+ ": <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 @@ package android.graphics.drawable;
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.DisplayMetrics;
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 @@ public class NinePatchDrawable extends Drawable {
private int mBitmapHeight;
NinePatchDrawable() {
+ mNinePatchState = new NinePatchState();
}
/**
@@ -82,7 +86,7 @@ public class NinePatchDrawable extends Drawable {
*/
@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 class NinePatchDrawable extends Drawable {
*/
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 class NinePatchDrawable extends Drawable {
*/
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 @@ public class NinePatchDrawable extends Drawable {
*/
@Deprecated
public NinePatchDrawable(NinePatch patch) {
- this(new NinePatchState(patch, new Rect()), null);
+ this(new NinePatchState(patch, new Rect()), null, null);
}
/**
@@ -122,35 +127,10 @@ public class NinePatchDrawable extends Drawable {
* 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 @@ public class NinePatchDrawable extends Drawable {
}
}
+ 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 @@ public class NinePatchDrawable extends Drawable {
@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,70 +406,195 @@ public class NinePatchDrawable extends Drawable {
}
@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.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;
- TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.NinePatchDrawable);
+ // Extract the theme attributes, if any.
+ final int[] themeAttrs = a.extractThemeAttrs();
+ ninePatchState.mThemeAttrs = themeAttrs;
- 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");
+ if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_dither] == 0) {
+ final boolean dither = a.getBoolean(
+ R.styleable.NinePatchDrawable_dither, DEFAULT_DITHER);
+ ninePatchState.mDither = dither;
}
- 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;
+ 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);
}
- options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
- final Rect padding = new Rect();
- final Rect opticalInsets = new Rect();
- Bitmap bitmap = null;
+ if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_autoMirrored] == 0) {
+ final boolean autoMirrored = a.getBoolean(
+ R.styleable.NinePatchDrawable_autoMirrored, false);
+ ninePatchState.mAutoMirrored = autoMirrored;
+ }
- try {
- final TypedValue value = new TypedValue();
- final InputStream is = r.openRawResource(id, value);
+ 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);
+ }
- bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
+ 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);
+ }
+ }
- is.close();
- } catch (IOException e) {
- // Ignore
+ // 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");
}
- 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 int[] themeAttrs = state.mThemeAttrs;
+ if (themeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(
+ themeAttrs, R.styleable.NinePatchDrawable, 0, 0);
+ updateStateFromTypedArray(a);
+ a.recycle();
}
+ }
- 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);
+ /**
+ * 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;
- 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);
+ if (a.hasValue(R.styleable.NinePatchDrawable_dither)) {
+ state.mDither = a.getBoolean(R.styleable.NinePatchDrawable_dither, DEFAULT_DITHER);
}
- setNinePatchState(ninePatchState, r);
+ if (a.hasValue(R.styleable.NinePatchDrawable_autoMirrored)) {
+ state.mAutoMirrored = a.getBoolean(R.styleable.NinePatchDrawable_autoMirrored, false);
+ }
- mNinePatchState.mTargetDensity = mTargetDensity;
+ 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");
+ }
- a.recycle();
+ 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() {
@@ -568,9 +691,14 @@ public class NinePatchDrawable extends Drawable {
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 @@ public class NinePatchDrawable extends Drawable {
}
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 @@ public class NinePatchDrawable extends Drawable {
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 @@ public class NinePatchDrawable extends Drawable {
}
@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 @@ public class NinePatchDrawable extends Drawable {
}
}
- 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 @@ public class RevealDrawable extends LayerDrawable {
* @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 @@ public class RevealDrawable extends LayerDrawable {
}
@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 class RevealDrawable extends LayerDrawable {
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.ColorFilter;
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 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
}
@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 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
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 ec6b2c1..4c4d9af 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -21,6 +21,7 @@ import org.xmlpull.v1.XmlPullParserException;
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 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback {
}
@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 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback {
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.PorterDuffColorFilter;
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 @@ public class ShapeDrawable extends Drawable {
}
@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 java.util.Arrays;
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 @@ public class StateListDrawable extends DrawableContainer {
}
@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 @@ public class StateListDrawable extends DrawableContainer {
+ ": <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 @@ package android.graphics.drawable;
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 @@ public class TouchFeedbackDrawable extends DrawableWrapper {
/** 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 @@ public class TouchFeedbackDrawable extends DrawableWrapper {
} else {
mMask = wrapperState.mMaskState.newDrawable();
}
+
+ if (res != null) {
+ mDensity = res.getDisplayMetrics().density;
+ }
}
@Override
@@ -152,60 +147,31 @@ public class TouchFeedbackDrawable extends DrawableWrapper {
}
@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);
-
- final TypedArray a = r.obtainAttributes(attrs, R.styleable.TouchFeedbackDrawable);
+ super.inflate(r, parser, attrs, theme);
- 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;
+ 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 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) {
+ if (type == XmlPullParser.START_TAG) {
+ final Drawable dr = Drawable.createFromXmlInner(r, parser, attrs);
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());
}
/**
@@ -228,6 +194,62 @@ public class TouchFeedbackDrawable extends DrawableWrapper {
}
/**
+ * 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 @@ public class TouchFeedbackDrawable extends DrawableWrapper {
}
}
+ @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 @@ public class TouchFeedbackDrawable extends DrawableWrapper {
if (mAnimationRunnable == null) {
mAnimationRunnable = new Runnable() {
- @Override
+ @Override
public void run() {
mAnimating = false;
scheduleAnimation();
@@ -357,7 +451,7 @@ public class TouchFeedbackDrawable extends DrawableWrapper {
}
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 @@ public class TouchFeedbackDrawable extends DrawableWrapper {
}
static class TouchFeedbackState extends WrapperState {
+ int[] mThemeAttrs;
ConstantState mMaskState;
ColorStateList mTint;
Mode mTintMode;
@@ -460,6 +555,7 @@ public class TouchFeedbackDrawable extends DrawableWrapper {
super(orig);
if (orig != null) {
+ mThemeAttrs = orig.mThemeAttrs;
mTint = orig.mTint;
mTintMode = orig.mTintMode;
mMaskState = orig.mMaskState;
@@ -469,13 +565,34 @@ public class TouchFeedbackDrawable extends DrawableWrapper {
}
@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 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba
* @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 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba
@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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
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);
}