diff --git a/src/api-impl-jni/generated_headers/android_graphics_Canvas.h b/src/api-impl-jni/generated_headers/android_graphics_Canvas.h index 98c4890d..62d2908c 100644 --- a/src/api-impl-jni/generated_headers/android_graphics_Canvas.h +++ b/src/api-impl-jni/generated_headers/android_graphics_Canvas.h @@ -7,6 +7,8 @@ #ifdef __cplusplus extern "C" { #endif +#undef android_graphics_Canvas_HAS_ALPHA_LAYER_SAVE_FLAG +#define android_graphics_Canvas_HAS_ALPHA_LAYER_SAVE_FLAG 4L /* * Class: android_graphics_Canvas * Method: native_canvas_from_bitmap diff --git a/src/api-impl-jni/generated_headers/android_graphics_GskCanvas.h b/src/api-impl-jni/generated_headers/android_graphics_GskCanvas.h index ea5518bd..f54286a1 100644 --- a/src/api-impl-jni/generated_headers/android_graphics_GskCanvas.h +++ b/src/api-impl-jni/generated_headers/android_graphics_GskCanvas.h @@ -7,6 +7,8 @@ #ifdef __cplusplus extern "C" { #endif +#undef android_graphics_GskCanvas_HAS_ALPHA_LAYER_SAVE_FLAG +#define android_graphics_GskCanvas_HAS_ALPHA_LAYER_SAVE_FLAG 4L /* * Class: android_graphics_GskCanvas * Method: native_drawBitmap diff --git a/src/api-impl/android/app/usage/UsageStatsManager.java b/src/api-impl/android/app/usage/UsageStatsManager.java new file mode 100644 index 00000000..511d279b --- /dev/null +++ b/src/api-impl/android/app/usage/UsageStatsManager.java @@ -0,0 +1,5 @@ +package android.app.usage; + +public class UsageStatsManager { + +} diff --git a/src/api-impl/android/graphics/Canvas.java b/src/api-impl/android/graphics/Canvas.java index dd450f3c..908f1303 100644 --- a/src/api-impl/android/graphics/Canvas.java +++ b/src/api-impl/android/graphics/Canvas.java @@ -1,6 +1,8 @@ package android.graphics; public class Canvas { + public static final int HAS_ALPHA_LAYER_SAVE_FLAG = (1 << 2); + public long skia_canvas; public long widget; diff --git a/src/api-impl/android/graphics/PixelFormat.java b/src/api-impl/android/graphics/PixelFormat.java new file mode 100644 index 00000000..76057986 --- /dev/null +++ b/src/api-impl/android/graphics/PixelFormat.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +public class PixelFormat { + /* these constants need to match those in hardware/hardware.h */ + + public static final int UNKNOWN = 0; + + /** + * System chooses a format that supports translucency (many alpha bits) + */ + public static final int TRANSLUCENT = -3; + + /** + * System chooses a format that supports transparency + * (at least 1 alpha bit) + */ + public static final int TRANSPARENT = -2; + + /** + * System chooses an opaque format (no alpha bits required) + */ + public static final int OPAQUE = -1; + + public static final int RGBA_8888 = 1; + public static final int RGBX_8888 = 2; + public static final int RGB_888 = 3; + public static final int RGB_565 = 4; + + @Deprecated + public static final int RGBA_5551 = 6; + @Deprecated + public static final int RGBA_4444 = 7; + @Deprecated + public static final int A_8 = 8; + @Deprecated + public static final int L_8 = 9; + @Deprecated + public static final int LA_88 = 0xA; + @Deprecated + public static final int RGB_332 = 0xB; + + /** + * @deprecated use {@link android.graphics.ImageFormat#NV16 + * ImageFormat.NV16} instead. + */ + @Deprecated + public static final int YCbCr_422_SP = 0x10; + + /** + * @deprecated use {@link android.graphics.ImageFormat#NV21 + * ImageFormat.NV21} instead. + */ + @Deprecated + public static final int YCbCr_420_SP = 0x11; + + /** + * @deprecated use {@link android.graphics.ImageFormat#YUY2 + * ImageFormat.YUY2} instead. + */ + @Deprecated + public static final int YCbCr_422_I = 0x14; + + /** + * @deprecated use {@link android.graphics.ImageFormat#JPEG + * ImageFormat.JPEG} instead. + */ + @Deprecated + public static final int JPEG = 0x100; + + public static void getPixelFormatInfo(int format, PixelFormat info) { + switch (format) { + case RGBA_8888: + case RGBX_8888: + info.bitsPerPixel = 32; + info.bytesPerPixel = 4; + break; + case RGB_888: + info.bitsPerPixel = 24; + info.bytesPerPixel = 3; + break; + case RGB_565: + case RGBA_5551: + case RGBA_4444: + case LA_88: + info.bitsPerPixel = 16; + info.bytesPerPixel = 2; + break; + case A_8: + case L_8: + case RGB_332: + info.bitsPerPixel = 8; + info.bytesPerPixel = 1; + break; + case YCbCr_422_SP: + case YCbCr_422_I: + info.bitsPerPixel = 16; + info.bytesPerPixel = 1; + break; + case YCbCr_420_SP: + info.bitsPerPixel = 12; + info.bytesPerPixel = 1; + break; + default: + throw new IllegalArgumentException("unkonwon pixel format " + format); + } + } + + public static boolean formatHasAlpha(int format) { + switch (format) { + case PixelFormat.A_8: + case PixelFormat.LA_88: + case PixelFormat.RGBA_4444: + case PixelFormat.RGBA_5551: + case PixelFormat.RGBA_8888: + case PixelFormat.TRANSLUCENT: + case PixelFormat.TRANSPARENT: + return true; + } + return false; + } + + public int bytesPerPixel; + public int bitsPerPixel; +} diff --git a/src/api-impl/android/graphics/drawable/Drawable.java b/src/api-impl/android/graphics/drawable/Drawable.java index ceb28255..c7f69ae7 100644 --- a/src/api-impl/android/graphics/drawable/Drawable.java +++ b/src/api-impl/android/graphics/drawable/Drawable.java @@ -15,8 +15,10 @@ import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.content.res.Resources.Theme; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.util.AttributeSet; import android.util.TypedValue; @@ -28,6 +30,8 @@ public class Drawable { public void unscheduleDrawable(Drawable drawable, Runnable runnable); } + static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN; + private Rect mBounds = new Rect(); private int[] mStateSet = new int[0]; public long paintable; @@ -140,9 +144,24 @@ public class Drawable { public int getIntrinsicWidth() {return 0;} public int getIntrinsicHeight() {return 0;} - public void setTintList (ColorStateList tint) {} + public void setTintList(ColorStateList tint) {} - public void setTint(int tint) {} + public void setTint(int tint) { + System.out.println("setTint("+tint+")"); + setTintList(ColorStateList.valueOf(tint)); + } + + PorterDuffColorFilter updateTintFilter(PorterDuffColorFilter tintFilter, ColorStateList tint, PorterDuff.Mode tintMode) { + System.out.println("updateTintFilter("+tintFilter+", "+tint+", "+tintMode+")"); + if (tint == null || tintMode == null) { + return null; + } + final int color = tint.getColorForState(getState(), Color.TRANSPARENT); + if (tintFilter == null || tintFilter.getColor() != color || tintFilter.getMode() != tintMode) { + return new PorterDuffColorFilter(color, tintMode); + } + return tintFilter; + } public boolean isStateful() { return false; diff --git a/src/api-impl/android/graphics/drawable/GradientDrawable.java b/src/api-impl/android/graphics/drawable/GradientDrawable.java index c2e98911..d9f41752 100644 --- a/src/api-impl/android/graphics/drawable/GradientDrawable.java +++ b/src/api-impl/android/graphics/drawable/GradientDrawable.java @@ -1,10 +1,1350 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.graphics.drawable; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.DashPathEffect; +import android.graphics.LinearGradient; +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; +import android.graphics.Shader; +//import android.graphics.SweepGradient; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; + +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** + * A Drawable with a color gradient for buttons, backgrounds, etc. + * + *
It can be defined in an XML file with the <shape> element. For more
+ * information, see the guide to Drawable Resources.
Specify radii for each of the 4 corners. For each corner, the array
+ * contains 2 values, [X_radius, Y_radius]. The corners are ordered
+ * top-left, top-right, bottom-right, bottom-left. This property
+ * is honored only when the shape is of type {@link #RECTANGLE}.
Note: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.
+ * + * @param radii 4 pairs of X and Y radius for each corner, specified in pixels. + * The length of this array must be >= 8 + * + * @see #mutate() + * @see #setCornerRadii(float[]) + * @see #setShape(int) + */ + public void setCornerRadii(float[] radii) { + mGradientState.setCornerRadii(radii); + mPathIsDirty = true; + invalidateSelf(); + } + + /** + *Specify radius for the corners of the gradient. If this is > 0, then the + * drawable is drawn in a round-rectangle, rather than a rectangle. This property + * is honored only when the shape is of type {@link #RECTANGLE}.
+ *Note: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.
+ * + * @param radius The radius in pixels of the corners of the rectangle shape + * + * @see #mutate() + * @see #setCornerRadii(float[]) + * @see #setShape(int) + */ + public void setCornerRadius(float radius) { + mGradientState.setCornerRadius(radius); + mPathIsDirty = true; + invalidateSelf(); + } + + /** + *Set the stroke width and color for the drawable. If width is zero, + * then no stroke is drawn.
+ *Note: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.
+ * + * @param width The width in pixels of the stroke + * @param color The color of the stroke + * + * @see #mutate() + * @see #setStroke(int, int, float, float) + */ + public void setStroke(int width, int color) { + setStroke(width, color, 0, 0); + } + + /** + *Set the stroke width and color for the drawable. If width is zero, + * then no stroke is drawn. This method can also be used to dash the stroke.
+ *Note: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.
+ * + * @param width The width in pixels of the stroke + * @param color The color of the stroke + * @param dashWidth The length in pixels of the dashes, set to 0 to disable dashes + * @param dashGap The gap in pixels between dashes + * + * @see #mutate() + * @see #setStroke(int, int) + */ + public void setStroke(int width, int color, float dashWidth, float dashGap) { + mGradientState.setStroke(width, color, dashWidth, dashGap); + + if (mStrokePaint == null) { + mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mStrokePaint.setStyle(Paint.Style.STROKE); + } + mStrokePaint.setStrokeWidth(width); + mStrokePaint.setColor(color); + + DashPathEffect e = null; + if (dashWidth > 0) { + e = new DashPathEffect(new float[] {dashWidth, dashGap}, 0); + } + mStrokePaint.setPathEffect(e); + invalidateSelf(); + } + + /** + *Sets the size of the shape drawn by this drawable.
+ *Note: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.
+ * + * @param width The width of the shape used by this drawable + * @param height The height of the shape used by this drawable + * + * @see #mutate() + * @see #setGradientType(int) + */ + public void setSize(int width, int height) { + mGradientState.setSize(width, height); + mPathIsDirty = true; + invalidateSelf(); + } + + /** + *Sets the type of shape used to draw the gradient.
+ *Note: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.
+ * + * @param shape The desired shape for this drawable: {@link #LINE}, + * {@link #OVAL}, {@link #RECTANGLE} or {@link #RING} + * + * @see #mutate() + */ + public void setShape(int shape) { + mRingPath = null; + mPathIsDirty = true; + mGradientState.setShape(shape); + invalidateSelf(); + } + + /** + *Sets the type of gradient used by this drawable..
+ *Note: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.
+ * + * @param gradient The type of the gradient: {@link #LINEAR_GRADIENT}, + * {@link #RADIAL_GRADIENT} or {@link #SWEEP_GRADIENT} + * + * @see #mutate() + */ + public void setGradientType(int gradient) { + mGradientState.setGradientType(gradient); + mRectIsDirty = true; + invalidateSelf(); + } + + /** + *Sets the center location of the gradient. The radius is honored only when + * the gradient type is set to {@link #RADIAL_GRADIENT} or {@link #SWEEP_GRADIENT}.
+ *Note: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.
+ * + * @param x The x coordinate of the gradient's center + * @param y The y coordinate of the gradient's center + * + * @see #mutate() + * @see #setGradientType(int) + */ + public void setGradientCenter(float x, float y) { + mGradientState.setGradientCenter(x, y); + mRectIsDirty = true; + invalidateSelf(); + } + + /** + *Sets the radius of the gradient. The radius is honored only when the + * gradient type is set to {@link #RADIAL_GRADIENT}.
+ *Note: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.
+ * + * @param gradientRadius The radius of the gradient in pixels + * + * @see #mutate() + * @see #setGradientType(int) + */ + public void setGradientRadius(float gradientRadius) { + mGradientState.setGradientRadius(gradientRadius); + mRectIsDirty = true; + invalidateSelf(); + } + + /** + *Sets whether or not this drawable will honor its level
+ * property.
Note: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.
+ * + * @param useLevel True if this drawable should honor its level, false otherwise + * + * @see #mutate() + * @see #setLevel(int) + * @see #getLevel() + */ + public void setUseLevel(boolean useLevel) { + mGradientState.mUseLevel = useLevel; + mRectIsDirty = true; + invalidateSelf(); + } + + private int modulateAlpha(int alpha) { + int scale = mAlpha + (mAlpha >> 7); + return alpha * scale >> 8; + } + + /** + * Returns the orientation of the gradient defined in this drawable. + */ + public Orientation getOrientation() { + return mGradientState.mOrientation; + } + + /** + *Changes the orientation of the gradient defined in this drawable.
+ *Note: changing orientation will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing the orientation.
+ * + * @param orientation The desired orientation (angle) of the gradient + * + * @see #mutate() + */ + public void setOrientation(Orientation orientation) { + mGradientState.mOrientation = orientation; + mRectIsDirty = true; + invalidateSelf(); + } + + /** + *Sets the colors used to draw the gradient. Each color is specified as an + * ARGB integer and the array must contain at least 2 colors.
+ *Note: changing orientation will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing the orientation.
+ * + * @param colors 2 or more ARGB colors + * + * @see #mutate() + * @see #setColor(int) + */ + public void setColors(int[] colors) { + mGradientState.setColors(colors); + mRectIsDirty = true; + invalidateSelf(); + } + + @Override + public void draw(Canvas canvas) { + if (!ensureValidRect()) { + // nothing to draw + return; + } + + // remember the alpha values, in case we temporarily overwrite them + // when we modulate them with mAlpha + final int prevFillAlpha = mFillPaint.getAlpha(); + final int prevStrokeAlpha = mStrokePaint != null ? mStrokePaint.getAlpha() : 0; + // compute the modulate alpha values + final int currFillAlpha = modulateAlpha(prevFillAlpha); + final int currStrokeAlpha = modulateAlpha(prevStrokeAlpha); + + final boolean haveStroke = currStrokeAlpha > 0 && mStrokePaint != null && + mStrokePaint.getStrokeWidth() > 0; + final boolean haveFill = currFillAlpha > 0; + final GradientState st = mGradientState; + final ColorFilter colorFilter = mColorFilter != null ? mColorFilter : mTintFilter; + System.out.println("draw(): colorFilter: "+colorFilter); + /* 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 || colorFilter != null); + + /* Drawing with a layer is slower than direct drawing, but it + allows us to apply paint effects like alpha and colorfilter to + the result of multiple separate draws. In our case, if the user + asks for a non-opaque alpha value (via setAlpha), and we're + stroking, then we need to apply the alpha AFTER we've drawn + both the fill and the stroke. + */ + /*if (useLayer) { + if (mLayerPaint == null) { + mLayerPaint = new Paint(); + } + mLayerPaint.setDither(mDither); + mLayerPaint.setAlpha(mAlpha); + mLayerPaint.setColorFilter(colorFilter); + + float rad = mStrokePaint.getStrokeWidth(); + canvas.saveLayer(mRect.left - rad, mRect.top - rad, + mRect.right + rad, mRect.bottom + rad, + mLayerPaint, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG); + + // don't perform the filter in our individual paints + // since the layer will do it for us + mFillPaint.setColorFilter(null); + mStrokePaint.setColorFilter(null); + } else {*/ + /* if we're not using a layer, apply the dither/filter to our + individual paints + */ + mFillPaint.setAlpha(currFillAlpha); + mFillPaint.setDither(mDither); + mFillPaint.setColorFilter(colorFilter); + if (colorFilter != null && !mGradientState.mHasSolidColor) { + mFillPaint.setColor(mAlpha << 24); + } + if (haveStroke) { + mStrokePaint.setAlpha(currStrokeAlpha); + mStrokePaint.setDither(mDither); + mStrokePaint.setColorFilter(colorFilter); + } + /*}*/ + + switch (st.mShape) { + case RECTANGLE: + if (st.mRadiusArray != null) { + if (mPathIsDirty || mRectIsDirty) { + mPath.reset(); + mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW); + mPathIsDirty = mRectIsDirty = false; + } + canvas.drawPath(mPath, mFillPaint); + if (haveStroke) { + canvas.drawPath(mPath, mStrokePaint); + } + } else if (st.mRadius > 0.0f) { + // since the caller is only giving us 1 value, we will force + // it to be square if the rect is too small in one dimension + // to show it. If we did nothing, Skia would clamp the rad + // independently along each axis, giving us a thin ellipse + // if the rect were very wide but not very tall + float rad = st.mRadius; + float r = Math.min(mRect.width(), mRect.height()) * 0.5f; + if (rad > r) { + rad = r; + } + canvas.drawRoundRect(mRect, rad, rad, mFillPaint); + if (haveStroke) { + canvas.drawRoundRect(mRect, rad, rad, mStrokePaint); + } + } else { + if (mFillPaint.getColor() != 0 || colorFilter != null || + mFillPaint.getShader() != null) { + canvas.drawRect(mRect, mFillPaint); + } + if (haveStroke) { + canvas.drawRect(mRect, mStrokePaint); + } + } + break; + case OVAL: + /*canvas.drawOval(mRect, mFillPaint); + if (haveStroke) { + canvas.drawOval(mRect, mStrokePaint); + }*/ + break; + case LINE: { + RectF r = mRect; + float y = r.centerY(); + canvas.drawLine(r.left, y, r.right, y, mStrokePaint); + break; + } + case RING: + Path path = buildRing(st); + canvas.drawPath(path, mFillPaint); + if (haveStroke) { + canvas.drawPath(path, mStrokePaint); + } + break; + } + + if (useLayer) { + canvas.restore(); + } else { + mFillPaint.setAlpha(prevFillAlpha); + if (haveStroke) { + mStrokePaint.setAlpha(prevStrokeAlpha); + } + } + } + + private Path buildRing(GradientState st) { + if (mRingPath != null && (!st.mUseLevelForShape || !mPathIsDirty)) + return mRingPath; + mPathIsDirty = false; + + float sweep = st.mUseLevelForShape ? (360.0f * getLevel() / 10000.0f) : 360f; + + RectF bounds = new RectF(mRect); + + float x = bounds.width() / 2.0f; + float y = bounds.height() / 2.0f; + + float thickness = st.mThickness != -1 ? st.mThickness : bounds.width() / st.mThicknessRatio; + // inner radius + float radius = st.mInnerRadius != -1 ? st.mInnerRadius : bounds.width() / st.mInnerRadiusRatio; + + RectF innerBounds = new RectF(bounds); + innerBounds.inset(x - radius, y - radius); + + bounds = new RectF(innerBounds); + bounds.inset(-thickness, -thickness); + + if (mRingPath == null) { + mRingPath = new Path(); + } else { + mRingPath.reset(); + } + + final Path ringPath = mRingPath; + // arcTo treats the sweep angle mod 360, so check for that, since we + // think 360 means draw the entire oval + if (sweep < 360 && sweep > -360) { + ringPath.setFillType(Path.FillType.EVEN_ODD); + // inner top + ringPath.moveTo(x + radius, y); + // outer top + ringPath.lineTo(x + radius + thickness, y); + // outer arc + ringPath.arcTo(bounds, 0.0f, sweep, false); + // inner arc + ringPath.arcTo(innerBounds, sweep, -sweep, false); + ringPath.close(); + } else { + // add the entire ovals + ringPath.addOval(bounds, Path.Direction.CW); + ringPath.addOval(innerBounds, Path.Direction.CCW); + } + + return ringPath; + } + + /** + *Changes this drawbale to use a single color instead of a gradient.
+ *Note: changing color will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing the color.
+ * + * @param argb The color used to fill the shape + * + * @see #mutate() + * @see #setColors(int[]) + */ + public void setColor(int argb) { + mGradientState.setSolidColor(argb); + mFillPaint.setColor(argb); + invalidateSelf(); + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() | mGradientState.mChangingConfigurations; + } + + @Override + public void setAlpha(int alpha) { + if (alpha != mAlpha) { + mAlpha = alpha; + invalidateSelf(); + } + } + + //@Override + public int getAlpha() { + return mAlpha; + } + + //@Override + public void setDither(boolean dither) { + if (dither != mDither) { + mDither = dither; + invalidateSelf(); + } + } + + @Override + public void setColorFilter(ColorFilter cf) { + if (cf != mColorFilter) { + mColorFilter = cf; + invalidateSelf(); + } + } + + @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 mGradientState.mOpaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT; + } + + @Override + protected void onBoundsChange(Rect r) { + super.onBoundsChange(r); + mRingPath = null; + mPathIsDirty = true; + mRectIsDirty = true; + } + + //@Override + protected boolean onLevelChange(int level) { + //super.onLevelChange(level); + mRectIsDirty = true; + mPathIsDirty = true; + invalidateSelf(); + return true; + } + + /** + * This checks mRectIsDirty, and if it is true, recomputes both our drawing + * rectangle (mRect) and the gradient itself, since it depends on our + * rectangle too. + * @return true if the resulting rectangle is not empty, false otherwise + */ + private boolean ensureValidRect() { + if (mRectIsDirty) { + mRectIsDirty = false; + + Rect bounds = getBounds(); + float inset = 0; + + if (mStrokePaint != null) { + inset = mStrokePaint.getStrokeWidth() * 0.5f; + } + + final GradientState st = mGradientState; + + mRect.set(bounds.left + inset, bounds.top + inset, + bounds.right - inset, bounds.bottom - inset); + + final int[] colors = st.mColors; + if (colors != null) { + RectF r = mRect; + float x0, x1, y0, y1; + + if (st.mGradient == LINEAR_GRADIENT) { + final float level = st.mUseLevel ? (float)getLevel() / 10000.0f : 1.0f; + switch (st.mOrientation) { + case TOP_BOTTOM: + x0 = r.left; + y0 = r.top; + x1 = x0; + y1 = level * r.bottom; + break; + case TR_BL: + x0 = r.right; + y0 = r.top; + x1 = level * r.left; + y1 = level * r.bottom; + break; + case RIGHT_LEFT: + x0 = r.right; + y0 = r.top; + x1 = level * r.left; + y1 = y0; + break; + case BR_TL: + x0 = r.right; + y0 = r.bottom; + x1 = level * r.left; + y1 = level * r.top; + break; + case BOTTOM_TOP: + x0 = r.left; + y0 = r.bottom; + x1 = x0; + y1 = level * r.top; + break; + case BL_TR: + x0 = r.left; + y0 = r.bottom; + x1 = level * r.right; + y1 = level * r.top; + break; + case LEFT_RIGHT: + x0 = r.left; + y0 = r.top; + x1 = level * r.right; + y1 = y0; + break; + default: /* TL_BR */ + x0 = r.left; + y0 = r.top; + x1 = level * r.right; + y1 = level * r.bottom; + break; + } + + mFillPaint.setShader(new LinearGradient(x0, y0, x1, y1, + colors, st.mPositions, Shader.TileMode.CLAMP)); + } else if (st.mGradient == RADIAL_GRADIENT) { + x0 = r.left + (r.right - r.left) * st.mCenterX; + y0 = r.top + (r.bottom - r.top) * st.mCenterY; + + final float level = st.mUseLevel ? (float)getLevel() / 10000.0f : 1.0f; + + mFillPaint.setShader(new RadialGradient(x0, y0, + level * st.mGradientRadius, colors, null, + Shader.TileMode.CLAMP)); + } else if (st.mGradient == SWEEP_GRADIENT) { + /*x0 = r.left + (r.right - r.left) * st.mCenterX; + y0 = r.top + (r.bottom - r.top) * st.mCenterY; + + int[] tempColors = colors; + float[] tempPositions = null; + + if (st.mUseLevel) { + tempColors = st.mTempColors; + final int length = colors.length; + if (tempColors == null || tempColors.length != length + 1) { + tempColors = st.mTempColors = new int[length + 1]; + } + System.arraycopy(colors, 0, tempColors, 0, length); + tempColors[length] = colors[length - 1]; + + tempPositions = st.mTempPositions; + final float fraction = 1.0f / (float)(length - 1); + if (tempPositions == null || tempPositions.length != length + 1) { + tempPositions = st.mTempPositions = new float[length + 1]; + } + + final float level = (float)getLevel() / 10000.0f; + for (int i = 0; i < length; i++) { + tempPositions[i] = i * fraction * level; + } + tempPositions[length] = 1.0f; + } + mFillPaint.setShader(new SweepGradient(x0, y0, tempColors, tempPositions));*/ + } + + // If we don't have a solid color, the alpha channel must be + // maxed out so that alpha modulation works correctly. + if (!st.mHasSolidColor) { + mFillPaint.setColor(Color.BLACK); + } + } + } + return !mRect.isEmpty(); + } + +/* @Override + public void inflate(Resources r, XmlPullParser parser, + AttributeSet attrs) + throws XmlPullParserException, IOException { + + final GradientState st = mGradientState; + + TypedArray a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.GradientDrawable); + + super.inflateWithAttributes(r, parser, a, + com.android.internal.R.styleable.GradientDrawable_visible); + + int shapeType = a.getInt( + com.android.internal.R.styleable.GradientDrawable_shape, RECTANGLE); + boolean dither = a.getBoolean( + com.android.internal.R.styleable.GradientDrawable_dither, false); + + if (shapeType == RING) { + st.mInnerRadius = a.getDimensionPixelSize( + com.android.internal.R.styleable.GradientDrawable_innerRadius, -1); + if (st.mInnerRadius == -1) { + st.mInnerRadiusRatio = a.getFloat( + com.android.internal.R.styleable.GradientDrawable_innerRadiusRatio, 3.0f); + } + st.mThickness = a.getDimensionPixelSize( + com.android.internal.R.styleable.GradientDrawable_thickness, -1); + if (st.mThickness == -1) { + st.mThicknessRatio = a.getFloat( + com.android.internal.R.styleable.GradientDrawable_thicknessRatio, 9.0f); + } + st.mUseLevelForShape = a.getBoolean( + com.android.internal.R.styleable.GradientDrawable_useLevel, true); + } + + a.recycle(); + + setShape(shapeType); + setDither(dither); + + 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)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + if (depth > innerDepth) { + continue; + } + + String name = parser.getName(); + + if (name.equals("size")) { + a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.GradientDrawableSize); + int width = a.getDimensionPixelSize( + com.android.internal.R.styleable.GradientDrawableSize_width, -1); + int height = a.getDimensionPixelSize( + com.android.internal.R.styleable.GradientDrawableSize_height, -1); + a.recycle(); + setSize(width, height); + } else if (name.equals("gradient")) { + a = r.obtainAttributes(attrs, + com.android.internal.R.styleable.GradientDrawableGradient); + int startColor = a.getColor( + com.android.internal.R.styleable.GradientDrawableGradient_startColor, 0); + boolean hasCenterColor = a + .hasValue(com.android.internal.R.styleable.GradientDrawableGradient_centerColor); + int centerColor = a.getColor( + com.android.internal.R.styleable.GradientDrawableGradient_centerColor, 0); + int endColor = a.getColor( + com.android.internal.R.styleable.GradientDrawableGradient_endColor, 0); + int gradientType = a.getInt( + com.android.internal.R.styleable.GradientDrawableGradient_type, + LINEAR_GRADIENT); + + st.mCenterX = getFloatOrFraction( + a, + com.android.internal.R.styleable.GradientDrawableGradient_centerX, + 0.5f); + + st.mCenterY = getFloatOrFraction( + a, + com.android.internal.R.styleable.GradientDrawableGradient_centerY, + 0.5f); + + st.mUseLevel = a.getBoolean( + com.android.internal.R.styleable.GradientDrawableGradient_useLevel, false); + st.mGradient = gradientType; + + if (gradientType == LINEAR_GRADIENT) { + int angle = (int)a.getFloat( + com.android.internal.R.styleable.GradientDrawableGradient_angle, 0); + angle %= 360; + if (angle % 45 != 0) { + throw new XmlPullParserException(a.getPositionDescription() + "