1d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette/*
2d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette * Copyright (C) 2013 The Android Open Source Project
3d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette *
4d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette * Licensed under the Apache License, Version 2.0 (the "License");
5d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette * you may not use this file except in compliance with the License.
6d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette * You may obtain a copy of the License at
7d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette *
8d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette *      http://www.apache.org/licenses/LICENSE-2.0
9d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette *
10d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette * Unless required by applicable law or agreed to in writing, software
11d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette * distributed under the License is distributed on an "AS IS" BASIS,
12d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette * See the License for the specific language governing permissions and
14d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette * limitations under the License.
15d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette */
16d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
17d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverettepackage android.graphics.drawable;
18d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
19cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viveretteimport com.android.internal.R;
20cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette
21cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viveretteimport org.xmlpull.v1.XmlPullParser;
22cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viveretteimport org.xmlpull.v1.XmlPullParserException;
23cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette
247275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viveretteimport android.annotation.NonNull;
257275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viveretteimport android.annotation.Nullable;
26d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.content.res.ColorStateList;
27d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.content.res.Resources;
2852b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viveretteimport android.content.res.Resources.Theme;
29d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.content.res.TypedArray;
306dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viveretteimport android.graphics.Bitmap;
316dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viveretteimport android.graphics.BitmapShader;
32d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.graphics.Canvas;
33d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.graphics.Color;
34c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viveretteimport android.graphics.ColorFilter;
356dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viveretteimport android.graphics.Matrix;
367c0517272ba2d97084739a14fea78641b265eb5dAlan Viveretteimport android.graphics.Outline;
37d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.graphics.Paint;
38d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.graphics.PixelFormat;
396dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viveretteimport android.graphics.PorterDuff;
406dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viveretteimport android.graphics.PorterDuffColorFilter;
41d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.graphics.Rect;
426dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viveretteimport android.graphics.Shader;
43d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.util.AttributeSet;
44d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.util.DisplayMetrics;
45d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
46d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport java.io.IOException;
4740e38d43675b9baa4383058e5afd5291291abc81Alan Viveretteimport java.util.Arrays;
48d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
49d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette/**
50ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * Drawable that shows a ripple effect in response to state changes. The
51ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * anchoring position of the ripple for a given state may be specified by
52c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette * calling {@link #setHotspot(float, float)} with the corresponding state
53ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * attribute identifier.
54ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * <p>
55ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * A touch feedback drawable may contain multiple child layers, including a
56ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * special mask layer that is not drawn to the screen. A single layer may be set
57ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * as the mask by specifying its android:id value as {@link android.R.id#mask}.
58a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <pre>
59a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <code>&lt!-- A red ripple masked against an opaque rectangle. --/>
60a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * &ltripple android:color="#ffff0000">
61a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette *   &ltitem android:id="@android:id/mask"
627275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette *         android:drawable="@android:color/white" />
636dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * &lt/ripple></code>
64a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * </pre>
65ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * <p>
66ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * If a mask layer is set, the ripple effect will be masked against that layer
67a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * before it is drawn over the composite of the remaining child layers.
68ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * <p>
69a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * If no mask layer is set, the ripple effect is masked against the composite
70a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * of the child layers.
71a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <pre>
726dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * <code>&lt!-- A green ripple drawn atop a black rectangle. --/>
73a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * &ltripple android:color="#ff00ff00">
747275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette *   &ltitem android:drawable="@android:color/black" />
756dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * &lt/ripple>
76a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette *
776dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * &lt!-- A blue ripple drawn atop a drawable resource. --/>
786dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * &ltripple android:color="#ff0000ff">
79a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette *   &ltitem android:drawable="@drawable/my_drawable" />
806dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * &lt/ripple></code>
81a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * </pre>
82ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * <p>
83ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * If no child layers or mask is specified and the ripple is set as a View
84a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * background, the ripple will be drawn atop the first available parent
85a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * background within the View's hierarchy. In this case, the drawing region
86a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * may extend outside of the Drawable bounds.
87a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <pre>
886dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * <code>&lt!-- An unbounded red ripple. --/>
896dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * &ltripple android:color="#ffff0000" /></code>
90a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * </pre>
91ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette *
92a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * @attr ref android.R.styleable#RippleDrawable_color
93d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette */
94c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverettepublic class RippleDrawable extends LayerDrawable {
956dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private static final int MASK_UNKNOWN = -1;
966dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private static final int MASK_NONE = 0;
976dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private static final int MASK_CONTENT = 1;
986dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private static final int MASK_EXPLICIT = 2;
99dccbe8b02a34a3c78028a31ee158d4d2818c72baAlan Viverette
1004d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    /**
1014d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * Constant for automatically determining the maximum ripple radius.
1024d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     *
1034d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * @see #setMaxRadius(int)
1044d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * @hide
1054d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     */
1064d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    public static final int RADIUS_AUTO = -1;
1074d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
10847bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette    /** The maximum number of ripples supported. */
10947bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette    private static final int MAX_RIPPLES = 10;
11047bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette
111d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    private final Rect mTempRect = new Rect();
112d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
11361956606818918194a38e045a8e35e7108480e5eAlan Viverette    /** Current ripple effect bounds, used to constrain ripple effects. */
11461956606818918194a38e045a8e35e7108480e5eAlan Viverette    private final Rect mHotspotBounds = new Rect();
11561956606818918194a38e045a8e35e7108480e5eAlan Viverette
116d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    /** Current drawing bounds, used to compute dirty region. */
117d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    private final Rect mDrawingBounds = new Rect();
118d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
119d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    /** Current dirty bounds, union of current and previous drawing bounds. */
120d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    private final Rect mDirtyBounds = new Rect();
121d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
1225004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette    /** Mirrors mLayerState with some extra information. */
1235004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette    private RippleState mState;
124d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
125b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette    /** The masking layer, e.g. the layer with id R.id.mask. */
126b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette    private Drawable mMask;
127b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette
1286ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    /** The current background. May be actively animating or pending entry. */
1296ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    private RippleBackground mBackground;
1306ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
1316dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private Bitmap mMaskBuffer;
1326dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private BitmapShader mMaskShader;
1336dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private Canvas mMaskCanvas;
1346dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private Matrix mMaskMatrix;
1356dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private PorterDuffColorFilter mMaskColorFilter;
1366dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private boolean mHasValidMask;
1376dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
1386ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    /** Whether we expect to draw a background when visible. */
1396ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    private boolean mBackgroundActive;
1406ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
1416ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    /** The current ripple. May be actively animating or pending entry. */
1426ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    private Ripple mRipple;
1436ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
1446ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    /** Whether we expect to draw a ripple when visible. */
1456ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    private boolean mRippleActive;
1466ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
1476ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    // Hotspot coordinates that are awaiting activation.
1486ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    private float mPendingX;
1496ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    private float mPendingY;
1506ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    private boolean mHasPending;
151d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
152ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    /**
153ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette     * Lazily-created array of actively animating ripples. Inactive ripples are
154ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette     * pruned during draw(). The locations of these will not change.
155ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette     */
156fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette    private Ripple[] mExitingRipples;
157fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette    private int mExitingRipplesCount = 0;
158d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
159d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    /** Paint used to control appearance of ripples. */
160d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    private Paint mRipplePaint;
161d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
162d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    /** Target density of the display into which ripples are drawn. */
163cb29189c29a553a8005044f32de37a610f2857dbAlan Viverette    private float mDensity = 1.0f;
164d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
16561956606818918194a38e045a8e35e7108480e5eAlan Viverette    /** Whether bounds are being overridden. */
16661956606818918194a38e045a8e35e7108480e5eAlan Viverette    private boolean mOverrideBounds;
16761956606818918194a38e045a8e35e7108480e5eAlan Viverette
1686ae2d7cefafd7fbf5e7c8d7d3c1869e66056b7f8Alan Viverette    /**
1696ae2d7cefafd7fbf5e7c8d7d3c1869e66056b7f8Alan Viverette     * Constructor used for drawable inflation.
1706ae2d7cefafd7fbf5e7c8d7d3c1869e66056b7f8Alan Viverette     */
171c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette    RippleDrawable() {
17217cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette        this(new RippleState(null, null, null), null);
1734d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
1744d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
1754d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    /**
1767275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette     * Creates a new ripple drawable with the specified ripple color and
1777275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette     * optional content and mask drawables.
1784d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     *
1797275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette     * @param color The ripple color
1804d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * @param content The content drawable, may be {@code null}
1814d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * @param mask The mask drawable, may be {@code null}
1824d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     */
1837275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette    public RippleDrawable(@NonNull ColorStateList color, @Nullable Drawable content,
1847275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette            @Nullable Drawable mask) {
18517cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette        this(new RippleState(null, null, null), null);
1864d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
1877275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette        if (color == null) {
1887275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette            throw new IllegalArgumentException("RippleDrawable requires a non-null color");
1897275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette        }
1907275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette
1914d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        if (content != null) {
1924d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            addLayer(content, null, 0, 0, 0, 0, 0);
1934d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        }
1944d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
1954d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        if (mask != null) {
196935b1fa24d05533a95ee47425ab9bedb31641012Alan Viverette            addLayer(mask, null, android.R.id.mask, 0, 0, 0, 0);
1974d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        }
1984d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
1997275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette        setColor(color);
2004d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        ensurePadding();
201ade9ef236c5258d7369597f2f8a08ab277396513Alan Viverette        initializeFromState();
202c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette    }
203c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette
204c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette    @Override
205d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette    public void jumpToCurrentState() {
206d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        super.jumpToCurrentState();
207d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette
208d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        if (mRipple != null) {
209d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette            mRipple.jump();
210d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        }
211d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette
212d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        if (mBackground != null) {
213d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette            mBackground.jump();
214d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        }
215d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette
216cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        cancelExitingRipples();
217fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        invalidateSelf();
218fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette    }
219fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette
2202ae56403542582bd39c0e522bf29844d59300f37Alan Viverette    private boolean cancelExitingRipples() {
2212ae56403542582bd39c0e522bf29844d59300f37Alan Viverette        boolean needsDraw = false;
2222ae56403542582bd39c0e522bf29844d59300f37Alan Viverette
223fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        final int count = mExitingRipplesCount;
224fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        final Ripple[] ripples = mExitingRipples;
225d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        for (int i = 0; i < count; i++) {
2262ae56403542582bd39c0e522bf29844d59300f37Alan Viverette            needsDraw |= ripples[i].isHardwareAnimating();
227fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette            ripples[i].cancel();
22840e38d43675b9baa4383058e5afd5291291abc81Alan Viverette        }
229fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette
23040e38d43675b9baa4383058e5afd5291291abc81Alan Viverette        if (ripples != null) {
23140e38d43675b9baa4383058e5afd5291291abc81Alan Viverette            Arrays.fill(ripples, 0, count, null);
232d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        }
233fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        mExitingRipplesCount = 0;
2342ae56403542582bd39c0e522bf29844d59300f37Alan Viverette
2352ae56403542582bd39c0e522bf29844d59300f37Alan Viverette        return needsDraw;
236d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette    }
237d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette
238d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette    @Override
239c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette    public void setAlpha(int alpha) {
2404d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        super.setAlpha(alpha);
2414d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
2424d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        // TODO: Should we support this?
243c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette    }
244c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette
245c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette    @Override
246c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette    public void setColorFilter(ColorFilter cf) {
2474d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        super.setColorFilter(cf);
2484d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
2494d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        // TODO: Should we support this?
250d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    }
251d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
252d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    @Override
253d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    public int getOpacity() {
25447bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette        // Worst-case scenario.
25547bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette        return PixelFormat.TRANSLUCENT;
256ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette    }
257ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette
258ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette    @Override
25912b97f5d2b15194ed6673c9838b13c8312157709Alan Viverette    protected boolean onStateChange(int[] stateSet) {
260a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        final boolean changed = super.onStateChange(stateSet);
261ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette
2621b6e856e6f9dab4464e3c556b2f68527439fc329Alan Viverette        boolean enabled = false;
2636ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        boolean pressed = false;
2646ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        boolean focused = false;
2656ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
266a8a8ff000b2902eb4e187e62be39fd9535c6c839Alan Viverette        for (int state : stateSet) {
267a8a8ff000b2902eb4e187e62be39fd9535c6c839Alan Viverette            if (state == R.attr.state_enabled) {
2681b6e856e6f9dab4464e3c556b2f68527439fc329Alan Viverette                enabled = true;
2691b6e856e6f9dab4464e3c556b2f68527439fc329Alan Viverette            }
270a8a8ff000b2902eb4e187e62be39fd9535c6c839Alan Viverette            if (state == R.attr.state_focused) {
2716ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette                focused = true;
2726ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            }
273a8a8ff000b2902eb4e187e62be39fd9535c6c839Alan Viverette            if (state == R.attr.state_pressed) {
2746ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette                pressed = true;
2754d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            }
276d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette        }
2776ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
2786ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        setRippleActive(enabled && pressed);
279f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette        setBackgroundActive(focused || (enabled && pressed), focused);
280d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette
281a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        return changed;
282d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    }
283d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
2846ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    private void setRippleActive(boolean active) {
2856ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        if (mRippleActive != active) {
2866ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            mRippleActive = active;
2876ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            if (active) {
288fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette                tryRippleEnter();
2896ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            } else {
290fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette                tryRippleExit();
2916ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            }
2926ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        }
2936ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    }
2944d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
295f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette    private void setBackgroundActive(boolean active, boolean focused) {
2966ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        if (mBackgroundActive != active) {
2976ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            mBackgroundActive = active;
2984d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            if (active) {
299f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette                tryBackgroundEnter(focused);
3004d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            } else {
301fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette                tryBackgroundExit();
3024d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            }
3034d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        }
3044d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
3054d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
306307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette    @Override
307c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette    protected void onBoundsChange(Rect bounds) {
308c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette        super.onBoundsChange(bounds);
309d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette
31061956606818918194a38e045a8e35e7108480e5eAlan Viverette        if (!mOverrideBounds) {
31161956606818918194a38e045a8e35e7108480e5eAlan Viverette            mHotspotBounds.set(bounds);
312dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette            onHotspotBoundsChanged();
31361956606818918194a38e045a8e35e7108480e5eAlan Viverette        }
314c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette
315ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        invalidateSelf();
316c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette    }
317c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette
318c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette    @Override
319307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette    public boolean setVisible(boolean visible, boolean restart) {
3206ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        final boolean changed = super.setVisible(visible, restart);
3216ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
322307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette        if (!visible) {
32353d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette            clearHotspots();
3246ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        } else if (changed) {
3256ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            // If we just became visible, ensure the background and ripple
3266ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            // visibilities are consistent with their internal states.
3276ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            if (mRippleActive) {
328fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette                tryRippleEnter();
3296ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            }
3306ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
3316ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            if (mBackgroundActive) {
332f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette                tryBackgroundEnter(false);
3336ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            }
334f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette
335f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette            // Skip animations, just show the correct final states.
336f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette            jumpToCurrentState();
337307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette        }
338307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette
3396ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        return changed;
340307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette    }
341307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette
342d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    /**
343d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette     * @hide
344d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette     */
345d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    @Override
346d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    public boolean isProjected() {
3472627206e1b1658ccade3669d1794dc0d90e36264Alan Viverette        return getNumberOfLayers() == 0;
348d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    }
349d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
350d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    @Override
351d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    public boolean isStateful() {
352ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        return true;
353d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    }
354d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
355a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette    public void setColor(ColorStateList color) {
356a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        mState.mColor = color;
357d30688f0a0a75743df247831688efcbe2fa62cc4Alan Viverette        invalidateSelf();
3582f8ba8f7fad2b608102a9282219aaea2223e94f5Alan Viverette    }
3592f8ba8f7fad2b608102a9282219aaea2223e94f5Alan Viverette
360d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    @Override
36152b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
362d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette            throws XmlPullParserException, IOException {
363e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.RippleDrawable);
364ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        updateStateFromTypedArray(a);
365d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette        a.recycle();
366cb29189c29a553a8005044f32de37a610f2857dbAlan Viverette
367e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette        // Force padding default to STACK before inflating.
368e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette        setPaddingMode(PADDING_MODE_STACK);
369e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette
37047bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette        super.inflate(r, parser, attrs, theme);
371ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette
37247bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette        setTargetDensity(r.getDisplayMetrics());
373b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        initializeFromState();
374d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette    }
375d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette
376d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette    @Override
377d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette    public boolean setDrawableByLayerId(int id, Drawable drawable) {
378d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette        if (super.setDrawableByLayerId(id, drawable)) {
379d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette            if (id == R.id.mask) {
380b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette                mMask = drawable;
381d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette            }
382d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette
383d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette            return true;
384d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette        }
385d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette
386d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette        return false;
387ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette    }
388ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette
389ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette    /**
390e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     * Specifies how layer padding should affect the bounds of subsequent
391e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     * layers. The default and recommended value for RippleDrawable is
392e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     * {@link #PADDING_MODE_STACK}.
393e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     *
394e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     * @param mode padding mode, one of:
395e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     *            <ul>
396e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     *            <li>{@link #PADDING_MODE_NEST} to nest each layer inside the
397e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     *            padding of the previous layer
398e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     *            <li>{@link #PADDING_MODE_STACK} to stack each layer directly
399e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     *            atop the previous layer
400e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     *            </ul>
401e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     * @see #getPaddingMode()
402e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette     */
403e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette    @Override
404e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette    public void setPaddingMode(int mode) {
405e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette        super.setPaddingMode(mode);
406e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette    }
407e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette
408e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette    /**
40952b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette     * Initializes the constant state from the values in the typed array.
41052b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette     */
4114d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
412c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette        final RippleState state = mState;
41352b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette
4148e5e11b99fac942122ee2d6cdd30af51564861aeAlan Viverette        // Account for any configuration changes.
4158e5e11b99fac942122ee2d6cdd30af51564861aeAlan Viverette        state.mChangingConfigurations |= a.getChangingConfigurations();
4168e5e11b99fac942122ee2d6cdd30af51564861aeAlan Viverette
41752b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette        // Extract the theme attributes, if any.
418ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        state.mTouchThemeAttrs = a.extractThemeAttrs();
41952b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette
420a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        final ColorStateList color = a.getColorStateList(R.styleable.RippleDrawable_color);
421a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        if (color != null) {
422a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette            mState.mColor = color;
42352b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette        }
42452b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette
42534a14f967ab6c88829c9a36ce6e909c47b3ee398Alan Viverette        verifyRequiredAttributes(a);
42634a14f967ab6c88829c9a36ce6e909c47b3ee398Alan Viverette    }
42734a14f967ab6c88829c9a36ce6e909c47b3ee398Alan Viverette
42834a14f967ab6c88829c9a36ce6e909c47b3ee398Alan Viverette    private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
42934a14f967ab6c88829c9a36ce6e909c47b3ee398Alan Viverette        if (mState.mColor == null && (mState.mTouchThemeAttrs == null
43034a14f967ab6c88829c9a36ce6e909c47b3ee398Alan Viverette                || mState.mTouchThemeAttrs[R.styleable.RippleDrawable_color] == 0)) {
4314d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            throw new XmlPullParserException(a.getPositionDescription() +
432a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette                    ": <ripple> requires a valid color attribute");
4334d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        }
43452b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette    }
43552b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette
43652b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette    /**
437cb29189c29a553a8005044f32de37a610f2857dbAlan Viverette     * Set the density at which this drawable will be rendered.
438cb29189c29a553a8005044f32de37a610f2857dbAlan Viverette     *
439cb29189c29a553a8005044f32de37a610f2857dbAlan Viverette     * @param metrics The display metrics for this drawable.
440cb29189c29a553a8005044f32de37a610f2857dbAlan Viverette     */
441cb29189c29a553a8005044f32de37a610f2857dbAlan Viverette    private void setTargetDensity(DisplayMetrics metrics) {
442cb29189c29a553a8005044f32de37a610f2857dbAlan Viverette        if (mDensity != metrics.density) {
443cb29189c29a553a8005044f32de37a610f2857dbAlan Viverette            mDensity = metrics.density;
444cb29189c29a553a8005044f32de37a610f2857dbAlan Viverette            invalidateSelf();
445cb29189c29a553a8005044f32de37a610f2857dbAlan Viverette        }
446d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    }
447d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
44852b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette    @Override
44952b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette    public void applyTheme(Theme t) {
45052b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette        super.applyTheme(t);
45152b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette
452c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette        final RippleState state = mState;
453ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        if (state == null || state.mTouchThemeAttrs == null) {
454ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette            return;
45552b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette        }
45652b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette
457ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final TypedArray a = t.resolveAttributes(state.mTouchThemeAttrs,
458c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette                R.styleable.RippleDrawable);
4594d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        try {
4604d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            updateStateFromTypedArray(a);
4614d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        } catch (XmlPullParserException e) {
4624d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            throw new RuntimeException(e);
4634d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        } finally {
4644d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            a.recycle();
4654d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        }
466b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette
467b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        initializeFromState();
46852b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette    }
46952b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette
47052b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette    @Override
47152b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette    public boolean canApplyTheme() {
472d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        return (mState != null && mState.canApplyTheme()) || super.canApplyTheme();
47352b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette    }
47452b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette
475d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    @Override
476c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette    public void setHotspot(float x, float y) {
4776ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        if (mRipple == null || mBackground == null) {
4786ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            mPendingX = x;
4796ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            mPendingY = y;
4806ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            mHasPending = true;
4816ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        }
4826ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
4836ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        if (mRipple != null) {
4846ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            mRipple.move(x, y);
4856ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        }
4866ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    }
4876ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
4886ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    /**
4896ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette     * Creates an active hotspot at the specified location.
4906ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette     */
491f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette    private void tryBackgroundEnter(boolean focused) {
4926ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        if (mBackground == null) {
493a8a8ff000b2902eb4e187e62be39fd9535c6c839Alan Viverette            mBackground = new RippleBackground(this, mHotspotBounds);
4946ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        }
4956ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
496cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        mBackground.setup(mState.mMaxRadius, mDensity);
497f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette        mBackground.enter(focused);
4986ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    }
4996ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
500fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette    private void tryBackgroundExit() {
5016ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        if (mBackground != null) {
5026ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            // Don't null out the background, we need it to draw!
5036ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            mBackground.exit();
504ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        }
505ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    }
506ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
507ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    /**
508fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     * Attempts to start an enter animation for the active hotspot. Fails if
509fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     * there are too many animating ripples.
510ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette     */
511fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette    private void tryRippleEnter() {
512fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        if (mExitingRipplesCount >= MAX_RIPPLES) {
5134d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            // This should never happen unless the user is tapping like a maniac
5144d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            // or there is a bug that's preventing ripples from being removed.
5154d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            return;
5164d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        }
5174d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
5186ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        if (mRipple == null) {
5196ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            final float x;
5206ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            final float y;
5216ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            if (mHasPending) {
5226ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette                mHasPending = false;
5236ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette                x = mPendingX;
5246ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette                y = mPendingY;
5256ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            } else {
5266ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette                x = mHotspotBounds.exactCenterX();
5276ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette                y = mHotspotBounds.exactCenterY();
5286ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            }
5296ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            mRipple = new Ripple(this, mHotspotBounds, x, y);
530ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        }
531ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
532cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        mRipple.setup(mState.mMaxRadius, mDensity);
5336ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        mRipple.enter();
534ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    }
535ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
536fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette    /**
537fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     * Attempts to start an exit animation for the active hotspot. Fails if
538fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     * there is no active hotspot.
539fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     */
540fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette    private void tryRippleExit() {
5416ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        if (mRipple != null) {
542fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette            if (mExitingRipples == null) {
543fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette                mExitingRipples = new Ripple[MAX_RIPPLES];
544fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette            }
545fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette            mExitingRipples[mExitingRipplesCount++] = mRipple;
5466ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            mRipple.exit();
5476ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            mRipple = null;
548d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette        }
549d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    }
550d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
551fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette    /**
552fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     * Cancels and removes the active ripple, all exiting ripples, and the
553fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     * background. Nothing will be drawn after this method is called.
554fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     */
555c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette    private void clearHotspots() {
556b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette        if (mRipple != null) {
557b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette            mRipple.cancel();
558b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette            mRipple = null;
559b942b6f15c51c2ff48c59d8f620ee6156d00f67eAlan Viverette            mRippleActive = false;
560b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette        }
561b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette
562b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette        if (mBackground != null) {
563b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette            mBackground.cancel();
564b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette            mBackground = null;
565b942b6f15c51c2ff48c59d8f620ee6156d00f67eAlan Viverette            mBackgroundActive = false;
566d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette        }
567d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
568cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        cancelExitingRipples();
56953d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette        invalidateSelf();
570d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    }
571d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
57261956606818918194a38e045a8e35e7108480e5eAlan Viverette    @Override
57361956606818918194a38e045a8e35e7108480e5eAlan Viverette    public void setHotspotBounds(int left, int top, int right, int bottom) {
57461956606818918194a38e045a8e35e7108480e5eAlan Viverette        mOverrideBounds = true;
57561956606818918194a38e045a8e35e7108480e5eAlan Viverette        mHotspotBounds.set(left, top, right, bottom);
576dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette
577dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        onHotspotBoundsChanged();
578dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    }
579dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette
5807068c39526459c18a020e29c1ebfa6aed54e2d0fAlan Viverette    /** @hide */
5817068c39526459c18a020e29c1ebfa6aed54e2d0fAlan Viverette    @Override
5827068c39526459c18a020e29c1ebfa6aed54e2d0fAlan Viverette    public void getHotspotBounds(Rect outRect) {
5837068c39526459c18a020e29c1ebfa6aed54e2d0fAlan Viverette        outRect.set(mHotspotBounds);
5847068c39526459c18a020e29c1ebfa6aed54e2d0fAlan Viverette    }
5857068c39526459c18a020e29c1ebfa6aed54e2d0fAlan Viverette
586dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    /**
587dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette     * Notifies all the animating ripples that the hotspot bounds have changed.
588dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette     */
589dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    private void onHotspotBoundsChanged() {
590fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        final int count = mExitingRipplesCount;
591fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        final Ripple[] ripples = mExitingRipples;
592dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        for (int i = 0; i < count; i++) {
593dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette            ripples[i].onHotspotBoundsChanged();
594dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        }
5956ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
596a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        if (mRipple != null) {
597a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette            mRipple.onHotspotBoundsChanged();
598a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        }
599a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette
6006ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        if (mBackground != null) {
6016ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            mBackground.onHotspotBoundsChanged();
6026ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        }
60361956606818918194a38e045a8e35e7108480e5eAlan Viverette    }
60461956606818918194a38e045a8e35e7108480e5eAlan Viverette
6057c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette    /**
6067c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette     * Populates <code>outline</code> with the first available layer outline,
60777b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik     * excluding the mask layer.
6087c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette     *
6097c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette     * @param outline Outline in which to place the first available layer outline
6107c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette     */
6117c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette    @Override
61231ba192dd201df2cad96a8c503f730130ab0d80fChris Craik    public void getOutline(@NonNull Outline outline) {
6137c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette        final LayerState state = mLayerState;
6147c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette        final ChildDrawable[] children = state.mChildren;
6157c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette        final int N = state.mNum;
6167c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette        for (int i = 0; i < N; i++) {
61731ba192dd201df2cad96a8c503f730130ab0d80fChris Craik            if (children[i].mId != R.id.mask) {
61831ba192dd201df2cad96a8c503f730130ab0d80fChris Craik                children[i].mDrawable.getOutline(outline);
61931ba192dd201df2cad96a8c503f730130ab0d80fChris Craik                if (!outline.isEmpty()) return;
6207c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette            }
6217c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette        }
6227c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette    }
6237c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette
624cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette    /**
625cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette     * Optimized for drawing ripples with a mask layer and optional content.
626cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette     */
627d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    @Override
6287c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette    public void draw(@NonNull Canvas canvas) {
629cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        // Clip to the dirty bounds, which will be the drawable bounds if we
630cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        // have a mask or content and the ripple bounds if we're projecting.
631be0dd99bac781d1dd73cb67f33bcd931e3693af5Alan Viverette        final Rect bounds = getDirtyBounds();
632a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
633a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        canvas.clipRect(bounds);
6344d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
6356dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        drawContent(canvas);
6366dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        drawBackgroundAndRipples(canvas);
6376dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
6386dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        canvas.restoreToCount(saveCount);
6396dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    }
6406dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
6416dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    @Override
6426dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    public void invalidateSelf() {
6436dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        super.invalidateSelf();
6446dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
6456dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        // Force the mask to update on the next draw().
6466dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        mHasValidMask = false;
6476dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    }
6486dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
6496dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    /**
6506dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette     * @return whether we need to use a mask
6516dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette     */
6526dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private void updateMaskShaderIfNeeded() {
6536dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (mHasValidMask) {
6546dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            return;
6556dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        }
6566dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
6576dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        final int maskType = getMaskType();
6586dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (maskType == MASK_UNKNOWN) {
6596dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            return;
6606dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        }
6616dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
6626dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        mHasValidMask = true;
6636dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
664b74155cf01f959fc9b7909de5a22806ad519f7c9Alan Viverette        final Rect bounds = getBounds();
665b74155cf01f959fc9b7909de5a22806ad519f7c9Alan Viverette        if (maskType == MASK_NONE || bounds.isEmpty()) {
6666dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            if (mMaskBuffer != null) {
6676dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                mMaskBuffer.recycle();
6686dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                mMaskBuffer = null;
6696dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                mMaskShader = null;
6706dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                mMaskCanvas = null;
6716ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            }
6726dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            mMaskMatrix = null;
6736dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            mMaskColorFilter = null;
6746dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            return;
6756ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        }
6764d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
6776dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        // Ensure we have a correctly-sized buffer.
6786dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (mMaskBuffer == null
6796dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                || mMaskBuffer.getWidth() != bounds.width()
6806dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                || mMaskBuffer.getHeight() != bounds.height()) {
6816dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            if (mMaskBuffer != null) {
6826dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                mMaskBuffer.recycle();
6836dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            }
6846dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
6856dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            mMaskBuffer = Bitmap.createBitmap(
6866dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                    bounds.width(), bounds.height(), Bitmap.Config.ALPHA_8);
6876dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            mMaskShader = new BitmapShader(mMaskBuffer,
6886dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                    Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
6896dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            mMaskCanvas = new Canvas(mMaskBuffer);
6906dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        } else {
6916dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            mMaskBuffer.eraseColor(Color.TRANSPARENT);
6926dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        }
6936dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
6946dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (mMaskMatrix == null) {
6956dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            mMaskMatrix = new Matrix();
6966dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        } else {
6976dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            mMaskMatrix.reset();
6986dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        }
6996dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
7006dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (mMaskColorFilter == null) {
7016dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            mMaskColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_IN);
7026dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        }
7036dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
7046dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        // Draw the appropriate mask.
7056dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (maskType == MASK_EXPLICIT) {
7066dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            drawMask(mMaskCanvas);
7076dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        } else if (maskType == MASK_CONTENT) {
7086dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            drawContent(mMaskCanvas);
7096dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        }
7106dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    }
7116dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
7126dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private int getMaskType() {
7136dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (mRipple == null && mExitingRipplesCount <= 0
7146dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                && (mBackground == null || !mBackground.shouldDraw())) {
7156dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            // We might need a mask later.
7166dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            return MASK_UNKNOWN;
7176dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        }
7186dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
7196dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (mMask != null) {
7206dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            if (mMask.getOpacity() == PixelFormat.OPAQUE) {
7216dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                // Clipping handles opaque explicit masks.
7226dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                return MASK_NONE;
7236dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            } else {
7246dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                return MASK_EXPLICIT;
7256dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            }
7266dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        }
7276dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
7286dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        // Check for non-opaque, non-mask content.
7296dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        final ChildDrawable[] array = mLayerState.mChildren;
7306dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        final int count = mLayerState.mNum;
7316dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        for (int i = 0; i < count; i++) {
7326dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            if (array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) {
7336dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette                return MASK_CONTENT;
7346dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            }
7356dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        }
7366dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
7376dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        // Clipping handles opaque content.
7386dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        return MASK_NONE;
7394d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
7404d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
7414d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    /**
742fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     * Removes a ripple from the exiting ripple list.
7434d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     *
7444d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * @param ripple the ripple to remove
7454d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     */
7464d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    void removeRipple(Ripple ripple) {
747fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        // Ripple ripple ripple ripple. Ripple ripple.
748fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        final Ripple[] ripples = mExitingRipples;
749fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        final int count = mExitingRipplesCount;
750fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        final int index = getRippleIndex(ripple);
751fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        if (index >= 0) {
752fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette            System.arraycopy(ripples, index + 1, ripples, index, count - (index + 1));
753fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette            ripples[count - 1] = null;
754fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette            mExitingRipplesCount--;
755fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette
756fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette            invalidateSelf();
7574d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        }
7584d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
759d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette
7604d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    private int getRippleIndex(Ripple ripple) {
761fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        final Ripple[] ripples = mExitingRipples;
762fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        final int count = mExitingRipplesCount;
7634d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        for (int i = 0; i < count; i++) {
7644d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            if (ripples[i] == ripple) {
7654d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette                return i;
766d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette            }
767d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette        }
7684d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        return -1;
7694d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
7704d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
7716dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private void drawContent(Canvas canvas) {
7726dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        // Draw everything except the mask.
773a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        final ChildDrawable[] array = mLayerState.mChildren;
774a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        final int count = mLayerState.mNum;
7754d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        for (int i = 0; i < count; i++) {
776d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette            if (array[i].mId != R.id.mask) {
777d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette                array[i].mDrawable.draw(canvas);
778d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette            }
779d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette        }
780d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette    }
781d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette
7826dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private void drawBackgroundAndRipples(Canvas canvas) {
7836dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        final Ripple active = mRipple;
7846dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        final RippleBackground background = mBackground;
7856dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        final int count = mExitingRipplesCount;
7866dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (active == null && count <= 0 && (background == null || !background.shouldDraw())) {
7876dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            // Move along, nothing to draw here.
7886dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            return;
789cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        }
790323596de4efc46149719b41de5a9f668d7f3f784Alan Viverette
791cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        final float x = mHotspotBounds.exactCenterX();
792cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        final float y = mHotspotBounds.exactCenterY();
793cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        canvas.translate(x, y);
7946ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
7956dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        updateMaskShaderIfNeeded();
7966dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
7976dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        // Position the shader to account for canvas translation.
7986dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (mMaskShader != null) {
7996dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            mMaskMatrix.setTranslate(-x, -y);
8006dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            mMaskShader.setLocalMatrix(mMaskMatrix);
8016dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        }
8026ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
803cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        // Grab the color for the current state and cut the alpha channel in
804cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        // half so that the ripple and background together yield full alpha.
805cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        final int color = mState.mColor.getColorForState(getState(), Color.BLACK);
8066dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        final int halfAlpha = (Color.alpha(color) / 2) << 24;
8076dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        final Paint p = getRipplePaint();
8086dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
8096dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (mMaskColorFilter != null) {
8106dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            // The ripple timing depends on the paint's alpha value, so we need
8116dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            // to push just the alpha channel into the paint and let the filter
8126dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            // handle the full-alpha color.
8136dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            final int fullAlphaColor = color | (0xFF << 24);
8146dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            mMaskColorFilter.setColor(fullAlphaColor);
8156dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
8166dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            p.setColor(halfAlpha);
8176dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            p.setColorFilter(mMaskColorFilter);
8186dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            p.setShader(mMaskShader);
8196dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        } else {
8206dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            final int halfAlphaColor = (color & 0xFFFFFF) | halfAlpha;
8216dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            p.setColor(halfAlphaColor);
8226dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            p.setColorFilter(null);
8236dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            p.setShader(null);
8246dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        }
8256ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
826cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        if (background != null && background.shouldDraw()) {
827cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette            background.draw(canvas, p);
828cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        }
829d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette
830cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        if (count > 0) {
831cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette            final Ripple[] ripples = mExitingRipples;
832cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette            for (int i = 0; i < count; i++) {
833cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette                ripples[i].draw(canvas, p);
834d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette            }
835ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette        }
836ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette
837cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        if (active != null) {
838cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette            active.draw(canvas, p);
839ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        }
840ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
841cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        canvas.translate(-x, -y);
842ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette    }
843ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette
8446dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private void drawMask(Canvas canvas) {
8451b6e856e6f9dab4464e3c556b2f68527439fc329Alan Viverette        mMask.draw(canvas);
8464d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
8474d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
848a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette    private Paint getRipplePaint() {
849a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        if (mRipplePaint == null) {
850a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette            mRipplePaint = new Paint();
851a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette            mRipplePaint.setAntiAlias(true);
852cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette            mRipplePaint.setStyle(Paint.Style.FILL);
853a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        }
854a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        return mRipplePaint;
855a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette    }
856a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette
857d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    @Override
858d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    public Rect getDirtyBounds() {
8592627206e1b1658ccade3669d1794dc0d90e36264Alan Viverette        if (isProjected()) {
860e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            final Rect drawingBounds = mDrawingBounds;
861e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            final Rect dirtyBounds = mDirtyBounds;
862e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            dirtyBounds.set(drawingBounds);
863e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            drawingBounds.setEmpty();
864e3c433aa457138425e514494e4d06590076a1d07Alan Viverette
865e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            final int cX = (int) mHotspotBounds.exactCenterX();
866e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            final int cY = (int) mHotspotBounds.exactCenterY();
867e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            final Rect rippleBounds = mTempRect;
868fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette
869fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette            final Ripple[] activeRipples = mExitingRipples;
870fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette            final int N = mExitingRipplesCount;
871e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            for (int i = 0; i < N; i++) {
872e3c433aa457138425e514494e4d06590076a1d07Alan Viverette                activeRipples[i].getBounds(rippleBounds);
873e3c433aa457138425e514494e4d06590076a1d07Alan Viverette                rippleBounds.offset(cX, cY);
874e3c433aa457138425e514494e4d06590076a1d07Alan Viverette                drawingBounds.union(rippleBounds);
875e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            }
876d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
8776ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            final RippleBackground background = mBackground;
8786ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            if (background != null) {
8796ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette                background.getBounds(rippleBounds);
8806ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette                rippleBounds.offset(cX, cY);
8816ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette                drawingBounds.union(rippleBounds);
8826ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            }
8836ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
884e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            dirtyBounds.union(drawingBounds);
885e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            dirtyBounds.union(super.getDirtyBounds());
886e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            return dirtyBounds;
887e3c433aa457138425e514494e4d06590076a1d07Alan Viverette        } else {
888e3c433aa457138425e514494e4d06590076a1d07Alan Viverette            return getBounds();
889e3c433aa457138425e514494e4d06590076a1d07Alan Viverette        }
890d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    }
891d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
8927f610fed107b158c144dca1b20a44ee91eb8c934Alan Viverette    @Override
8937f610fed107b158c144dca1b20a44ee91eb8c934Alan Viverette    public ConstantState getConstantState() {
8947f610fed107b158c144dca1b20a44ee91eb8c934Alan Viverette        return mState;
8957f610fed107b158c144dca1b20a44ee91eb8c934Alan Viverette    }
8967f610fed107b158c144dca1b20a44ee91eb8c934Alan Viverette
8975004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette    @Override
8985004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette    public Drawable mutate() {
8995004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette        super.mutate();
9005004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette
9015004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette        // LayerDrawable creates a new state using createConstantState, so
9025004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette        // this should always be a safe cast.
9035004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette        mState = (RippleState) mLayerState;
904ebc9f2e773c3ebb4d4af3025fc6770844cc8fdefAlan Viverette
905ebc9f2e773c3ebb4d4af3025fc6770844cc8fdefAlan Viverette        // The locally cached drawable may have changed.
906ebc9f2e773c3ebb4d4af3025fc6770844cc8fdefAlan Viverette        mMask = findDrawableByLayerId(R.id.mask);
907ebc9f2e773c3ebb4d4af3025fc6770844cc8fdefAlan Viverette
9085004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette        return this;
9095004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette    }
9105004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette
9115004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette    @Override
9125004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette    RippleState createConstantState(LayerState state, Resources res) {
9135004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette        return new RippleState(state, this, res);
9145004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette    }
9155004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette
916c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette    static class RippleState extends LayerState {
91747bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette        int[] mTouchThemeAttrs;
91834a14f967ab6c88829c9a36ce6e909c47b3ee398Alan Viverette        ColorStateList mColor = ColorStateList.valueOf(Color.MAGENTA);
9194d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        int mMaxRadius = RADIUS_AUTO;
920d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
9215004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette        public RippleState(LayerState orig, RippleDrawable owner, Resources res) {
92247bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette            super(orig, owner, res);
923ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette
9245004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette            if (orig != null && orig instanceof RippleState) {
9255004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette                final RippleState origs = (RippleState) orig;
9265004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette                mTouchThemeAttrs = origs.mTouchThemeAttrs;
9275004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette                mColor = origs.mColor;
9285004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette                mMaxRadius = origs.mMaxRadius;
929d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette            }
930d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette        }
931d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette
932d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette        @Override
93352b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette        public boolean canApplyTheme() {
93447bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette            return mTouchThemeAttrs != null || super.canApplyTheme();
93552b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette        }
93652b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette
93752b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette        @Override
938d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette        public Drawable newDrawable() {
93917cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette            return new RippleDrawable(this, null);
940d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette        }
941d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
942d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette        @Override
943d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette        public Drawable newDrawable(Resources res) {
94417cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette            return new RippleDrawable(this, res);
945d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette        }
9464d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
947d30688f0a0a75743df247831688efcbe2fa62cc4Alan Viverette
9484d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    /**
9494d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * Sets the maximum ripple radius in pixels. The default value of
9504d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * {@link #RADIUS_AUTO} defines the radius as the distance from the center
9514d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * of the drawable bounds (or hotspot bounds, if specified) to a corner.
9524d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     *
9534d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * @param maxRadius the maximum ripple radius in pixels or
9544d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     *            {@link #RADIUS_AUTO} to automatically determine the maximum
9554d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     *            radius based on the bounds
9564d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * @see #getMaxRadius()
9574d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * @see #setHotspotBounds(int, int, int, int)
9584d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * @hide
9594d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     */
9604d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    public void setMaxRadius(int maxRadius) {
9614d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        if (maxRadius != RADIUS_AUTO && maxRadius < 0) {
9624d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            throw new IllegalArgumentException("maxRadius must be RADIUS_AUTO or >= 0");
963d30688f0a0a75743df247831688efcbe2fa62cc4Alan Viverette        }
9644d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
9654d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        mState.mMaxRadius = maxRadius;
9664d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
9674d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
9684d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    /**
9694d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * @return the maximum ripple radius in pixels, or {@link #RADIUS_AUTO} if
9704d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     *         the radius is determined automatically
9714d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * @see #setMaxRadius(int)
9724d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * @hide
9734d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     */
9744d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    public int getMaxRadius() {
9754d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        return mState.mMaxRadius;
976d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    }
97752b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette
97817cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette    private RippleDrawable(RippleState state, Resources res) {
97917cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette        mState = new RippleState(state, this, res);
98017cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette        mLayerState = mState;
98147bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette
98217cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette        if (mState.mNum > 0) {
98317cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette            ensurePadding();
98447bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette        }
98547bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette
98647bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette        if (res != null) {
98747bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette            mDensity = res.getDisplayMetrics().density;
98847bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette        }
98947bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette
990b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        initializeFromState();
991b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette    }
992b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette
993b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette    private void initializeFromState() {
994b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        // Initialize from constant state.
995b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        mMask = findDrawableByLayerId(R.id.mask);
99652b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette    }
997d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette}
998