diff options
Diffstat (limited to 'graphics/java/android')
24 files changed, 1397 insertions, 711 deletions
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java index 9fb3fb4..d2799e1 100644 --- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java @@ -16,6 +16,8 @@ package android.graphics.drawable; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.ColorFilter; @@ -41,6 +43,7 @@ import com.android.internal.R; */ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable, Animatable { + private static final String TAG = "AnimatedRotateDrawable"; private AnimatedRotateState mState; private boolean mMutated; @@ -186,6 +189,11 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac } @Override + public boolean canApplyTheme() { + return (mState != null && mState.canApplyTheme()) || super.canApplyTheme(); + } + + @Override public void invalidateDrawable(Drawable who) { final Callback callback = getCallback(); if (callback != null) { @@ -254,62 +262,112 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac } @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.AnimatedRotateDrawable); - super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible); + updateStateFromTypedArray(a); + a.recycle(); - TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX); - final boolean pivotXRel = tv.type == TypedValue.TYPE_FRACTION; - final float pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + inflateChildElements(r, parser, attrs, theme); - tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY); - final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION; - final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + init(); + } - setFramesCount(a.getInt(R.styleable.AnimatedRotateDrawable_framesCount, 12)); - setFramesDuration(a.getInt(R.styleable.AnimatedRotateDrawable_frameDuration, 150)); + @Override + public void applyTheme(@Nullable Theme t) { + super.applyTheme(t); - final int res = a.getResourceId(R.styleable.AnimatedRotateDrawable_drawable, 0); - Drawable drawable = null; - if (res > 0) { - drawable = r.getDrawable(res, theme); + final AnimatedRotateState state = mState; + if (state == null) { + return; } - a.recycle(); + if (state.mThemeAttrs != null) { + final TypedArray a = t.resolveAttributes( + state.mThemeAttrs, R.styleable.AnimatedRotateDrawable); + try { + updateStateFromTypedArray(a); + verifyRequiredAttributes(a); + } catch (XmlPullParserException e) { + throw new RuntimeException(e); + } finally { + a.recycle(); + } + } + + if (state.mDrawable != null && state.mDrawable.canApplyTheme()) { + state.mDrawable.applyTheme(t); + } + + init(); + } + private void updateStateFromTypedArray(TypedArray a) { + final AnimatedRotateState state = mState; + + // 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)); + + final Drawable dr = a.getDrawable(R.styleable.AnimatedRotateDrawable_drawable); + if (dr != null) { + state.mDrawable = dr; + dr.setCallback(this); + } + } + + private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs, + Theme theme) throws XmlPullParserException, IOException { + final AnimatedRotateState state = mState; + + Drawable dr = null; int outerDepth = parser.getDepth(); int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && - (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type != XmlPullParser.START_TAG) { continue; } - if ((drawable = Drawable.createFromXmlInner(r, parser, attrs, theme)) == null) { - Log.w("drawable", "Bad element under <animated-rotate>: " - + parser .getName()); + if ((dr = Drawable.createFromXmlInner(r, parser, attrs, theme)) == null) { + Log.w(TAG, "Bad element under <animated-rotate>: " + parser.getName()); } } - if (drawable == null) { - Log.w("drawable", "No drawable specified for <animated-rotate>"); + if (dr != null) { + state.mDrawable = dr; + dr.setCallback(this); } + } - final AnimatedRotateState rotateState = mState; - rotateState.mDrawable = drawable; - rotateState.mPivotXRel = pivotXRel; - rotateState.mPivotX = pivotX; - rotateState.mPivotYRel = pivotYRel; - rotateState.mPivotY = pivotY; - - init(); - - if (drawable != null) { - drawable.setCallback(this); + private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException { + // If we're not waiting on a theme, verify required attributes. + if (mState.mDrawable == 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"); } } @@ -331,17 +389,27 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mState.mDrawable.clearMutated(); + mMutated = false; + } + final static class AnimatedRotateState extends Drawable.ConstantState { Drawable mDrawable; + int[] mThemeAttrs; int mChangingConfigurations; - boolean mPivotXRel; - float mPivotX; - boolean mPivotYRel; - float mPivotY; - int mFrameDuration; - int mFramesCount; + boolean mPivotXRel = false; + float mPivotX = 0; + boolean mPivotYRel = false; + float mPivotY = 0; + int mFrameDuration = 150; + int mFramesCount = 12; private boolean mCanConstantState; private boolean mCheckedConstantState; @@ -358,6 +426,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac mDrawable.setLayoutDirection(orig.mDrawable.getLayoutDirection()); mDrawable.setBounds(orig.mDrawable.getBounds()); mDrawable.setLevel(orig.mDrawable.getLevel()); + mThemeAttrs = orig.mThemeAttrs; mPivotXRel = orig.mPivotXRel; mPivotX = orig.mPivotX; mPivotYRel = orig.mPivotYRel; @@ -379,6 +448,12 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac } @Override + public boolean canApplyTheme() { + return mThemeAttrs != null || (mDrawable != null && mDrawable.canApplyTheme()) + || super.canApplyTheme(); + } + + @Override public int getChangingConfigurations() { return mChangingConfigurations; } diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java index cb09bbf..5a3a617 100644 --- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java @@ -139,27 +139,15 @@ public class AnimatedStateListDrawable extends StateListDrawable { @Override protected boolean onStateChange(int[] stateSet) { - final int keyframeIndex = mState.indexOfKeyframe(stateSet); - if (keyframeIndex == getCurrentIndex()) { - // Propagate state change to current keyframe. - final Drawable current = getCurrent(); - if (current != null) { - return current.setState(stateSet); - } - return false; - } - - // Attempt to find a valid transition to the keyframe. - if (selectTransition(keyframeIndex)) { - return true; - } - - // No valid transition, attempt to jump directly to the keyframe. - if (selectDrawable(keyframeIndex)) { - return true; - } - - return super.onStateChange(stateSet); + // If we're not already at the target index, either attempt to find a + // valid transition to it or jump directly there. + final int targetIndex = mState.indexOfKeyframe(stateSet); + final boolean changedIndex = targetIndex != getCurrentIndex() + && (selectTransition(targetIndex) || selectDrawable(targetIndex)); + + // Always call super.onStateChanged() to propagate the state change to + // the current drawable. + return super.onStateChange(stateSet) || changedIndex; } private boolean selectTransition(int toIndex) { @@ -359,24 +347,62 @@ public class AnimatedStateListDrawable extends StateListDrawable { throws XmlPullParserException, IOException { final TypedArray a = obtainAttributes( r, theme, attrs, R.styleable.AnimatedStateListDrawable); - super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedStateListDrawable_visible); + updateStateFromTypedArray(a); + a.recycle(); - final StateListState stateListState = getStateListState(); - stateListState.setVariablePadding(a.getBoolean( - R.styleable.AnimatedStateListDrawable_variablePadding, false)); - stateListState.setConstantSize(a.getBoolean( - R.styleable.AnimatedStateListDrawable_constantSize, false)); - stateListState.setEnterFadeDuration(a.getInt( - R.styleable.AnimatedStateListDrawable_enterFadeDuration, 0)); - stateListState.setExitFadeDuration(a.getInt( - R.styleable.AnimatedStateListDrawable_exitFadeDuration, 0)); + inflateChildElements(r, parser, attrs, theme); - setDither(a.getBoolean(R.styleable.AnimatedStateListDrawable_dither, true)); - setAutoMirrored(a.getBoolean(R.styleable.AnimatedStateListDrawable_autoMirrored, false)); + init(); + } + + @Override + public void applyTheme(@Nullable Theme theme) { + super.applyTheme(theme); + final AnimatedStateListState state = mState; + if (state == null || state.mAnimThemeAttrs == null) { + return; + } + + final TypedArray a = theme.resolveAttributes( + state.mAnimThemeAttrs, R.styleable.AnimatedRotateDrawable); + updateStateFromTypedArray(a); a.recycle(); + init(); + } + + private void updateStateFromTypedArray(TypedArray a) { + final AnimatedStateListState state = mState; + + // Account for any configuration changes. + state.mChangingConfigurations |= a.getChangingConfigurations(); + + // Extract the theme attributes, if any. + state.mAnimThemeAttrs = a.extractThemeAttrs(); + + state.setVariablePadding(a.getBoolean( + R.styleable.AnimatedStateListDrawable_variablePadding, state.mVariablePadding)); + state.setConstantSize(a.getBoolean( + R.styleable.AnimatedStateListDrawable_constantSize, state.mConstantSize)); + state.setEnterFadeDuration(a.getInt( + R.styleable.AnimatedStateListDrawable_enterFadeDuration, state.mEnterFadeDuration)); + state.setExitFadeDuration(a.getInt( + R.styleable.AnimatedStateListDrawable_exitFadeDuration, state.mExitFadeDuration)); + + setDither(a.getBoolean( + R.styleable.AnimatedStateListDrawable_dither, state.mDither)); + setAutoMirrored(a.getBoolean( + R.styleable.AnimatedStateListDrawable_autoMirrored, state.mAutoMirrored)); + } + + private void init() { + onStateChange(getState()); + } + + private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs, + Theme theme) throws XmlPullParserException, IOException { int type; final int innerDepth = parser.getDepth() + 1; @@ -398,8 +424,6 @@ public class AnimatedStateListDrawable extends StateListDrawable { parseTransition(r, parser, attrs, theme); } } - - onStateChange(getState()); } private int parseTransition(@NonNull Resources r, @NonNull XmlPullParser parser, @@ -507,10 +531,20 @@ public class AnimatedStateListDrawable extends StateListDrawable { return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mMutated = false; + } + static class AnimatedStateListState extends StateListState { private static final int REVERSE_SHIFT = 32; private static final int REVERSE_MASK = 0x1; + int[] mAnimThemeAttrs; + final LongSparseLongArray mTransitions; final SparseIntArray mStateIds; @@ -519,6 +553,7 @@ public class AnimatedStateListDrawable extends StateListDrawable { super(orig, owner, res); if (orig != null) { + mAnimThemeAttrs = orig.mAnimThemeAttrs; mTransitions = orig.mTransitions.clone(); mStateIds = orig.mStateIds.clone(); } else { @@ -570,6 +605,11 @@ public class AnimatedStateListDrawable extends StateListDrawable { } @Override + public boolean canApplyTheme() { + return mAnimThemeAttrs != null || super.canApplyTheme(); + } + + @Override public Drawable newDrawable() { return new AnimatedStateListDrawable(this, null); } diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index ad0b415..e65dbaf 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -16,7 +16,6 @@ package android.graphics.drawable; import android.animation.Animator; import android.animation.AnimatorInflater; -import android.animation.ValueAnimator; import android.annotation.NonNull; import android.content.res.ColorStateList; import android.content.res.Resources; @@ -137,15 +136,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { private boolean mMutated; public AnimatedVectorDrawable() { - mAnimatedVectorState = new AnimatedVectorDrawableState(null); + this(null, null); } - private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res, - Theme theme) { - mAnimatedVectorState = new AnimatedVectorDrawableState(state); - if (theme != null && canApplyTheme()) { - applyTheme(theme); - } + private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res) { + mAnimatedVectorState = new AnimatedVectorDrawableState(state, mCallback, res); } @Override @@ -157,6 +152,15 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mAnimatedVectorState.mVectorDrawable.clearMutated(); + mMutated = false; + } + @Override public ConstantState getConstantState() { mAnimatedVectorState.mChangingConfigurations = getChangingConfigurations(); @@ -281,7 +285,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { VectorDrawable vectorDrawable = (VectorDrawable) res.getDrawable( drawableRes, theme).mutate(); vectorDrawable.setAllowCaching(false); + vectorDrawable.setCallback(mCallback); pathErrorScale = vectorDrawable.getPixelSize(); + if (mAnimatedVectorState.mVectorDrawable != null) { + mAnimatedVectorState.mVectorDrawable.setCallback(null); + } mAnimatedVectorState.mVectorDrawable = vectorDrawable; } a.recycle(); @@ -308,9 +316,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { @Override public boolean canApplyTheme() { - return super.canApplyTheme() || mAnimatedVectorState != null - && mAnimatedVectorState.mVectorDrawable != null - && mAnimatedVectorState.mVectorDrawable.canApplyTheme(); + return (mAnimatedVectorState != null && mAnimatedVectorState.canApplyTheme()) + || super.canApplyTheme(); } @Override @@ -329,14 +336,22 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { ArrayList<Animator> mAnimators; ArrayMap<Animator, String> mTargetNameMap; - public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy) { + public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy, + Callback owner, Resources res) { if (copy != null) { mChangingConfigurations = copy.mChangingConfigurations; if (copy.mVectorDrawable != null) { - mVectorDrawable = (VectorDrawable) copy.mVectorDrawable.getConstantState().newDrawable(); - mVectorDrawable.mutate(); - mVectorDrawable.setAllowCaching(false); + final ConstantState cs = copy.mVectorDrawable.getConstantState(); + if (res != null) { + mVectorDrawable = (VectorDrawable) cs.newDrawable(res); + } else { + mVectorDrawable = (VectorDrawable) cs.newDrawable(); + } + mVectorDrawable = (VectorDrawable) mVectorDrawable.mutate(); + mVectorDrawable.setCallback(owner); + mVectorDrawable.setLayoutDirection(copy.mVectorDrawable.getLayoutDirection()); mVectorDrawable.setBounds(copy.mVectorDrawable.getBounds()); + mVectorDrawable.setAllowCaching(false); } if (copy.mAnimators != null) { final int numAnimators = copy.mAnimators.size(); @@ -358,18 +373,19 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { } @Override - public Drawable newDrawable() { - return new AnimatedVectorDrawable(this, null, null); + public boolean canApplyTheme() { + return (mVectorDrawable != null && mVectorDrawable.canApplyTheme()) + || super.canApplyTheme(); } @Override - public Drawable newDrawable(Resources res) { - return new AnimatedVectorDrawable(this, res, null); + public Drawable newDrawable() { + return new AnimatedVectorDrawable(this, null); } @Override - public Drawable newDrawable(Resources res, Theme theme) { - return new AnimatedVectorDrawable(this, res, theme); + public Drawable newDrawable(Resources res) { + return new AnimatedVectorDrawable(this, res); } @Override @@ -473,4 +489,21 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { } return true; } + + private final Callback mCallback = new Callback() { + @Override + public void invalidateDrawable(Drawable who) { + invalidateSelf(); + } + + @Override + public void scheduleDrawable(Drawable who, Runnable what, long when) { + scheduleSelf(what, when); + } + + @Override + public void unscheduleDrawable(Drawable who, Runnable what) { + unscheduleSelf(what); + } + }; } diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java index 9a9fd82..a8b6c94 100644 --- a/graphics/java/android/graphics/drawable/AnimationDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java @@ -16,6 +16,8 @@ package android.graphics.drawable; +import com.android.internal.R; + import java.io.IOException; import org.xmlpull.v1.XmlPullParser; @@ -271,27 +273,25 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { + final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimationDrawable); + super.inflateWithAttributes(r, parser, a, R.styleable.AnimationDrawable_visible); + updateStateFromTypedArray(a); + a.recycle(); - TypedArray a = obtainAttributes(r, theme, attrs, - com.android.internal.R.styleable.AnimationDrawable); - - super.inflateWithAttributes(r, parser, a, - com.android.internal.R.styleable.AnimationDrawable_visible); - - mAnimationState.setVariablePadding(a.getBoolean( - com.android.internal.R.styleable.AnimationDrawable_variablePadding, false)); - - mAnimationState.mOneShot = a.getBoolean( - com.android.internal.R.styleable.AnimationDrawable_oneshot, false); + inflateChildElements(r, parser, attrs, theme); - a.recycle(); + setFrame(0, true, false); + } + private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs, + Theme theme) throws XmlPullParserException, IOException { + TypedArray a; int type; final int innerDepth = parser.getDepth()+1; int depth; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && - ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { if (type != XmlPullParser.START_TAG) { continue; } @@ -300,17 +300,15 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An continue; } - a = obtainAttributes( - r, theme, attrs, com.android.internal.R.styleable.AnimationDrawableItem); - int duration = a.getInt( - com.android.internal.R.styleable.AnimationDrawableItem_duration, -1); + a = obtainAttributes(r, theme, attrs, R.styleable.AnimationDrawableItem); + + final int duration = a.getInt(R.styleable.AnimationDrawableItem_duration, -1); if (duration < 0) { - throw new XmlPullParserException( - parser.getPositionDescription() + throw new XmlPullParserException(parser.getPositionDescription() + ": <item> tag requires a 'duration' attribute"); } - int drawableRes = a.getResourceId( - com.android.internal.R.styleable.AnimationDrawableItem_drawable, 0); + + final int drawableRes = a.getResourceId(R.styleable.AnimationDrawableItem_drawable, 0); a.recycle(); @@ -322,9 +320,9 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An // Empty } if (type != XmlPullParser.START_TAG) { - throw new XmlPullParserException(parser.getPositionDescription() + - ": <item> tag requires a 'drawable' attribute or child tag" + - " defining a drawable"); + throw new XmlPullParserException(parser.getPositionDescription() + + ": <item> tag requires a 'drawable' attribute or child tag" + + " defining a drawable"); } dr = Drawable.createFromXmlInner(r, parser, attrs, theme); } @@ -334,8 +332,14 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An dr.setCallback(this); } } + } - setFrame(0, true, false); + private void updateStateFromTypedArray(TypedArray a) { + mAnimationState.mVariablePadding = a.getBoolean( + R.styleable.AnimationDrawable_variablePadding, mAnimationState.mVariablePadding); + + mAnimationState.mOneShot = a.getBoolean( + R.styleable.AnimationDrawable_oneshot, mAnimationState.mOneShot); } @Override @@ -347,9 +351,17 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mMutated = false; + } + private final static class AnimationState extends DrawableContainerState { private int[] mDurations; - private boolean mOneShot; + private boolean mOneShot = false; AnimationState(AnimationState orig, AnimationDrawable owner, Resources res) { diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index cf6be48..79ac651 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -132,7 +132,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(Bitmap bitmap) { - this(new BitmapState(bitmap), null, null); + this(new BitmapState(bitmap), null); } /** @@ -140,7 +140,7 @@ public class BitmapDrawable extends Drawable { * the display metrics of the resources. */ public BitmapDrawable(Resources res, Bitmap bitmap) { - this(new BitmapState(bitmap), res, null); + this(new BitmapState(bitmap), res); mBitmapState.mTargetDensity = mTargetDensity; } @@ -151,7 +151,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null, null); + this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); if (mBitmapState.mBitmap == null) { android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); } @@ -162,7 +162,7 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings("unused") public BitmapDrawable(Resources res, String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null, null); + this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); mBitmapState.mTargetDensity = mTargetDensity; if (mBitmapState.mBitmap == null) { android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); @@ -176,7 +176,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null, null); + this(new BitmapState(BitmapFactory.decodeStream(is)), null); if (mBitmapState.mBitmap == null) { android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); } @@ -187,7 +187,7 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings("unused") public BitmapDrawable(Resources res, java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null, null); + this(new BitmapState(BitmapFactory.decodeStream(is)), null); mBitmapState.mTargetDensity = mTargetDensity; if (mBitmapState.mBitmap == null) { android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); @@ -684,6 +684,14 @@ public class BitmapDrawable extends Drawable { return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mMutated = false; + } + @Override protected boolean onStateChange(int[] stateSet) { final BitmapState state = mBitmapState; @@ -911,17 +919,12 @@ public class BitmapDrawable extends Drawable { @Override public Drawable newDrawable() { - return new BitmapDrawable(this, null, null); + return new BitmapDrawable(this, null); } @Override public Drawable newDrawable(Resources res) { - return new BitmapDrawable(this, res, null); - } - - @Override - public Drawable newDrawable(Resources res, Theme theme) { - return new BitmapDrawable(this, res, theme); + return new BitmapDrawable(this, res); } @Override @@ -934,16 +937,10 @@ public class BitmapDrawable extends Drawable { * The one constructor to rule them all. This is called by all public * constructors to set the state and initialize local properties. */ - private BitmapDrawable(BitmapState state, Resources res, Theme theme) { - if (theme != null && state.canApplyTheme()) { - // If we need to apply a theme, implicitly mutate. - mBitmapState = new BitmapState(state); - applyTheme(theme); - } else { - mBitmapState = state; - } + private BitmapDrawable(BitmapState state, Resources res) { + mBitmapState = state; - initializeWithState(state, res); + initializeWithState(mBitmapState, res); } /** diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java index 40711cf..e5b2b76 100644 --- a/graphics/java/android/graphics/drawable/ClipDrawable.java +++ b/graphics/java/android/graphics/drawable/ClipDrawable.java @@ -16,6 +16,8 @@ package android.graphics.drawable; +import com.android.internal.R; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -49,12 +51,14 @@ import java.io.IOException; * @attr ref android.R.styleable#ClipDrawable_drawable */ public class ClipDrawable extends Drawable implements Drawable.Callback { - private ClipState mClipState; + private ClipState mState; private final Rect mTmpRect = new Rect(); public static final int HORIZONTAL = 1; public static final int VERTICAL = 2; + private boolean mMutated; + ClipDrawable() { this(null, null); } @@ -65,9 +69,9 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { public ClipDrawable(Drawable drawable, int gravity, int orientation) { this(null, null); - mClipState.mDrawable = drawable; - mClipState.mGravity = gravity; - mClipState.mOrientation = orientation; + mState.mDrawable = drawable; + mState.mGravity = gravity; + mState.mOrientation = orientation; if (drawable != null) { drawable.setCallback(this); @@ -79,19 +83,22 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { throws XmlPullParserException, IOException { super.inflate(r, parser, attrs, theme); - int type; - - TypedArray a = obtainAttributes( - r, theme, attrs, com.android.internal.R.styleable.ClipDrawable); + final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ClipDrawable); - int orientation = a.getInt( - com.android.internal.R.styleable.ClipDrawable_clipOrientation, - HORIZONTAL); - int g = a.getInt(com.android.internal.R.styleable.ClipDrawable_gravity, Gravity.LEFT); - Drawable dr = a.getDrawable(com.android.internal.R.styleable.ClipDrawable_drawable); + // Reset mDrawable to preserve old multiple-inflate behavior. This is + // silly, but we have CTS tests that rely on it. + mState.mDrawable = null; + updateStateFromTypedArray(a); + inflateChildElements(r, parser, attrs, theme); + verifyRequiredAttributes(a); a.recycle(); + } + private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs, + Theme theme) throws XmlPullParserException, IOException { + Drawable dr = null; + int type; final int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { @@ -101,15 +108,68 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { dr = Drawable.createFromXmlInner(r, parser, attrs, theme); } - if (dr == null) { - throw new IllegalArgumentException("No drawable specified for <clip>"); + if (dr != null) { + mState.mDrawable = dr; + dr.setCallback(this); + } + } + + private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException { + // If we're not waiting on a theme, verify required attributes. + if (mState.mDrawable == 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(TypedArray a) { + final ClipState state = mState; + + // 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); + + final Drawable dr = a.getDrawable(R.styleable.ClipDrawable_drawable); + if (dr != null) { + state.mDrawable = dr; + dr.setCallback(this); + } + } + + @Override + public void applyTheme(Theme t) { + super.applyTheme(t); + + final ClipState state = mState; + if (state == null || state.mThemeAttrs == null) { + return; + } + + final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ClipDrawable); + try { + updateStateFromTypedArray(a); + verifyRequiredAttributes(a); + } catch (XmlPullParserException e) { + throw new RuntimeException(e); + } finally { + a.recycle(); } - mClipState.mDrawable = dr; - mClipState.mOrientation = orientation; - mClipState.mGravity = g; + if (state.mDrawable != null && state.mDrawable.canApplyTheme()) { + state.mDrawable.applyTheme(t); + } + } - dr.setCallback(this); + @Override + public boolean canApplyTheme() { + return (mState != null && mState.canApplyTheme()) || super.canApplyTheme(); } // overrides from Drawable.Callback @@ -143,78 +203,78 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { @Override public int getChangingConfigurations() { return super.getChangingConfigurations() - | mClipState.mChangingConfigurations - | mClipState.mDrawable.getChangingConfigurations(); + | mState.mChangingConfigurations + | mState.mDrawable.getChangingConfigurations(); } @Override public boolean getPadding(Rect padding) { // XXX need to adjust padding! - return mClipState.mDrawable.getPadding(padding); + return mState.mDrawable.getPadding(padding); } @Override public boolean setVisible(boolean visible, boolean restart) { - mClipState.mDrawable.setVisible(visible, restart); + mState.mDrawable.setVisible(visible, restart); return super.setVisible(visible, restart); } @Override public void setAlpha(int alpha) { - mClipState.mDrawable.setAlpha(alpha); + mState.mDrawable.setAlpha(alpha); } @Override public int getAlpha() { - return mClipState.mDrawable.getAlpha(); + return mState.mDrawable.getAlpha(); } @Override public void setColorFilter(ColorFilter cf) { - mClipState.mDrawable.setColorFilter(cf); + mState.mDrawable.setColorFilter(cf); } @Override public void setTintList(ColorStateList tint) { - mClipState.mDrawable.setTintList(tint); + mState.mDrawable.setTintList(tint); } @Override public void setTintMode(Mode tintMode) { - mClipState.mDrawable.setTintMode(tintMode); + mState.mDrawable.setTintMode(tintMode); } @Override public int getOpacity() { - return mClipState.mDrawable.getOpacity(); + return mState.mDrawable.getOpacity(); } @Override public boolean isStateful() { - return mClipState.mDrawable.isStateful(); + return mState.mDrawable.isStateful(); } @Override protected boolean onStateChange(int[] state) { - return mClipState.mDrawable.setState(state); + return mState.mDrawable.setState(state); } @Override protected boolean onLevelChange(int level) { - mClipState.mDrawable.setLevel(level); + mState.mDrawable.setLevel(level); invalidateSelf(); return true; } @Override protected void onBoundsChange(Rect bounds) { - mClipState.mDrawable.setBounds(bounds); + mState.mDrawable.setBounds(bounds); } @Override public void draw(Canvas canvas) { - if (mClipState.mDrawable.getLevel() == 0) { + if (mState.mDrawable.getLevel() == 0) { return; } @@ -222,41 +282,41 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { final Rect bounds = getBounds(); int level = getLevel(); int w = bounds.width(); - final int iw = 0; //mClipState.mDrawable.getIntrinsicWidth(); - if ((mClipState.mOrientation & HORIZONTAL) != 0) { + final int iw = 0; //mState.mDrawable.getIntrinsicWidth(); + if ((mState.mOrientation & HORIZONTAL) != 0) { w -= (w - iw) * (10000 - level) / 10000; } int h = bounds.height(); - final int ih = 0; //mClipState.mDrawable.getIntrinsicHeight(); - if ((mClipState.mOrientation & VERTICAL) != 0) { + final int ih = 0; //mState.mDrawable.getIntrinsicHeight(); + if ((mState.mOrientation & VERTICAL) != 0) { h -= (h - ih) * (10000 - level) / 10000; } final int layoutDirection = getLayoutDirection(); - Gravity.apply(mClipState.mGravity, w, h, bounds, r, layoutDirection); + Gravity.apply(mState.mGravity, w, h, bounds, r, layoutDirection); if (w > 0 && h > 0) { canvas.save(); canvas.clipRect(r); - mClipState.mDrawable.draw(canvas); + mState.mDrawable.draw(canvas); canvas.restore(); } } @Override public int getIntrinsicWidth() { - return mClipState.mDrawable.getIntrinsicWidth(); + return mState.mDrawable.getIntrinsicWidth(); } @Override public int getIntrinsicHeight() { - return mClipState.mDrawable.getIntrinsicHeight(); + return mState.mDrawable.getIntrinsicHeight(); } @Override public ConstantState getConstantState() { - if (mClipState.canConstantState()) { - mClipState.mChangingConfigurations = getChangingConfigurations(); - return mClipState; + if (mState.canConstantState()) { + mState.mChangingConfigurations = getChangingConfigurations(); + return mState; } return null; } @@ -264,21 +324,44 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { /** @hide */ @Override public void setLayoutDirection(int layoutDirection) { - mClipState.mDrawable.setLayoutDirection(layoutDirection); + mState.mDrawable.setLayoutDirection(layoutDirection); super.setLayoutDirection(layoutDirection); } + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mState.mDrawable.mutate(); + mMutated = true; + } + return this; + } + + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mState.mDrawable.clearMutated(); + mMutated = false; + } + final static class ClipState extends ConstantState { - Drawable mDrawable; + int[] mThemeAttrs; int mChangingConfigurations; - int mOrientation; - int mGravity; + + Drawable mDrawable; + + int mOrientation = HORIZONTAL; + int mGravity = Gravity.LEFT; private boolean mCheckedConstantState; private boolean mCanConstantState; ClipState(ClipState orig, ClipDrawable owner, Resources res) { if (orig != null) { + mThemeAttrs = orig.mThemeAttrs; + mChangingConfigurations = orig.mChangingConfigurations; if (res != null) { mDrawable = orig.mDrawable.getConstantState().newDrawable(res); } else { @@ -295,6 +378,12 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { } @Override + public boolean canApplyTheme() { + return mThemeAttrs != null || (mDrawable != null && mDrawable.canApplyTheme()) + || super.canApplyTheme(); + } + + @Override public Drawable newDrawable() { return new ClipDrawable(this, null); } @@ -320,7 +409,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { } private ClipDrawable(ClipState state, Resources res) { - mClipState = new ClipState(state, this, res); + mState = new ClipState(state, this, res); } } diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java index 1253c46..0f0c844 100644 --- a/graphics/java/android/graphics/drawable/ColorDrawable.java +++ b/graphics/java/android/graphics/drawable/ColorDrawable.java @@ -88,6 +88,14 @@ public class ColorDrawable extends Drawable { return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mMutated = false; + } + @Override public void draw(Canvas canvas) { final ColorFilter colorFilter = mPaint.getColorFilter(); @@ -291,17 +299,12 @@ public class ColorDrawable extends Drawable { @Override public Drawable newDrawable() { - return new ColorDrawable(this, null, null); + return new ColorDrawable(this); } @Override public Drawable newDrawable(Resources res) { - return new ColorDrawable(this, res, null); - } - - @Override - public Drawable newDrawable(Resources res, Theme theme) { - return new ColorDrawable(this, res, theme); + return new ColorDrawable(this); } @Override @@ -310,14 +313,8 @@ public class ColorDrawable extends Drawable { } } - private ColorDrawable(ColorState state, Resources res, Theme theme) { - if (theme != null && state.canApplyTheme()) { - mColorState = new ColorState(state); - applyTheme(theme); - } else { - mColorState = state; - } - + private ColorDrawable(ColorState state) { + mColorState = state; mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); } } diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 9ae788c..1fac5b6 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -917,6 +917,20 @@ public abstract class Drawable { } /** + * Clears the mutated state, allowing this drawable to be cached and + * mutated again. + * <p> + * This is hidden because only framework drawables can be cached, so + * custom drawables don't need to support constant state, mutate(), or + * clearMutated(). + * + * @hide + */ + public void clearMutated() { + // Default implementation is no-op. + } + + /** * Create a drawable from an inputstream */ public static Drawable createFromStream(InputStream is, String srcName) { @@ -1044,54 +1058,72 @@ public abstract class Drawable { final Drawable drawable; final String name = parser.getName(); - if (name.equals("selector")) { - drawable = new StateListDrawable(); - } else if (name.equals("animated-selector")) { - drawable = new AnimatedStateListDrawable(); - } else if (name.equals("level-list")) { - drawable = new LevelListDrawable(); - } else if (name.equals("layer-list")) { - drawable = new LayerDrawable(); - } else if (name.equals("transition")) { - drawable = new TransitionDrawable(); - } else if (name.equals("ripple")) { - drawable = new RippleDrawable(); - } else if (name.equals("color")) { - drawable = new ColorDrawable(); - } else if (name.equals("shape")) { - drawable = new GradientDrawable(); - } else if (name.equals("vector")) { - drawable = new VectorDrawable(); - } else if (name.equals("animated-vector")) { - drawable = new AnimatedVectorDrawable(); - } else if (name.equals("scale")) { - drawable = new ScaleDrawable(); - } else if (name.equals("clip")) { - drawable = new ClipDrawable(); - } else if (name.equals("rotate")) { - drawable = new RotateDrawable(); - } else if (name.equals("animated-rotate")) { - drawable = new AnimatedRotateDrawable(); - } else if (name.equals("animation-list")) { - drawable = new AnimationDrawable(); - } else if (name.equals("inset")) { - drawable = new InsetDrawable(); - } else if (name.equals("bitmap")) { - //noinspection deprecation - drawable = new BitmapDrawable(r); - if (r != null) { - ((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics()); - } - } else if (name.equals("nine-patch")) { - drawable = new NinePatchDrawable(); - if (r != null) { - ((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics()); - } - } else { - throw new XmlPullParserException(parser.getPositionDescription() + - ": invalid drawable tag " + name); - } + switch (name) { + case "selector": + drawable = new StateListDrawable(); + break; + case "animated-selector": + drawable = new AnimatedStateListDrawable(); + break; + case "level-list": + drawable = new LevelListDrawable(); + break; + case "layer-list": + drawable = new LayerDrawable(); + break; + case "transition": + drawable = new TransitionDrawable(); + break; + case "ripple": + drawable = new RippleDrawable(); + break; + case "color": + drawable = new ColorDrawable(); + break; + case "shape": + drawable = new GradientDrawable(); + break; + case "vector": + drawable = new VectorDrawable(); + break; + case "animated-vector": + drawable = new AnimatedVectorDrawable(); + break; + case "scale": + drawable = new ScaleDrawable(); + break; + case "clip": + drawable = new ClipDrawable(); + break; + case "rotate": + drawable = new RotateDrawable(); + break; + case "animated-rotate": + drawable = new AnimatedRotateDrawable(); + break; + case "animation-list": + drawable = new AnimationDrawable(); + break; + case "inset": + drawable = new InsetDrawable(); + break; + case "bitmap": + drawable = new BitmapDrawable(r); + if (r != null) { + ((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics()); + } + break; + case "nine-patch": + drawable = new NinePatchDrawable(); + if (r != null) { + ((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics()); + } + break; + default: + throw new XmlPullParserException(parser.getPositionDescription() + + ": invalid drawable tag " + name); + } drawable.inflate(r, parser, attrs, theme); return drawable; } @@ -1202,7 +1234,7 @@ public abstract class Drawable { * implemented for drawables that can have a theme applied. */ public Drawable newDrawable(Resources res, Theme theme) { - return newDrawable(); + return newDrawable(null); } /** diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index 4a719fe..326490f 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -574,6 +574,15 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mDrawableContainerState.clearMutated(); + mMutated = false; + } + + /** * A ConstantState that can contain several {@link Drawable}s. * * This class was made public to enable testing, and its visibility may change in a future @@ -583,8 +592,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { final DrawableContainer mOwner; final Resources mRes; - Theme mTheme; - SparseArray<ConstantStateFuture> mDrawableFutures; int mChangingConfigurations; @@ -593,11 +600,11 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { Drawable[] mDrawables; int mNumChildren; - boolean mVariablePadding; + boolean mVariablePadding = false; boolean mPaddingChecked; Rect mConstantPadding; - boolean mConstantSize; + boolean mConstantSize = false; boolean mComputedConstantSize; int mConstantWidth; int mConstantHeight; @@ -618,8 +625,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { boolean mMutated; int mLayoutDirection; - int mEnterFadeDuration; - int mExitFadeDuration; + int mEnterFadeDuration = 0; + int mExitFadeDuration = 0; boolean mAutoMirrored; @@ -792,17 +799,17 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } 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); + if (theme != null) { + createAllFutures(); + + final int N = mNumChildren; + final Drawable[] drawables = mDrawables; + for (int i = 0; i < N; i++) { + if (drawables[i] != null && drawables[i].canApplyTheme()) { + drawables[i].applyTheme(theme); + } } } - - mTheme = theme; } @Override @@ -840,6 +847,18 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mMutated = true; } + final void clearMutated() { + final int N = mNumChildren; + final Drawable[] drawables = mDrawables; + for (int i = 0; i < N; i++) { + if (drawables[i] != null) { + drawables[i].clearMutated(); + } + } + + mMutated = false; + } + /** * A boolean value indicating whether to use the maximum padding value * of all frames in the set (false), or to use the padding value of the @@ -1047,10 +1066,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { 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 = mConstantState.newDrawable(state.mRes); } result.setLayoutDirection(state.mLayoutDirection); result.setCallback(state.mOwner); diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index cd6297b..1458238 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -29,6 +29,8 @@ import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.RadialGradient; import android.graphics.Rect; import android.graphics.RectF; @@ -134,6 +136,7 @@ public class GradientDrawable extends Drawable { private Rect mPadding; private Paint mStrokePaint; // optional, set by the caller private ColorFilter mColorFilter; // optional, set by the caller + private PorterDuffColorFilter mTintFilter; private int mAlpha = 0xFF; // modified by the caller private final Path mPath = new Path(); @@ -171,7 +174,7 @@ public class GradientDrawable extends Drawable { } public GradientDrawable() { - this(new GradientState(Orientation.TOP_BOTTOM, null), null); + this(new GradientState(Orientation.TOP_BOTTOM, null)); } /** @@ -179,7 +182,7 @@ public class GradientDrawable extends Drawable { * of colors for the gradient. */ public GradientDrawable(Orientation orientation, int[] colors) { - this(new GradientState(orientation, colors), null); + this(new GradientState(orientation, colors)); } @Override @@ -523,13 +526,15 @@ public class GradientDrawable extends Drawable { mStrokePaint.getStrokeWidth() > 0; final boolean haveFill = currFillAlpha > 0; final GradientState st = mGradientState; + final ColorFilter colorFilter = mColorFilter != null ? mColorFilter : mTintFilter; + /* we need a layer iff we're drawing both a fill and stroke, and the stroke is non-opaque, and our shapetype actually supports fill+stroke. Otherwise we can just draw the stroke (if any) on top of the fill (if any) without worrying about blending artifacts. */ - final boolean useLayer = haveStroke && haveFill && st.mShape != LINE && - currStrokeAlpha < 255 && (mAlpha < 255 || mColorFilter != null); + final boolean useLayer = haveStroke && haveFill && st.mShape != LINE && + currStrokeAlpha < 255 && (mAlpha < 255 || colorFilter != null); /* Drawing with a layer is slower than direct drawing, but it allows us to apply paint effects like alpha and colorfilter to @@ -544,7 +549,7 @@ public class GradientDrawable extends Drawable { } mLayerPaint.setDither(st.mDither); mLayerPaint.setAlpha(mAlpha); - mLayerPaint.setColorFilter(mColorFilter); + mLayerPaint.setColorFilter(colorFilter); float rad = mStrokePaint.getStrokeWidth(); canvas.saveLayer(mRect.left - rad, mRect.top - rad, @@ -561,14 +566,14 @@ public class GradientDrawable extends Drawable { */ mFillPaint.setAlpha(currFillAlpha); mFillPaint.setDither(st.mDither); - mFillPaint.setColorFilter(mColorFilter); - if (mColorFilter != null && st.mColorStateList == null) { + mFillPaint.setColorFilter(colorFilter); + if (colorFilter != null && st.mColorStateList == null) { mFillPaint.setColor(mAlpha << 24); } if (haveStroke) { mStrokePaint.setAlpha(currStrokeAlpha); mStrokePaint.setDither(st.mDither); - mStrokePaint.setColorFilter(mColorFilter); + mStrokePaint.setColorFilter(colorFilter); } } @@ -593,7 +598,7 @@ public class GradientDrawable extends Drawable { canvas.drawRoundRect(mRect, rad, rad, mStrokePaint); } } else { - if (mFillPaint.getColor() != 0 || mColorFilter != null || + if (mFillPaint.getColor() != 0 || colorFilter != null || mFillPaint.getShader() != null) { canvas.drawRect(mRect, mFillPaint); } @@ -768,6 +773,11 @@ public class GradientDrawable extends Drawable { } } + if (s.mTint != null && s.mTintMode != null) { + mTintFilter = updateTintFilter(mTintFilter, s.mTint, s.mTintMode); + invalidateSelf = true; + } + if (invalidateSelf) { invalidateSelf(); return true; @@ -781,7 +791,8 @@ public class GradientDrawable extends Drawable { final GradientState s = mGradientState; return super.isStateful() || (s.mColorStateList != null && s.mColorStateList.isStateful()) - || (s.mStrokeColorStateList != null && s.mStrokeColorStateList.isStateful()); + || (s.mStrokeColorStateList != null && s.mStrokeColorStateList.isStateful()) + || (s.mTint != null && s.mTint.isStateful()); } @Override @@ -824,6 +835,20 @@ public class GradientDrawable extends Drawable { } @Override + public void setTintList(ColorStateList tint) { + mGradientState.mTint = tint; + mTintFilter = updateTintFilter(mTintFilter, tint, mGradientState.mTintMode); + invalidateSelf(); + } + + @Override + public void setTintMode(PorterDuff.Mode tintMode) { + mGradientState.mTintMode = tintMode; + mTintFilter = updateTintFilter(mTintFilter, mGradientState.mTint, tintMode); + invalidateSelf(); + } + + @Override public int getOpacity() { return (mAlpha == 255 && mGradientState.mOpaqueOverBounds && isOpaqueForState()) ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT; @@ -997,13 +1022,16 @@ public class GradientDrawable extends Drawable { super.applyTheme(t); final GradientState state = mGradientState; - if (state == null || state.mThemeAttrs == null) { + if (state == null) { return; } - final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.GradientDrawable); - updateStateFromTypedArray(a); - a.recycle(); + if (state.mThemeAttrs != null) { + final TypedArray a = t.resolveAttributes( + state.mThemeAttrs, R.styleable.GradientDrawable); + updateStateFromTypedArray(a); + a.recycle(); + } applyThemeChildElements(t); @@ -1045,15 +1073,23 @@ public class GradientDrawable extends Drawable { state.mUseLevelForShape = a.getBoolean( R.styleable.GradientDrawable_useLevel, state.mUseLevelForShape); } + + final int tintMode = a.getInt(R.styleable.GradientDrawable_tintMode, -1); + if (tintMode != -1) { + state.mTintMode = Drawable.parseTintMode(tintMode, PorterDuff.Mode.SRC_IN); + } + + final ColorStateList tint = a.getColorStateList(R.styleable.GradientDrawable_tint); + if (tint != null) { + state.mTint = tint; + } + + mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); } @Override public boolean canApplyTheme() { - final GradientState st = mGradientState; - return st != null && (st.mThemeAttrs != null || st.mAttrSize != null - || st.mAttrGradient != null || st.mAttrSolid != null - || st.mAttrStroke != null || st.mAttrCorners != null - || st.mAttrPadding != null); + return (mGradientState != null && mGradientState.canApplyTheme()) || super.canApplyTheme(); } private void applyThemeChildElements(Theme t) { @@ -1359,9 +1395,12 @@ public class GradientDrawable extends Drawable { } else { radiusType = RADIUS_TYPE_FRACTION; } - } else { + } else if (tv.type == TypedValue.TYPE_DIMENSION) { radius = tv.getDimension(r.getDisplayMetrics()); radiusType = RADIUS_TYPE_PIXELS; + } else { + radius = tv.getFloat(); + radiusType = RADIUS_TYPE_PIXELS; } st.mGradientRadius = radius; @@ -1479,6 +1518,14 @@ public class GradientDrawable extends Drawable { return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mMutated = false; + } + final static class GradientState extends ConstantState { public int mChangingConfigurations; public int mShape = RECTANGLE; @@ -1505,14 +1552,18 @@ public class GradientDrawable extends Drawable { public int mThickness = -1; public boolean mDither = false; - private float mCenterX = 0.5f; - private float mCenterY = 0.5f; - private float mGradientRadius = 0.5f; - private int mGradientRadiusType = RADIUS_TYPE_PIXELS; - private boolean mUseLevel; - private boolean mUseLevelForShape; - private boolean mOpaqueOverBounds; - private boolean mOpaqueOverShape; + float mCenterX = 0.5f; + float mCenterY = 0.5f; + float mGradientRadius = 0.5f; + int mGradientRadiusType = RADIUS_TYPE_PIXELS; + boolean mUseLevel = false; + boolean mUseLevelForShape = true; + + boolean mOpaqueOverBounds; + boolean mOpaqueOverShape; + + ColorStateList mTint = null; + PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE; int[] mThemeAttrs; int[] mAttrSize; @@ -1566,6 +1617,8 @@ public class GradientDrawable extends Drawable { mUseLevelForShape = state.mUseLevelForShape; mOpaqueOverBounds = state.mOpaqueOverBounds; mOpaqueOverShape = state.mOpaqueOverShape; + mTint = state.mTint; + mTintMode = state.mTintMode; mThemeAttrs = state.mThemeAttrs; mAttrSize = state.mAttrSize; mAttrGradient = state.mAttrGradient; @@ -1577,22 +1630,19 @@ public class GradientDrawable extends Drawable { @Override public boolean canApplyTheme() { - return mThemeAttrs != null; + return mThemeAttrs != null || mAttrSize != null || mAttrGradient != null + || mAttrSolid != null || mAttrStroke != null || mAttrCorners != null + || mAttrPadding != null || super.canApplyTheme(); } @Override public Drawable newDrawable() { - return new GradientDrawable(this, null); + return new GradientDrawable(this); } @Override public Drawable newDrawable(Resources res) { - return new GradientDrawable(this, null); - } - - @Override - public Drawable newDrawable(Resources res, Theme theme) { - return new GradientDrawable(this, theme); + return new GradientDrawable(this); } @Override @@ -1696,18 +1746,11 @@ public class GradientDrawable extends Drawable { * 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) { - if (theme != null && state.canApplyTheme()) { - // If we need to apply a theme, implicitly mutate. - mGradientState = new GradientState(state); - applyTheme(theme); - } else { - mGradientState = state; - } + private GradientDrawable(GradientState state) { + mGradientState = state; - initializeWithState(state); + initializeWithState(mGradientState); mGradientIsDirty = true; mMutated = false; diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java index 961d160..b88d9e6 100644 --- a/graphics/java/android/graphics/drawable/InsetDrawable.java +++ b/graphics/java/android/graphics/drawable/InsetDrawable.java @@ -55,7 +55,8 @@ import java.io.IOException; public class InsetDrawable extends Drawable implements Drawable.Callback { private final Rect mTmpRect = new Rect(); - private InsetState mInsetState; + private final InsetState mState; + private boolean mMutated; /*package*/ InsetDrawable() { @@ -70,11 +71,11 @@ public class InsetDrawable extends Drawable implements Drawable.Callback { int insetRight, int insetBottom) { this(null, null); - mInsetState.mDrawable = drawable; - mInsetState.mInsetLeft = insetLeft; - mInsetState.mInsetTop = insetTop; - mInsetState.mInsetRight = insetRight; - mInsetState.mInsetBottom = insetBottom; + mState.mDrawable = drawable; + mState.mInsetLeft = insetLeft; + mState.mInsetTop = insetTop; + mState.mInsetRight = insetRight; + mState.mInsetBottom = insetBottom; if (drawable != null) { drawable.setCallback(this); @@ -87,11 +88,20 @@ public class InsetDrawable extends Drawable implements Drawable.Callback { final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.InsetDrawable); super.inflateWithAttributes(r, parser, a, R.styleable.InsetDrawable_visible); - mInsetState.mDrawable = null; + // Reset mDrawable to preserve old multiple-inflate behavior. This is + // silly, but we have CTS tests that rely on it. + mState.mDrawable = null; + updateStateFromTypedArray(a); + inflateChildElements(r, parser, attrs, theme); + verifyRequiredAttributes(a); + a.recycle(); + } + private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs, + Theme theme) throws XmlPullParserException, IOException { // Load inner XML elements. - if (mInsetState.mDrawable == null) { + if (mState.mDrawable == null) { int type; while ((type=parser.next()) == XmlPullParser.TEXT) { } @@ -102,26 +112,23 @@ public class InsetDrawable extends Drawable implements Drawable.Callback { + "child tag defining a drawable"); } final Drawable dr = Drawable.createFromXmlInner(r, parser, attrs, theme); - mInsetState.mDrawable = dr; + mState.mDrawable = dr; dr.setCallback(this); } - - verifyRequiredAttributes(a); - a.recycle(); } private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException { // If we're not waiting on a theme, verify required attributes. - if (mInsetState.mDrawable == null && (mInsetState.mThemeAttrs == null - || mInsetState.mThemeAttrs[R.styleable.InsetDrawable_drawable] == 0)) { - throw new XmlPullParserException(a.getPositionDescription() + - ": <inset> tag requires a 'drawable' attribute or " + if (mState.mDrawable == 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(TypedArray a) throws XmlPullParserException { - final InsetState state = mInsetState; + final InsetState state = mState; // Account for any configuration changes. state.mChangingConfigurations |= a.getChangingConfigurations(); @@ -169,25 +176,31 @@ public class InsetDrawable extends Drawable implements Drawable.Callback { public void applyTheme(Theme t) { super.applyTheme(t); - final InsetState state = mInsetState; - if (state == null || state.mThemeAttrs == null) { + final InsetState state = mState; + if (state == null) { return; } - final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.InsetDrawable); - try { - updateStateFromTypedArray(a); - verifyRequiredAttributes(a); - } catch (XmlPullParserException e) { - throw new RuntimeException(e); - } finally { - a.recycle(); + if (state.mThemeAttrs != null) { + final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.InsetDrawable); + try { + updateStateFromTypedArray(a); + verifyRequiredAttributes(a); + } catch (XmlPullParserException e) { + throw new RuntimeException(e); + } finally { + a.recycle(); + } + } + + if (state.mDrawable != null && state.mDrawable.canApplyTheme()) { + state.mDrawable.applyTheme(t); } } @Override public boolean canApplyTheme() { - return mInsetState != null && mInsetState.mThemeAttrs != null; + return (mState != null && mState.canApplyTheme()) || super.canApplyTheme(); } @Override @@ -216,112 +229,112 @@ public class InsetDrawable extends Drawable implements Drawable.Callback { @Override public void draw(Canvas canvas) { - mInsetState.mDrawable.draw(canvas); + mState.mDrawable.draw(canvas); } @Override public int getChangingConfigurations() { return super.getChangingConfigurations() - | mInsetState.mChangingConfigurations - | mInsetState.mDrawable.getChangingConfigurations(); + | mState.mChangingConfigurations + | mState.mDrawable.getChangingConfigurations(); } @Override public boolean getPadding(Rect padding) { - boolean pad = mInsetState.mDrawable.getPadding(padding); + boolean pad = mState.mDrawable.getPadding(padding); - padding.left += mInsetState.mInsetLeft; - padding.right += mInsetState.mInsetRight; - padding.top += mInsetState.mInsetTop; - padding.bottom += mInsetState.mInsetBottom; + padding.left += mState.mInsetLeft; + padding.right += mState.mInsetRight; + padding.top += mState.mInsetTop; + padding.bottom += mState.mInsetBottom; - return pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight | - mInsetState.mInsetTop | mInsetState.mInsetBottom) != 0; + return pad || (mState.mInsetLeft | mState.mInsetRight | + mState.mInsetTop | mState.mInsetBottom) != 0; } /** @hide */ @Override public Insets getOpticalInsets() { final Insets contentInsets = super.getOpticalInsets(); - return Insets.of(contentInsets.left + mInsetState.mInsetLeft, - contentInsets.top + mInsetState.mInsetTop, - contentInsets.right + mInsetState.mInsetRight, - contentInsets.bottom + mInsetState.mInsetBottom); + return Insets.of(contentInsets.left + mState.mInsetLeft, + contentInsets.top + mState.mInsetTop, + contentInsets.right + mState.mInsetRight, + contentInsets.bottom + mState.mInsetBottom); } @Override public void setHotspot(float x, float y) { - mInsetState.mDrawable.setHotspot(x, y); + mState.mDrawable.setHotspot(x, y); } @Override public void setHotspotBounds(int left, int top, int right, int bottom) { - mInsetState.mDrawable.setHotspotBounds(left, top, right, bottom); + mState.mDrawable.setHotspotBounds(left, top, right, bottom); } /** @hide */ @Override public void getHotspotBounds(Rect outRect) { - mInsetState.mDrawable.getHotspotBounds(outRect); + mState.mDrawable.getHotspotBounds(outRect); } @Override public boolean setVisible(boolean visible, boolean restart) { - mInsetState.mDrawable.setVisible(visible, restart); + mState.mDrawable.setVisible(visible, restart); return super.setVisible(visible, restart); } @Override public void setAlpha(int alpha) { - mInsetState.mDrawable.setAlpha(alpha); + mState.mDrawable.setAlpha(alpha); } @Override public int getAlpha() { - return mInsetState.mDrawable.getAlpha(); + return mState.mDrawable.getAlpha(); } @Override public void setColorFilter(ColorFilter cf) { - mInsetState.mDrawable.setColorFilter(cf); + mState.mDrawable.setColorFilter(cf); } @Override public void setTintList(ColorStateList tint) { - mInsetState.mDrawable.setTintList(tint); + mState.mDrawable.setTintList(tint); } @Override public void setTintMode(Mode tintMode) { - mInsetState.mDrawable.setTintMode(tintMode); + mState.mDrawable.setTintMode(tintMode); } /** {@hide} */ @Override public void setLayoutDirection(int layoutDirection) { - mInsetState.mDrawable.setLayoutDirection(layoutDirection); + mState.mDrawable.setLayoutDirection(layoutDirection); } @Override public int getOpacity() { - return mInsetState.mDrawable.getOpacity(); + return mState.mDrawable.getOpacity(); } @Override public boolean isStateful() { - return mInsetState.mDrawable.isStateful(); + return mState.mDrawable.isStateful(); } @Override protected boolean onStateChange(int[] state) { - boolean changed = mInsetState.mDrawable.setState(state); + boolean changed = mState.mDrawable.setState(state); onBoundsChange(getBounds()); return changed; } @Override protected boolean onLevelChange(int level) { - return mInsetState.mDrawable.setLevel(level); + return mState.mDrawable.setLevel(level); } @Override @@ -329,34 +342,36 @@ public class InsetDrawable extends Drawable implements Drawable.Callback { final Rect r = mTmpRect; r.set(bounds); - r.left += mInsetState.mInsetLeft; - r.top += mInsetState.mInsetTop; - r.right -= mInsetState.mInsetRight; - r.bottom -= mInsetState.mInsetBottom; + r.left += mState.mInsetLeft; + r.top += mState.mInsetTop; + r.right -= mState.mInsetRight; + r.bottom -= mState.mInsetBottom; - mInsetState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom); + mState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom); } @Override public int getIntrinsicWidth() { - return mInsetState.mDrawable.getIntrinsicWidth(); + return mState.mDrawable.getIntrinsicWidth() + + mState.mInsetLeft + mState.mInsetRight; } @Override public int getIntrinsicHeight() { - return mInsetState.mDrawable.getIntrinsicHeight(); + return mState.mDrawable.getIntrinsicHeight() + + mState.mInsetTop + mState.mInsetBottom; } @Override public void getOutline(@NonNull Outline outline) { - mInsetState.mDrawable.getOutline(outline); + mState.mDrawable.getOutline(outline); } @Override public ConstantState getConstantState() { - if (mInsetState.canConstantState()) { - mInsetState.mChangingConfigurations = getChangingConfigurations(); - return mInsetState; + if (mState.canConstantState()) { + mState.mChangingConfigurations = getChangingConfigurations(); + return mState; } return null; } @@ -364,17 +379,26 @@ public class InsetDrawable extends Drawable implements Drawable.Callback { @Override public Drawable mutate() { if (!mMutated && super.mutate() == this) { - mInsetState.mDrawable.mutate(); + mState.mDrawable.mutate(); mMutated = true; } return this; } /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mState.mDrawable.clearMutated(); + mMutated = false; + } + + /** * Returns the drawable wrapped by this InsetDrawable. May be null. */ public Drawable getDrawable() { - return mInsetState.mDrawable; + return mState.mDrawable; } final static class InsetState extends ConstantState { @@ -383,13 +407,13 @@ public class InsetDrawable extends Drawable implements Drawable.Callback { Drawable mDrawable; - int mInsetLeft; - int mInsetTop; - int mInsetRight; - int mInsetBottom; + int mInsetLeft = 0; + int mInsetTop = 0; + int mInsetRight = 0; + int mInsetBottom = 0; - boolean mCheckedConstantState; - boolean mCanConstantState; + private boolean mCheckedConstantState; + private boolean mCanConstantState; InsetState(InsetState orig, InsetDrawable owner, Resources res) { if (orig != null) { @@ -413,6 +437,12 @@ public class InsetDrawable extends Drawable implements Drawable.Callback { } @Override + public boolean canApplyTheme() { + return mThemeAttrs != null || (mDrawable != null && mDrawable.canApplyTheme()) + || super.canApplyTheme(); + } + + @Override public Drawable newDrawable() { return new InsetDrawable(this, null); } @@ -438,7 +468,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback { } private InsetDrawable(InsetState state, Resources res) { - mInsetState = new InsetState(state, this, res); + mState = new InsetState(state, this, res); } } diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java index 001ed88..689d225 100644 --- a/graphics/java/android/graphics/drawable/LayerDrawable.java +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -100,10 +100,10 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { * @param state The constant drawable state. */ LayerDrawable(Drawable[] layers, LayerState state) { - this(state, null, null); - int length = layers.length; - ChildDrawable[] r = new ChildDrawable[length]; + this(state, null); + final int length = layers.length; + final ChildDrawable[] r = new ChildDrawable[length]; for (int i = 0; i < length; i++) { r[i] = new ChildDrawable(); r[i].mDrawable = layers[i]; @@ -117,18 +117,14 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { } LayerDrawable() { - this((LayerState) null, null, null); + this((LayerState) null, null); } - LayerDrawable(LayerState state, Resources res, Theme theme) { - final LayerState as = createConstantState(state, res); - mLayerState = as; - if (as.mNum > 0) { + LayerDrawable(LayerState state, Resources res) { + mLayerState = createConstantState(state, res); + if (mLayerState.mNum > 0) { ensurePadding(); } - if (theme != null && canApplyTheme()) { - applyTheme(theme); - } } LayerState createConstantState(LayerState state, Resources res) { @@ -256,8 +252,8 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { a.recycle(); } - final ChildDrawable[] array = mLayerState.mChildren; - final int N = mLayerState.mNum; + final ChildDrawable[] array = state.mChildren; + final int N = state.mNum; for (int i = 0; i < N; i++) { final ChildDrawable layer = array[i]; if (layer.mThemeAttrs != null) { @@ -279,25 +275,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { @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++) { - final ChildDrawable layer = array[i]; - if (layer.mThemeAttrs != null || layer.mDrawable.canApplyTheme()) { - return true; - } - } - - return false; + return (mLayerState != null && mLayerState.canApplyTheme()) || super.canApplyTheme(); } /** @@ -940,6 +918,19 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + final ChildDrawable[] array = mLayerState.mChildren; + final int N = mLayerState.mNum; + for (int i = 0; i < N; i++) { + array[i].mDrawable.clearMutated(); + } + mMutated = false; + } + /** @hide */ @Override public void setLayoutDirection(int layoutDirection) { @@ -1029,22 +1020,30 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { @Override public boolean canApplyTheme() { - return mThemeAttrs != null; + if (mThemeAttrs != null || super.canApplyTheme()) { + return true; + } + + final ChildDrawable[] array = mChildren; + final int N = mNum; + for (int i = 0; i < N; i++) { + final ChildDrawable layer = array[i]; + if (layer.mThemeAttrs != null || layer.mDrawable.canApplyTheme()) { + return true; + } + } + + return false; } @Override public Drawable newDrawable() { - return new LayerDrawable(this, null, null); + return new LayerDrawable(this, null); } @Override public Drawable newDrawable(Resources res) { - return new LayerDrawable(this, res, null); - } - - @Override - public Drawable newDrawable(Resources res, Theme theme) { - return new LayerDrawable(this, res, theme); + return new LayerDrawable(this, res); } @Override diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java index bc1c61d..9e918f6 100644 --- a/graphics/java/android/graphics/drawable/LevelListDrawable.java +++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java @@ -153,6 +153,14 @@ public class LevelListDrawable extends DrawableContainer { return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mMutated = false; + } + private final static class LevelListState extends DrawableContainerState { private int[] mLows; private int[] mHighs; diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 6c62ccf..d821224 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -90,7 +90,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, null); + this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), null); } /** @@ -99,7 +99,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, null); + this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), res); mNinePatchState.mTargetDensity = mTargetDensity; } @@ -112,7 +112,7 @@ 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, null); + res); mNinePatchState.mTargetDensity = mTargetDensity; } @@ -123,7 +123,7 @@ public class NinePatchDrawable extends Drawable { */ @Deprecated public NinePatchDrawable(NinePatch patch) { - this(new NinePatchState(patch, new Rect()), null, null); + this(new NinePatchState(patch, new Rect()), null); } /** @@ -131,7 +131,7 @@ 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, null); + this(new NinePatchState(patch, new Rect()), res); mNinePatchState.mTargetDensity = mTargetDensity; } @@ -563,6 +563,14 @@ public class NinePatchDrawable extends Drawable { return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mMutated = false; + } + @Override protected boolean onStateChange(int[] stateSet) { final NinePatchState state = mNinePatchState; @@ -646,17 +654,12 @@ public class NinePatchDrawable extends Drawable { @Override public Drawable newDrawable() { - return new NinePatchDrawable(this, null, null); + return new NinePatchDrawable(this, null); } @Override public Drawable newDrawable(Resources res) { - return new NinePatchDrawable(this, res, null); - } - - @Override - public Drawable newDrawable(Resources res, Theme theme) { - return new NinePatchDrawable(this, res, theme); + return new NinePatchDrawable(this, res); } @Override @@ -669,16 +672,10 @@ public class NinePatchDrawable extends Drawable { * The one constructor to rule them all. This is called by all public * constructors to set the state and initialize local properties. */ - private NinePatchDrawable(NinePatchState state, Resources res, Theme theme) { - if (theme != null && state.canApplyTheme()) { - // If we need to apply a theme, implicitly mutate. - mNinePatchState = new NinePatchState(state); - applyTheme(theme); - } else { - mNinePatchState = state; - } + private NinePatchDrawable(NinePatchState state, Resources res) { + mNinePatchState = state; - initializeWithState(state, res); + initializeWithState(mNinePatchState, res); } /** diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java index faa89bf..21d865f 100644 --- a/graphics/java/android/graphics/drawable/RippleBackground.java +++ b/graphics/java/android/graphics/drawable/RippleBackground.java @@ -43,10 +43,12 @@ class RippleBackground { private static final float WAVE_OPACITY_DECAY_VELOCITY = 3.0f / GLOBAL_SPEED; private static final float WAVE_OUTER_OPACITY_EXIT_VELOCITY_MAX = 4.5f * GLOBAL_SPEED; private static final float WAVE_OUTER_OPACITY_EXIT_VELOCITY_MIN = 1.5f * GLOBAL_SPEED; - private static final float WAVE_OUTER_OPACITY_ENTER_VELOCITY = 10.0f * GLOBAL_SPEED; private static final float WAVE_OUTER_SIZE_INFLUENCE_MAX = 200f; private static final float WAVE_OUTER_SIZE_INFLUENCE_MIN = 40f; + private static final int ENTER_DURATION = 667; + private static final int ENTER_DURATION_FAST = 100; + // Hardware animators. private final ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<RenderNodeAnimator>(); @@ -224,21 +226,20 @@ class RippleBackground { /** * Starts the enter animation. */ - public void enter() { + public void enter(boolean fast) { cancel(); - final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_ENTER_VELOCITY); - final ObjectAnimator outer = ObjectAnimator.ofFloat(this, "outerOpacity", 0, 1); - outer.setAutoCancel(true); - outer.setDuration(outerDuration); - outer.setInterpolator(LINEAR_INTERPOLATOR); + final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, "outerOpacity", 0, 1); + opacity.setAutoCancel(true); + opacity.setDuration(fast ? ENTER_DURATION_FAST : ENTER_DURATION); + opacity.setInterpolator(LINEAR_INTERPOLATOR); - mAnimOuterOpacity = outer; + mAnimOuterOpacity = opacity; // Enter animations always run on the UI thread, since it's unlikely // that anything interesting is happening until the user lifts their // finger. - outer.start(); + opacity.start(); } /** diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index c7aa98e..e658279 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -167,7 +167,7 @@ public class RippleDrawable extends LayerDrawable { * Constructor used for drawable inflation. */ RippleDrawable() { - this(new RippleState(null, null, null), null, null); + this(new RippleState(null, null, null), null); } /** @@ -180,7 +180,7 @@ public class RippleDrawable extends LayerDrawable { */ public RippleDrawable(@NonNull ColorStateList color, @Nullable Drawable content, @Nullable Drawable mask) { - this(new RippleState(null, null, null), null, null); + this(new RippleState(null, null, null), null); if (color == null) { throw new IllegalArgumentException("RippleDrawable requires a non-null color"); @@ -280,7 +280,7 @@ public class RippleDrawable extends LayerDrawable { } setRippleActive(enabled && pressed); - setBackgroundActive(focused || (enabled && pressed)); + setBackgroundActive(focused || (enabled && pressed), focused); return changed; } @@ -296,11 +296,11 @@ public class RippleDrawable extends LayerDrawable { } } - private void setBackgroundActive(boolean active) { + private void setBackgroundActive(boolean active, boolean focused) { if (mBackgroundActive != active) { mBackgroundActive = active; if (active) { - tryBackgroundEnter(); + tryBackgroundEnter(focused); } else { tryBackgroundExit(); } @@ -333,8 +333,11 @@ public class RippleDrawable extends LayerDrawable { } if (mBackgroundActive) { - tryBackgroundEnter(); + tryBackgroundEnter(false); } + + // Skip animations, just show the correct final states. + jumpToCurrentState(); } return changed; @@ -470,7 +473,7 @@ public class RippleDrawable extends LayerDrawable { @Override public boolean canApplyTheme() { - return super.canApplyTheme() || mState != null && mState.mTouchThemeAttrs != null; + return (mState != null && mState.canApplyTheme()) || super.canApplyTheme(); } @Override @@ -489,14 +492,14 @@ public class RippleDrawable extends LayerDrawable { /** * Creates an active hotspot at the specified location. */ - private void tryBackgroundEnter() { + private void tryBackgroundEnter(boolean focused) { if (mBackground == null) { mBackground = new RippleBackground(this, mHotspotBounds); } final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT); mBackground.setup(mState.mMaxRadius, color, mDensity); - mBackground.enter(); + mBackground.enter(focused); } private void tryBackgroundExit() { @@ -715,10 +718,12 @@ public class RippleDrawable extends LayerDrawable { final ChildDrawable[] array = mLayerState.mChildren; final int count = mLayerState.mNum; - // We don't need a layer if we don't expect to draw any ripples, we have - // an explicit mask, or if the non-mask content is all opaque. + // We don't need a layer if we don't expect to draw any ripples or + // a background, we have an explicit mask, or if the non-mask content + // is all opaque. boolean needsLayer = false; - if ((mExitingRipplesCount > 0 || mBackground != null) && mMask == null) { + if ((mExitingRipplesCount > 0 || (mBackground != null && mBackground.shouldDraw())) + && mMask == null) { for (int i = 0; i < count; i++) { if (array[i].mId != R.id.mask && array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) { @@ -924,17 +929,12 @@ public class RippleDrawable extends LayerDrawable { @Override public Drawable newDrawable() { - return new RippleDrawable(this, null, null); + return new RippleDrawable(this, null); } @Override public Drawable newDrawable(Resources res) { - return new RippleDrawable(this, res, null); - } - - @Override - public Drawable newDrawable(Resources res, Theme theme) { - return new RippleDrawable(this, res, theme); + return new RippleDrawable(this, res); } } @@ -968,37 +968,18 @@ public class RippleDrawable extends LayerDrawable { return mState.mMaxRadius; } - private RippleDrawable(RippleState state, Resources res, Theme theme) { - boolean needsTheme = false; + private RippleDrawable(RippleState state, Resources res) { + mState = new RippleState(state, this, res); + mLayerState = mState; - final RippleState ns; - if (theme != null && state != null && state.canApplyTheme()) { - ns = new RippleState(state, this, res); - needsTheme = true; - } else if (state == null) { - ns = new RippleState(null, this, res); - } else { - // We always need a new state since child drawables contain local - // state but live within the parent's constant state. - // TODO: Move child drawables into local state. - ns = new RippleState(state, this, res); + if (mState.mNum > 0) { + ensurePadding(); } if (res != null) { mDensity = res.getDisplayMetrics().density; } - mState = ns; - mLayerState = ns; - - if (ns.mNum > 0) { - ensurePadding(); - } - - if (needsTheme) { - applyTheme(theme); - } - initializeFromState(); } diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java index 55c9637..3304b33 100644 --- a/graphics/java/android/graphics/drawable/RotateDrawable.java +++ b/graphics/java/android/graphics/drawable/RotateDrawable.java @@ -16,6 +16,8 @@ package android.graphics.drawable; +import com.android.internal.R; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -29,7 +31,6 @@ import android.content.res.TypedArray; import android.content.res.Resources.Theme; import android.util.TypedValue; import android.util.AttributeSet; -import android.util.Log; import java.io.IOException; @@ -312,6 +313,11 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { } @Override + public boolean canApplyTheme() { + return (mState != null && mState.canApplyTheme()) || super.canApplyTheme(); + } + + @Override public void invalidateDrawable(Drawable who) { final Callback callback = getCallback(); if (callback != null) { @@ -399,79 +405,103 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { - final TypedArray a = obtainAttributes(r, theme, attrs, - com.android.internal.R.styleable.RotateDrawable); - - super.inflateWithAttributes(r, parser, a, - com.android.internal.R.styleable.RotateDrawable_visible); - - TypedValue tv = a.peekValue(com.android.internal.R.styleable.RotateDrawable_pivotX); - final boolean pivotXRel; - final float pivotX; - if (tv == null) { - pivotXRel = true; - pivotX = 0.5f; - } else { - pivotXRel = tv.type == TypedValue.TYPE_FRACTION; - pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); - } + final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.RotateDrawable); + super.inflateWithAttributes(r, parser, a, R.styleable.RotateDrawable_visible); + + // Reset mDrawable to preserve old multiple-inflate behavior. This is + // silly, but we have CTS tests that rely on it. + mState.mDrawable = null; + + updateStateFromTypedArray(a); + inflateChildElements(r, parser, attrs, theme); + verifyRequiredAttributes(a); + a.recycle(); + } + + @Override + public void applyTheme(Theme t) { + super.applyTheme(t); - tv = a.peekValue(com.android.internal.R.styleable.RotateDrawable_pivotY); - final boolean pivotYRel; - final float pivotY; - if (tv == null) { - pivotYRel = true; - pivotY = 0.5f; - } else { - pivotYRel = tv.type == TypedValue.TYPE_FRACTION; - pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + final RotateState state = mState; + if (state == null) { + return; } - final float fromDegrees = a.getFloat( - com.android.internal.R.styleable.RotateDrawable_fromDegrees, 0.0f); - final float toDegrees = a.getFloat( - com.android.internal.R.styleable.RotateDrawable_toDegrees, 360.0f); + 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(); + } + } - final int res = a.getResourceId( - com.android.internal.R.styleable.RotateDrawable_drawable, 0); - Drawable drawable = null; - if (res > 0) { - drawable = r.getDrawable(res, theme); + if (state.mDrawable != null && state.mDrawable.canApplyTheme()) { + state.mDrawable.applyTheme(t); } - a.recycle(); + } - final int outerDepth = parser.getDepth(); + private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs, + Theme theme) throws XmlPullParserException, IOException { + Drawable dr = null; int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && - (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - + final int outerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type != XmlPullParser.START_TAG) { continue; } + dr = Drawable.createFromXmlInner(r, parser, attrs, theme); + } - if ((drawable = Drawable.createFromXmlInner(r, parser, attrs, theme)) == null) { - Log.w("drawable", "Bad element under <rotate>: " - + parser .getName()); - } + if (dr != null) { + mState.mDrawable = dr; + dr.setCallback(this); + } + } + + private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException { + // If we're not waiting on a theme, verify required attributes. + if (mState.mDrawable == null && (mState.mThemeAttrs == null + || mState.mThemeAttrs[R.styleable.ScaleDrawable_drawable] == 0)) { + throw new XmlPullParserException(a.getPositionDescription() + + ": <rotate> tag requires a 'drawable' attribute or " + + "child tag defining a drawable"); } + } + + private void updateStateFromTypedArray(TypedArray a) { + final RotateState state = mState; - if (drawable == null) { - Log.w("drawable", "No drawable specified for <rotate>"); + // Account for any configuration changes. + state.mChangingConfigurations |= a.getChangingConfigurations(); + + // Extract the theme attributes, if any. + state.mThemeAttrs = a.extractThemeAttrs(); + + if (a.hasValue(R.styleable.RotateDrawable_pivotX)) { + final TypedValue tv = a.peekValue(R.styleable.RotateDrawable_pivotX); + state.mPivotXRel = tv.type == TypedValue.TYPE_FRACTION; + state.mPivotX = state.mPivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); } - final RotateState st = mState; - st.mDrawable = drawable; - st.mPivotXRel = pivotXRel; - st.mPivotX = pivotX; - st.mPivotYRel = pivotYRel; - st.mPivotY = pivotY; - st.mFromDegrees = fromDegrees; - st.mCurrentDegrees = fromDegrees; - st.mToDegrees = toDegrees; - - if (drawable != null) { - drawable.setCallback(this); + if (a.hasValue(R.styleable.RotateDrawable_pivotY)) { + final TypedValue tv = a.peekValue(R.styleable.RotateDrawable_pivotY); + state.mPivotYRel = tv.type == TypedValue.TYPE_FRACTION; + state.mPivotY = state.mPivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + } + + state.mFromDegrees = a.getFloat(R.styleable.RotateDrawable_fromDegrees, state.mFromDegrees); + state.mToDegrees = a.getFloat(R.styleable.RotateDrawable_toDegrees, state.mToDegrees); + + final Drawable dr = a.getDrawable(R.styleable.RotateDrawable_drawable); + if (dr != null) { + state.mDrawable = dr; + dr.setCallback(this); } } @@ -485,30 +515,42 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { } /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mState.mDrawable.clearMutated(); + mMutated = false; + } + + /** * Represents the state of a rotation for a given drawable. The same * rotate drawable can be invoked with different states to drive several * rotations at the same time. */ final static class RotateState extends Drawable.ConstantState { - Drawable mDrawable; - + int[] mThemeAttrs; int mChangingConfigurations; - boolean mPivotXRel; - float mPivotX; - boolean mPivotYRel; - float mPivotY; + Drawable mDrawable; - float mFromDegrees; - float mToDegrees; + boolean mPivotXRel = true; + float mPivotX = 0.5f; + boolean mPivotYRel = true; + float mPivotY = 0.5f; - float mCurrentDegrees; + float mFromDegrees = 0.0f; + float mToDegrees = 360.0f; + + float mCurrentDegrees = 0.0f; - private boolean mCanConstantState; private boolean mCheckedConstantState; + private boolean mCanConstantState; - public RotateState(RotateState orig, RotateDrawable owner, Resources res) { + RotateState(RotateState orig, RotateDrawable owner, Resources res) { if (orig != null) { + mThemeAttrs = orig.mThemeAttrs; + mChangingConfigurations = orig.mChangingConfigurations; if (res != null) { mDrawable = orig.mDrawable.getConstantState().newDrawable(res); } else { @@ -522,13 +564,20 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { mPivotX = orig.mPivotX; mPivotYRel = orig.mPivotYRel; mPivotY = orig.mPivotY; - mFromDegrees = mCurrentDegrees = orig.mFromDegrees; + mFromDegrees = orig.mFromDegrees; mToDegrees = orig.mToDegrees; - mCanConstantState = mCheckedConstantState = true; + mCurrentDegrees = orig.mCurrentDegrees; + mCheckedConstantState = mCanConstantState = true; } } @Override + public boolean canApplyTheme() { + return mThemeAttrs != null || (mDrawable != null && mDrawable.canApplyTheme()) + || super.canApplyTheme(); + } + + @Override public Drawable newDrawable() { return new RotateDrawable(this, null); } diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java index b990249..35ef76f 100644 --- a/graphics/java/android/graphics/drawable/ScaleDrawable.java +++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java @@ -16,6 +16,8 @@ package android.graphics.drawable; +import com.android.internal.R; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -47,7 +49,7 @@ import java.io.IOException; * @attr ref android.R.styleable#ScaleDrawable_drawable */ public class ScaleDrawable extends Drawable implements Drawable.Callback { - private ScaleState mScaleState; + private ScaleState mState; private boolean mMutated; private final Rect mTmpRect = new Rect(); @@ -58,10 +60,10 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { public ScaleDrawable(Drawable drawable, int gravity, float scaleWidth, float scaleHeight) { this(null, null); - mScaleState.mDrawable = drawable; - mScaleState.mGravity = gravity; - mScaleState.mScaleWidth = scaleWidth; - mScaleState.mScaleHeight = scaleHeight; + mState.mDrawable = drawable; + mState.mGravity = gravity; + mState.mScaleWidth = scaleWidth; + mState.mScaleHeight = scaleHeight; if (drawable != null) { drawable.setCallback(this); @@ -72,18 +74,18 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { * Returns the drawable scaled by this ScaleDrawable. */ public Drawable getDrawable() { - return mScaleState.mDrawable; + return mState.mDrawable; } - private static float getPercent(TypedArray a, int name) { - String s = a.getString(name); + private static float getPercent(TypedArray a, int name, float defaultValue) { + final String s = a.getString(name); if (s != null) { if (s.endsWith("%")) { String f = s.substring(0, s.length() - 1); return Float.parseFloat(f) / 100.0f; } } - return -1; + return defaultValue; } @Override @@ -91,20 +93,48 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { throws XmlPullParserException, IOException { super.inflate(r, parser, attrs, theme); - int type; - - TypedArray a = obtainAttributes( - r, theme, attrs, com.android.internal.R.styleable.ScaleDrawable); + final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ScaleDrawable); - float sw = getPercent(a, com.android.internal.R.styleable.ScaleDrawable_scaleWidth); - float sh = getPercent(a, com.android.internal.R.styleable.ScaleDrawable_scaleHeight); - int g = a.getInt(com.android.internal.R.styleable.ScaleDrawable_scaleGravity, Gravity.LEFT); - boolean min = a.getBoolean( - com.android.internal.R.styleable.ScaleDrawable_useIntrinsicSizeAsMinimum, false); - Drawable dr = a.getDrawable(com.android.internal.R.styleable.ScaleDrawable_drawable); + // Reset mDrawable to preserve old multiple-inflate behavior. This is + // silly, but we have CTS tests that rely on it. + mState.mDrawable = null; + updateStateFromTypedArray(a); + inflateChildElements(r, parser, attrs, theme); + verifyRequiredAttributes(a); a.recycle(); + } + + @Override + public void applyTheme(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(); + } + } + if (state.mDrawable != null && state.mDrawable.canApplyTheme()) { + state.mDrawable.applyTheme(t); + } + } + + private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs, + Theme theme) throws XmlPullParserException, IOException { + Drawable dr = null; + int type; final int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { @@ -114,20 +144,51 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { dr = Drawable.createFromXmlInner(r, parser, attrs, theme); } - if (dr == null) { - throw new IllegalArgumentException("No drawable specified for <scale>"); + if (dr != null) { + mState.mDrawable = dr; + dr.setCallback(this); } + } - mScaleState.mDrawable = dr; - mScaleState.mScaleWidth = sw; - mScaleState.mScaleHeight = sh; - mScaleState.mGravity = g; - mScaleState.mUseIntrinsicSizeAsMin = min; + private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException { + // If we're not waiting on a theme, verify required attributes. + if (mState.mDrawable == null && (mState.mThemeAttrs == null + || mState.mThemeAttrs[R.styleable.ScaleDrawable_drawable] == 0)) { + throw new XmlPullParserException(a.getPositionDescription() + + ": <scale> tag requires a 'drawable' attribute or " + + "child tag defining a drawable"); + } + } + + private void updateStateFromTypedArray(TypedArray a) { + final ScaleState state = mState; + + // 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, R.styleable.ScaleDrawable_scaleHeight, state.mScaleHeight); + state.mGravity = a.getInt(R.styleable.ScaleDrawable_scaleGravity, Gravity.LEFT); + state.mUseIntrinsicSizeAsMin = a.getBoolean( + R.styleable.ScaleDrawable_useIntrinsicSizeAsMinimum, false); + + final Drawable dr = a.getDrawable(R.styleable.ScaleDrawable_drawable); if (dr != null) { + state.mDrawable = dr; dr.setCallback(this); } } + @Override + public boolean canApplyTheme() { + return (mState != null && mState.canApplyTheme()) || super.canApplyTheme(); + } + // overrides from Drawable.Callback public void invalidateDrawable(Drawable who) { @@ -152,74 +213,74 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { @Override public void draw(Canvas canvas) { - if (mScaleState.mDrawable.getLevel() != 0) - mScaleState.mDrawable.draw(canvas); + if (mState.mDrawable.getLevel() != 0) + mState.mDrawable.draw(canvas); } @Override public int getChangingConfigurations() { return super.getChangingConfigurations() - | mScaleState.mChangingConfigurations - | mScaleState.mDrawable.getChangingConfigurations(); + | mState.mChangingConfigurations + | mState.mDrawable.getChangingConfigurations(); } @Override public boolean getPadding(Rect padding) { // XXX need to adjust padding! - return mScaleState.mDrawable.getPadding(padding); + return mState.mDrawable.getPadding(padding); } @Override public boolean setVisible(boolean visible, boolean restart) { - mScaleState.mDrawable.setVisible(visible, restart); + mState.mDrawable.setVisible(visible, restart); return super.setVisible(visible, restart); } @Override public void setAlpha(int alpha) { - mScaleState.mDrawable.setAlpha(alpha); + mState.mDrawable.setAlpha(alpha); } @Override public int getAlpha() { - return mScaleState.mDrawable.getAlpha(); + return mState.mDrawable.getAlpha(); } @Override public void setColorFilter(ColorFilter cf) { - mScaleState.mDrawable.setColorFilter(cf); + mState.mDrawable.setColorFilter(cf); } @Override public void setTintList(ColorStateList tint) { - mScaleState.mDrawable.setTintList(tint); + mState.mDrawable.setTintList(tint); } @Override public void setTintMode(Mode tintMode) { - mScaleState.mDrawable.setTintMode(tintMode); + mState.mDrawable.setTintMode(tintMode); } @Override public int getOpacity() { - return mScaleState.mDrawable.getOpacity(); + return mState.mDrawable.getOpacity(); } @Override public boolean isStateful() { - return mScaleState.mDrawable.isStateful(); + return mState.mDrawable.isStateful(); } @Override protected boolean onStateChange(int[] state) { - boolean changed = mScaleState.mDrawable.setState(state); + boolean changed = mState.mDrawable.setState(state); onBoundsChange(getBounds()); return changed; } @Override protected boolean onLevelChange(int level) { - mScaleState.mDrawable.setLevel(level); + mState.mDrawable.setLevel(level); onBoundsChange(getBounds()); invalidateSelf(); return true; @@ -228,41 +289,41 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { @Override protected void onBoundsChange(Rect bounds) { final Rect r = mTmpRect; - final boolean min = mScaleState.mUseIntrinsicSizeAsMin; + final boolean min = mState.mUseIntrinsicSizeAsMin; int level = getLevel(); int w = bounds.width(); - if (mScaleState.mScaleWidth > 0) { - final int iw = min ? mScaleState.mDrawable.getIntrinsicWidth() : 0; - w -= (int) ((w - iw) * (10000 - level) * mScaleState.mScaleWidth / 10000); + if (mState.mScaleWidth > 0) { + final int iw = min ? mState.mDrawable.getIntrinsicWidth() : 0; + w -= (int) ((w - iw) * (10000 - level) * mState.mScaleWidth / 10000); } int h = bounds.height(); - if (mScaleState.mScaleHeight > 0) { - final int ih = min ? mScaleState.mDrawable.getIntrinsicHeight() : 0; - h -= (int) ((h - ih) * (10000 - level) * mScaleState.mScaleHeight / 10000); + if (mState.mScaleHeight > 0) { + final int ih = min ? mState.mDrawable.getIntrinsicHeight() : 0; + h -= (int) ((h - ih) * (10000 - level) * mState.mScaleHeight / 10000); } final int layoutDirection = getLayoutDirection(); - Gravity.apply(mScaleState.mGravity, w, h, bounds, r, layoutDirection); + Gravity.apply(mState.mGravity, w, h, bounds, r, layoutDirection); if (w > 0 && h > 0) { - mScaleState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom); + mState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom); } } @Override public int getIntrinsicWidth() { - return mScaleState.mDrawable.getIntrinsicWidth(); + return mState.mDrawable.getIntrinsicWidth(); } @Override public int getIntrinsicHeight() { - return mScaleState.mDrawable.getIntrinsicHeight(); + return mState.mDrawable.getIntrinsicHeight(); } @Override public ConstantState getConstantState() { - if (mScaleState.canConstantState()) { - mScaleState.mChangingConfigurations = getChangingConfigurations(); - return mScaleState; + if (mState.canConstantState()) { + mState.mChangingConfigurations = getChangingConfigurations(); + return mState; } return null; } @@ -270,25 +331,39 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { @Override public Drawable mutate() { if (!mMutated && super.mutate() == this) { - mScaleState.mDrawable.mutate(); + mState.mDrawable.mutate(); mMutated = true; } return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mState.mDrawable.clearMutated(); + mMutated = false; + } + final static class ScaleState extends ConstantState { - Drawable mDrawable; + int[] mThemeAttrs; int mChangingConfigurations; - float mScaleWidth; - float mScaleHeight; - int mGravity; - boolean mUseIntrinsicSizeAsMin; + + Drawable mDrawable; + + float mScaleWidth = 1.0f; + float mScaleHeight = 1.0f; + int mGravity = Gravity.LEFT; + boolean mUseIntrinsicSizeAsMin = false; private boolean mCheckedConstantState; private boolean mCanConstantState; ScaleState(ScaleState orig, ScaleDrawable owner, Resources res) { if (orig != null) { + mThemeAttrs = orig.mThemeAttrs; + mChangingConfigurations = orig.mChangingConfigurations; if (res != null) { mDrawable = orig.mDrawable.getConstantState().newDrawable(res); } else { @@ -307,6 +382,12 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { } @Override + public boolean canApplyTheme() { + return mThemeAttrs != null || (mDrawable != null && mDrawable.canApplyTheme()) + || super.canApplyTheme(); + } + + @Override public Drawable newDrawable() { return new ScaleDrawable(this, null); } @@ -332,7 +413,7 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { } private ScaleDrawable(ScaleState state, Resources res) { - mScaleState = new ScaleState(state, this, res); + mState = new ScaleState(state, this, res); } } diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java index bd69d76..a3d8c92 100644 --- a/graphics/java/android/graphics/drawable/ShapeDrawable.java +++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java @@ -76,7 +76,7 @@ public class ShapeDrawable extends Drawable { * ShapeDrawable constructor. */ public ShapeDrawable() { - this(new ShapeState(null), null, null); + this(new ShapeState(null), null); } /** @@ -85,7 +85,7 @@ public class ShapeDrawable extends Drawable { * @param s the Shape that this ShapeDrawable should be */ public ShapeDrawable(Shape s) { - this(new ShapeState(null), null, null); + this(new ShapeState(null), null); mShapeState.mShape = s; } @@ -508,6 +508,14 @@ public class ShapeDrawable extends Drawable { } /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mMutated = false; + } + + /** * Defines the intrinsic properties of this ShapeDrawable's Shape. */ final static class ShapeState extends ConstantState { @@ -547,17 +555,12 @@ public class ShapeDrawable extends Drawable { @Override public Drawable newDrawable() { - return new ShapeDrawable(this, null, null); + return new ShapeDrawable(this, null); } @Override public Drawable newDrawable(Resources res) { - return new ShapeDrawable(this, res, null); - } - - @Override - public Drawable newDrawable(Resources res, Theme theme) { - return new ShapeDrawable(this, res, theme); + return new ShapeDrawable(this, res); } @Override @@ -570,13 +573,8 @@ public class ShapeDrawable extends Drawable { * The one constructor to rule them all. This is called by all public * constructors to set the state and initialize local properties. */ - private ShapeDrawable(ShapeState state, Resources res, Theme theme) { - if (theme != null && state.canApplyTheme()) { - mShapeState = new ShapeState(state); - applyTheme(theme); - } else { - mShapeState = state; - } + private ShapeDrawable(ShapeState state, Resources res) { + mShapeState = state; initializeWithState(state, res); } diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java index 2eb8a80..963943b 100644 --- a/graphics/java/android/graphics/drawable/StateListDrawable.java +++ b/graphics/java/android/graphics/drawable/StateListDrawable.java @@ -117,26 +117,48 @@ public class StateListDrawable extends DrawableContainer { @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { - final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.StateListDrawable); + super.inflateWithAttributes(r, parser, a, R.styleable.StateListDrawable_visible); + updateStateFromTypedArray(a); + a.recycle(); - super.inflateWithAttributes(r, parser, a, - R.styleable.StateListDrawable_visible); - - mStateListState.setVariablePadding(a.getBoolean( - R.styleable.StateListDrawable_variablePadding, false)); - mStateListState.setConstantSize(a.getBoolean( - R.styleable.StateListDrawable_constantSize, false)); - mStateListState.setEnterFadeDuration(a.getInt( - R.styleable.StateListDrawable_enterFadeDuration, 0)); - mStateListState.setExitFadeDuration(a.getInt( - R.styleable.StateListDrawable_exitFadeDuration, 0)); + inflateChildElements(r, parser, attrs, theme); - setDither(a.getBoolean(R.styleable.StateListDrawable_dither, DEFAULT_DITHER)); - setAutoMirrored(a.getBoolean(R.styleable.StateListDrawable_autoMirrored, false)); + onStateChange(getState()); + } - a.recycle(); + /** + * Updates the constant state from the values in the typed array. + */ + private void updateStateFromTypedArray(TypedArray a) { + final StateListState state = mStateListState; + + // Account for any configuration changes. + state.mChangingConfigurations |= a.getChangingConfigurations(); + + // Extract the theme attributes, if any. + state.mThemeAttrs = a.extractThemeAttrs(); + + state.mVariablePadding = a.getBoolean( + R.styleable.StateListDrawable_variablePadding, state.mVariablePadding); + state.mConstantSize = a.getBoolean( + R.styleable.StateListDrawable_constantSize, state.mConstantSize); + state.mEnterFadeDuration = a.getInt( + R.styleable.StateListDrawable_enterFadeDuration, state.mEnterFadeDuration); + state.mExitFadeDuration = a.getInt( + R.styleable.StateListDrawable_exitFadeDuration, state.mExitFadeDuration); + state.mDither = a.getBoolean( + R.styleable.StateListDrawable_dither, state.mDither); + state.mAutoMirrored = a.getBoolean( + R.styleable.StateListDrawable_autoMirrored, state.mAutoMirrored); + } + /** + * Inflates child elements from XML. + */ + private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs, + Theme theme) throws XmlPullParserException, IOException { + final StateListState state = mStateListState; final int innerDepth = parser.getDepth() + 1; int type; int depth; @@ -185,10 +207,8 @@ public class StateListDrawable extends DrawableContainer { dr = Drawable.createFromXmlInner(r, parser, attrs, theme); } - mStateListState.addStateSet(states, dr); + state.addStateSet(states, dr); } - - onStateChange(getState()); } StateListState getStateListState() { @@ -263,6 +283,14 @@ public class StateListDrawable extends DrawableContainer { return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mMutated = false; + } + /** @hide */ @Override public void setLayoutDirection(int layoutDirection) { @@ -274,14 +302,28 @@ public class StateListDrawable extends DrawableContainer { } static class StateListState extends DrawableContainerState { + int[] mThemeAttrs; int[][] mStateSets; StateListState(StateListState orig, StateListDrawable owner, Resources res) { super(orig, owner, res); if (orig != null) { + // Perform a deep copy. + final int[][] sets = orig.mStateSets; + final int count = sets.length; + mStateSets = new int[count][]; + for (int i = 0; i < count; i++) { + final int[] set = sets[i]; + if (set != null) { + mStateSets[i] = set.clone(); + } + } + + mThemeAttrs = orig.mThemeAttrs; mStateSets = Arrays.copyOf(orig.mStateSets, orig.mStateSets.length); } else { + mThemeAttrs = null; mStateSets = new int[getCapacity()][]; } } @@ -314,6 +356,11 @@ public class StateListDrawable extends DrawableContainer { } @Override + public boolean canApplyTheme() { + return mThemeAttrs != null || super.canApplyTheme(); + } + + @Override public void growArray(int oldSize, int newSize) { super.growArray(oldSize, newSize); final int[][] newStateSets = new int[newSize][]; @@ -322,6 +369,13 @@ public class StateListDrawable extends DrawableContainer { } } + @Override + public void applyTheme(Theme theme) { + super.applyTheme(theme); + + onStateChange(getState()); + } + void setConstantState(StateListState state) { super.setConstantState(state); diff --git a/graphics/java/android/graphics/drawable/TransitionDrawable.java b/graphics/java/android/graphics/drawable/TransitionDrawable.java index 4380ca4..e5c235e 100644 --- a/graphics/java/android/graphics/drawable/TransitionDrawable.java +++ b/graphics/java/android/graphics/drawable/TransitionDrawable.java @@ -17,7 +17,6 @@ package android.graphics.drawable; import android.content.res.Resources; -import android.content.res.Resources.Theme; import android.graphics.Canvas; import android.os.SystemClock; @@ -86,11 +85,11 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba * @see #TransitionDrawable(Drawable[]) */ TransitionDrawable() { - this(new TransitionState(null, null, null), null, null); + this(new TransitionState(null, null, null), (Resources) null); } - private TransitionDrawable(TransitionState state, Resources res, Theme theme) { - super(state, res, theme); + private TransitionDrawable(TransitionState state, Resources res) { + super(state, res); } private TransitionDrawable(TransitionState state, Drawable[] layers) { @@ -245,24 +244,18 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba } static class TransitionState extends LayerState { - TransitionState(TransitionState orig, TransitionDrawable owner, - Resources res) { + TransitionState(TransitionState orig, TransitionDrawable owner, Resources res) { super(orig, owner, res); } @Override public Drawable newDrawable() { - return new TransitionDrawable(this, null, null); + return new TransitionDrawable(this, (Resources) null); } @Override public Drawable newDrawable(Resources res) { - return new TransitionDrawable(this, res, null); - } - - @Override - public Drawable newDrawable(Resources res, Theme theme) { - return new TransitionDrawable(this, res, theme); + return new TransitionDrawable(this, res); } @Override diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index db0c94f..8b0f635 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -14,6 +14,7 @@ package android.graphics.drawable; +import android.annotation.NonNull; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; @@ -212,15 +213,8 @@ public class VectorDrawable extends Drawable { mVectorState = new VectorDrawableState(); } - private VectorDrawable(VectorDrawableState state, Resources res, Theme theme) { - if (theme != null && state.canApplyTheme()) { - // If we need to apply a theme, implicitly mutate. - mVectorState = new VectorDrawableState(state); - applyTheme(theme); - } else { - mVectorState = state; - } - + private VectorDrawable(@NonNull VectorDrawableState state) { + mVectorState = state; mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); } @@ -233,6 +227,14 @@ public class VectorDrawable extends Drawable { return this; } + /** + * @hide + */ + public void clearMutated() { + super.clearMutated(); + mMutated = false; + } + Object getTargetByName(String name) { return mVectorState.mVPathRenderer.mVGTargetsMap.get(name); } @@ -359,7 +361,7 @@ public class VectorDrawable extends Drawable { @Override public boolean canApplyTheme() { - return super.canApplyTheme() || mVectorState != null && mVectorState.canApplyTheme(); + return (mVectorState != null && mVectorState.canApplyTheme()) || super.canApplyTheme(); } @Override @@ -748,8 +750,8 @@ public class VectorDrawable extends Drawable { @Override public boolean canApplyTheme() { - return super.canApplyTheme() || mThemeAttrs != null - || (mVPathRenderer != null && mVPathRenderer.canApplyTheme()); + return mThemeAttrs != null || (mVPathRenderer != null && mVPathRenderer.canApplyTheme()) + || super.canApplyTheme(); } public VectorDrawableState() { @@ -758,17 +760,12 @@ public class VectorDrawable extends Drawable { @Override public Drawable newDrawable() { - return new VectorDrawable(this, null, null); + return new VectorDrawable(this); } @Override public Drawable newDrawable(Resources res) { - return new VectorDrawable(this, res, null); - } - - @Override - public Drawable newDrawable(Resources res, Theme theme) { - return new VectorDrawable(this, res, theme); + return new VectorDrawable(this); } @Override diff --git a/graphics/java/android/graphics/pdf/PdfEditor.java b/graphics/java/android/graphics/pdf/PdfEditor.java index 9837139..2b70b6a 100644 --- a/graphics/java/android/graphics/pdf/PdfEditor.java +++ b/graphics/java/android/graphics/pdf/PdfEditor.java @@ -17,6 +17,10 @@ package android.graphics.pdf; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Matrix; +import android.graphics.Point; +import android.graphics.Rect; import android.os.ParcelFileDescriptor; import android.system.ErrnoException; import android.system.OsConstants; @@ -55,6 +59,10 @@ public final class PdfEditor { * * @param input Seekable file descriptor to read from. * + * @throws java.io.IOException If an error occurs while reading the file. + * @throws java.lang.SecurityException If the file requires a password or + * the security scheme is not supported. + * * @see #close() */ public PdfEditor(@NonNull ParcelFileDescriptor input) throws IOException { @@ -98,6 +106,109 @@ public final class PdfEditor { } /** + * Sets a transformation and clip for a given page. The transformation matrix if + * non-null must be affine as per {@link android.graphics.Matrix#isAffine()}. If + * the clip is null, then no clipping is performed. + * + * @param pageIndex The page whose transform to set. + * @param transform The transformation to apply. + * @param clip The clip to apply. + */ + public void setTransformAndClip(int pageIndex, @Nullable Matrix transform, + @Nullable Rect clip) { + throwIfClosed(); + throwIfPageNotInDocument(pageIndex); + throwIfNotNullAndNotAfine(transform); + if (transform == null) { + transform = Matrix.IDENTITY_MATRIX; + } + if (clip == null) { + Point size = new Point(); + getPageSize(pageIndex, size); + nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance, + 0, 0, size.x, size.y); + } else { + nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance, + clip.left, clip.top, clip.right, clip.bottom); + } + } + + /** + * Gets the size of a given page in mils (1/72"). + * + * @param pageIndex The page index. + * @param outSize The size output. + */ + public void getPageSize(int pageIndex, @NonNull Point outSize) { + throwIfClosed(); + throwIfOutSizeNull(outSize); + throwIfPageNotInDocument(pageIndex); + nativeGetPageSize(mNativeDocument, pageIndex, outSize); + } + + /** + * Gets the media box of a given page in mils (1/72"). + * + * @param pageIndex The page index. + * @param outMediaBox The media box output. + */ + public boolean getPageMediaBox(int pageIndex, @NonNull Rect outMediaBox) { + throwIfClosed(); + throwIfOutMediaBoxNull(outMediaBox); + throwIfPageNotInDocument(pageIndex); + return nativeGetPageMediaBox(mNativeDocument, pageIndex, outMediaBox); + } + + /** + * Sets the media box of a given page in mils (1/72"). + * + * @param pageIndex The page index. + * @param mediaBox The media box. + */ + public void setPageMediaBox(int pageIndex, @NonNull Rect mediaBox) { + throwIfClosed(); + throwIfMediaBoxNull(mediaBox); + throwIfPageNotInDocument(pageIndex); + nativeSetPageMediaBox(mNativeDocument, pageIndex, mediaBox); + } + + /** + * Gets the crop box of a given page in mils (1/72"). + * + * @param pageIndex The page index. + * @param outCropBox The crop box output. + */ + public boolean getPageCropBox(int pageIndex, @NonNull Rect outCropBox) { + throwIfClosed(); + throwIfOutCropBoxNull(outCropBox); + throwIfPageNotInDocument(pageIndex); + return nativeGetPageCropBox(mNativeDocument, pageIndex, outCropBox); + } + + /** + * Sets the crop box of a given page in mils (1/72"). + * + * @param pageIndex The page index. + * @param cropBox The crop box. + */ + public void setPageCropBox(int pageIndex, @NonNull Rect cropBox) { + throwIfClosed(); + throwIfCropBoxNull(cropBox); + throwIfPageNotInDocument(pageIndex); + nativeSetPageCropBox(mNativeDocument, pageIndex, cropBox); + } + + /** + * Gets whether the document prefers to be scaled for printing. + * + * @return Whether to scale the document. + */ + public boolean shouldScaleForPrinting() { + throwIfClosed(); + return nativeScaleForPrinting(mNativeDocument); + } + + /** * Writes the PDF file to the provided destination. * <p> * <strong>Note:</strong> This method takes ownership of the passed in file @@ -154,9 +265,57 @@ public final class PdfEditor { } } + private void throwIfNotNullAndNotAfine(Matrix matrix) { + if (matrix != null && !matrix.isAffine()) { + throw new IllegalStateException("Matrix must be afine"); + } + } + + private void throwIfOutSizeNull(Point outSize) { + if (outSize == null) { + throw new NullPointerException("outSize cannot be null"); + } + } + + private void throwIfOutMediaBoxNull(Rect outMediaBox) { + if (outMediaBox == null) { + throw new NullPointerException("outMediaBox cannot be null"); + } + } + + private void throwIfMediaBoxNull(Rect mediaBox) { + if (mediaBox == null) { + throw new NullPointerException("mediaBox cannot be null"); + } + } + + private void throwIfOutCropBoxNull(Rect outCropBox) { + if (outCropBox == null) { + throw new NullPointerException("outCropBox cannot be null"); + } + } + + private void throwIfCropBoxNull(Rect cropBox) { + if (cropBox == null) { + throw new NullPointerException("cropBox cannot be null"); + } + } + private static native long nativeOpen(int fd, long size); private static native void nativeClose(long documentPtr); private static native int nativeGetPageCount(long documentPtr); private static native int nativeRemovePage(long documentPtr, int pageIndex); private static native void nativeWrite(long documentPtr, int fd); + private static native void nativeSetTransformAndClip(long documentPtr, int pageIndex, + long transformPtr, int clipLeft, int clipTop, int clipRight, int clipBottom); + private static native void nativeGetPageSize(long documentPtr, int pageIndex, Point outSize); + private static native boolean nativeGetPageMediaBox(long documentPtr, int pageIndex, + Rect outMediaBox); + private static native void nativeSetPageMediaBox(long documentPtr, int pageIndex, + Rect mediaBox); + private static native boolean nativeGetPageCropBox(long documentPtr, int pageIndex, + Rect outMediaBox); + private static native void nativeSetPageCropBox(long documentPtr, int pageIndex, + Rect mediaBox); + private static native boolean nativeScaleForPrinting(long documentPtr); } diff --git a/graphics/java/android/graphics/pdf/PdfRenderer.java b/graphics/java/android/graphics/pdf/PdfRenderer.java index 359c294..79934da 100644 --- a/graphics/java/android/graphics/pdf/PdfRenderer.java +++ b/graphics/java/android/graphics/pdf/PdfRenderer.java @@ -131,6 +131,10 @@ public final class PdfRenderer implements AutoCloseable { * </p> * * @param input Seekable file descriptor to read from. + * + * @throws java.io.IOException If an error occurs while reading the file. + * @throws java.lang.SecurityException If the file requires a password or + * the security scheme is not supported. */ public PdfRenderer(@NonNull ParcelFileDescriptor input) throws IOException { if (input == null) { |