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