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><!-- A red ripple masked against an opaque rectangle. --/> 60a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <ripple android:color="#ffff0000"> 61a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <item android:id="@android:id/mask" 627275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette * android:drawable="@android:color/white" /> 636dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * </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><!-- A green ripple drawn atop a black rectangle. --/> 73a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <ripple android:color="#ff00ff00"> 747275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette * <item android:drawable="@android:color/black" /> 756dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * </ripple> 76a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * 776dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * <!-- A blue ripple drawn atop a drawable resource. --/> 786dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * <ripple android:color="#ff0000ff"> 79a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <item android:drawable="@drawable/my_drawable" /> 806dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * </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><!-- An unbounded red ripple. --/> 896dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * <ripple 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