1d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette/* 245c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette * Copyright (C) 2014 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; 26ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viveretteimport android.content.pm.ActivityInfo.Config; 27d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.content.res.ColorStateList; 28d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.content.res.Resources; 2952b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viveretteimport android.content.res.Resources.Theme; 30d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.content.res.TypedArray; 316dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viveretteimport android.graphics.Bitmap; 326dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viveretteimport android.graphics.BitmapShader; 33d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.graphics.Canvas; 34d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport android.graphics.Color; 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 Viverette 45d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viveretteimport java.io.IOException; 4640e38d43675b9baa4383058e5afd5291291abc81Alan Viveretteimport java.util.Arrays; 47d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 48d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette/** 49ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * Drawable that shows a ripple effect in response to state changes. The 50ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * anchoring position of the ripple for a given state may be specified by 51c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette * calling {@link #setHotspot(float, float)} with the corresponding state 52ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * attribute identifier. 53ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * <p> 54ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * A touch feedback drawable may contain multiple child layers, including a 55d21be499753b964433faefbe0fd770c62a4c2e12Alan Viverette * special mask layer that is not drawn to the screen. A single layer may be 56d21be499753b964433faefbe0fd770c62a4c2e12Alan Viverette * set as the mask from XML by specifying its {@code android:id} value as 57d21be499753b964433faefbe0fd770c62a4c2e12Alan Viverette * {@link android.R.id#mask}. At run time, a single layer may be set as the 58d21be499753b964433faefbe0fd770c62a4c2e12Alan Viverette * mask using {@code setId(..., android.R.id.mask)} or an existing mask layer 59d21be499753b964433faefbe0fd770c62a4c2e12Alan Viverette * may be replaced using {@code setDrawableByLayerId(android.R.id.mask, ...)}. 60a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <pre> 61a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <code><!-- A red ripple masked against an opaque rectangle. --/> 62a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <ripple android:color="#ffff0000"> 63a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <item android:id="@android:id/mask" 647275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette * android:drawable="@android:color/white" /> 656dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * </ripple></code> 66a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * </pre> 67ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * <p> 68ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * If a mask layer is set, the ripple effect will be masked against that layer 69a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * before it is drawn over the composite of the remaining child layers. 70ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * <p> 71a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * If no mask layer is set, the ripple effect is masked against the composite 72a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * of the child layers. 73a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <pre> 746dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * <code><!-- A green ripple drawn atop a black rectangle. --/> 75a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <ripple android:color="#ff00ff00"> 767275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette * <item android:drawable="@android:color/black" /> 776dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * </ripple> 78a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * 796dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * <!-- A blue ripple drawn atop a drawable resource. --/> 806dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * <ripple android:color="#ff0000ff"> 81a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <item android:drawable="@drawable/my_drawable" /> 826dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * </ripple></code> 83a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * </pre> 84ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * <p> 85ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * If no child layers or mask is specified and the ripple is set as a View 86a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * background, the ripple will be drawn atop the first available parent 87a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * background within the View's hierarchy. In this case, the drawing region 88a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * may extend outside of the Drawable bounds. 89a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * <pre> 906dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * <code><!-- An unbounded red ripple. --/> 916dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * <ripple android:color="#ffff0000" /></code> 92a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * </pre> 93ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette * 94a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette * @attr ref android.R.styleable#RippleDrawable_color 95d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette */ 96c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverettepublic class RippleDrawable extends LayerDrawable { 9788d44d4adaa68db02e4eef68606591028a92add0Alan Viverette /** 9888d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * Radius value that specifies the ripple radius should be computed based 9988d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * on the size of the ripple's container. 10088d44d4adaa68db02e4eef68606591028a92add0Alan Viverette */ 10188d44d4adaa68db02e4eef68606591028a92add0Alan Viverette public static final int RADIUS_AUTO = -1; 10288d44d4adaa68db02e4eef68606591028a92add0Alan Viverette 1036dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette private static final int MASK_UNKNOWN = -1; 1046dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette private static final int MASK_NONE = 0; 1056dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette private static final int MASK_CONTENT = 1; 1066dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette private static final int MASK_EXPLICIT = 2; 107dccbe8b02a34a3c78028a31ee158d4d2818c72baAlan 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. */ 142f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette private RippleForeground 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 */ 156f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette private RippleForeground[] 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. */ 163bd5294bcb857a48ad22ddd54b13208ec2903c3f6Alan Viverette private int mDensity; 164d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 16561956606818918194a38e045a8e35e7108480e5eAlan Viverette /** Whether bounds are being overridden. */ 16661956606818918194a38e045a8e35e7108480e5eAlan Viverette private boolean mOverrideBounds; 16761956606818918194a38e045a8e35e7108480e5eAlan Viverette 1686ae2d7cefafd7fbf5e7c8d7d3c1869e66056b7f8Alan Viverette /** 169b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi * If set, force all ripple animations to not run on RenderThread, even if it would be 170b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi * available. 171b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi */ 172b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi private boolean mForceSoftware; 173b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi 174b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi /** 1756ae2d7cefafd7fbf5e7c8d7d3c1869e66056b7f8Alan Viverette * Constructor used for drawable inflation. 1766ae2d7cefafd7fbf5e7c8d7d3c1869e66056b7f8Alan Viverette */ 177c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette RippleDrawable() { 17817cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette this(new RippleState(null, null, null), null); 1794d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 1804d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette 1814d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette /** 1827275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette * Creates a new ripple drawable with the specified ripple color and 1837275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette * optional content and mask drawables. 1844d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette * 1857275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette * @param color The ripple color 1864d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette * @param content The content drawable, may be {@code null} 1874d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette * @param mask The mask drawable, may be {@code null} 1884d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette */ 1897275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette public RippleDrawable(@NonNull ColorStateList color, @Nullable Drawable content, 1907275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette @Nullable Drawable mask) { 19117cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette this(new RippleState(null, null, null), null); 1924d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette 1937275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette if (color == null) { 1947275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette throw new IllegalArgumentException("RippleDrawable requires a non-null color"); 1957275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette } 1967275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette 1974d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette if (content != null) { 1984d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette addLayer(content, null, 0, 0, 0, 0, 0); 1994d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 2004d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette 2014d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette if (mask != null) { 202935b1fa24d05533a95ee47425ab9bedb31641012Alan Viverette addLayer(mask, null, android.R.id.mask, 0, 0, 0, 0); 2034d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 2044d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette 2057275abde37f0ad2df50e78de2fee1c0cfeb9cd92Alan Viverette setColor(color); 2064d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette ensurePadding(); 20706ff2af68aa1041eeb26778e994e0fe196bf8b1eAlan Viverette refreshPadding(); 20845c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette updateLocalState(); 209c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette } 210c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette 211c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette @Override 212d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette public void jumpToCurrentState() { 213d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette super.jumpToCurrentState(); 214d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette 215d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette if (mRipple != null) { 216f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette mRipple.end(); 217d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette } 218d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette 219d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette if (mBackground != null) { 220f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette mBackground.end(); 221d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette } 222d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette 223cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette cancelExitingRipples(); 224fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette } 225fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette 226fc8882f1340055e00f6c21449983e92413045275Alan Viverette private void cancelExitingRipples() { 227fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette final int count = mExitingRipplesCount; 228f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette final RippleForeground[] ripples = mExitingRipples; 229d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette for (int i = 0; i < count; i++) { 230f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette ripples[i].end(); 23140e38d43675b9baa4383058e5afd5291291abc81Alan Viverette } 232fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette 23340e38d43675b9baa4383058e5afd5291291abc81Alan Viverette if (ripples != null) { 23440e38d43675b9baa4383058e5afd5291291abc81Alan Viverette Arrays.fill(ripples, 0, count, null); 235d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette } 236fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette mExitingRipplesCount = 0; 2372ae56403542582bd39c0e522bf29844d59300f37Alan Viverette 238fc8882f1340055e00f6c21449983e92413045275Alan Viverette // Always draw an additional "clean" frame after canceling animations. 23915ce834e52806378d6ab2b90f573bae14cb3fd4bAlan Viverette invalidateSelf(false); 240d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 241d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 242d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette @Override 243d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette public int getOpacity() { 24447bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette // Worst-case scenario. 24547bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette return PixelFormat.TRANSLUCENT; 246ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette } 247ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette 248ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette @Override 24912b97f5d2b15194ed6673c9838b13c8312157709Alan Viverette protected boolean onStateChange(int[] stateSet) { 250a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette final boolean changed = super.onStateChange(stateSet); 251ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette 2521b6e856e6f9dab4464e3c556b2f68527439fc329Alan Viverette boolean enabled = false; 2536ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette boolean pressed = false; 2546ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette boolean focused = false; 255e4f976dc3b7dd2548deb409b6fd421c6c47f6b42George Mount boolean hovered = false; 2566ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 257a8a8ff000b2902eb4e187e62be39fd9535c6c839Alan Viverette for (int state : stateSet) { 258a8a8ff000b2902eb4e187e62be39fd9535c6c839Alan Viverette if (state == R.attr.state_enabled) { 2591b6e856e6f9dab4464e3c556b2f68527439fc329Alan Viverette enabled = true; 260f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette } else if (state == R.attr.state_focused) { 2616ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette focused = true; 262f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette } else if (state == R.attr.state_pressed) { 2636ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette pressed = true; 264e4f976dc3b7dd2548deb409b6fd421c6c47f6b42George Mount } else if (state == R.attr.state_hovered) { 265e4f976dc3b7dd2548deb409b6fd421c6c47f6b42George Mount hovered = true; 2664d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 267d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette } 2686ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 2696ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette setRippleActive(enabled && pressed); 270e4f976dc3b7dd2548deb409b6fd421c6c47f6b42George Mount setBackgroundActive(hovered || focused || (enabled && pressed), focused || hovered); 271d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette 272a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette return changed; 273d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 274d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 2756ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette private void setRippleActive(boolean active) { 2766ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (mRippleActive != active) { 2776ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette mRippleActive = active; 2786ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (active) { 279fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette tryRippleEnter(); 2806ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } else { 281fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette tryRippleExit(); 2826ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 2836ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 2846ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 2854d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette 286f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette private void setBackgroundActive(boolean active, boolean focused) { 2876ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (mBackgroundActive != active) { 2886ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette mBackgroundActive = active; 2894d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette if (active) { 290f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette tryBackgroundEnter(focused); 2914d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } else { 292fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette tryBackgroundExit(); 2934d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 2944d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 2954d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 2964d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette 297307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette @Override 298c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette protected void onBoundsChange(Rect bounds) { 299c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette super.onBoundsChange(bounds); 300d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette 30161956606818918194a38e045a8e35e7108480e5eAlan Viverette if (!mOverrideBounds) { 30261956606818918194a38e045a8e35e7108480e5eAlan Viverette mHotspotBounds.set(bounds); 303dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette onHotspotBoundsChanged(); 30461956606818918194a38e045a8e35e7108480e5eAlan Viverette } 305c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette 3065bc0144302bb378e5f007baa4bf6620ab9031879Alan Viverette if (mBackground != null) { 3075bc0144302bb378e5f007baa4bf6620ab9031879Alan Viverette mBackground.onBoundsChange(); 3085bc0144302bb378e5f007baa4bf6620ab9031879Alan Viverette } 3095bc0144302bb378e5f007baa4bf6620ab9031879Alan Viverette 3105bc0144302bb378e5f007baa4bf6620ab9031879Alan Viverette if (mRipple != null) { 3115bc0144302bb378e5f007baa4bf6620ab9031879Alan Viverette mRipple.onBoundsChange(); 3125bc0144302bb378e5f007baa4bf6620ab9031879Alan Viverette } 3135bc0144302bb378e5f007baa4bf6620ab9031879Alan Viverette 314ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette invalidateSelf(); 315c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette } 316c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette 317c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette @Override 318307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette public boolean setVisible(boolean visible, boolean restart) { 3196ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette final boolean changed = super.setVisible(visible, restart); 3206ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 321307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette if (!visible) { 32253d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette clearHotspots(); 3236ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } else if (changed) { 3246ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette // If we just became visible, ensure the background and ripple 3256ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette // visibilities are consistent with their internal states. 3266ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (mRippleActive) { 327fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette tryRippleEnter(); 3286ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 3296ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 3306ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (mBackgroundActive) { 331f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette tryBackgroundEnter(false); 3326ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 333f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette 334f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette // Skip animations, just show the correct final states. 335f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette jumpToCurrentState(); 336307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette } 337307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette 3386ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette return changed; 339307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette } 340307ad09228ebf70f1b456f5f00540c0126277850Alan Viverette 341d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette /** 342d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette * @hide 343d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette */ 344d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette @Override 345d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette public boolean isProjected() { 34699ca2a8470a48906aaba2d76c856037933496352Alan Viverette // If the layer is bounded, then we don't need to project. 34799ca2a8470a48906aaba2d76c856037933496352Alan Viverette if (isBounded()) { 34899ca2a8470a48906aaba2d76c856037933496352Alan Viverette return false; 34999ca2a8470a48906aaba2d76c856037933496352Alan Viverette } 35099ca2a8470a48906aaba2d76c856037933496352Alan Viverette 35199ca2a8470a48906aaba2d76c856037933496352Alan Viverette // Otherwise, if the maximum radius is contained entirely within the 35299ca2a8470a48906aaba2d76c856037933496352Alan Viverette // bounds then we don't need to project. This is sort of a hack to 35399ca2a8470a48906aaba2d76c856037933496352Alan Viverette // prevent check box ripples from being projected across the edges of 35499ca2a8470a48906aaba2d76c856037933496352Alan Viverette // scroll views. It does not impact rendering performance, and it can 35599ca2a8470a48906aaba2d76c856037933496352Alan Viverette // be removed once we have better handling of projection in scrollable 35699ca2a8470a48906aaba2d76c856037933496352Alan Viverette // views. 357388cd3b69c51a449b8bbb8086dbc429f24783ad1Alan Viverette final int radius = mState.mMaxRadius; 35899ca2a8470a48906aaba2d76c856037933496352Alan Viverette final Rect drawableBounds = getBounds(); 35999ca2a8470a48906aaba2d76c856037933496352Alan Viverette final Rect hotspotBounds = mHotspotBounds; 36099ca2a8470a48906aaba2d76c856037933496352Alan Viverette if (radius != RADIUS_AUTO 36199ca2a8470a48906aaba2d76c856037933496352Alan Viverette && radius <= hotspotBounds.width() / 2 36299ca2a8470a48906aaba2d76c856037933496352Alan Viverette && radius <= hotspotBounds.height() / 2 36399ca2a8470a48906aaba2d76c856037933496352Alan Viverette && (drawableBounds.equals(hotspotBounds) 36499ca2a8470a48906aaba2d76c856037933496352Alan Viverette || drawableBounds.contains(hotspotBounds))) { 365388cd3b69c51a449b8bbb8086dbc429f24783ad1Alan Viverette return false; 366388cd3b69c51a449b8bbb8086dbc429f24783ad1Alan Viverette } 367388cd3b69c51a449b8bbb8086dbc429f24783ad1Alan Viverette 36899ca2a8470a48906aaba2d76c856037933496352Alan Viverette return true; 369388cd3b69c51a449b8bbb8086dbc429f24783ad1Alan Viverette } 370388cd3b69c51a449b8bbb8086dbc429f24783ad1Alan Viverette 371388cd3b69c51a449b8bbb8086dbc429f24783ad1Alan Viverette private boolean isBounded() { 372388cd3b69c51a449b8bbb8086dbc429f24783ad1Alan Viverette return getNumberOfLayers() > 0; 373d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 374d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 375d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette @Override 376d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette public boolean isStateful() { 377ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette return true; 378d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 379d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 38045c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette /** 38145c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette * Sets the ripple color. 38245c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette * 38345c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette * @param color Ripple color as a color state list. 38488d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * 38588d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * @attr ref android.R.styleable#RippleDrawable_color 38645c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette */ 387a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette public void setColor(ColorStateList color) { 388a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette mState.mColor = color; 38915ce834e52806378d6ab2b90f573bae14cb3fd4bAlan Viverette invalidateSelf(false); 3902f8ba8f7fad2b608102a9282219aaea2223e94f5Alan Viverette } 3912f8ba8f7fad2b608102a9282219aaea2223e94f5Alan Viverette 39288d44d4adaa68db02e4eef68606591028a92add0Alan Viverette /** 39388d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * Sets the radius in pixels of the fully expanded ripple. 39488d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * 39588d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * @param radius ripple radius in pixels, or {@link #RADIUS_AUTO} to 39688d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * compute the radius based on the container size 39788d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * @attr ref android.R.styleable#RippleDrawable_radius 39888d44d4adaa68db02e4eef68606591028a92add0Alan Viverette */ 39988d44d4adaa68db02e4eef68606591028a92add0Alan Viverette public void setRadius(int radius) { 40088d44d4adaa68db02e4eef68606591028a92add0Alan Viverette mState.mMaxRadius = radius; 40115ce834e52806378d6ab2b90f573bae14cb3fd4bAlan Viverette invalidateSelf(false); 40288d44d4adaa68db02e4eef68606591028a92add0Alan Viverette } 40388d44d4adaa68db02e4eef68606591028a92add0Alan Viverette 40488d44d4adaa68db02e4eef68606591028a92add0Alan Viverette /** 40588d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * @return the radius in pixels of the fully expanded ripple if an explicit 40688d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * radius has been set, or {@link #RADIUS_AUTO} if the radius is 40788d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * computed based on the container size 40888d44d4adaa68db02e4eef68606591028a92add0Alan Viverette * @attr ref android.R.styleable#RippleDrawable_radius 40988d44d4adaa68db02e4eef68606591028a92add0Alan Viverette */ 41088d44d4adaa68db02e4eef68606591028a92add0Alan Viverette public int getRadius() { 41188d44d4adaa68db02e4eef68606591028a92add0Alan Viverette return mState.mMaxRadius; 41288d44d4adaa68db02e4eef68606591028a92add0Alan Viverette } 41388d44d4adaa68db02e4eef68606591028a92add0Alan Viverette 414d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette @Override 4150cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser, 4160cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette @NonNull AttributeSet attrs, @Nullable Theme theme) 417d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette throws XmlPullParserException, IOException { 418e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.RippleDrawable); 419cb29189c29a553a8005044f32de37a610f2857dbAlan Viverette 420e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette // Force padding default to STACK before inflating. 421e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette setPaddingMode(PADDING_MODE_STACK); 422e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette 4230cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette // Inflation will advance the XmlPullParser and AttributeSet. 42447bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette super.inflate(r, parser, attrs, theme); 425ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette 4260cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette updateStateFromTypedArray(a); 4270cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette verifyRequiredAttributes(a); 4280cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette a.recycle(); 42945c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette 43045c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette updateLocalState(); 431d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette } 432d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette 433d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette @Override 434d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette public boolean setDrawableByLayerId(int id, Drawable drawable) { 435d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette if (super.setDrawableByLayerId(id, drawable)) { 436d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette if (id == R.id.mask) { 437b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette mMask = drawable; 438690a16c35031b4474f429de1967d6738f9d00322Alan Viverette mHasValidMask = false; 439d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette } 440d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette 441d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette return true; 442d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette } 443d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette 444d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette return false; 445ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette } 446ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette 447ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette /** 448e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette * Specifies how layer padding should affect the bounds of subsequent 449e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette * layers. The default and recommended value for RippleDrawable is 450e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette * {@link #PADDING_MODE_STACK}. 451e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette * 452e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette * @param mode padding mode, one of: 453e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette * <ul> 454e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette * <li>{@link #PADDING_MODE_NEST} to nest each layer inside the 455e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette * padding of the previous layer 456e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette * <li>{@link #PADDING_MODE_STACK} to stack each layer directly 457e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette * atop the previous layer 458e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette * </ul> 459e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette * @see #getPaddingMode() 460e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette */ 461e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette @Override 462e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette public void setPaddingMode(int mode) { 463e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette super.setPaddingMode(mode); 464e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette } 465e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette 466e7772d313b88e9fd3366670a9c8d1b145edef344Alan Viverette /** 46752b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette * Initializes the constant state from the values in the typed array. 46852b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette */ 4690cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette private void updateStateFromTypedArray(@NonNull TypedArray a) throws XmlPullParserException { 470c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette final RippleState state = mState; 47152b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette 4728e5e11b99fac942122ee2d6cdd30af51564861aeAlan Viverette // Account for any configuration changes. 4738e5e11b99fac942122ee2d6cdd30af51564861aeAlan Viverette state.mChangingConfigurations |= a.getChangingConfigurations(); 4748e5e11b99fac942122ee2d6cdd30af51564861aeAlan Viverette 47552b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette // Extract the theme attributes, if any. 476ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette state.mTouchThemeAttrs = a.extractThemeAttrs(); 47752b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette 478a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette final ColorStateList color = a.getColorStateList(R.styleable.RippleDrawable_color); 479a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette if (color != null) { 480a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette mState.mColor = color; 48152b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette } 48252b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette 48388d44d4adaa68db02e4eef68606591028a92add0Alan Viverette mState.mMaxRadius = a.getDimensionPixelSize( 48488d44d4adaa68db02e4eef68606591028a92add0Alan Viverette R.styleable.RippleDrawable_radius, mState.mMaxRadius); 48534a14f967ab6c88829c9a36ce6e909c47b3ee398Alan Viverette } 48634a14f967ab6c88829c9a36ce6e909c47b3ee398Alan Viverette 4870cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException { 48834a14f967ab6c88829c9a36ce6e909c47b3ee398Alan Viverette if (mState.mColor == null && (mState.mTouchThemeAttrs == null 48934a14f967ab6c88829c9a36ce6e909c47b3ee398Alan Viverette || mState.mTouchThemeAttrs[R.styleable.RippleDrawable_color] == 0)) { 4904d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette throw new XmlPullParserException(a.getPositionDescription() + 491a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette ": <ripple> requires a valid color attribute"); 4924d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 49352b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette } 49452b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette 49552b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette @Override 4960cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette public void applyTheme(@NonNull Theme t) { 49752b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette super.applyTheme(t); 49852b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette 499c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette final RippleState state = mState; 50045c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette if (state == null) { 501ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette return; 50252b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette } 50352b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette 50445c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette if (state.mTouchThemeAttrs != null) { 50545c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette final TypedArray a = t.resolveAttributes(state.mTouchThemeAttrs, 50645c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette R.styleable.RippleDrawable); 50745c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette try { 50845c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette updateStateFromTypedArray(a); 5090cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette verifyRequiredAttributes(a); 51045c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette } catch (XmlPullParserException e) { 511c078c605ab904b0e4a5d793cbeffd78c340f2816Alan Viverette rethrowAsRuntimeException(e); 51245c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette } finally { 51345c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette a.recycle(); 51445c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette } 51545c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette } 51645c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette 51745c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette if (state.mColor != null && state.mColor.canApplyTheme()) { 518e0f95f39c5a669a48ee3ebb8dc45bf2d7ee940f1Alan Viverette state.mColor = state.mColor.obtainForTheme(t); 5194d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 520b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette 52145c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette updateLocalState(); 52252b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette } 52352b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette 52452b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette @Override 52552b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette public boolean canApplyTheme() { 526d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette return (mState != null && mState.canApplyTheme()) || super.canApplyTheme(); 52752b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette } 52852b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette 529d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette @Override 530c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette public void setHotspot(float x, float y) { 5316ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (mRipple == null || mBackground == null) { 5326ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette mPendingX = x; 5336ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette mPendingY = y; 5346ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette mHasPending = true; 5356ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 5366ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 5376ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (mRipple != null) { 5386ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette mRipple.move(x, y); 5396ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 5406ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 5416ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 5426ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette /** 5436ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette * Creates an active hotspot at the specified location. 5446ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette */ 545f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette private void tryBackgroundEnter(boolean focused) { 5466ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (mBackground == null) { 5470671f05fa94a234652c1cf3c6e0c2e123566f76fAlan Viverette final boolean isBounded = isBounded(); 5480671f05fa94a234652c1cf3c6e0c2e123566f76fAlan Viverette mBackground = new RippleBackground(this, mHotspotBounds, isBounded, mForceSoftware); 5496ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 5506ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 551bd5294bcb857a48ad22ddd54b13208ec2903c3f6Alan Viverette mBackground.setup(mState.mMaxRadius, mDensity); 552f92f26fef215897bd302c1c06adbe5d853881b3fAlan Viverette mBackground.enter(focused); 5536ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 5546ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 555fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette private void tryBackgroundExit() { 5566ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (mBackground != null) { 5576ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette // Don't null out the background, we need it to draw! 5586ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette mBackground.exit(); 559ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette } 560ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette } 561ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette 562ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette /** 563fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette * Attempts to start an enter animation for the active hotspot. Fails if 564fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette * there are too many animating ripples. 565ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette */ 566fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette private void tryRippleEnter() { 567fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette if (mExitingRipplesCount >= MAX_RIPPLES) { 5684d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette // This should never happen unless the user is tapping like a maniac 5694d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette // or there is a bug that's preventing ripples from being removed. 5704d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette return; 5714d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 5724d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette 5736ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (mRipple == null) { 5746ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette final float x; 5756ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette final float y; 5766ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (mHasPending) { 5776ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette mHasPending = false; 5786ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette x = mPendingX; 5796ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette y = mPendingY; 5806ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } else { 5816ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette x = mHotspotBounds.exactCenterX(); 5826ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette y = mHotspotBounds.exactCenterY(); 5836ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 5846a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette 585388cd3b69c51a449b8bbb8086dbc429f24783ad1Alan Viverette final boolean isBounded = isBounded(); 586b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded, mForceSoftware); 587ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette } 588ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette 589cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette mRipple.setup(mState.mMaxRadius, mDensity); 590f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette mRipple.enter(false); 591ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette } 592ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette 593fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette /** 594fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette * Attempts to start an exit animation for the active hotspot. Fails if 595fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette * there is no active hotspot. 596fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette */ 597fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette private void tryRippleExit() { 5986ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (mRipple != null) { 599fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette if (mExitingRipples == null) { 600f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette mExitingRipples = new RippleForeground[MAX_RIPPLES]; 601fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette } 602fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette mExitingRipples[mExitingRipplesCount++] = mRipple; 6036ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette mRipple.exit(); 6046ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette mRipple = null; 605d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 606d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 607d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 608fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette /** 609fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette * Cancels and removes the active ripple, all exiting ripples, and the 610fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette * background. Nothing will be drawn after this method is called. 611fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette */ 612c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette private void clearHotspots() { 613b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette if (mRipple != null) { 614f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette mRipple.end(); 615b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette mRipple = null; 616b942b6f15c51c2ff48c59d8f620ee6156d00f67eAlan Viverette mRippleActive = false; 617b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette } 618b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette 619b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette if (mBackground != null) { 620f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette mBackground.end(); 621b6417b8b9492d88ccfbb723decaece1bb9ff0f73Alan Viverette mBackground = null; 622b942b6f15c51c2ff48c59d8f620ee6156d00f67eAlan Viverette mBackgroundActive = false; 623d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 624d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 625cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette cancelExitingRipples(); 626d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 627d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 62861956606818918194a38e045a8e35e7108480e5eAlan Viverette @Override 62961956606818918194a38e045a8e35e7108480e5eAlan Viverette public void setHotspotBounds(int left, int top, int right, int bottom) { 63061956606818918194a38e045a8e35e7108480e5eAlan Viverette mOverrideBounds = true; 63161956606818918194a38e045a8e35e7108480e5eAlan Viverette mHotspotBounds.set(left, top, right, bottom); 632dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette 633dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette onHotspotBoundsChanged(); 634dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette } 635dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette 6367068c39526459c18a020e29c1ebfa6aed54e2d0fAlan Viverette @Override 6377068c39526459c18a020e29c1ebfa6aed54e2d0fAlan Viverette public void getHotspotBounds(Rect outRect) { 6387068c39526459c18a020e29c1ebfa6aed54e2d0fAlan Viverette outRect.set(mHotspotBounds); 6397068c39526459c18a020e29c1ebfa6aed54e2d0fAlan Viverette } 6407068c39526459c18a020e29c1ebfa6aed54e2d0fAlan Viverette 641dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette /** 642dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette * Notifies all the animating ripples that the hotspot bounds have changed. 643dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette */ 644dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette private void onHotspotBoundsChanged() { 645fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette final int count = mExitingRipplesCount; 646f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette final RippleForeground[] ripples = mExitingRipples; 647dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette for (int i = 0; i < count; i++) { 648dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette ripples[i].onHotspotBoundsChanged(); 649dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette } 6506ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 651a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette if (mRipple != null) { 652a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette mRipple.onHotspotBoundsChanged(); 653a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette } 654a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette 6556ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (mBackground != null) { 6566ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette mBackground.onHotspotBoundsChanged(); 6576ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 65861956606818918194a38e045a8e35e7108480e5eAlan Viverette } 65961956606818918194a38e045a8e35e7108480e5eAlan Viverette 6607c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette /** 6617c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette * Populates <code>outline</code> with the first available layer outline, 66277b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik * excluding the mask layer. 6637c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette * 6647c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette * @param outline Outline in which to place the first available layer outline 6657c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette */ 6667c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette @Override 66731ba192dd201df2cad96a8c503f730130ab0d80fChris Craik public void getOutline(@NonNull Outline outline) { 6687c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette final LayerState state = mLayerState; 6697c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette final ChildDrawable[] children = state.mChildren; 6707c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette final int N = state.mNum; 6717c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette for (int i = 0; i < N; i++) { 67231ba192dd201df2cad96a8c503f730130ab0d80fChris Craik if (children[i].mId != R.id.mask) { 67331ba192dd201df2cad96a8c503f730130ab0d80fChris Craik children[i].mDrawable.getOutline(outline); 67431ba192dd201df2cad96a8c503f730130ab0d80fChris Craik if (!outline.isEmpty()) return; 6757c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette } 6767c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette } 6777c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette } 6787c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette 679cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette /** 680cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette * Optimized for drawing ripples with a mask layer and optional content. 681cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette */ 682d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette @Override 6837c0517272ba2d97084739a14fea78641b265eb5dAlan Viverette public void draw(@NonNull Canvas canvas) { 684f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette pruneRipples(); 685f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette 686cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette // Clip to the dirty bounds, which will be the drawable bounds if we 687cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette // have a mask or content and the ripple bounds if we're projecting. 688be0dd99bac781d1dd73cb67f33bcd931e3693af5Alan Viverette final Rect bounds = getDirtyBounds(); 689a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG); 690a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette canvas.clipRect(bounds); 6914d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette 6926dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette drawContent(canvas); 6936dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette drawBackgroundAndRipples(canvas); 6946dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 6956dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette canvas.restoreToCount(saveCount); 6966dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 6976dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 6986dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette @Override 6996dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette public void invalidateSelf() { 70015ce834e52806378d6ab2b90f573bae14cb3fd4bAlan Viverette invalidateSelf(true); 70115ce834e52806378d6ab2b90f573bae14cb3fd4bAlan Viverette } 70215ce834e52806378d6ab2b90f573bae14cb3fd4bAlan Viverette 70315ce834e52806378d6ab2b90f573bae14cb3fd4bAlan Viverette void invalidateSelf(boolean invalidateMask) { 7046dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette super.invalidateSelf(); 7056dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 70615ce834e52806378d6ab2b90f573bae14cb3fd4bAlan Viverette if (invalidateMask) { 70715ce834e52806378d6ab2b90f573bae14cb3fd4bAlan Viverette // Force the mask to update on the next draw(). 70815ce834e52806378d6ab2b90f573bae14cb3fd4bAlan Viverette mHasValidMask = false; 70915ce834e52806378d6ab2b90f573bae14cb3fd4bAlan Viverette } 71015ce834e52806378d6ab2b90f573bae14cb3fd4bAlan Viverette 7116dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 7126dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 713f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette private void pruneRipples() { 714f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette int remaining = 0; 715f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette 716f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette // Move remaining entries into pruned spaces. 717f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette final RippleForeground[] ripples = mExitingRipples; 718f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette final int count = mExitingRipplesCount; 719f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette for (int i = 0; i < count; i++) { 720f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette if (!ripples[i].hasFinishedExit()) { 721f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette ripples[remaining++] = ripples[i]; 722f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette } 723f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette } 724f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette 725f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette // Null out the remaining entries. 726f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette for (int i = remaining; i < count; i++) { 727f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette ripples[i] = null; 728f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette } 729f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette 730f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette mExitingRipplesCount = remaining; 731f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette } 732f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette 7336dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette /** 7346dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette * @return whether we need to use a mask 7356dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette */ 7366dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette private void updateMaskShaderIfNeeded() { 7376dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (mHasValidMask) { 7386dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette return; 7396dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 7406dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 7416dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette final int maskType = getMaskType(); 7426dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (maskType == MASK_UNKNOWN) { 7436dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette return; 7446dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 7456dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 7466dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mHasValidMask = true; 7476dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 748b74155cf01f959fc9b7909de5a22806ad519f7c9Alan Viverette final Rect bounds = getBounds(); 749b74155cf01f959fc9b7909de5a22806ad519f7c9Alan Viverette if (maskType == MASK_NONE || bounds.isEmpty()) { 7506dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (mMaskBuffer != null) { 7516dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskBuffer.recycle(); 7526dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskBuffer = null; 7536dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskShader = null; 7546dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskCanvas = null; 7556ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 7566dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskMatrix = null; 7576dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskColorFilter = null; 7586dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette return; 7596ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 7604d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette 7616dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette // Ensure we have a correctly-sized buffer. 7626dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (mMaskBuffer == null 7636dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette || mMaskBuffer.getWidth() != bounds.width() 7646dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette || mMaskBuffer.getHeight() != bounds.height()) { 7656dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (mMaskBuffer != null) { 7666dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskBuffer.recycle(); 7676dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 7686dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 7696dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskBuffer = Bitmap.createBitmap( 7706dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette bounds.width(), bounds.height(), Bitmap.Config.ALPHA_8); 7716dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskShader = new BitmapShader(mMaskBuffer, 7726dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 7736dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskCanvas = new Canvas(mMaskBuffer); 7746dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } else { 7756dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskBuffer.eraseColor(Color.TRANSPARENT); 7766dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 7776dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 7786dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (mMaskMatrix == null) { 7796dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskMatrix = new Matrix(); 7806dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } else { 7816dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskMatrix.reset(); 7826dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 7836dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 7846dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (mMaskColorFilter == null) { 7856dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_IN); 7866dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 7876dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 788bac9769146af07753728e5c6b3a30eae3076aed0Alan Viverette // Draw the appropriate mask anchored to (0,0). 789bac9769146af07753728e5c6b3a30eae3076aed0Alan Viverette final int left = bounds.left; 790bac9769146af07753728e5c6b3a30eae3076aed0Alan Viverette final int top = bounds.top; 791bac9769146af07753728e5c6b3a30eae3076aed0Alan Viverette mMaskCanvas.translate(-left, -top); 7926dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (maskType == MASK_EXPLICIT) { 7936dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette drawMask(mMaskCanvas); 7946dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } else if (maskType == MASK_CONTENT) { 7956dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette drawContent(mMaskCanvas); 7966dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 797bac9769146af07753728e5c6b3a30eae3076aed0Alan Viverette mMaskCanvas.translate(left, top); 7986dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 7996dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 8006dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette private int getMaskType() { 8016dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (mRipple == null && mExitingRipplesCount <= 0 802f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette && (mBackground == null || !mBackground.isVisible())) { 8036dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette // We might need a mask later. 8046dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette return MASK_UNKNOWN; 8056dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 8066dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 8076dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (mMask != null) { 8086dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (mMask.getOpacity() == PixelFormat.OPAQUE) { 8096dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette // Clipping handles opaque explicit masks. 8106dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette return MASK_NONE; 8116dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } else { 8126dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette return MASK_EXPLICIT; 8136dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 8146dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 8156dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 8166dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette // Check for non-opaque, non-mask content. 8176dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette final ChildDrawable[] array = mLayerState.mChildren; 8186dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette final int count = mLayerState.mNum; 8196dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette for (int i = 0; i < count; i++) { 8206dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) { 8216dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette return MASK_CONTENT; 8226dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 8236dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 8246dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 8256dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette // Clipping handles opaque content. 8266dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette return MASK_NONE; 8274d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 8284d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette 8296dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette private void drawContent(Canvas canvas) { 8306dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette // Draw everything except the mask. 831a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette final ChildDrawable[] array = mLayerState.mChildren; 832a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette final int count = mLayerState.mNum; 8334d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette for (int i = 0; i < count; i++) { 834d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette if (array[i].mId != R.id.mask) { 835d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette array[i].mDrawable.draw(canvas); 836d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette } 837d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette } 838d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette } 839d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette 8406dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette private void drawBackgroundAndRipples(Canvas canvas) { 841f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette final RippleForeground active = mRipple; 8426dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette final RippleBackground background = mBackground; 8436dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette final int count = mExitingRipplesCount; 844f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette if (active == null && count <= 0 && (background == null || !background.isVisible())) { 8456dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette // Move along, nothing to draw here. 8466dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette return; 847cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette } 848323596de4efc46149719b41de5a9f668d7f3f784Alan Viverette 849cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette final float x = mHotspotBounds.exactCenterX(); 850cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette final float y = mHotspotBounds.exactCenterY(); 851cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette canvas.translate(x, y); 8526ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 8536dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette updateMaskShaderIfNeeded(); 8546dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 8556dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette // Position the shader to account for canvas translation. 8566dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (mMaskShader != null) { 857f0d69b9e33e32b60bf953fc775b7843825e99036Alan Viverette final Rect bounds = getBounds(); 858f0d69b9e33e32b60bf953fc775b7843825e99036Alan Viverette mMaskMatrix.setTranslate(bounds.left - x, bounds.top - y); 8596dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskShader.setLocalMatrix(mMaskMatrix); 8606dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 8616ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 862cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette // Grab the color for the current state and cut the alpha channel in 863cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette // half so that the ripple and background together yield full alpha. 864cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette final int color = mState.mColor.getColorForState(getState(), Color.BLACK); 8656dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette final int halfAlpha = (Color.alpha(color) / 2) << 24; 8666dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette final Paint p = getRipplePaint(); 8676dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 8686dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette if (mMaskColorFilter != null) { 8696dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette // The ripple timing depends on the paint's alpha value, so we need 8706dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette // to push just the alpha channel into the paint and let the filter 8716dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette // handle the full-alpha color. 8726dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette final int fullAlphaColor = color | (0xFF << 24); 8736dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette mMaskColorFilter.setColor(fullAlphaColor); 8746dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette 8756dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette p.setColor(halfAlpha); 8766dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette p.setColorFilter(mMaskColorFilter); 8776dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette p.setShader(mMaskShader); 8786dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } else { 8796dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette final int halfAlphaColor = (color & 0xFFFFFF) | halfAlpha; 8806dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette p.setColor(halfAlphaColor); 8816dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette p.setColorFilter(null); 8826dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette p.setShader(null); 8836dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette } 8846ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 885f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette if (background != null && background.isVisible()) { 886cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette background.draw(canvas, p); 887cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette } 888d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette 889cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette if (count > 0) { 890f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette final RippleForeground[] ripples = mExitingRipples; 891cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette for (int i = 0; i < count; i++) { 892cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette ripples[i].draw(canvas, p); 893d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette } 894ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette } 895ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette 896cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette if (active != null) { 897cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette active.draw(canvas, p); 898ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette } 899ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette 900cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette canvas.translate(-x, -y); 901ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette } 902ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette 9036dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette private void drawMask(Canvas canvas) { 9041b6e856e6f9dab4464e3c556b2f68527439fc329Alan Viverette mMask.draw(canvas); 9054d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 9064d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette 907a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette private Paint getRipplePaint() { 908a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette if (mRipplePaint == null) { 909a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette mRipplePaint = new Paint(); 910a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette mRipplePaint.setAntiAlias(true); 911cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette mRipplePaint.setStyle(Paint.Style.FILL); 912a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette } 913a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette return mRipplePaint; 914a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette } 915a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette 916d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette @Override 917d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette public Rect getDirtyBounds() { 918388cd3b69c51a449b8bbb8086dbc429f24783ad1Alan Viverette if (!isBounded()) { 919e3c433aa457138425e514494e4d06590076a1d07Alan Viverette final Rect drawingBounds = mDrawingBounds; 920e3c433aa457138425e514494e4d06590076a1d07Alan Viverette final Rect dirtyBounds = mDirtyBounds; 921e3c433aa457138425e514494e4d06590076a1d07Alan Viverette dirtyBounds.set(drawingBounds); 922e3c433aa457138425e514494e4d06590076a1d07Alan Viverette drawingBounds.setEmpty(); 923e3c433aa457138425e514494e4d06590076a1d07Alan Viverette 924e3c433aa457138425e514494e4d06590076a1d07Alan Viverette final int cX = (int) mHotspotBounds.exactCenterX(); 925e3c433aa457138425e514494e4d06590076a1d07Alan Viverette final int cY = (int) mHotspotBounds.exactCenterY(); 926e3c433aa457138425e514494e4d06590076a1d07Alan Viverette final Rect rippleBounds = mTempRect; 927fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette 928f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette final RippleForeground[] activeRipples = mExitingRipples; 929fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette final int N = mExitingRipplesCount; 930e3c433aa457138425e514494e4d06590076a1d07Alan Viverette for (int i = 0; i < N; i++) { 931e3c433aa457138425e514494e4d06590076a1d07Alan Viverette activeRipples[i].getBounds(rippleBounds); 932e3c433aa457138425e514494e4d06590076a1d07Alan Viverette rippleBounds.offset(cX, cY); 933e3c433aa457138425e514494e4d06590076a1d07Alan Viverette drawingBounds.union(rippleBounds); 934e3c433aa457138425e514494e4d06590076a1d07Alan Viverette } 935d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 9366ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette final RippleBackground background = mBackground; 9376ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette if (background != null) { 9386ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette background.getBounds(rippleBounds); 9396ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette rippleBounds.offset(cX, cY); 9406ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette drawingBounds.union(rippleBounds); 9416ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette } 9426ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette 943e3c433aa457138425e514494e4d06590076a1d07Alan Viverette dirtyBounds.union(drawingBounds); 944e3c433aa457138425e514494e4d06590076a1d07Alan Viverette dirtyBounds.union(super.getDirtyBounds()); 945e3c433aa457138425e514494e4d06590076a1d07Alan Viverette return dirtyBounds; 946e3c433aa457138425e514494e4d06590076a1d07Alan Viverette } else { 947e3c433aa457138425e514494e4d06590076a1d07Alan Viverette return getBounds(); 948e3c433aa457138425e514494e4d06590076a1d07Alan Viverette } 949d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 950d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 951b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi /** 952b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi * Sets whether to disable RenderThread animations for this ripple. 953b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi * 954b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi * @param forceSoftware true if RenderThread animations should be disabled, false otherwise 955b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi * @hide 956b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi */ 957b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi public void setForceSoftware(boolean forceSoftware) { 958b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi mForceSoftware = forceSoftware; 959b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi } 960b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi 9617f610fed107b158c144dca1b20a44ee91eb8c934Alan Viverette @Override 9627f610fed107b158c144dca1b20a44ee91eb8c934Alan Viverette public ConstantState getConstantState() { 9637f610fed107b158c144dca1b20a44ee91eb8c934Alan Viverette return mState; 9647f610fed107b158c144dca1b20a44ee91eb8c934Alan Viverette } 9657f610fed107b158c144dca1b20a44ee91eb8c934Alan Viverette 9665004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette @Override 9675004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette public Drawable mutate() { 9685004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette super.mutate(); 9695004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette 9705004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette // LayerDrawable creates a new state using createConstantState, so 9715004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette // this should always be a safe cast. 9725004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette mState = (RippleState) mLayerState; 973ebc9f2e773c3ebb4d4af3025fc6770844cc8fdefAlan Viverette 974ebc9f2e773c3ebb4d4af3025fc6770844cc8fdefAlan Viverette // The locally cached drawable may have changed. 975ebc9f2e773c3ebb4d4af3025fc6770844cc8fdefAlan Viverette mMask = findDrawableByLayerId(R.id.mask); 976ebc9f2e773c3ebb4d4af3025fc6770844cc8fdefAlan Viverette 9775004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette return this; 9785004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette } 9795004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette 9805004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette @Override 9815004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette RippleState createConstantState(LayerState state, Resources res) { 9825004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette return new RippleState(state, this, res); 9835004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette } 9845004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette 985c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette static class RippleState extends LayerState { 98647bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette int[] mTouchThemeAttrs; 98734a14f967ab6c88829c9a36ce6e909c47b3ee398Alan Viverette ColorStateList mColor = ColorStateList.valueOf(Color.MAGENTA); 9884d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette int mMaxRadius = RADIUS_AUTO; 989d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 9905004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette public RippleState(LayerState orig, RippleDrawable owner, Resources res) { 99147bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette super(orig, owner, res); 992ba346f9d8d681c3c8166609382eb882e538b9b05Alan Viverette 9935004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette if (orig != null && orig instanceof RippleState) { 9945004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette final RippleState origs = (RippleState) orig; 9955004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette mTouchThemeAttrs = origs.mTouchThemeAttrs; 9965004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette mColor = origs.mColor; 9975004032ebc2aee97c5884b7f91cc33d2f98ae8b5Alan Viverette mMaxRadius = origs.mMaxRadius; 9980cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette 9990cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette if (origs.mDensity != mDensity) { 10000cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette applyDensityScaling(orig.mDensity, mDensity); 10010cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette } 10020cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette } 10030cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette } 10040cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette 10050cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette @Override 10060cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette protected void onDensityChanged(int sourceDensity, int targetDensity) { 10070cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette super.onDensityChanged(sourceDensity, targetDensity); 10080cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette 10090cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette applyDensityScaling(sourceDensity, targetDensity); 10100cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette } 10110cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette 10120cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette private void applyDensityScaling(int sourceDensity, int targetDensity) { 10130cacad7023ad30ad886ef85751e9e39b10f846f9Alan Viverette if (mMaxRadius != RADIUS_AUTO) { 1014ce52037e0ae0c380f5b834fb3dad105bfaf5e374Alan Viverette mMaxRadius = Drawable.scaleFromDensity( 1015ce52037e0ae0c380f5b834fb3dad105bfaf5e374Alan Viverette mMaxRadius, sourceDensity, targetDensity, true); 1016d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 1017d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 1018d1ca75bffef070f62ab70ed514f7f91824f73cbcAlan Viverette 1019d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette @Override 102052b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette public boolean canApplyTheme() { 102145c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette return mTouchThemeAttrs != null 102245c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette || (mColor != null && mColor.canApplyTheme()) 102345c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette || super.canApplyTheme(); 102452b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette } 102552b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette 102652b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette @Override 1027d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette public Drawable newDrawable() { 102817cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette return new RippleDrawable(this, null); 1029d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 1030d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette 1031d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette @Override 1032d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette public Drawable newDrawable(Resources res) { 103317cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette return new RippleDrawable(this, res); 1034d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette } 1035e0f95f39c5a669a48ee3ebb8dc45bf2d7ee940f1Alan Viverette 1036e0f95f39c5a669a48ee3ebb8dc45bf2d7ee940f1Alan Viverette @Override 1037ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette public @Config int getChangingConfigurations() { 1038e0f95f39c5a669a48ee3ebb8dc45bf2d7ee940f1Alan Viverette return super.getChangingConfigurations() 1039e0f95f39c5a669a48ee3ebb8dc45bf2d7ee940f1Alan Viverette | (mColor != null ? mColor.getChangingConfigurations() : 0); 1040e0f95f39c5a669a48ee3ebb8dc45bf2d7ee940f1Alan Viverette } 10414d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette } 1042d30688f0a0a75743df247831688efcbe2fa62cc4Alan Viverette 104317cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette private RippleDrawable(RippleState state, Resources res) { 104417cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette mState = new RippleState(state, this, res); 104517cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette mLayerState = mState; 1046ce52037e0ae0c380f5b834fb3dad105bfaf5e374Alan Viverette mDensity = Drawable.resolveDensity(res, mState.mDensity); 104747bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette 104817cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette if (mState.mNum > 0) { 104917cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette ensurePadding(); 105006ff2af68aa1041eeb26778e994e0fe196bf8b1eAlan Viverette refreshPadding(); 105147bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette } 105247bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette 105345c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette updateLocalState(); 1054b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette } 1055b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette 105645c4bbbbce6bbad50a033efcba7948a23f1f117aAlan Viverette private void updateLocalState() { 1057b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette // Initialize from constant state. 1058b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette mMask = findDrawableByLayerId(R.id.mask); 105952b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette } 1060d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette} 1061