ShapeDrawable.java revision b3c56086d802ae28888dd97ba1f49bd6cee0b673
1e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet/*
2e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * Copyright (C) 2007 The Android Open Source Project
3e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet *
4e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * Licensed under the Apache License, Version 2.0 (the "License");
5e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * you may not use this file except in compliance with the License.
6e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * You may obtain a copy of the License at
7e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet *
8e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet *      http://www.apache.org/licenses/LICENSE-2.0
9e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet *
10e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * Unless required by applicable law or agreed to in writing, software
11e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * distributed under the License is distributed on an "AS IS" BASIS,
12e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * See the License for the specific language governing permissions and
14e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * limitations under the License.
15e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet */
16e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet
17e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetpackage android.graphics.drawable;
18e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet
19aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stoutimport android.content.res.ColorStateList;
20aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stoutimport android.content.res.Resources;
21e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport android.content.res.TypedArray;
225a59bde085588f95dc067bd1ed64a940f355343cbulicimport android.graphics.Canvas;
23f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichetimport android.graphics.ColorFilter;
24aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stoutimport android.graphics.Outline;
25e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport android.graphics.Paint;
26f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichetimport android.graphics.PixelFormat;
274cf79b1c4d38a190317961891f9fd052836710fdCraig Stoutimport android.graphics.PorterDuff.Mode;
2870acb0c19be3831a2080e4f902324de16bfbf62eTor Norbyeimport android.graphics.PorterDuffColorFilter;
29e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport android.graphics.Rect;
30e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport android.graphics.Shader;
31e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport android.graphics.drawable.shapes.Shape;
32e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport android.content.res.Resources.Theme;
334cf79b1c4d38a190317961891f9fd052836710fdCraig Stoutimport android.util.AttributeSet;
34f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichet
35e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport org.xmlpull.v1.XmlPullParser;
36937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichetimport org.xmlpull.v1.XmlPullParserException;
37937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet
38937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichetimport java.io.IOException;
39a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout
40937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet/**
414cf79b1c4d38a190317961891f9fd052836710fdCraig Stout * A Drawable object that draws primitive shapes. A ShapeDrawable takes a
42e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * {@link android.graphics.drawable.shapes.Shape} object and manages its
43558f7b70035c50045908efca5f4f3d65df685cacCraig Stout * presence on the screen. If no Shape is given, then the ShapeDrawable will
444cf79b1c4d38a190317961891f9fd052836710fdCraig Stout * default to a {@link android.graphics.drawable.shapes.RectShape}.
45937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet * <p>
46937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet * This object can be defined in an XML file with the <code>&lt;shape></code>
474fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * element.
484cf79b1c4d38a190317961891f9fd052836710fdCraig Stout * </p>
49aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout * <div class="special reference"> <h3>Developer Guides</h3>
5044f004b2612b04510a09a4c1e33f5a109582d669Craig Stout * <p>
51c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout * For more information about how to use ShapeDrawable, read the <a
52c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout * href="{@docRoot}guide/topics/graphics/2d-graphics.html#shape-drawable">
53aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout * Canvas and Drawables</a> document. For more information about defining a
5477b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov * ShapeDrawable in XML, read the <a href="{@docRoot}
5577b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov * guide/topics/resources/drawable-resource.html#Shape">Drawable Resources</a>
56aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout * document.
574fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * </p>
584fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * </div>
594fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout *
604fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @attr ref android.R.styleable#ShapeDrawablePadding_left
614fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @attr ref android.R.styleable#ShapeDrawablePadding_top
624fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @attr ref android.R.styleable#ShapeDrawablePadding_right
634fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @attr ref android.R.styleable#ShapeDrawablePadding_bottom
644fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @attr ref android.R.styleable#ShapeDrawable_color
654fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @attr ref android.R.styleable#ShapeDrawable_width
66c4788eac909f51e73e05885314d3ace65d0a2f26bulic * @attr ref android.R.styleable#ShapeDrawable_height
674fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout */
684fdd3589c982860b831c0fad63c0082cb9079f47Craig Stoutpublic class ShapeDrawable extends Drawable {
6970acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye    private ShapeState mShapeState;
704fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    private PorterDuffColorFilter mTintFilter;
714fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    private boolean mMutated;
724fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout
734fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    /**
744fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     * ShapeDrawable constructor.
754fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     */
76c4788eac909f51e73e05885314d3ace65d0a2f26bulic    public ShapeDrawable() {
774fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        this((ShapeState) null);
784fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    }
794fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout
8070acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye    /**
814fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     * Creates a ShapeDrawable with a specified Shape.
824fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     *
834fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     * @param s the Shape that this ShapeDrawable should be
844fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     */
854fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    public ShapeDrawable(Shape s) {
864fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        this((ShapeState) null);
874fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout
884fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        mShapeState.mShape = s;
894fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    }
904fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout
9170acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye    private ShapeDrawable(ShapeState state) {
924fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        mShapeState = new ShapeState(state);
934fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout
944fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
954fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    }
964fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout
974fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    /**
984fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     * Returns the Shape of this ShapeDrawable.
994fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     */
10070acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye    public Shape getShape() {
1014fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        return mShapeState.mShape;
1024fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    }
1034fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout
1044fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    /**
1054fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     * Sets the Shape of this ShapeDrawable.
10670acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye     */
1074fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    public void setShape(Shape s) {
1084fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        mShapeState.mShape = s;
1094fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        updateShape();
1104fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    }
1114fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout
11270acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye    /**
1134fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     * Sets a ShaderFactory to which requests for a
1144fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     * {@link android.graphics.Shader} object will be made.
1154fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     *
1164fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     * @param fact an instance of your ShaderFactory implementation
1174fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     */
1184fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    public void setShaderFactory(ShaderFactory fact) {
1194fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        mShapeState.mShaderFactory = fact;
1204fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    }
1214fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout
1224fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    /**
1234fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     * Returns the ShaderFactory used by this ShapeDrawable for requesting a
1244fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     * {@link android.graphics.Shader}.
1254fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout     */
1264fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    public ShaderFactory getShaderFactory() {
1274fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        return mShapeState.mShaderFactory;
128aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout    }
129aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout
130aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout    /**
131aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout     * Returns the Paint used to draw the shape.
132aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout     */
133aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout    public Paint getPaint() {
134aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout        return mShapeState.mPaint;
135aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout    }
136aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout
137aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout    /**
138e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet     * Sets padding for the shape.
13969e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout     *
14069e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout     * @param left padding for the left side (in pixels)
14169e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout     * @param top padding for the top (in pixels)
14269e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout     * @param right padding for the right side (in pixels)
14369e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout     * @param bottom padding for the bottom (in pixels)
14469e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout     */
145c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout    public void setPadding(int left, int top, int right, int bottom) {
14669e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout        if ((left | top | right | bottom) == 0) {
14769e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout            mShapeState.mPadding = null;
14869e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout        } else {
14999ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas            if (mShapeState.mPadding == null) {
150c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout                mShapeState.mPadding = new Rect();
151c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout            }
152c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout            mShapeState.mPadding.set(left, top, right, bottom);
153c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout        }
154e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet        invalidateSelf();
155e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet    }
156e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet
157e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet    /**
158e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet     * Sets padding for this shape, defined by a Rect object. Define the padding
159937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet     * in the Rect object as: left, top, right, bottom.
160e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet     */
161e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet    public void setPadding(Rect padding) {
162937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet        if (padding == null) {
163937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet            mShapeState.mPadding = null;
164e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet        } else {
1655a59bde085588f95dc067bd1ed64a940f355343cbulic            if (mShapeState.mPadding == null) {
1665a59bde085588f95dc067bd1ed64a940f355343cbulic                mShapeState.mPadding = new Rect();
167e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet            }
168e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet            mShapeState.mPadding.set(padding);
1694c696a0ad561090035960c2f34a058562c7f2aadCraig Stout        }
170558f7b70035c50045908efca5f4f3d65df685cacCraig Stout        invalidateSelf();
171558f7b70035c50045908efca5f4f3d65df685cacCraig Stout    }
172e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet
173ed57770cf2211040bffe0bddd55e7c28fb47a42aJerome Poichet    /**
174ed57770cf2211040bffe0bddd55e7c28fb47a42aJerome Poichet     * Sets the intrinsic (default) width for this shape.
175ed57770cf2211040bffe0bddd55e7c28fb47a42aJerome Poichet     *
176ed57770cf2211040bffe0bddd55e7c28fb47a42aJerome Poichet     * @param width the intrinsic width (in pixels)
17744f004b2612b04510a09a4c1e33f5a109582d669Craig Stout     */
17844f004b2612b04510a09a4c1e33f5a109582d669Craig Stout    public void setIntrinsicWidth(int width) {
179c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout        mShapeState.mIntrinsicWidth = width;
180c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout        invalidateSelf();
181c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout    }
182c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout
183ed57770cf2211040bffe0bddd55e7c28fb47a42aJerome Poichet    /**
184937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet     * Sets the intrinsic (default) height for this shape.
185937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet     *
1865a59bde085588f95dc067bd1ed64a940f355343cbulic     * @param height the intrinsic height (in pixels)
187f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichet     */
1885a59bde085588f95dc067bd1ed64a940f355343cbulic    public void setIntrinsicHeight(int height) {
1895a59bde085588f95dc067bd1ed64a940f355343cbulic        mShapeState.mIntrinsicHeight = height;
1905a59bde085588f95dc067bd1ed64a940f355343cbulic        invalidateSelf();
191937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    }
192f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichet
1935a59bde085588f95dc067bd1ed64a940f355343cbulic    @Override
1945a59bde085588f95dc067bd1ed64a940f355343cbulic    public int getIntrinsicWidth() {
1958b55ff20146055bb0c4c5544814fcf530e03649abulic        return mShapeState.mIntrinsicWidth;
1964fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    }
1974fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout
1984fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    @Override
199f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichet    public int getIntrinsicHeight() {
200f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichet        return mShapeState.mIntrinsicHeight;
201e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet    }
2024cf79b1c4d38a190317961891f9fd052836710fdCraig Stout
203e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet    @Override
204c4788eac909f51e73e05885314d3ace65d0a2f26bulic    public boolean getPadding(Rect padding) {
205c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout        if (mShapeState.mPadding != null) {
206c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout            padding.set(mShapeState.mPadding);
20769e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout            return true;
208c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout        } else {
209e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet            return super.getPadding(padding);
210e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet        }
2114c696a0ad561090035960c2f34a058562c7f2aadCraig Stout    }
2124c696a0ad561090035960c2f34a058562c7f2aadCraig Stout
2134c696a0ad561090035960c2f34a058562c7f2aadCraig Stout    private static int modulateAlpha(int paintAlpha, int alpha) {
2144c696a0ad561090035960c2f34a058562c7f2aadCraig Stout        int scale = alpha + (alpha >>> 7); // convert to 0..256
2154c696a0ad561090035960c2f34a058562c7f2aadCraig Stout        return paintAlpha * scale >>> 8;
2164c696a0ad561090035960c2f34a058562c7f2aadCraig Stout    }
2174c696a0ad561090035960c2f34a058562c7f2aadCraig Stout
2184c696a0ad561090035960c2f34a058562c7f2aadCraig Stout    /**
2194c696a0ad561090035960c2f34a058562c7f2aadCraig Stout     * Called from the drawable's draw() method after the canvas has been set to
2204c696a0ad561090035960c2f34a058562c7f2aadCraig Stout     * draw the shape at (0,0). Subclasses can override for special effects such
2214c696a0ad561090035960c2f34a058562c7f2aadCraig Stout     * as multiple layers, stroking, etc.
2224c696a0ad561090035960c2f34a058562c7f2aadCraig Stout     */
2234c696a0ad561090035960c2f34a058562c7f2aadCraig Stout    protected void onDraw(Shape shape, Canvas canvas, Paint paint) {
224e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet        shape.draw(canvas, paint);
225e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet    }
226e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet
227e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet    @Override
228e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet    public void draw(Canvas canvas) {
229e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet        final Rect r = getBounds();
230e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet        final ShapeState state = mShapeState;
23169e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout        final Paint paint = state.mPaint;
23269e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout
23369e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout        final int prevAlpha = paint.getAlpha();
23469e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout        paint.setAlpha(modulateAlpha(prevAlpha, state.mAlpha));
23569e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout
23669e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout        // only draw shape if it may affect output
23769e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout        if (paint.getAlpha() != 0 || paint.getXfermode() != null || paint.hasShadowLayer()) {
23869e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout            final boolean clearColorFilter;
23969e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout            if (mTintFilter != null && paint.getColorFilter() == null) {
24069e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout                paint.setColorFilter(mTintFilter);
24169e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout                clearColorFilter = true;
24269e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout            } else {
24369e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout                clearColorFilter = false;
244e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet            }
245e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet
246e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet            if (state.mShape != null) {
2474c696a0ad561090035960c2f34a058562c7f2aadCraig Stout                // need the save both for the translate, and for the (unknown)
2484c696a0ad561090035960c2f34a058562c7f2aadCraig Stout                // Shape
2494c696a0ad561090035960c2f34a058562c7f2aadCraig Stout                final int count = canvas.save();
2504c696a0ad561090035960c2f34a058562c7f2aadCraig Stout                canvas.translate(r.left, r.top);
2514c696a0ad561090035960c2f34a058562c7f2aadCraig Stout                onDraw(state.mShape, canvas, paint);
25244f004b2612b04510a09a4c1e33f5a109582d669Craig Stout                canvas.restoreToCount(count);
2534c696a0ad561090035960c2f34a058562c7f2aadCraig Stout            } else {
2544c696a0ad561090035960c2f34a058562c7f2aadCraig Stout                canvas.drawRect(r, paint);
255aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout            }
256aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout
257937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet            if (clearColorFilter) {
258a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout                paint.setColorFilter(null);
259937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet            }
260937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet        }
261937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet
262937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet        // restore
263937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet        paint.setAlpha(prevAlpha);
264aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout    }
265aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout
266937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    @Override
267937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    public int getChangingConfigurations() {
268937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet        return super.getChangingConfigurations()
269937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet                | mShapeState.mChangingConfigurations;
270937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    }
271937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet
272e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet    /**
273e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet     * Set the alpha level for this drawable [0..255]. Note that this drawable
274e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet     * also has a color in its paint, which has an alpha as well. These two
275a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * values are automatically combined during drawing. Thus if the color's
276e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet     * alpha is 75% (i.e. 192) and the drawable's alpha is 50% (i.e. 128), then
277e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet     * the combined alpha that will be used during drawing will be 37.5% (i.e.
278e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet     * 96).
279e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet     */
280e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet    @Override
281e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet    public void setAlpha(int alpha) {
282937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet        mShapeState.mAlpha = alpha;
2834fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        invalidateSelf();
2844fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    }
285c4788eac909f51e73e05885314d3ace65d0a2f26bulic
286937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    @Override
287937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    public int getAlpha() {
2884cf79b1c4d38a190317961891f9fd052836710fdCraig Stout        return mShapeState.mAlpha;
2894fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    }
2908b55ff20146055bb0c4c5544814fcf530e03649abulic
2918b55ff20146055bb0c4c5544814fcf530e03649abulic    @Override
2924fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    public void setTint(ColorStateList tint, Mode tintMode) {
2934fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        if (mShapeState.mTint != tint || mShapeState.mTintMode != tintMode) {
2944fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout            mShapeState.mTint = tint;
2954fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout            mShapeState.mTintMode = tintMode;
2964fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout            updateTintFilter();
2974fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout            invalidateSelf();
29870acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye        }
2994fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    }
3004cf79b1c4d38a190317961891f9fd052836710fdCraig Stout
301731066a59e10ddc7bb6c95d0b91b3e0e11e10396Craig Stout    /**
302937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet     * Ensures the tint filter is consistent with the current tint color and
303937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet     * mode.
304937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet     */
305937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    private void updateTintFilter() {
30670acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye        final ColorStateList tint = mShapeState.mTint;
307731066a59e10ddc7bb6c95d0b91b3e0e11e10396Craig Stout        final Mode tintMode = mShapeState.mTintMode;
3084fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        if (tint != null && tintMode != null) {
3094fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout            if (mTintFilter == null) {
3104fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout                mTintFilter = new PorterDuffColorFilter(0, tintMode);
3114fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout            } else {
312a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout                mTintFilter.setMode(tintMode);
3134fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout            }
3144fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        } else {
3154fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout            mTintFilter = null;
3164fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        }
3174fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    }
3184fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout
3194fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    @Override
3204fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    public void setColorFilter(ColorFilter cf) {
3214fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        mShapeState.mPaint.setColorFilter(cf);
3224fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        invalidateSelf();
323731066a59e10ddc7bb6c95d0b91b3e0e11e10396Craig Stout    }
324937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet
3254fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    @Override
3264fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout    public int getOpacity() {
3274fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout        if (mShapeState.mShape == null) {
3284fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout            final Paint p = mShapeState.mPaint;
3294fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout            if (p.getXfermode() == null) {
330937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet                final int alpha = p.getAlpha();
331937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet                if (alpha == 0) {
332d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout                    return PixelFormat.TRANSPARENT;
333d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout                }
334c4788eac909f51e73e05885314d3ace65d0a2f26bulic                if (alpha == 255) {
335d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout                    return PixelFormat.OPAQUE;
336d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout                }
337d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout            }
338d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout        }
339d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout        // not sure, so be safe
340d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout        return PixelFormat.TRANSLUCENT;
341d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout    }
34277b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov
34377b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov    @Override
34477b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov    public void setDither(boolean dither) {
34577b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov        mShapeState.mPaint.setDither(dither);
34677b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov        invalidateSelf();
347937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    }
348937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet
349937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    @Override
350937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    protected void onBoundsChange(Rect bounds) {
35177b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov        super.onBoundsChange(bounds);
352937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet        updateShape();
353937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    }
3544fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout
355937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    @Override
356937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet    protected boolean onStateChange(int[] stateSet) {
357937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet        final ColorStateList tint = mShapeState.mTint;
358937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet        if (tint != null) {
359937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet            final int newColor = tint.getColorForState(stateSet, 0);
360937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet            final int oldColor = mTintFilter.getColor();
361937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet            if (oldColor != newColor) {
36299ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas                mTintFilter.setColor(newColor);
363937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet                invalidateSelf();
364937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet                return true;
365937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet            }
366937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet        }
367937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet
3686ced181ce948d1ae03c1a8402cf5ca31e2bef170Craig Stout        return false;
36977b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov    }
37077b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov
37177b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov    @Override
37277b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov    public boolean isStateful() {
37377b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov        final ShapeState s = mShapeState;
37477b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov        return super.isStateful() || (s.mTint != null && s.mTint.isStateful());
37577b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov    }
3766ced181ce948d1ae03c1a8402cf5ca31e2bef170Craig Stout
37777b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov    /**
3786ced181ce948d1ae03c1a8402cf5ca31e2bef170Craig Stout     * Subclasses override this to parse custom subelements. If you handle it,
37977b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov     * return true, else return <em>super.inflateTag(...)</em>.
3806ced181ce948d1ae03c1a8402cf5ca31e2bef170Craig Stout     */
3816ced181ce948d1ae03c1a8402cf5ca31e2bef170Craig Stout    protected boolean inflateTag(String name, Resources r, XmlPullParser parser,
382e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet            AttributeSet attrs) {
383
384        if ("padding".equals(name)) {
385            TypedArray a = r.obtainAttributes(attrs,
386                    com.android.internal.R.styleable.ShapeDrawablePadding);
387            setPadding(
388                    a.getDimensionPixelOffset(
389                            com.android.internal.R.styleable.ShapeDrawablePadding_left, 0),
390                    a.getDimensionPixelOffset(
391                            com.android.internal.R.styleable.ShapeDrawablePadding_top, 0),
392                    a.getDimensionPixelOffset(
393                            com.android.internal.R.styleable.ShapeDrawablePadding_right, 0),
394                    a.getDimensionPixelOffset(
395                            com.android.internal.R.styleable.ShapeDrawablePadding_bottom, 0));
396            a.recycle();
397            return true;
398        }
399
400        return false;
401    }
402
403    @Override
404    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
405            throws XmlPullParserException, IOException {
406        super.inflate(r, parser, attrs, theme);
407
408        TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ShapeDrawable);
409
410        int color = mShapeState.mPaint.getColor();
411        color = a.getColor(com.android.internal.R.styleable.ShapeDrawable_color, color);
412        mShapeState.mPaint.setColor(color);
413
414        boolean dither = a.getBoolean(com.android.internal.R.styleable.ShapeDrawable_dither, false);
415        mShapeState.mPaint.setDither(dither);
416
417        setIntrinsicWidth((int)
418                a.getDimension(com.android.internal.R.styleable.ShapeDrawable_width, 0f));
419        setIntrinsicHeight((int)
420                a.getDimension(com.android.internal.R.styleable.ShapeDrawable_height, 0f));
421
422        a.recycle();
423
424        int type;
425        final int outerDepth = parser.getDepth();
426        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
427                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
428            if (type != XmlPullParser.START_TAG) {
429                continue;
430            }
431
432            final String name = parser.getName();
433            // call our subclass
434            if (!inflateTag(name, r, parser, attrs)) {
435                android.util.Log.w("drawable", "Unknown element: " + name +
436                        " for ShapeDrawable " + this);
437            }
438        }
439    }
440
441    private void updateShape() {
442        if (mShapeState.mShape != null) {
443            final Rect r = getBounds();
444            final int w = r.width();
445            final int h = r.height();
446
447            mShapeState.mShape.resize(w, h);
448            if (mShapeState.mShaderFactory != null) {
449                mShapeState.mPaint.setShader(mShapeState.mShaderFactory.resize(w, h));
450            }
451        }
452        invalidateSelf();
453    }
454
455    @Override
456    public boolean getOutline(Outline outline) {
457        if (mShapeState.mShape == null) {
458            // don't publish outline without a shape
459            return false;
460        }
461
462        return mShapeState.mShape.getOutline(outline);
463    }
464
465    @Override
466    public ConstantState getConstantState() {
467        mShapeState.mChangingConfigurations = getChangingConfigurations();
468        return mShapeState;
469    }
470
471    @Override
472    public Drawable mutate() {
473        if (!mMutated && super.mutate() == this) {
474            if (mShapeState.mPaint != null) {
475                mShapeState.mPaint = new Paint(mShapeState.mPaint);
476            } else {
477                mShapeState.mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
478            }
479            if (mShapeState.mPadding != null) {
480                mShapeState.mPadding = new Rect(mShapeState.mPadding);
481            } else {
482                mShapeState.mPadding = new Rect();
483            }
484            try {
485                mShapeState.mShape = mShapeState.mShape.clone();
486            } catch (CloneNotSupportedException e) {
487                return null;
488            }
489            mMutated = true;
490        }
491        return this;
492    }
493
494    /**
495     * Defines the intrinsic properties of this ShapeDrawable's Shape.
496     */
497    final static class ShapeState extends ConstantState {
498        int mChangingConfigurations;
499        Paint mPaint;
500        Shape mShape;
501        ColorStateList mTint;
502        Mode mTintMode = Mode.SRC_IN;
503        Rect mPadding;
504        int mIntrinsicWidth;
505        int mIntrinsicHeight;
506        int mAlpha = 255;
507        ShaderFactory mShaderFactory;
508
509        ShapeState(ShapeState orig) {
510            if (orig != null) {
511                mPaint = orig.mPaint;
512                mShape = orig.mShape;
513                mTint = orig.mTint;
514                mTintMode = orig.mTintMode;
515                mPadding = orig.mPadding;
516                mIntrinsicWidth = orig.mIntrinsicWidth;
517                mIntrinsicHeight = orig.mIntrinsicHeight;
518                mAlpha = orig.mAlpha;
519                mShaderFactory = orig.mShaderFactory;
520            } else {
521                mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
522            }
523        }
524
525        @Override
526        public Drawable newDrawable() {
527            return new ShapeDrawable(this);
528        }
529
530        @Override
531        public Drawable newDrawable(Resources res) {
532            return new ShapeDrawable(this);
533        }
534
535        @Override
536        public int getChangingConfigurations() {
537            return mChangingConfigurations;
538        }
539    }
540
541    /**
542     * Base class defines a factory object that is called each time the drawable
543     * is resized (has a new width or height). Its resize() method returns a
544     * corresponding shader, or null. Implement this class if you'd like your
545     * ShapeDrawable to use a special {@link android.graphics.Shader}, such as a
546     * {@link android.graphics.LinearGradient}.
547     */
548    public static abstract class ShaderFactory {
549        /**
550         * Returns the Shader to be drawn when a Drawable is drawn. The
551         * dimensions of the Drawable are passed because they may be needed to
552         * adjust how the Shader is configured for drawing. This is called by
553         * ShapeDrawable.setShape().
554         *
555         * @param width the width of the Drawable being drawn
556         * @param height the heigh of the Drawable being drawn
557         * @return the Shader to be drawn
558         */
559        public abstract Shader resize(int width, int height);
560    }
561
562    // other subclass could wack the Shader's localmatrix based on the
563    // resize params (e.g. scaletofit, etc.). This could be used to scale
564    // a bitmap to fill the bounds without needing any other special casing.
565}
566