Merge "Support for changing density of DrawableWrapper subclasses"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 093ea80..d7dd3ec 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5018,6 +5018,12 @@
<attr name="autoMirrored" format="boolean" />
</declare-styleable>
+ <!-- Drawable class used to wrap other drawables. -->
+ <declare-styleable name="DrawableWrapper">
+ <!-- The wrapped drawable. -->
+ <attr name="drawable" />
+ </declare-styleable>
+
<!-- Drawable used to render several states. Each state is represented by
a child drawable. -->
<declare-styleable name="StateListDrawable">
@@ -5385,6 +5391,7 @@
<attr name="color" />
</declare-styleable>
+ <!-- Drawable used to wrap and inset another drawable. -->
<declare-styleable name="InsetDrawable">
<attr name="visible" />
<attr name="drawable" />
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 4fc5ede..971a3a2 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -50,7 +50,7 @@
* Creates a new animated rotating drawable with no wrapped drawable.
*/
public AnimatedRotateDrawable() {
- this(new AnimatedRotateState(null), null);
+ this(new AnimatedRotateState(null, null), null);
}
@Override
@@ -126,58 +126,22 @@
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimatedRotateDrawable);
- super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
+ super.inflate(r, parser, attrs, theme);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
updateLocalState();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
- // If we're not waiting on a theme, verify required attributes.
- if (getDrawable() == null && (mState.mThemeAttrs == null
- || mState.mThemeAttrs[R.styleable.AnimatedRotateDrawable_drawable] == 0)) {
- throw new XmlPullParserException(a.getPositionDescription()
- + ": <animated-rotate> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
- }
- }
-
@Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
final AnimatedRotateState state = mState;
-
- if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotX)) {
- final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
- state.mPivotXRel = tv.type == TypedValue.TYPE_FRACTION;
- state.mPivotX = state.mPivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
- }
-
- if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotY)) {
- final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
- state.mPivotYRel = tv.type == TypedValue.TYPE_FRACTION;
- state.mPivotY = state.mPivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
- }
-
- setFramesCount(a.getInt(
- R.styleable.AnimatedRotateDrawable_framesCount, state.mFramesCount));
- setFramesDuration(a.getInt(
- R.styleable.AnimatedRotateDrawable_frameDuration, state.mFrameDuration));
-
- final Drawable dr = a.getDrawable(R.styleable.AnimatedRotateDrawable_drawable);
- if (dr != null) {
- setDrawable(dr);
- }
- }
-
- @Override
- public void applyTheme(@Nullable Theme t) {
- final AnimatedRotateState state = mState;
if (state == null) {
return;
}
@@ -195,13 +159,49 @@
}
}
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
-
updateLocalState();
}
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
+ // If we're not waiting on a theme, verify required attributes.
+ if (getDrawable() == null && (mState.mThemeAttrs == null
+ || mState.mThemeAttrs[R.styleable.AnimatedRotateDrawable_drawable] == 0)) {
+ throw new XmlPullParserException(a.getPositionDescription()
+ + ": <animated-rotate> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ }
+
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
+ final AnimatedRotateState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotX)) {
+ final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
+ state.mPivotXRel = tv.type == TypedValue.TYPE_FRACTION;
+ state.mPivotX = state.mPivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
+ }
+
+ if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotY)) {
+ final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
+ state.mPivotYRel = tv.type == TypedValue.TYPE_FRACTION;
+ state.mPivotY = state.mPivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
+ }
+
+ setFramesCount(a.getInt(
+ R.styleable.AnimatedRotateDrawable_framesCount, state.mFramesCount));
+ setFramesDuration(a.getInt(
+ R.styleable.AnimatedRotateDrawable_frameDuration, state.mFrameDuration));
+ }
+
public void setFramesCount(int framesCount) {
mState.mFramesCount = framesCount;
mIncrement = 360.0f / mState.mFramesCount;
@@ -211,7 +211,15 @@
mState.mFrameDuration = framesDuration;
}
+ @Override
+ DrawableWrapperState mutateConstantState() {
+ mState = new AnimatedRotateState(mState, null);
+ return mState;
+ }
+
static final class AnimatedRotateState extends DrawableWrapper.DrawableWrapperState {
+ private int[] mThemeAttrs;
+
boolean mPivotXRel = false;
float mPivotX = 0;
boolean mPivotYRel = false;
@@ -219,8 +227,8 @@
int mFrameDuration = 150;
int mFramesCount = 12;
- public AnimatedRotateState(AnimatedRotateState orig) {
- super(orig);
+ public AnimatedRotateState(AnimatedRotateState orig, Resources res) {
+ super(orig, res);
if (orig != null) {
mPivotXRel = orig.mPivotXRel;
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index 31fccd0..cdd336d 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -21,6 +21,8 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
@@ -59,7 +61,7 @@
private ClipState mState;
ClipDrawable() {
- this(new ClipState(null), null);
+ this(new ClipState(null, null), null);
}
/**
@@ -72,7 +74,7 @@
* {@link #VERTICAL}
*/
public ClipDrawable(Drawable drawable, int gravity, int orientation) {
- this(new ClipState(null), null);
+ this(new ClipState(null, null), null);
mState.mGravity = gravity;
mState.mOrientation = orientation;
@@ -81,46 +83,24 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ClipDrawable);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
super.inflate(r, parser, attrs, theme);
- final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ClipDrawable);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
- // If we're not waiting on a theme, verify required attributes.
- if (getDrawable() == null && (mState.mThemeAttrs == null
- || mState.mThemeAttrs[R.styleable.ClipDrawable_drawable] == 0)) {
- throw new XmlPullParserException(a.getPositionDescription()
- + ": <clip> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
- }
- }
-
@Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
final ClipState state = mState;
- state.mOrientation = a.getInt(
- R.styleable.ClipDrawable_clipOrientation, state.mOrientation);
- state.mGravity = a.getInt(
- R.styleable.ClipDrawable_gravity, state.mGravity);
-
- final Drawable dr = a.getDrawable(R.styleable.ClipDrawable_drawable);
- if (dr != null) {
- setDrawable(dr);
- }
- }
-
- @Override
- public void applyTheme(Theme t) {
- final ClipState state = mState;
if (state == null) {
return;
}
@@ -136,10 +116,34 @@
a.recycle();
}
}
+ }
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
+ // If we're not waiting on a theme, verify required attributes.
+ if (getDrawable() == null && (mState.mThemeAttrs == null
+ || mState.mThemeAttrs[R.styleable.ClipDrawable_drawable] == 0)) {
+ throw new XmlPullParserException(a.getPositionDescription()
+ + ": <clip> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ }
+
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
+ final ClipState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ state.mOrientation = a.getInt(
+ R.styleable.ClipDrawable_clipOrientation, state.mOrientation);
+ state.mGravity = a.getInt(
+ R.styleable.ClipDrawable_gravity, state.mGravity);
}
@Override
@@ -200,12 +204,20 @@
}
}
+ @Override
+ DrawableWrapperState mutateConstantState() {
+ mState = new ClipState(mState, null);
+ return mState;
+ }
+
static final class ClipState extends DrawableWrapper.DrawableWrapperState {
+ private int[] mThemeAttrs;
+
int mOrientation = HORIZONTAL;
int mGravity = Gravity.LEFT;
- ClipState(ClipState orig) {
- super(orig);
+ ClipState(ClipState orig, Resources res) {
+ super(orig, res);
if (orig != null) {
mOrientation = orig.mOrientation;
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index b95c183..ff28777 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -55,6 +55,8 @@
import java.util.Arrays;
import java.util.Collection;
+import com.android.internal.R;
+
/**
* A Drawable is a general abstraction for "something that can be drawn." Most
* often you will deal with Drawable as the type of resource retrieved for
@@ -791,8 +793,10 @@
/**
* Applies the specified theme to this Drawable and its children.
+ *
+ * @param t the theme to apply
*/
- public void applyTheme(@SuppressWarnings("unused") Theme t) {
+ public void applyTheme(@NonNull @SuppressWarnings("unused") Theme t) {
}
public boolean canApplyTheme() {
@@ -1177,8 +1181,8 @@
*
* @see #inflate(Resources, XmlPullParser, AttributeSet, Theme)
*/
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
- throws XmlPullParserException, IOException {
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs) throws XmlPullParserException, IOException {
inflate(r, parser, attrs, null);
}
@@ -1192,17 +1196,11 @@
* @throws XmlPullParserException
* @throws IOException
*/
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
- final TypedArray a;
- if (theme != null) {
- a = theme.obtainStyledAttributes(
- attrs, com.android.internal.R.styleable.Drawable, 0, 0);
- } else {
- a = r.obtainAttributes(attrs, com.android.internal.R.styleable.Drawable);
- }
-
- inflateWithAttributes(r, parser, a, com.android.internal.R.styleable.Drawable_visible);
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.Drawable);
+ mVisible = a.getBoolean(R.styleable.Drawable_visible, mVisible);
a.recycle();
}
@@ -1212,8 +1210,8 @@
* @throws XmlPullParserException
* @throws IOException
*/
- void inflateWithAttributes(Resources r, XmlPullParser parser, TypedArray attrs, int visibleAttr)
- throws XmlPullParserException, IOException {
+ void inflateWithAttributes(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull TypedArray attrs, int visibleAttr) throws XmlPullParserException, IOException {
mVisible = attrs.getBoolean(visibleAttr, mVisible);
}
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index 9185e1a..c427870 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -16,6 +16,8 @@
package android.graphics.drawable;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -23,6 +25,7 @@
import android.annotation.Nullable;
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.Canvas;
@@ -33,6 +36,7 @@
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.view.View;
import java.io.IOException;
@@ -112,7 +116,66 @@
return mDrawable;
}
- void updateStateFromTypedArray(TypedArray a) {
+ @Override
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws XmlPullParserException, IOException {
+ super.inflate(r, parser, attrs, theme);
+
+ final DrawableWrapperState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // The density may have changed since the last update. This will
+ // apply scaling to any existing constant state properties.
+ final int densityDpi = r.getDisplayMetrics().densityDpi;
+ final int targetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ state.setDensity(targetDensity);
+
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.DrawableWrapper);
+ updateStateFromTypedArray(a);
+ a.recycle();
+
+ inflateChildDrawable(r, parser, attrs, theme);
+ }
+
+ @Override
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
+
+ // If we load the drawable later as part of updating from the typed
+ // array, it will already be themed correctly. So, we can theme the
+ // local drawable first.
+ if (mDrawable != null && mDrawable.canApplyTheme()) {
+ mDrawable.applyTheme(t);
+ }
+
+ final DrawableWrapperState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ final int densityDpi = t.getResources().getDisplayMetrics().densityDpi;
+ final int density = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ state.setDensity(density);
+
+ if (state.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(
+ state.mThemeAttrs, R.styleable.DrawableWrapper);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+ }
+
+ /**
+ * Updates constant state properties from the provided typed array.
+ * <p>
+ * Implementing subclasses should call through to the super method first.
+ *
+ * @param a the typed array rom which properties should be read
+ */
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
final DrawableWrapperState state = mState;
if (state == null) {
return;
@@ -124,20 +187,8 @@
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
- // TODO: Consider using R.styleable.DrawableWrapper_drawable
- }
-
- @Override
- public void applyTheme(Resources.Theme t) {
- super.applyTheme(t);
-
- final DrawableWrapperState state = mState;
- if (state == null) {
- return;
- }
-
- if (mDrawable != null && mDrawable.canApplyTheme()) {
- mDrawable.applyTheme(t);
+ if (a.hasValueOrEmpty(R.styleable.DrawableWrapper_drawable)) {
+ setDrawable(a.getDrawable(R.styleable.DrawableWrapper_drawable));
}
}
@@ -371,8 +422,9 @@
* child element will take precedence over any other child elements or
* explicit drawable attribute.
*/
- void inflateChildDrawable(Resources r, XmlPullParser parser, AttributeSet attrs,
- Resources.Theme theme) throws XmlPullParserException, IOException {
+ private void inflateChildDrawable(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws XmlPullParserException, IOException {
// Seek to the first child element.
Drawable dr = null;
int type;
@@ -390,17 +442,61 @@
}
abstract static class DrawableWrapperState extends Drawable.ConstantState {
- int[] mThemeAttrs;
+ private int[] mThemeAttrs;
+
int mChangingConfigurations;
+ int mDensity = DisplayMetrics.DENSITY_DEFAULT;
Drawable.ConstantState mDrawableState;
- DrawableWrapperState(DrawableWrapperState orig) {
+ DrawableWrapperState(@Nullable DrawableWrapperState orig, @Nullable Resources res) {
if (orig != null) {
mThemeAttrs = orig.mThemeAttrs;
mChangingConfigurations = orig.mChangingConfigurations;
mDrawableState = orig.mDrawableState;
}
+
+ final int density;
+ if (res != null) {
+ density = res.getDisplayMetrics().densityDpi;
+ } else if (orig != null) {
+ density = orig.mDensity;
+ } else {
+ density = 0;
+ }
+
+ mDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density;
+ }
+
+ /**
+ * Sets the constant state density.
+ * <p>
+ * If the density has been previously set, dispatches the change to
+ * subclasses so that density-dependent properties may be scaled as
+ * necessary.
+ *
+ * @param targetDensity the new constant state density
+ */
+ public final void setDensity(int targetDensity) {
+ if (mDensity != targetDensity) {
+ final int sourceDensity = mDensity;
+ mDensity = targetDensity;
+
+ onDensityChanged(sourceDensity, targetDensity);
+ }
+ }
+
+ /**
+ * Called when the constant state density changes.
+ * <p>
+ * Subclasses with density-dependent constant state properties should
+ * override this method and scale their properties as necessary.
+ *
+ * @param sourceDensity the previous constant state density
+ * @param targetDensity the new constant state density
+ */
+ void onDensityChanged(int sourceDensity, int targetDensity) {
+ // Stub method.
}
@Override
@@ -425,7 +521,7 @@
}
@Override
- public abstract Drawable newDrawable(Resources res);
+ public abstract Drawable newDrawable(@Nullable Resources res);
@Override
public int getChangingConfigurations() {
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index e1ebdbb..927b9c9 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -22,14 +22,17 @@
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import java.io.IOException;
@@ -58,7 +61,7 @@
* No-arg constructor used by drawable inflation.
*/
InsetDrawable() {
- this(new InsetState(null), null);
+ this(new InsetState(null, null), null);
}
/**
@@ -67,7 +70,7 @@
* @param drawable The drawable to inset.
* @param inset Inset in pixels around the drawable.
*/
- public InsetDrawable(Drawable drawable, int inset) {
+ public InsetDrawable(@Nullable Drawable drawable, int inset) {
this(drawable, inset, inset, inset, inset);
}
@@ -80,9 +83,9 @@
* @param insetRight Right inset in pixels.
* @param insetBottom Bottom inset in pixels.
*/
- public InsetDrawable(Drawable drawable, int insetLeft, int insetTop,int insetRight,
- int insetBottom) {
- this(new InsetState(null), null);
+ public InsetDrawable(@Nullable Drawable drawable, int insetLeft, int insetTop,
+ int insetRight, int insetBottom) {
+ this(new InsetState(null, null), null);
mState.mInsetLeft = insetLeft;
mState.mInsetTop = insetTop;
@@ -93,70 +96,24 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.InsetDrawable);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
super.inflate(r, parser, attrs, theme);
- final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.InsetDrawable);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
- // If we're not waiting on a theme, verify required attributes.
- if (getDrawable() == null && (mState.mThemeAttrs == null
- || mState.mThemeAttrs[R.styleable.InsetDrawable_drawable] == 0)) {
- throw new XmlPullParserException(a.getPositionDescription()
- + ": <inset> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
- }
- }
-
@Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
final InsetState state = mState;
- final int N = a.getIndexCount();
- for (int i = 0; i < N; i++) {
- final int attr = a.getIndex(i);
- switch (attr) {
- case R.styleable.InsetDrawable_drawable:
- final Drawable dr = a.getDrawable(attr);
- if (dr != null) {
- setDrawable(dr);
- }
- break;
- case R.styleable.InsetDrawable_inset:
- final int inset = a.getDimensionPixelOffset(attr, Integer.MIN_VALUE);
- if (inset != Integer.MIN_VALUE) {
- state.mInsetLeft = inset;
- state.mInsetTop = inset;
- state.mInsetRight = inset;
- state.mInsetBottom = inset;
- }
- break;
- case R.styleable.InsetDrawable_insetLeft:
- state.mInsetLeft = a.getDimensionPixelOffset(attr, state.mInsetLeft);
- break;
- case R.styleable.InsetDrawable_insetTop:
- state.mInsetTop = a.getDimensionPixelOffset(attr, state.mInsetTop);
- break;
- case R.styleable.InsetDrawable_insetRight:
- state.mInsetRight = a.getDimensionPixelOffset(attr, state.mInsetRight);
- break;
- case R.styleable.InsetDrawable_insetBottom:
- state.mInsetBottom = a.getDimensionPixelOffset(attr, state.mInsetBottom);
- break;
- }
- }
- }
-
- @Override
- public void applyTheme(Theme t) {
- final InsetState state = mState;
if (state == null) {
return;
}
@@ -172,10 +129,47 @@
a.recycle();
}
}
+ }
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
+ // If we're not waiting on a theme, verify required attributes.
+ if (getDrawable() == null && (mState.mThemeAttrs == null
+ || mState.mThemeAttrs[R.styleable.InsetDrawable_drawable] == 0)) {
+ throw new XmlPullParserException(a.getPositionDescription()
+ + ": <inset> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ }
+
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
+ final InsetState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ // Inset attribute may be overridden by more specific attributes.
+ if (a.hasValue(R.styleable.InsetDrawable_inset)) {
+ final int inset = a.getDimensionPixelOffset(R.styleable.InsetDrawable_inset, 0);
+ state.mInsetLeft = inset;
+ state.mInsetTop = inset;
+ state.mInsetRight = inset;
+ state.mInsetBottom = inset;
+ }
+
+ state.mInsetLeft = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetLeft, state.mInsetLeft);
+ state.mInsetRight = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetRight, state.mInsetRight);
+ state.mInsetTop = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetTop, state.mInsetTop);
+ state.mInsetBottom = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetBottom, state.mInsetBottom);
}
@Override
@@ -243,30 +237,72 @@
@Override
DrawableWrapperState mutateConstantState() {
- mState = new InsetState(mState);
+ mState = new InsetState(mState, null);
return mState;
}
static final class InsetState extends DrawableWrapper.DrawableWrapperState {
+ private int[] mThemeAttrs;
+
int mInsetLeft = 0;
int mInsetTop = 0;
int mInsetRight = 0;
int mInsetBottom = 0;
- InsetState(InsetState orig) {
- super(orig);
+ InsetState(@Nullable InsetState orig, @Nullable Resources res) {
+ super(orig, res);
if (orig != null) {
mInsetLeft = orig.mInsetLeft;
mInsetTop = orig.mInsetTop;
mInsetRight = orig.mInsetRight;
mInsetBottom = orig.mInsetBottom;
+
+ if (orig.mDensity != mDensity) {
+ applyDensityScaling(orig.mDensity, mDensity);
+ }
}
}
@Override
- public Drawable newDrawable(Resources res) {
- return new InsetDrawable(this, res);
+ void onDensityChanged(int sourceDensity, int targetDensity) {
+ super.onDensityChanged(sourceDensity, targetDensity);
+
+ applyDensityScaling(sourceDensity, targetDensity);
+ }
+
+ /**
+ * Called when the constant state density changes to scale
+ * density-dependent properties specific to insets.
+ *
+ * @param sourceDensity the previous constant state density
+ * @param targetDensity the new constant state density
+ */
+ private void applyDensityScaling(int sourceDensity, int targetDensity) {
+ mInsetLeft = Bitmap.scaleFromDensity(mInsetLeft, sourceDensity, targetDensity);
+ mInsetTop = Bitmap.scaleFromDensity(mInsetTop, sourceDensity, targetDensity);
+ mInsetRight = Bitmap.scaleFromDensity(mInsetRight, sourceDensity, targetDensity);
+ mInsetBottom = Bitmap.scaleFromDensity(mInsetBottom, sourceDensity, targetDensity);
+ }
+
+ @Override
+ public Drawable newDrawable(@Nullable Resources res) {
+ // If this drawable is being created for a different density,
+ // just create a new constant state and call it a day.
+ final InsetState state;
+ if (res != null) {
+ final int densityDpi = res.getDisplayMetrics().densityDpi;
+ final int density = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ if (density != mDensity) {
+ state = new InsetState(this, res);
+ } else {
+ state = this;
+ }
+ } else {
+ state = this;
+ }
+
+ return new InsetDrawable(state, res);
}
}
@@ -274,7 +310,7 @@
* The one constructor to rule them all. This is called by all public
* constructors to set the state and initialize local properties.
*/
- private InsetDrawable(InsetState state, Resources res) {
+ private InsetDrawable(@NonNull InsetState state, @Nullable Resources res) {
super(state, res);
mState = state;
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 036a078..1531ba2 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -21,6 +21,8 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.content.res.Resources;
@@ -58,22 +60,46 @@
* Creates a new rotating drawable with no wrapped drawable.
*/
public RotateDrawable() {
- this(new RotateState(null), null);
+ this(new RotateState(null, null), null);
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.RotateDrawable);
- super.inflateWithAttributes(r, parser, a, R.styleable.RotateDrawable_visible);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
+ super.inflate(r, parser, attrs, theme);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
+ @Override
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
+
+ final RotateState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ if (state.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.RotateDrawable);
+ try {
+ updateStateFromTypedArray(a);
+ verifyRequiredAttributes(a);
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException(e);
+ } finally {
+ a.recycle();
+ }
+ }
+ }
+
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (getDrawable() == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.RotateDrawable_drawable] == 0)) {
@@ -83,11 +109,14 @@
}
}
- @Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
-
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
final RotateState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
@@ -109,35 +138,6 @@
state.mToDegrees = a.getFloat(
R.styleable.RotateDrawable_toDegrees, state.mToDegrees);
state.mCurrentDegrees = state.mFromDegrees;
-
- final Drawable dr = a.getDrawable(R.styleable.RotateDrawable_drawable);
- if (dr != null) {
- setDrawable(dr);
- }
- }
-
- @Override
- public void applyTheme(Theme t) {
- final RotateState state = mState;
- if (state == null) {
- return;
- }
-
- if (state.mThemeAttrs != null) {
- final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.RotateDrawable);
- try {
- updateStateFromTypedArray(a);
- verifyRequiredAttributes(a);
- } catch (XmlPullParserException e) {
- throw new RuntimeException(e);
- } finally {
- a.recycle();
- }
- }
-
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
}
@Override
@@ -316,11 +316,13 @@
@Override
DrawableWrapperState mutateConstantState() {
- mState = new RotateState(mState);
+ mState = new RotateState(mState, null);
return mState;
}
static final class RotateState extends DrawableWrapper.DrawableWrapperState {
+ private int[] mThemeAttrs;
+
boolean mPivotXRel = true;
float mPivotX = 0.5f;
boolean mPivotYRel = true;
@@ -329,8 +331,8 @@
float mToDegrees = 360.0f;
float mCurrentDegrees = 0.0f;
- RotateState(RotateState orig) {
- super(orig);
+ RotateState(RotateState orig, Resources res) {
+ super(orig, res);
if (orig != null) {
mPivotXRel = orig.mPivotXRel;
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index f9206b7..f87c19a 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -21,6 +21,8 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -67,7 +69,7 @@
private ScaleState mState;
ScaleDrawable() {
- this(new ScaleState(null), null);
+ this(new ScaleState(null, null), null);
}
/**
@@ -83,7 +85,7 @@
* is at the maximum value, or -1 to not scale height
*/
public ScaleDrawable(Drawable drawable, int gravity, float scaleWidth, float scaleHeight) {
- this(new ScaleState(null), null);
+ this(new ScaleState(null, null), null);
mState.mGravity = gravity;
mState.mScaleWidth = scaleWidth;
@@ -93,20 +95,46 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ScaleDrawable);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
super.inflate(r, parser, attrs, theme);
- final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ScaleDrawable);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
updateLocalState();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
+ @Override
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
+
+ final ScaleState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ if (state.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ScaleDrawable);
+ try {
+ updateStateFromTypedArray(a);
+ verifyRequiredAttributes(a);
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException(e);
+ } finally {
+ a.recycle();
+ }
+ }
+
+ updateLocalState();
+ }
+
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (getDrawable() == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.ScaleDrawable_drawable] == 0)) {
@@ -116,11 +144,18 @@
}
}
- @Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
-
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
final ScaleState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
state.mScaleWidth = getPercent(a,
R.styleable.ScaleDrawable_scaleWidth, state.mScaleWidth);
state.mScaleHeight = getPercent(a,
@@ -131,11 +166,6 @@
R.styleable.ScaleDrawable_useIntrinsicSizeAsMinimum, state.mUseIntrinsicSizeAsMin);
state.mInitialLevel = a.getInt(
R.styleable.ScaleDrawable_level, state.mInitialLevel);
-
- final Drawable dr = a.getDrawable(R.styleable.ScaleDrawable_drawable);
- if (dr != null) {
- setDrawable(dr);
- }
}
private static float getPercent(TypedArray a, int index, float defaultValue) {
@@ -157,33 +187,6 @@
}
@Override
- public void applyTheme(Theme t) {
- final ScaleState state = mState;
- if (state == null) {
- return;
- }
-
- if (state.mThemeAttrs != null) {
- final TypedArray a = t.resolveAttributes(
- state.mThemeAttrs, R.styleable.ScaleDrawable);
- try {
- updateStateFromTypedArray(a);
- verifyRequiredAttributes(a);
- } catch (XmlPullParserException e) {
- throw new RuntimeException(e);
- } finally {
- a.recycle();
- }
- }
-
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
-
- updateLocalState();
- }
-
- @Override
public void draw(Canvas canvas) {
final Drawable d = getDrawable();
if (d != null && d.getLevel() != 0) {
@@ -243,7 +246,7 @@
@Override
DrawableWrapperState mutateConstantState() {
- mState = new ScaleState(mState);
+ mState = new ScaleState(mState, null);
return mState;
}
@@ -251,14 +254,16 @@
/** Constant used to disable scaling for a particular dimension. */
private static final float DO_NOT_SCALE = -1.0f;
+ private int[] mThemeAttrs;
+
float mScaleWidth = DO_NOT_SCALE;
float mScaleHeight = DO_NOT_SCALE;
int mGravity = Gravity.LEFT;
boolean mUseIntrinsicSizeAsMin = false;
int mInitialLevel = 0;
- ScaleState(ScaleState orig) {
- super(orig);
+ ScaleState(ScaleState orig, Resources res) {
+ super(orig, res);
if (orig != null) {
mScaleWidth = orig.mScaleWidth;