RippleDrawable.java revision 8683a4f819c5b4c77593568048428a6a18a883ca
1231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn/*
2231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * Copyright (C) 2013 The Android Open Source Project
3231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn *
4231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * Licensed under the Apache License, Version 2.0 (the "License");
5231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * you may not use this file except in compliance with the License.
6231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * You may obtain a copy of the License at
7231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn *
8231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn *      http://www.apache.org/licenses/LICENSE-2.0
9231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn *
10231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * Unless required by applicable law or agreed to in writing, software
11231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * distributed under the License is distributed on an "AS IS" BASIS,
12231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * See the License for the specific language governing permissions and
14231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * limitations under the License.
15231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn */
16231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
177a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkeypackage android.graphics.drawable;
18a3cdaa5337fa573c4c61770195d6232c2e587090Jason parks
19d9d2f1140b52fd0c014e9deac59f6000564b7e84Fred Quintanaimport android.annotation.NonNull;
20f29f2369cf4e1de090c985ed53d3f5e59535986aAmith Yamasaniimport android.annotation.Nullable;
21a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paulimport android.content.res.ColorStateList;
22fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williamsimport android.content.res.Resources;
237a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkeyimport android.content.res.Resources.Theme;
247a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkeyimport android.content.res.TypedArray;
257a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkeyimport android.graphics.Canvas;
267a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkeyimport android.graphics.Color;
277a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkeyimport android.graphics.ColorFilter;
2856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williamsimport android.graphics.Outline;
297a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkeyimport android.graphics.Paint;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.PixelFormat;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.PorterDuff.Mode;
32231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport android.graphics.PorterDuffXfermode;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect;
34231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport android.util.AttributeSet;
35231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport android.util.DisplayMetrics;
36231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
37231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport com.android.internal.R;
38231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
39231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport org.xmlpull.v1.XmlPullParser;
40231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport org.xmlpull.v1.XmlPullParserException;
4156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
428c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basargeimport java.io.IOException;
437a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkeyimport java.util.Arrays;
447a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkey
457a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkey/**
467a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkey * Drawable that shows a ripple effect in response to state changes. The
477a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkey * anchoring position of the ripple for a given state may be specified by
487a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkey * calling {@link #setHotspot(float, float)} with the corresponding state
497a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkey * attribute identifier.
507a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkey * <p>
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * A touch feedback drawable may contain multiple child layers, including a
52231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * special mask layer that is not drawn to the screen. A single layer may be set
53231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * as the mask by specifying its android:id value as {@link android.R.id#mask}.
54231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * <pre>
559e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewicz * <code>&lt!-- A red ripple masked against an opaque rectangle. --/>
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * &ltripple android:color="#ffff0000">
57231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn *   &ltitem android:id="@android:id/mask"
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         android:drawable="@android:color/white" />
59231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * &ltripple /></code>
607a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkey * </pre>
6169d95de53bc82e6c23c64ad566e428fbefae0543Ashish Sharma * <p>
621125d780a8b61703b8eb28c5c77dac5f3f0022ddJason parks * If a mask layer is set, the ripple effect will be masked against that layer
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * before it is drawn over the composite of the remaining child layers.
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>
65231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * If no mask layer is set, the ripple effect is masked against the composite
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * of the child layers.
67360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache * <pre>
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <code>&lt!-- A blue ripple drawn atop a black rectangle. --/>
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * &ltripple android:color="#ff00ff00">
70231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn *   &ltitem android:drawable="@android:color/black" />
7104e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani * &ltripple />
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
73ba35271e91648c9cde2bb606aaf2001282231b07Matthew Williams * &lt!-- A red ripple drawn atop a drawable resource. --/>
74360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache * &ltripple android:color="#ff00ff00">
7504e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani *   &ltitem android:drawable="@drawable/my_drawable" />
7604e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani * &ltripple /></code>
7769d95de53bc82e6c23c64ad566e428fbefae0543Ashish Sharma * </pre>
7804e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani * <p>
7904e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani * If no child layers or mask is specified and the ripple is set as a View
8004e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani * background, the ripple will be drawn atop the first available parent
8104e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani * background within the View's hierarchy. In this case, the drawing region
82fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams * may extend outside of the Drawable bounds.
83cf939c14092ad74edea2e9bb0b5ed2eed222e253Shreyas Basarge * <pre>
84c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana * <code>&lt!-- An unbounded green ripple. --/>
858c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge * &ltripple android:color="#ff0000ff" /></code>
86fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams * </pre>
87fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams *
88fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams * @attr ref android.R.styleable#RippleDrawable_color
89fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams */
90fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williamspublic class RippleDrawable extends LayerDrawable {
918b2c3a14603d163d7564e6f60286995079687690Jeff Sharkey    private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
92231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private static final PorterDuffXfermode SRC_ATOP = new PorterDuffXfermode(Mode.SRC_ATOP);
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final PorterDuffXfermode SRC_OVER = new PorterDuffXfermode(Mode.SRC_OVER);
94231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
95231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    /**
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Constant for automatically determining the maximum ripple radius.
97231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     *
98231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * @see #setMaxRadius(int)
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide
100231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     */
101231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    public static final int RADIUS_AUTO = -1;
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
103231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    /** The maximum number of ripples supported. */
104231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private static final int MAX_RIPPLES = 10;
10556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
106231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private final Rect mTempRect = new Rect();
107231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
108231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    /** Current ripple effect bounds, used to constrain ripple effects. */
109231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private final Rect mHotspotBounds = new Rect();
110231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
111c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana    /** Current drawing bounds, used to compute dirty region. */
112c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana    private final Rect mDrawingBounds = new Rect();
113c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana
114307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana    /** Current dirty bounds, union of current and previous drawing bounds. */
115307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana    private final Rect mDirtyBounds = new Rect();
116231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
117231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private final RippleState mState;
118231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
1198c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    /** The masking layer, e.g. the layer with id R.id.mask. */
1208c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    private Drawable mMask;
1218c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
1228c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    /** The current background. May be actively animating or pending entry. */
1238c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    private RippleBackground mBackground;
124231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
125231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    /** Whether we expect to draw a background when visible. */
126231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private boolean mBackgroundActive;
127231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
128231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    /** The current ripple. May be actively animating or pending entry. */
129a33e3f79259b56b03c7912af35944f34ad190e3cDianne Hackborn    private Ripple mRipple;
130360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache
131231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    /** Whether we expect to draw a ripple when visible. */
132231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private boolean mRippleActive;
133360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache
134231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    // Hotspot coordinates that are awaiting activation.
135231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private float mPendingX;
1368294fadb155a33da8a40d8412afb35e6b424afcbJoe Onorato    private float mPendingY;
1378294fadb155a33da8a40d8412afb35e6b424afcbJoe Onorato    private boolean mHasPending;
138360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache
139c2e4691d788088b22eadc9b2d35e9bdf0b6a0ffcFred Quintana    /**
140fb084400d6afa6443a421117fbcaee0265d38fb6Fred Quintana     * Lazily-created array of actively animating ripples. Inactive ripples are
141fb084400d6afa6443a421117fbcaee0265d38fb6Fred Quintana     * pruned during draw(). The locations of these will not change.
142fb084400d6afa6443a421117fbcaee0265d38fb6Fred Quintana     */
1438c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    private Ripple[] mExitingRipples;
144fb084400d6afa6443a421117fbcaee0265d38fb6Fred Quintana    private int mExitingRipplesCount = 0;
145fb084400d6afa6443a421117fbcaee0265d38fb6Fred Quintana
146fb084400d6afa6443a421117fbcaee0265d38fb6Fred Quintana    /** Paint used to control appearance of ripples. */
147fb084400d6afa6443a421117fbcaee0265d38fb6Fred Quintana    private Paint mRipplePaint;
148fb084400d6afa6443a421117fbcaee0265d38fb6Fred Quintana
149fb084400d6afa6443a421117fbcaee0265d38fb6Fred Quintana    /** Paint used to control reveal layer masking. */
150c2e4691d788088b22eadc9b2d35e9bdf0b6a0ffcFred Quintana    private Paint mMaskingPaint;
151231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
15204e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani    /** Target density of the display into which ripples are drawn. */
153231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private float mDensity = 1.0f;
154231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
155360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache    /** Whether bounds are being overridden. */
15604e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani    private boolean mOverrideBounds;
15704e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani
158231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    /**
159231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * Whether the next draw MUST draw something to canvas. Used to work around
160360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache     * a bug in hardware invalidation following a render thread-accelerated
16156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams     * animation.
16256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams     */
16356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    private boolean mNeedsDraw;
16456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
1657a1355950172b7a549820e9a2cd4a9b2099ec32fDianne Hackborn    /**
16604e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani     * Constructor used for drawable inflation.
16756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams     */
16856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    RippleDrawable() {
16956dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        this(new RippleState(null, null, null), null, null);
17056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    }
17156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
17256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    /**
17356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams     * Creates a new ripple drawable with the specified ripple color and
17456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams     * optional content and mask drawables.
17556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams     *
1768ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams     * @param color The ripple color
1778ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams     * @param content The content drawable, may be {@code null}
1788ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams     * @param mask The mask drawable, may be {@code null}
1798c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge     */
18056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    public RippleDrawable(@NonNull ColorStateList color, @Nullable Drawable content,
1818ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams            @Nullable Drawable mask) {
1828ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams        this(new RippleState(null, null, null), null, null);
18356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
1848ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams        if (color == null) {
18556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            throw new IllegalArgumentException("RippleDrawable requires a non-null color");
18656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        }
1878c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
1888c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        if (content != null) {
1898c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            addLayer(content, null, 0, 0, 0, 0, 0);
1908c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        }
1918c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
19256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        if (mask != null) {
1938c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            addLayer(mask, null, android.R.id.mask, 0, 0, 0, 0);
1948c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        }
1958c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
1968c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        setColor(color);
1978c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        ensurePadding();
1988c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        initializeFromState();
1998c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    }
20056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
20156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    @Override
20256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    public void jumpToCurrentState() {
20356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        super.jumpToCurrentState();
2048c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
20556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        if (mRipple != null) {
20656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            mRipple.jump();
2078ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams        }
20856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
20956dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        if (mBackground != null) {
21056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            mBackground.jump();
21156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        }
21256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
21353abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        cancelExitingRipples();
21453abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams
21553abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        mNeedsDraw = true;
21653abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        invalidateSelf();
21753abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams    }
21853abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams
21953abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams    private void cancelExitingRipples() {
22053abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        final int count = mExitingRipplesCount;
22153abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        final Ripple[] ripples = mExitingRipples;
22253abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        for (int i = 0; i < count; i++) {
22353abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams            ripples[i].cancel();
22453abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        }
22553abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams
22653abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        if (ripples != null) {
22753abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams            Arrays.fill(ripples, 0, count, null);
22853abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        }
22953abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        mExitingRipplesCount = 0;
23053abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams    }
23153abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams
23253abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams    @Override
23353abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams    public void setAlpha(int alpha) {
23453abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        super.setAlpha(alpha);
23553abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams
23653abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        // TODO: Should we support this?
2378ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams    }
238231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
239231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    @Override
2405e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana    public void setColorFilter(ColorFilter cf) {
24156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        super.setColorFilter(cf);
242307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana
24356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        // TODO: Should we support this?
244307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana    }
2458ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams
246307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana    @Override
24756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    public int getOpacity() {
248fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams        // Worst-case scenario.
249ac9385ef3105fb7464e1f46049c62755a8b7f0e9Fred Quintana        return PixelFormat.TRANSLUCENT;
2503aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia    }
2513aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia
2523aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia    @Override
2533aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia    protected boolean onStateChange(int[] stateSet) {
2543aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia        final boolean changed = super.onStateChange(stateSet);
2553aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia
2563aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia        boolean enabled = false;
2578ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams        boolean pressed = false;
2583aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia        boolean focused = false;
2593aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia
2603aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia        for (int state : stateSet) {
2613aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia            if (state == R.attr.state_enabled) {
2623aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia                enabled = true;
2633aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia            }
264fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams            if (state == R.attr.state_focused) {
265fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams                focused = true;
2663aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia            }
267fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams            if (state == R.attr.state_pressed) {
2683aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia                pressed = true;
2693aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia            }
2703aca7d7bec4b48bc68480b9d6b0ccdabf6903dfbCarlos Valdivia        }
27156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
2728ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams        setRippleActive(enabled && pressed);
27356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        setBackgroundActive(focused || (enabled && pressed));
2748c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
27556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        return changed;
27656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    }
27756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
27856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    private void setRippleActive(boolean active) {
27956dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        if (mRippleActive != active) {
28053abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams            mRippleActive = active;
281307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana            if (active) {
282307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana                tryRippleEnter();
2838c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            } else {
2848c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge                tryRippleExit();
2858c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            }
2868c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        }
2878c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    }
28856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
289231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private void setBackgroundActive(boolean active) {
29006485a7c6e3bbea1f427f345ac3a18b2346750e0Matthew Williams        if (mBackgroundActive != active) {
29106485a7c6e3bbea1f427f345ac3a18b2346750e0Matthew Williams            mBackgroundActive = active;
29206485a7c6e3bbea1f427f345ac3a18b2346750e0Matthew Williams            if (active) {
29306485a7c6e3bbea1f427f345ac3a18b2346750e0Matthew Williams                tryBackgroundEnter();
29406485a7c6e3bbea1f427f345ac3a18b2346750e0Matthew Williams            } else {
29506485a7c6e3bbea1f427f345ac3a18b2346750e0Matthew Williams                tryBackgroundExit();
296231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            }
297360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache        }
298231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    }
299231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
300231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    @Override
301231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    protected void onBoundsChange(Rect bounds) {
302231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        super.onBoundsChange(bounds);
303231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
304231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        if (!mOverrideBounds) {
305231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            mHotspotBounds.set(bounds);
306231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            onHotspotBoundsChanged();
307231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        }
308dc47556cb37b6f25faceb5eb97cef60d384b55fbFred Quintana
30957286f970641493b315b0b42aba7ac6b672cc8b8Alon Albert        invalidateSelf();
31057286f970641493b315b0b42aba7ac6b672cc8b8Alon Albert    }
311231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
312360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache    @Override
313231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    public boolean setVisible(boolean visible, boolean restart) {
314231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        final boolean changed = super.setVisible(visible, restart);
315231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
316231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        if (!visible) {
317231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            clearHotspots();
318231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        } else if (changed) {
319360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache            // If we just became visible, ensure the background and ripple
320231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            // visibilities are consistent with their internal states.
321231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            if (mRippleActive) {
322231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn                tryRippleEnter();
323231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            }
324360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache
32504e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani            if (mBackgroundActive) {
32656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams                tryBackgroundEnter();
32756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            }
32856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        }
32904e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani
33004e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani        return changed;
3318c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    }
3328c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
3338c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    /**
3348c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge     * @hide
3358c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge     */
3368c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    @Override
3378c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    public boolean isProjected() {
3388c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        return getNumberOfLayers() == 0;
3398c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    }
3408c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
341231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    @Override
342231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    public boolean isStateful() {
343231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        return true;
344360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache    }
34504e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani
34604e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani    public void setColor(ColorStateList color) {
347231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        mState.mColor = color;
34804e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani        invalidateSelf();
34904e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani    }
350360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache
351231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    @Override
352231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
353360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache            throws XmlPullParserException, IOException {
354231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.RippleDrawable);
355231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        updateStateFromTypedArray(a);
356360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache        a.recycle();
357231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
358231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        // Force padding default to STACK before inflating.
359360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache        setPaddingMode(PADDING_MODE_STACK);
3608ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams
36156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        super.inflate(r, parser, attrs, theme);
36256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
363fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams        setTargetDensity(r.getDisplayMetrics());
36477c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana        initializeFromState();
36577c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana    }
366231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
367231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    @Override
368231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    public boolean setDrawableByLayerId(int id, Drawable drawable) {
369231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        if (super.setDrawableByLayerId(id, drawable)) {
370231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            if (id == R.id.mask) {
371360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache                mMask = drawable;
372231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            }
373c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana
374231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            return true;
375360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache        }
37669d95de53bc82e6c23c64ad566e428fbefae0543Ashish Sharma
37769d95de53bc82e6c23c64ad566e428fbefae0543Ashish Sharma        return false;
378231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    }
379231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
380231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    /**
381231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * Specifies how layer padding should affect the bounds of subsequent
382231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * layers. The default and recommended value for RippleDrawable is
383231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * {@link #PADDING_MODE_STACK}.
384360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache     *
385231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * @param mode padding mode, one of:
386231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     *            <ul>
387231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     *            <li>{@link #PADDING_MODE_NEST} to nest each layer inside the
388231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     *            padding of the previous layer
389231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     *            <li>{@link #PADDING_MODE_STACK} to stack each layer directly
390231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     *            atop the previous layer
391360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache     *            </ul>
392231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * @see #getPaddingMode()
393231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     */
394231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    @Override
395231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    public void setPaddingMode(int mode) {
396231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        super.setPaddingMode(mode);
397360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache    }
398231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
39904e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani    /**
4002b5d0ea5e90bd5b9a1b3afdd230e8907315e65e2Yameng Huang     * Initializes the constant state from the values in the typed array.
40104e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani     */
40204e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani    private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
4038c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        final RippleState state = mState;
404360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache
405c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana        // Account for any configuration changes.
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        state.mChangingConfigurations |= a.getChangingConfigurations();
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
408360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache        // Extract the theme attributes, if any.
409231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        state.mTouchThemeAttrs = a.extractThemeAttrs();
410360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache
4112b5d0ea5e90bd5b9a1b3afdd230e8907315e65e2Yameng Huang        final ColorStateList color = a.getColorStateList(R.styleable.RippleDrawable_color);
4128c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        if (color != null) {
4132b5d0ea5e90bd5b9a1b3afdd230e8907315e65e2Yameng Huang            mState.mColor = color;
414231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        }
415231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
416c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana        verifyRequiredAttributes(a);
417ba35271e91648c9cde2bb606aaf2001282231b07Matthew Williams    }
418ba35271e91648c9cde2bb606aaf2001282231b07Matthew Williams
419ba35271e91648c9cde2bb606aaf2001282231b07Matthew Williams    private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
420231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        if (mState.mColor == null && (mState.mTouchThemeAttrs == null
421231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn                || mState.mTouchThemeAttrs[R.styleable.RippleDrawable_color] == 0)) {
422231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            throw new XmlPullParserException(a.getPositionDescription() +
423360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache                    ": <ripple> requires a valid color attribute");
424231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        }
425231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    }
426231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
42777c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana    /**
42877c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana     * Set the density at which this drawable will be rendered.
42977c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana     *
43077c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana     * @param metrics The display metrics for this drawable.
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void setTargetDensity(DisplayMetrics metrics) {
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDensity != metrics.density) {
434c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana            mDensity = metrics.density;
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            invalidateSelf();
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
439307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana    @Override
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void applyTheme(Theme t) {
4418212ae0aee1700b9c287ebadf15af8dacdc8eae6Jeff Sharkey        super.applyTheme(t);
442c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final RippleState state = mState;
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (state == null || state.mTouchThemeAttrs == null) {
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final TypedArray a = t.resolveAttributes(state.mTouchThemeAttrs,
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                R.styleable.RippleDrawable);
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            updateStateFromTypedArray(a);
45204e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani        } catch (XmlPullParserException e) {
45304e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani            throw new RuntimeException(e);
45404e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani        } finally {
45504e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani            a.recycle();
45604e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani        }
45704e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani
4588c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        initializeFromState();
4598c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    }
4608c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
4618c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    @Override
4628c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    public boolean canApplyTheme() {
4638c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        return super.canApplyTheme() || mState != null && mState.mTouchThemeAttrs != null;
4648c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    }
4658c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
4668c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    @Override
4678c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    public void setHotspot(float x, float y) {
4688c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        if (mRipple == null || mBackground == null) {
4698c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            mPendingX = x;
470231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            mPendingY = y;
471231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            mHasPending = true;
4724e80820ab54f3985220ff06b2fcd381565e9f19dDianne Hackborn        }
473231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
474231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        if (mRipple != null) {
475231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            mRipple.move(x, y);
4764e80820ab54f3985220ff06b2fcd381565e9f19dDianne Hackborn        }
477231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    }
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
480231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * Creates an active hotspot at the specified location.
481360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache     */
48269d95de53bc82e6c23c64ad566e428fbefae0543Ashish Sharma    private void tryBackgroundEnter() {
48369d95de53bc82e6c23c64ad566e428fbefae0543Ashish Sharma        if (mBackground == null) {
48469d95de53bc82e6c23c64ad566e428fbefae0543Ashish Sharma            mBackground = new RippleBackground(this, mHotspotBounds);
48569d95de53bc82e6c23c64ad566e428fbefae0543Ashish Sharma        }
486231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
487231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
488231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        mBackground.setup(mState.mMaxRadius, color, mDensity);
489231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        mBackground.enter();
490231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    }
491360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache
492231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private void tryBackgroundExit() {
493231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        if (mBackground != null) {
494231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            // Don't null out the background, we need it to draw!
495231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            mBackground.exit();
496231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        }
497360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache    }
498fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams
499fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams    /**
500fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams     * Attempts to start an enter animation for the active hotspot. Fails if
501fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams     * there are too many animating ripples.
502fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams     */
503fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams    private void tryRippleEnter() {
50456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        if (mExitingRipplesCount >= MAX_RIPPLES) {
505fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams            // This should never happen unless the user is tapping like a maniac
506fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams            // or there is a bug that's preventing ripples from being removed.
507fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams            return;
508fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams        }
509fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams
510fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams        if (mRipple == null) {
511fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams            final float x;
512fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams            final float y;
513fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams            if (mHasPending) {
514fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams                mHasPending = false;
515fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams                x = mPendingX;
516fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams                y = mPendingY;
517fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams            } else {
518fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams                x = mHotspotBounds.exactCenterX();
519fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams                y = mHotspotBounds.exactCenterY();
5208c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            }
521231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            mRipple = new Ripple(this, mHotspotBounds, x, y);
522231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        }
523231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
524231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
525231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        mRipple.setup(mState.mMaxRadius, color, mDensity);
526231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        mRipple.enter();
527231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    }
528231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
529231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    /**
530231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * Attempts to start an exit animation for the active hotspot. Fails if
531231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * there is no active hotspot.
532231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     */
533231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private void tryRippleExit() {
534231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        if (mRipple != null) {
535b06ea706530e6d19eb2a1a9a7ae6c5dd77d80af0Dianne Hackborn            if (mExitingRipples == null) {
536231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn                mExitingRipples = new Ripple[MAX_RIPPLES];
537360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache            }
53856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            mExitingRipples[mExitingRipplesCount++] = mRipple;
5398c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            mRipple.exit();
54077c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana            mRipple = null;
541360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache        }
542231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    }
543231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
544231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    /**
545231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * Cancels and removes the active ripple, all exiting ripples, and the
546231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * background. Nothing will be drawn after this method is called.
547231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     */
548231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private void clearHotspots() {
549231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        if (mRipple != null) {
550231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            mRipple.cancel();
551231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            mRipple = null;
552231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        }
553231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
55470c874ba20b586712a7550b6c5efeb6dc0fdf9faAmith Yamasani        if (mBackground != null) {
55504e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani            mBackground.cancel();
556231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            mBackground = null;
557231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        }
55856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
55956dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        cancelExitingRipples();
560ac9385ef3105fb7464e1f46049c62755a8b7f0e9Fred Quintana
561ac9385ef3105fb7464e1f46049c62755a8b7f0e9Fred Quintana        mNeedsDraw = true;
562231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        invalidateSelf();
563ac9385ef3105fb7464e1f46049c62755a8b7f0e9Fred Quintana    }
564231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
565231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    @Override
566231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    public void setHotspotBounds(int left, int top, int right, int bottom) {
56756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        mOverrideBounds = true;
5688ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams        mHotspotBounds.set(left, top, right, bottom);
56956dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
570231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        onHotspotBoundsChanged();
571231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    }
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
573231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    /** @hide */
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void getHotspotBounds(Rect outRect) {
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        outRect.set(mHotspotBounds);
57704e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani    }
5788c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
57956dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    /**
5808c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge     * Notifies all the animating ripples that the hotspot bounds have changed.
58140e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn     */
58240e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn    private void onHotspotBoundsChanged() {
583231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        final int count = mExitingRipplesCount;
58456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        final Ripple[] ripples = mExitingRipples;
58556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        for (int i = 0; i < count; i++) {
58656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            ripples[i].onHotspotBoundsChanged();
58756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        }
58856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
58977c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana        if (mRipple != null) {
59056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            mRipple.onHotspotBoundsChanged();
5918c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        }
59240e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn
59377c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana        if (mBackground != null) {
59477c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana            mBackground.onHotspotBoundsChanged();
59553abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        }
59653abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams    }
59753abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams
59853abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams    /**
59953abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams     * Populates <code>outline</code> with the first available layer outline,
60053abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams     * excluding the mask layer.
6018294fadb155a33da8a40d8412afb35e6b424afcbJoe Onorato     *
602231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * @param outline Outline in which to place the first available layer outline
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6048294fadb155a33da8a40d8412afb35e6b424afcbJoe Onorato    @Override
60577c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana    public void getOutline(@NonNull Outline outline) {
60657286f970641493b315b0b42aba7ac6b672cc8b8Alon Albert        final LayerState state = mLayerState;
60757286f970641493b315b0b42aba7ac6b672cc8b8Alon Albert        final ChildDrawable[] children = state.mChildren;
6088294fadb155a33da8a40d8412afb35e6b424afcbJoe Onorato        final int N = state.mNum;
609ac9385ef3105fb7464e1f46049c62755a8b7f0e9Fred Quintana        for (int i = 0; i < N; i++) {
610a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul            if (children[i].mId != R.id.mask) {
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                children[i].mDrawable.getOutline(outline);
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!outline.isEmpty()) return;
61304e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani            }
6145e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana        }
6155e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana    }
61656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
61756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    @Override
61856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    public void draw(@NonNull Canvas canvas) {
6195e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana        final boolean hasMask = mMask != null;
62053abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        final boolean drawNonMaskContent = mLayerState.mNum > (hasMask ? 1 : 0);
6215e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana        final boolean drawMask = hasMask && mMask.getOpacity() != PixelFormat.OPAQUE;
6225e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana        final Rect bounds = getDirtyBounds();
6235e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana        final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
6245e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana        canvas.clipRect(bounds);
6255e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana
6265e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana        // If we have content, draw it into a layer first.
6275e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana        final int contentLayer;
62856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        if (drawNonMaskContent) {
6298ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams            contentLayer = drawContentLayer(canvas, bounds, SRC_OVER);
6308ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams        } else {
63156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            contentLayer = -1;
6325e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana        }
6335e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana
63453abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams        // Next, try to draw the ripples (into a layer if necessary). If we need
6355e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana        // to mask against the underlying content, set the xfermode to SRC_ATOP.
6365e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana        final PorterDuffXfermode xfermode = (hasMask || !drawNonMaskContent) ? SRC_OVER : SRC_ATOP;
6375e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana
63804e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani        // If we have a background and a non-opaque mask, draw the masking layer.
6398ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams        final int backgroundLayer = drawBackgroundLayer(canvas, bounds, xfermode, drawMask);
64056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        if (backgroundLayer >= 0) {
64156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            if (drawMask) {
64256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams                drawMaskingLayer(canvas, bounds, DST_IN);
64356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            }
64456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            canvas.restoreToCount(backgroundLayer);
6457a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams        }
6467a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams
64756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        // If we have ripples and a non-opaque mask, draw the masking layer.
6488ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams        final int rippleLayer = drawRippleLayer(canvas, bounds, xfermode);
6497a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams        if (rippleLayer >= 0) {
6507a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams            if (drawMask) {
6517a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams                drawMaskingLayer(canvas, bounds, DST_IN);
65253abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams            }
65353abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams            canvas.restoreToCount(rippleLayer);
6547a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams        }
65556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
6568c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        // If we failed to draw anything and we just canceled animations, at
65777c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana        // least draw a color so that hardware invalidation works correctly.
6587a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams        if (contentLayer < 0 && backgroundLayer < 0 && rippleLayer < 0 && mNeedsDraw) {
6597a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams            canvas.drawColor(Color.TRANSPARENT);
6608c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        }
6617a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams        mNeedsDraw = false;
6627a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams
6637a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams        canvas.restoreToCount(saveCount);
6647a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams    }
6657a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams
6665e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana    /**
66753abfdb86c2bf834777dbda61fc46083a93a4a83Matthew Williams     * Removes a ripple from the exiting ripple list.
66856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams     *
6695e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana     * @param ripple the ripple to remove
6705e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana     */
6715e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana    void removeRipple(Ripple ripple) {
6725e787c42f2a6b3afc8ec8320a08d51b2d44b8614Fred Quintana        // Ripple ripple ripple ripple. Ripple ripple.
67356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        final Ripple[] ripples = mExitingRipples;
674307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        final int count = mExitingRipplesCount;
67556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        final int index = getRippleIndex(ripple);
67656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        if (index >= 0) {
67756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            System.arraycopy(ripples, index + 1, ripples, index, count - (index + 1));
678307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana            ripples[count - 1] = null;
67956dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            mExitingRipplesCount--;
680307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana
681307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana            invalidateSelf();
682307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        }
68356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    }
68456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
68556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    private int getRippleIndex(Ripple ripple) {
68656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        final Ripple[] ripples = mExitingRipples;
68756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        final int count = mExitingRipplesCount;
68856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        for (int i = 0; i < count; i++) {
6898c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            if (ripples[i] == ripple) {
690307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana                return i;
691307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana            }
69256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        }
693307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        return -1;
6948c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    }
69556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
6968c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    private int drawContentLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
69756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        final ChildDrawable[] array = mLayerState.mChildren;
69856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        final int count = mLayerState.mNum;
69956dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
70056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        // We don't need a layer if we don't expect to draw any ripples, we have
70156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        // an explicit mask, or if the non-mask content is all opaque.
702307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        boolean needsLayer = false;
70356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        if ((mExitingRipplesCount > 0 || mBackground != null) && mMask == null) {
70456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            for (int i = 0; i < count; i++) {
70556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams                if (array[i].mId != R.id.mask
70656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams                        && array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) {
70756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams                    needsLayer = true;
70856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams                    break;
70956dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams                }
71056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            }
71156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        }
712307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana
713307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        final Paint maskingPaint = getMaskingPaint(mode);
714307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        final int restoreToCount = needsLayer ? canvas.saveLayer(bounds.left, bounds.top,
715307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana                bounds.right, bounds.bottom, maskingPaint) : -1;
716307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana
717307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        // Draw everything except the mask.
718307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        for (int i = 0; i < count; i++) {
719307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana            if (array[i].mId != R.id.mask) {
72056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams                array[i].mDrawable.draw(canvas);
72156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            }
72256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        }
72356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
72456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        return restoreToCount;
72556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    }
72656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
7277a2ab3a060f89c5de7f44f85bf58a7b6f6884873Matthew Williams    private int drawBackgroundLayer(
72856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            Canvas canvas, Rect bounds, PorterDuffXfermode mode, boolean drawMask) {
72956dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        int saveCount = -1;
7308c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
73156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        if (mBackground != null && mBackground.shouldDraw()) {
73256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            // TODO: We can avoid saveLayer here if we push the xfermode into
73356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            // the background's render thread animator at exit() time.
73456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            if (drawMask || mode != SRC_OVER) {
73556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams                saveCount = canvas.saveLayer(bounds.left, bounds.top, bounds.right,
73656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams                        bounds.bottom, getMaskingPaint(mode));
73756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            }
73856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
7398ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams            final float x = mHotspotBounds.exactCenterX();
74056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            final float y = mHotspotBounds.exactCenterY();
74156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            canvas.translate(x, y);
74256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            mBackground.draw(canvas, getRipplePaint());
74356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            canvas.translate(-x, -y);
74456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        }
74556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
74656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        return saveCount;
74756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    }
74856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
74956dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    private int drawRippleLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
75056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        boolean drewRipples = false;
75156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        int restoreToCount = -1;
75256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        int restoreTranslate = -1;
7538c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
754744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert        // Draw ripples and update the animating ripples array.
755744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert        final int count = mExitingRipplesCount;
7568c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        final Ripple[] ripples = mExitingRipples;
7578c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        for (int i = 0; i <= count; i++) {
7588c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            final Ripple ripple;
7598c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            if (i < count) {
7608c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge                ripple = ripples[i];
7618c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            } else if (mRipple != null) {
7628c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge                ripple = mRipple;
7638c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            } else {
7648c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge                continue;
7658c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            }
7668c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
7678c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            // If we're masking the ripple layer, make sure we have a layer
768744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert            // first. This will merge SRC_OVER (directly) onto the canvas.
7698c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            if (restoreToCount < 0) {
7708c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge                final Paint maskingPaint = getMaskingPaint(mode);
7718c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge                final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
772744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert                final int alpha = Color.alpha(color);
773744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert                maskingPaint.setAlpha(alpha / 2);
774744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert
775744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert                // TODO: We can avoid saveLayer here if we're only drawing one
776744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert                // ripple and we don't have content or a translucent mask.
777744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert                restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
778744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert                        bounds.right, bounds.bottom, maskingPaint);
779744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert
780744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert                // Translate the canvas to the current hotspot bounds.
781744e310f6635026396e32ab0a2c009e464c2be3cAlon Albert                restoreTranslate = canvas.save();
78256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams                canvas.translate(mHotspotBounds.exactCenterX(), mHotspotBounds.exactCenterY());
783307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana            }
78456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
78556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            drewRipples |= ripple.draw(canvas, getRipplePaint());
78656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        }
787307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana
78856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        // Always restore the translation.
789307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        if (restoreTranslate >= 0) {
790307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana            canvas.restoreToCount(restoreTranslate);
791307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        }
79256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
79356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        // If we created a layer with no content, merge it immediately.
7948c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        if (restoreToCount >= 0 && !drewRipples) {
79556dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            canvas.restoreToCount(restoreToCount);
79656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams            restoreToCount = -1;
797307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        }
79856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
79956dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        return restoreToCount;
80056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    }
801307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana
80256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    private int drawMaskingLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
803307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        final int restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
80456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams                bounds.right, bounds.bottom, getMaskingPaint(mode));
805307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana
806307da1a46b4c9b711bafe8fbaaa6b98e8868c18eFred Quintana        // Ensure that DST_IN blends using the entire layer.
80756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        canvas.drawColor(Color.TRANSPARENT);
8088c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
8098c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        mMask.draw(canvas);
81056dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
8118c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        return restoreToCount;
8128c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    }
8138c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
8148c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    private Paint getRipplePaint() {
815c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana        if (mRipplePaint == null) {
8168c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            mRipplePaint = new Paint();
8178c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            mRipplePaint.setAntiAlias(true);
8188c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        }
8198c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        return mRipplePaint;
8208c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    }
821c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana
8228c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    private Paint getMaskingPaint(PorterDuffXfermode xfermode) {
823c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana        if (mMaskingPaint == null) {
8248c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            mMaskingPaint = new Paint();
825c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana        }
8268c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        mMaskingPaint.setXfermode(xfermode);
827c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana        mMaskingPaint.setAlpha(0xFF);
828c5d1c6db61f208b206b260f897bb5bbc64be4d97Fred Quintana        return mMaskingPaint;
82904e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani    }
830231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
83104e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani    @Override
8328ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams    public Rect getDirtyBounds() {
83377c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana        if (isProjected()) {
83477c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana            final Rect drawingBounds = mDrawingBounds;
83504e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani            final Rect dirtyBounds = mDirtyBounds;
836231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            dirtyBounds.set(drawingBounds);
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            drawingBounds.setEmpty();
83877c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana
83957286f970641493b315b0b42aba7ac6b672cc8b8Alon Albert            final int cX = (int) mHotspotBounds.exactCenterX();
84057286f970641493b315b0b42aba7ac6b672cc8b8Alon Albert            final int cY = (int) mHotspotBounds.exactCenterY();
8418294fadb155a33da8a40d8412afb35e6b424afcbJoe Onorato            final Rect rippleBounds = mTempRect;
842ac9385ef3105fb7464e1f46049c62755a8b7f0e9Fred Quintana
8437a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkey            final Ripple[] activeRipples = mExitingRipples;
844a6533256ac1de9d0860688ca743aa7a2468470ccMarvin Paul            final int N = mExitingRipplesCount;
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < N; i++) {
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                activeRipples[i].getBounds(rippleBounds);
84704e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani                rippleBounds.offset(cX, cY);
848231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn                drawingBounds.union(rippleBounds);
84904e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani            }
8502b5d0ea5e90bd5b9a1b3afdd230e8907315e65e2Yameng Huang
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final RippleBackground background = mBackground;
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (background != null) {
853360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache                background.getBounds(rippleBounds);
854231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn                rippleBounds.offset(cX, cY);
855231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn                drawingBounds.union(rippleBounds);
856231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            }
857231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
858231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            dirtyBounds.union(drawingBounds);
859360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache            dirtyBounds.union(super.getDirtyBounds());
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return dirtyBounds;
86156dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        } else {
8628ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams            return getBounds();
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
86456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    }
865231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
86656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams    @Override
867918339ab8255f8e1d03d8448ab1d9036c7c15173Fred Quintana    public ConstantState getConstantState() {
8688ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams        return mState;
869231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    }
870231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
871231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    static class RippleState extends LayerState {
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] mTouchThemeAttrs;
873231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        ColorStateList mColor = ColorStateList.valueOf(Color.MAGENTA);
874231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        int mMaxRadius = RADIUS_AUTO;
875360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache
8768c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        public RippleState(RippleState orig, RippleDrawable owner, Resources res) {
877231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            super(orig, owner, res);
8788c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
8798c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge            if (orig != null) {
8808c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge                mTouchThemeAttrs = orig.mTouchThemeAttrs;
881231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn                mColor = orig.mColor;
8828c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge                mMaxRadius = orig.mMaxRadius;
883231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            }
884231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        }
8858c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
886231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        @Override
887ac9385ef3105fb7464e1f46049c62755a8b7f0e9Fred Quintana        public boolean canApplyTheme() {
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mTouchThemeAttrs != null || super.canApplyTheme();
889360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache        }
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
891231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        @Override
892231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        public Drawable newDrawable() {
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new RippleDrawable(this, null, null);
89404e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani        }
895231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
8968ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams        @Override
8978c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        public Drawable newDrawable(Resources res) {
8988ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams            return new RippleDrawable(this, res, null);
899231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        }
900231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
901231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        @Override
902231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        public Drawable newDrawable(Resources res, Theme theme) {
90304e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani            return new RippleDrawable(this, res, theme);
90404e0d265e3385e9d1fbc35e43c4e8caffbbe3290Amith Yamasani        }
905231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    }
90656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
9078c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    /**
90877c560f3d7891d9ae1ad714b5f65a22ff4f4c06bFred Quintana     * Sets the maximum ripple radius in pixels. The default value of
909231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * {@link #RADIUS_AUTO} defines the radius as the distance from the center
910231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * of the drawable bounds (or hotspot bounds, if specified) to a corner.
911231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     *
912231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * @param maxRadius the maximum ripple radius in pixels or
913231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     *            {@link #RADIUS_AUTO} to automatically determine the maximum
914231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     *            radius based on the bounds
915360e454b2b2f911e9e29268519017cc94ae9d4f4Costin Manolache     * @see #getMaxRadius()
916231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * @see #setHotspotBounds(int, int, int, int)
917231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * @hide
918231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     */
919231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    public void setMaxRadius(int maxRadius) {
920231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        if (maxRadius != RADIUS_AUTO && maxRadius < 0) {
921231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            throw new IllegalArgumentException("maxRadius must be RADIUS_AUTO or >= 0");
9228c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        }
9238c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
9248c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        mState.mMaxRadius = maxRadius;
9258c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge    }
926231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
927231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    /**
928231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * @return the maximum ripple radius in pixels, or {@link #RADIUS_AUTO} if
929231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     *         the radius is determined automatically
930231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * @see #setMaxRadius(int)
931231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     * @hide
932231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn     */
933231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    public int getMaxRadius() {
934231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        return mState.mMaxRadius;
935231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    }
936231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
937231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn    private RippleDrawable(RippleState state, Resources res, Theme theme) {
938231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        boolean needsTheme = false;
939231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
940231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        final RippleState ns;
941231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        if (theme != null && state != null && state.canApplyTheme()) {
942231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            ns = new RippleState(state, this, res);
943231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            needsTheme = true;
944231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        } else if (state == null) {
945231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            ns = new RippleState(null, this, res);
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // We always need a new state since child drawables contain local
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // state but live within the parent's constant state.
949231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn            // TODO: Move child drawables into local state.
950918339ab8255f8e1d03d8448ab1d9036c7c15173Fred Quintana            ns = new RippleState(state, this, res);
951918339ab8255f8e1d03d8448ab1d9036c7c15173Fred Quintana        }
952231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn
953918339ab8255f8e1d03d8448ab1d9036c7c15173Fred Quintana        if (res != null) {
954918339ab8255f8e1d03d8448ab1d9036c7c15173Fred Quintana            mDensity = res.getDisplayMetrics().density;
955231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn        }
95656dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
9578c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        mState = ns;
9588c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        mLayerState = ns;
9598c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge
9608c834c07675052c984728cbf79f7c2e0d8246e43Shreyas Basarge        if (ns.mNum > 0) {
961918339ab8255f8e1d03d8448ab1d9036c7c15173Fred Quintana            ensurePadding();
96256dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        }
96356dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
96456dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        if (needsTheme) {
9658ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams            applyTheme(theme);
966918339ab8255f8e1d03d8448ab1d9036c7c15173Fred Quintana        }
96756dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams
96856dbf8f23677d28615e61ef2fbb0e738cca02528Matthew Williams        initializeFromState();
9698ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams    }
9708ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams
971918339ab8255f8e1d03d8448ab1d9036c7c15173Fred Quintana    private void initializeFromState() {
9728ef2204c8f5f9744a2ff7abdbbf2d26a5ea02837Matthew Williams        // Initialize from constant state.
973918339ab8255f8e1d03d8448ab1d9036c7c15173Fred Quintana        mMask = findDrawableByLayerId(R.id.mask);
974918339ab8255f8e1d03d8448ab1d9036c7c15173Fred Quintana    }
975918339ab8255f8e1d03d8448ab1d9036c7c15173Fred Quintana}
976918339ab8255f8e1d03d8448ab1d9036c7c15173Fred Quintana