Add ripple to switches, radio buttons, check boxes, seek bars

BUG: 14231772
Change-Id: Ie40eac9f68815294460175965a999dd75f4144b5
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 438a9da..8a30def 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -29,6 +29,8 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+import com.android.internal.R;
+
 public abstract class AbsSeekBar extends ProgressBar {
     private Drawable mThumb;
     private int mThumbOffset;
@@ -289,28 +291,39 @@
      */
     private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
         int available = w - mPaddingLeft - mPaddingRight;
-        int thumbWidth = thumb.getIntrinsicWidth();
-        int thumbHeight = thumb.getIntrinsicHeight();
+        final int thumbWidth = thumb.getIntrinsicWidth();
+        final int thumbHeight = thumb.getIntrinsicHeight();
         available -= thumbWidth;
 
         // The extra space for the thumb to move on the track
         available += mThumbOffset * 2;
 
-        int thumbPos = (int) (scale * available + 0.5f);
+        final int thumbPos = (int) (scale * available + 0.5f);
 
-        int topBound, bottomBound;
+        final int top, bottom;
         if (gap == Integer.MIN_VALUE) {
-            Rect oldBounds = thumb.getBounds();
-            topBound = oldBounds.top;
-            bottomBound = oldBounds.bottom;
+            final Rect oldBounds = thumb.getBounds();
+            top = oldBounds.top;
+            bottom = oldBounds.bottom;
         } else {
-            topBound = gap;
-            bottomBound = gap + thumbHeight;
+            top = gap;
+            bottom = gap + thumbHeight;
         }
-        
-        // Canvas will be translated, so 0,0 is where we start drawing
+
         final int left = (isLayoutRtl() && mMirrorForRtl) ? available - thumbPos : thumbPos;
-        thumb.setBounds(left, topBound, left + thumbWidth, bottomBound);
+        final int right = left + thumbWidth;
+
+        final Drawable background = getBackground();
+        if (background.supportsHotspots()) {
+            final Rect bounds = mThumb.getBounds();
+            final int offsetX = mPaddingLeft - mThumbOffset;
+            final int offsetY = mPaddingTop;
+            background.setHotspotBounds(left + offsetX, bounds.top + offsetY,
+                    right + offsetX, bounds.bottom + offsetY);
+        }
+
+        // Canvas will be translated, so 0,0 is where we start drawing
+        thumb.setBounds(left, top, right, bottom);
     }
 
     /**
@@ -328,6 +341,7 @@
     @Override
     protected synchronized void onDraw(Canvas canvas) {
         super.onDraw(canvas);
+
         if (mThumb != null) {
             canvas.save();
             // Translate the padding. For the x, we need to allow the thumb to
@@ -424,10 +438,24 @@
         return true;
     }
 
+    private void setHotspot(int id, float x, float y) {
+        final Drawable bg = getBackground();
+        if (bg != null && bg.supportsHotspots()) {
+            bg.setHotspot(id, x, y);
+        }
+    }
+
+    private void clearHotspot(int id) {
+        final Drawable bg = getBackground();
+        if (bg != null && bg.supportsHotspots()) {
+            bg.removeHotspot(id);
+        }
+    }
+
     private void trackTouchEvent(MotionEvent event) {
         final int width = getWidth();
         final int available = width - mPaddingLeft - mPaddingRight;
-        int x = (int)event.getX();
+        final int x = (int) event.getX();
         float scale;
         float progress = 0;
         if (isLayoutRtl() && mMirrorForRtl) {
@@ -451,7 +479,8 @@
         }
         final int max = getMax();
         progress += scale * max;
-        
+
+        setHotspot(R.attr.state_pressed, x, (int) event.getY());
         setProgress((int) progress, true);
     }
 
@@ -477,6 +506,7 @@
      * canceled.
      */
     void onStopTrackingTouch() {
+        clearHotspot(R.attr.state_pressed);
         mIsDragging = false;
     }
 
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 4298545..ddc8b05 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -261,15 +261,13 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
         final Drawable buttonDrawable = mButtonDrawable;
         if (buttonDrawable != null) {
             final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
             final int drawableHeight = buttonDrawable.getIntrinsicHeight();
             final int drawableWidth = buttonDrawable.getIntrinsicWidth();
 
-            int top = 0;
+            final int top;
             switch (verticalGravity) {
                 case Gravity.BOTTOM:
                     top = getHeight() - drawableHeight;
@@ -277,12 +275,24 @@
                 case Gravity.CENTER_VERTICAL:
                     top = (getHeight() - drawableHeight) / 2;
                     break;
+                default:
+                    top = 0;
             }
-            int bottom = top + drawableHeight;
-            int left = isLayoutRtl() ? getWidth() - drawableWidth : 0;
-            int right = isLayoutRtl() ? getWidth() : drawableWidth;
+            final int bottom = top + drawableHeight;
+            final int left = isLayoutRtl() ? getWidth() - drawableWidth : 0;
+            final int right = isLayoutRtl() ? getWidth() : drawableWidth;
 
             buttonDrawable.setBounds(left, top, right, bottom);
+
+            final Drawable background = getBackground();
+            if (background.supportsHotspots()) {
+                background.setHotspotBounds(left, top, right, bottom);
+            }
+        }
+
+        super.onDraw(canvas);
+
+        if (buttonDrawable != null) {
             buttonDrawable.draw(canvas);
         }
     }
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 3d23e4d..d013e7b 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -773,8 +773,6 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
         final Rect tempRect = mTempRect;
         final Drawable trackDrawable = mTrackDrawable;
         final Drawable thumbDrawable = mThumbDrawable;
@@ -785,16 +783,12 @@
         final int switchRight = mSwitchRight;
         final int switchBottom = mSwitchBottom;
         trackDrawable.setBounds(switchLeft, switchTop, switchRight, switchBottom);
-        trackDrawable.draw(canvas);
-
-        final int saveCount = canvas.save();
-
         trackDrawable.getPadding(tempRect);
+
         final int switchInnerLeft = switchLeft + tempRect.left;
         final int switchInnerTop = switchTop + tempRect.top;
         final int switchInnerRight = switchRight - tempRect.right;
         final int switchInnerBottom = switchBottom - tempRect.bottom;
-        canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
 
         // Relies on mTempRect, MUST be called first!
         final int thumbPos = getThumbOffset();
@@ -803,6 +797,18 @@
         int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
         int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
         thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
+
+        final Drawable background = getBackground();
+        if (background.supportsHotspots()) {
+            background.setHotspotBounds(thumbLeft, switchTop, thumbRight, switchBottom);
+        }
+
+        super.onDraw(canvas);
+
+        trackDrawable.draw(canvas);
+
+        final int saveCount = canvas.save();
+        canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
         thumbDrawable.draw(canvas);
 
         final int drawableState[] = getDrawableState();
diff --git a/core/res/res/drawable/btn_radio_quantum.xml b/core/res/res/drawable/btn_radio_quantum.xml
index 152e3d3..0f9ebce 100644
--- a/core/res/res/drawable/btn_radio_quantum.xml
+++ b/core/res/res/drawable/btn_radio_quantum.xml
@@ -15,18 +15,20 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_checked="true" android:state_enabled="true" android:state_pressed="true">
-        <bitmap android:src="@drawable/btn_radio_on_pressed_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
+    <item android:state_enabled="false" android:state_checked="true">
+        <bitmap android:src="@drawable/btn_radio_on_qntm_alpha"
+            android:tint="?attr/colorControlNormal"
+            android:alpha="?attr/disabledAlpha" />
+    </item>
+    <item android:state_enabled="false">
+        <bitmap android:src="@drawable/btn_radio_off_qntm_alpha"
+            android:tint="?attr/colorControlNormal"
+            android:alpha="?attr/disabledAlpha" />
     </item>
     <item android:state_checked="true">
         <bitmap android:src="@drawable/btn_radio_on_qntm_alpha"
             android:tint="?attr/colorControlActivated" />
     </item>
-    <item android:state_enabled="true" android:state_pressed="true">
-        <bitmap android:src="@drawable/btn_radio_off_pressed_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
-    </item>
     <item>
         <bitmap android:src="@drawable/btn_radio_off_qntm_alpha"
             android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/scrubber_control_selector_quantum.xml b/core/res/res/drawable/scrubber_control_selector_quantum.xml
index e34f64a..ef3af0c 100644
--- a/core/res/res/drawable/scrubber_control_selector_quantum.xml
+++ b/core/res/res/drawable/scrubber_control_selector_quantum.xml
@@ -19,10 +19,6 @@
         <bitmap android:src="@drawable/scrubber_control_off_qntm_alpha"
             android:tint="?attr/colorControlNormal" />
     </item>
-    <item android:state_pressed="true">
-        <bitmap android:src="@drawable/scrubber_control_on_pressed_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
-    </item>
     <item>
         <bitmap android:src="@drawable/scrubber_control_on_qntm_alpha"
             android:tint="?attr/colorControlActivated" />
diff --git a/core/res/res/drawable/switch_inner_quantum.xml b/core/res/res/drawable/switch_inner_quantum.xml
index 915377e..856895e 100644
--- a/core/res/res/drawable/switch_inner_quantum.xml
+++ b/core/res/res/drawable/switch_inner_quantum.xml
@@ -15,18 +15,20 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_checked="true" android:state_pressed="true">
+    <item android:state_enabled="false" android:state_checked="true">
         <nine-patch android:src="@drawable/switch_on_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
+            android:tint="?attr/colorControlNormal"
+            android:alpha="?attr/disabledAlpha" />
+    </item>
+    <item android:state_enabled="false">
+        <nine-patch android:src="@drawable/switch_off_qntm_alpha"
+            android:tint="?attr/colorControlNormal"
+            android:alpha="?attr/disabledAlpha" />
     </item>
     <item android:state_checked="true">
         <nine-patch android:src="@drawable/switch_on_qntm_alpha"
             android:tint="?attr/colorControlActivated" />
     </item>
-    <item android:state_pressed="true">
-        <nine-patch android:src="@drawable/switch_off_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
-    </item>
     <item>
         <nine-patch android:src="@drawable/switch_off_qntm_alpha"
             android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index bdc7ad0..0cbb655 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -485,11 +485,18 @@
     </style>
 
     <style name="Widget.Quantum.CompoundButton" parent="Widget.CompoundButton"/>
-    <style name="Widget.Quantum.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox"/>
-    <style name="Widget.Quantum.CompoundButton.RadioButton" parent="Widget.CompoundButton.RadioButton"/>
+
+    <style name="Widget.Quantum.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">
+        <item name="background">?attr/selectableItemBackground</item>
+    </style>
+
+    <style name="Widget.Quantum.CompoundButton.RadioButton" parent="Widget.CompoundButton.RadioButton">
+        <item name="background">?attr/selectableItemBackground</item>
+    </style>
 
     <style name="Widget.Quantum.CompoundButton.Star" parent="Widget.CompoundButton.Star">
         <item name="button">@drawable/btn_star_quantum</item>
+        <item name="background">?attr/selectableItemBackground</item>
     </style>
 
     <style name="Widget.Quantum.CompoundButton.Switch">
@@ -501,6 +508,7 @@
         <item name="thumbTextPadding">12dip</item>
         <item name="switchMinWidth">72dip</item>
         <item name="switchPadding">16dip</item>
+        <item name="background">?attr/selectableItemBackground</item>
     </style>
 
     <style name="Widget.Quantum.EditText" parent="Widget.EditText"/>
@@ -619,6 +627,7 @@
         <item name="paddingStart">16dip</item>
         <item name="paddingEnd">16dip</item>
         <item name="mirrorForRtl">true</item>
+        <item name="background">?attr/selectableItemBackground</item>
     </style>
 
     <style name="Widget.Quantum.RatingBar" parent="Widget.RatingBar">
@@ -846,7 +855,15 @@
         <item name="popupBackground">?attr/colorBackground</item>
     </style>
 
+    <style name="Widget.Quantum.Light.CompoundButton" parent="Widget.Quantum.CompoundButton"/>
     <style name="Widget.Quantum.Light.CompoundButton.CheckBox" parent="Widget.Quantum.CompoundButton.CheckBox"/>
+    <style name="Widget.Quantum.Light.CompoundButton.RadioButton" parent="Widget.Quantum.CompoundButton.RadioButton"/>
+    <style name="Widget.Quantum.Light.CompoundButton.Star" parent="Widget.Quantum.CompoundButton.Star"/>
+
+    <style name="Widget.Quantum.Light.CompoundButton.Switch" parent="Widget.Quantum.CompoundButton.Switch">
+        <item name="switchTextAppearance">@style/TextAppearance.Quantum.Light.Widget.Switch</item>
+    </style>
+
     <style name="Widget.Quantum.Light.ListView.DropDown" parent="Widget.Quantum.ListView.DropDown"/>
     <style name="Widget.Quantum.Light.EditText" parent="Widget.Quantum.EditText"/>
     <style name="Widget.Quantum.Light.ExpandableListView" parent="Widget.Quantum.ExpandableListView"/>
@@ -916,7 +933,6 @@
         <item name="maxHeight">16dip</item>
     </style>
 
-    <style name="Widget.Quantum.Light.CompoundButton.RadioButton" parent="Widget.Quantum.CompoundButton.RadioButton"/>
     <style name="Widget.Quantum.Light.ScrollView" parent="Widget.Quantum.ScrollView"/>
     <style name="Widget.Quantum.Light.HorizontalScrollView" parent="Widget.Quantum.HorizontalScrollView"/>
 
@@ -932,7 +948,6 @@
 
     <style name="Widget.Quantum.Light.Spinner.DropDown" parent="Widget.Quantum.Spinner.DropDown"/>
     <style name="Widget.Quantum.Light.Spinner.DropDown.ActionBar" parent="Widget.Quantum.Spinner.DropDown.ActionBar"/>
-    <style name="Widget.Quantum.Light.CompoundButton.Star" parent="Widget.Quantum.CompoundButton.Star"/>
     <style name="Widget.Quantum.Light.TabWidget" parent="Widget.Quantum.TabWidget"/>
     <style name="Widget.Quantum.Light.WebTextView" parent="Widget.Quantum.WebTextView"/>
     <style name="Widget.Quantum.Light.WebView" parent="Widget.Quantum.WebView"/>
@@ -989,10 +1004,6 @@
         <item name="backgroundSplit">?attr/colorPrimary</item>
     </style>
 
-    <style name="Widget.Quantum.Light.CompoundButton.Switch" parent="Widget.Quantum.CompoundButton.Switch">
-        <item name="switchTextAppearance">@style/TextAppearance.Quantum.Light.Widget.Switch</item>
-    </style>
-
     <style name="Widget.Quantum.Light.FastScroll" parent="Widget.Quantum.FastScroll"/>
 
     <style name="Widget.Quantum.Light.MediaRouteButton" parent="Widget.Quantum.MediaRouteButton">
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 21cd5db..74d1219 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -518,9 +518,16 @@
     public void removeHotspot(int key) {}
 
     /**
-     * Removes all hotspots from the drawable.
+     * Immediately removes all hotspots from the drawable.
      */
     public void clearHotspots() {}
+    
+    /**
+     * Sets the bounds to which hotspots are constrained.
+     *
+     * @hide until we finalize these APIs
+     */
+    public void setHotspotBounds(int left, int top, int right, int bottom) {}
 
     /**
      * Whether this drawable requests projection.
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index b366987..639d719 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -290,6 +290,26 @@
 
         return false;
     }
+    
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isProjected() {
+        if (super.isProjected()) {
+            return true;
+        }
+
+        final ChildDrawable[] layers = mLayerState.mChildren;
+        final int N = mLayerState.mNum;
+        for (int i = 0; i < N; i++) {
+            if (layers[i].mDrawable.isProjected()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 
     /**
      * Add a new layer to this drawable. The new layer is identified by an id.
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 796da50..e3f57e9 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -139,8 +139,8 @@
     public boolean draw(Canvas c, Paint p) {
         final Rect bounds = mBounds;
         final Rect padding = mPadding;
-        final float dX = Math.max(mX, bounds.right - mX);
-        final float dY = Math.max(mY, bounds.bottom - mY);
+        final float dX = Math.max(mX - bounds.left, bounds.right - mX);
+        final float dY = Math.max(mY - bounds.top, bounds.bottom - mY);
         final int maxRadius = (int) Math.ceil(Math.sqrt(dX * dX + dY * dY));
 
         final float enterState = mEnterState;
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index 813d755c..1958cfe 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -53,6 +53,9 @@
     private final Rect mTempRect = new Rect();
     private final Rect mPaddingRect = new Rect();
 
+    /** Current ripple effect bounds, used to constrain ripple effects. */
+    private final Rect mHotspotBounds = new Rect();
+
     /** Current drawing bounds, used to compute dirty region. */
     private final Rect mDrawingBounds = new Rect();
 
@@ -83,6 +86,9 @@
     /** Whether the animation runnable has been posted. */
     private boolean mAnimating;
 
+    /** Whether bounds are being overridden. */
+    private boolean mOverrideBounds;
+
     TouchFeedbackDrawable() {
         this(new TouchFeedbackState(null, null, null), null, null);
     }
@@ -114,9 +120,22 @@
     @Override
     protected void onBoundsChange(Rect bounds) {
         super.onBoundsChange(bounds);
+        
+        if (!mOverrideBounds) {
+            mHotspotBounds.set(bounds);
+        }
 
+        onHotspotBoundsChange();
+    }
+
+    private void onHotspotBoundsChange() {
+        final int x = mHotspotBounds.centerX();
+        final int y = mHotspotBounds.centerY();
         final int N = mActiveRipplesCount;
         for (int i = 0; i < N; i++) {
+            if (mState.mPinned) {
+                mActiveRipples[i].move(x, y);
+            }
             mActiveRipples[i].onBoundsChanged();
         }
     }
@@ -292,10 +311,10 @@
 
         final Ripple ripple = mTouchedRipples.get(id);
         if (ripple == null) {
-            final Rect bounds = getBounds();
             final Rect padding = mPaddingRect;
             getPadding(padding);
 
+            final Rect bounds = mHotspotBounds;
             if (mState.mPinned) {
                 x = bounds.exactCenterX();
                 y = bounds.exactCenterY();
@@ -308,7 +327,12 @@
 
             mActiveRipples[mActiveRipplesCount++] = newRipple;
             mTouchedRipples.put(id, newRipple);
-        } else if (!mState.mPinned) {
+        } else if (mState.mPinned) {
+            final Rect bounds = mHotspotBounds;
+            x = bounds.exactCenterX();
+            y = bounds.exactCenterY();
+            ripple.move(x, y);
+        } else {
             ripple.move(x, y);
         }
 
@@ -338,6 +362,7 @@
 
         final int n = mTouchedRipples.size();
         for (int i = 0; i < n; i++) {
+            // TODO: Use a fast exit, maybe just fade out?
             mTouchedRipples.valueAt(i).animate().exit();
         }
 
@@ -348,6 +373,16 @@
     }
 
     /**
+     * @hide
+     */
+    @Override
+    public void setHotspotBounds(int left, int top, int right, int bottom) {
+        mOverrideBounds = true;
+        mHotspotBounds.set(left, top, right, bottom);
+        onHotspotBoundsChange();
+    }
+
+    /**
      * Schedules the next animation, if necessary.
      */
     private void scheduleAnimation() {