am 5a50a8bc: am e615da7e: am f1792c42: Merge "Fix hotspot coordinate propagation in ViewGroup and AbsListView" into lmp-mr1-dev

* commit '5a50a8bcabdb2593b48b06854a7455318399b6d1':
  Fix hotspot coordinate propagation in ViewGroup and AbsListView
diff --git a/api/current.txt b/api/current.txt
index b0517689..4796ccd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -33573,6 +33573,7 @@
     method public void dispatchDisplayHint(int);
     method public boolean dispatchDragEvent(android.view.DragEvent);
     method protected void dispatchDraw(android.graphics.Canvas);
+    method public void dispatchDrawableHotspotChanged(float, float);
     method protected boolean dispatchGenericFocusedEvent(android.view.MotionEvent);
     method public boolean dispatchGenericMotionEvent(android.view.MotionEvent);
     method protected boolean dispatchGenericPointerEvent(android.view.MotionEvent);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d74ab13..066865c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16059,7 +16059,10 @@
 
     /**
      * This function is called whenever the view hotspot changes and needs to
-     * be propagated to drawables managed by the view.
+     * be propagated to drawables or child views managed by the view.
+     * <p>
+     * Dispatching to child views is handled by
+     * {@link #dispatchDrawableHotspotChanged(float, float)}.
      * <p>
      * Be sure to call through to the superclass when overriding this function.
      *
@@ -16070,6 +16073,18 @@
         if (mBackground != null) {
             mBackground.setHotspot(x, y);
         }
+
+        dispatchDrawableHotspotChanged(x, y);
+    }
+
+    /**
+     * Dispatches drawableHotspotChanged to all of this View's children.
+     *
+     * @param x hotspot x coordinate
+     * @param y hotspot y coordinate
+     * @see #drawableHotspotChanged(float, float)
+     */
+    public void dispatchDrawableHotspotChanged(float x, float y) {
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 512ea99..c53af25 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -161,6 +161,9 @@
     // Used during drag dispatch
     private PointF mLocalPoint;
 
+    // Lazily-created holder for point computations.
+    private float[] mTempPoint;
+
     // Layout animation
     private LayoutAnimationController mLayoutAnimationController;
     private Animation.AnimationListener mAnimationListener;
@@ -2442,6 +2445,13 @@
                 || child.getAnimation() != null;
     }
 
+    private float[] getTempPoint() {
+        if (mTempPoint == null) {
+            mTempPoint = new float[2];
+        }
+        return mTempPoint;
+    }
+
     /**
      * Returns true if a child view contains the specified point when transformed
      * into its coordinate space.
@@ -2450,24 +2460,30 @@
      */
     protected boolean isTransformedTouchPointInView(float x, float y, View child,
             PointF outLocalPoint) {
-        float localX = x + mScrollX - child.mLeft;
-        float localY = y + mScrollY - child.mTop;
-        if (! child.hasIdentityMatrix() && mAttachInfo != null) {
-            final float[] localXY = mAttachInfo.mTmpTransformLocation;
-            localXY[0] = localX;
-            localXY[1] = localY;
-            child.getInverseMatrix().mapPoints(localXY);
-            localX = localXY[0];
-            localY = localXY[1];
-        }
-        final boolean isInView = child.pointInView(localX, localY);
+        final float[] point = getTempPoint();
+        point[0] = x;
+        point[1] = y;
+        transformPointToViewLocal(point, child);
+        final boolean isInView = child.pointInView(point[0], point[1]);
         if (isInView && outLocalPoint != null) {
-            outLocalPoint.set(localX, localY);
+            outLocalPoint.set(point[0], point[1]);
         }
         return isInView;
     }
 
     /**
+     * @hide
+     */
+    public void transformPointToViewLocal(float[] point, View child) {
+        point[0] += mScrollX - child.mLeft;
+        point[1] += mScrollY - child.mTop;
+
+        if (!child.hasIdentityMatrix()) {
+            child.getInverseMatrix().mapPoints(point);
+        }
+    }
+
+    /**
      * Transforms a motion event into the coordinate space of a particular child view,
      * filters out irrelevant pointer ids, and overrides its action if necessary.
      * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
@@ -3606,6 +3622,44 @@
         }
     }
 
+    /**
+     * Dispatches drawable hotspot changes to child views that meet at least
+     * one of the following criteria:
+     * <ul>
+     *     <li>Returns {@code false} from both {@link View#isClickable()} and
+     *     {@link View#isLongClickable()}</li>
+     *     <li>Requests duplication of parent state via
+     *     {@link View#setDuplicateParentStateEnabled(boolean)}</li>
+     * </ul>
+     *
+     * @param x hotspot x coordinate
+     * @param y hotspot y coordinate
+     * @see #drawableHotspotChanged(float, float)
+     */
+    @Override
+    public void dispatchDrawableHotspotChanged(float x, float y) {
+        final int count = mChildrenCount;
+        if (count == 0) {
+            return;
+        }
+
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            final View child = children[i];
+            // Children that are clickable on their own should not
+            // receive hotspots when their parent view does.
+            final boolean nonActionable = !child.isClickable() && !child.isLongClickable();
+            final boolean duplicatesState = (child.mViewFlags & DUPLICATE_PARENT_STATE) != 0;
+            if (nonActionable || duplicatesState) {
+                final float[] point = getTempPoint();
+                point[0] = x;
+                point[1] = y;
+                transformPointToViewLocal(point, child);
+                child.drawableHotspotChanged(point[0], point[1]);
+            }
+        }
+    }
+
     @Override
     void dispatchCancelPendingInputEvents() {
         super.dispatchCancelPendingInputEvents();
@@ -5961,28 +6015,6 @@
     }
 
     @Override
-    public void drawableHotspotChanged(float x, float y) {
-        super.drawableHotspotChanged(x, y);
-
-        if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
-            if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
-                throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
-                        + " child has duplicateParentState set to true");
-            }
-
-            final View[] children = mChildren;
-            final int count = mChildrenCount;
-
-            for (int i = 0; i < count; i++) {
-                final View child = children[i];
-                if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
-                    child.drawableHotspotChanged(x, y);
-                }
-            }
-        }
-    }
-
-    @Override
     protected int[] onCreateDrawableState(int extraSpace) {
         if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
             return super.onCreateDrawableState(extraSpace);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index ae10a9a..f6dd69fa 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.TransitionDrawable;
@@ -611,6 +612,8 @@
     private final int[] mScrollOffset = new int[2];
     private final int[] mScrollConsumed = new int[2];
 
+    private final float[] mTmpPoint = new float[2];
+
     // Used for offsetting MotionEvents that we feed to the VelocityTracker.
     // In the future it would be nice to be able to give this to the VelocityTracker
     // directly, or alternatively put a VT into absolute-positioning mode that only
@@ -2511,38 +2514,29 @@
      * Positions the selector in a way that mimics touch.
      */
     void positionSelectorLikeTouch(int position, View sel, float x, float y) {
-        positionSelectorLikeFocus(position, sel);
-
-        if (mSelector != null && position != INVALID_POSITION) {
-            mSelector.setHotspot(x, y);
-        }
+        positionSelector(position, sel, true, x, y);
     }
 
     /**
      * Positions the selector in a way that mimics keyboard focus.
      */
     void positionSelectorLikeFocus(int position, View sel) {
-        // If we're changing position, update the visibility since the selector
-        // is technically being detached from the previous selection.
-        final Drawable selector = mSelector;
-        final boolean manageState = selector != null && mSelectorPosition != position
-                && position != INVALID_POSITION;
-        if (manageState) {
-            selector.setVisible(false, false);
-        }
-
-        positionSelector(position, sel);
-
-        if (manageState) {
+        if (mSelector != null && mSelectorPosition != position && position != INVALID_POSITION) {
             final Rect bounds = mSelectorRect;
             final float x = bounds.exactCenterX();
             final float y = bounds.exactCenterY();
-            selector.setVisible(getVisibility() == VISIBLE, false);
-            selector.setHotspot(x, y);
+            positionSelector(position, sel, true, x, y);
+        } else {
+            positionSelector(position, sel);
         }
     }
 
     void positionSelector(int position, View sel) {
+        positionSelector(position, sel, false, -1, -1);
+    }
+
+    private void positionSelector(int position, View sel, boolean manageHotspot, float x, float y) {
+        final boolean positionChanged = position != mSelectorPosition;
         if (position != INVALID_POSITION) {
             mSelectorPosition = position;
         }
@@ -2562,7 +2556,22 @@
         // Update the selector drawable.
         final Drawable selector = mSelector;
         if (selector != null) {
+            if (positionChanged) {
+                // Wipe out the current selector state so that we can start
+                // over in the new position with a fresh state.
+                selector.setVisible(false, false);
+                selector.setState(StateSet.NOTHING);
+            }
             selector.setBounds(selectorRect);
+            if (positionChanged) {
+                if (getVisibility() == VISIBLE) {
+                    selector.setVisible(true, false);
+                }
+                selector.setState(getDrawableState());
+            }
+            if (manageHotspot) {
+                selector.setHotspot(x, y);
+            }
         }
 
         final boolean isChildViewEnabled = mIsChildViewEnabled;
@@ -3200,6 +3209,12 @@
         // get the selector in the right state, but we don't want to press each child.
     }
 
+    @Override
+    public void dispatchDrawableHotspotChanged(float x, float y) {
+        // Don't dispatch hotspot changes to children. We'll manually handle
+        // calling drawableHotspotChanged on the correct child.
+    }
+
     /**
      * Maps a point to a position in the list.
      *
@@ -3258,6 +3273,11 @@
                     mLayoutMode = LAYOUT_NORMAL;
 
                     if (!mDataChanged) {
+                        final float[] point = mTmpPoint;
+                        point[0] = x;
+                        point[1] = y;
+                        transformPointToViewLocal(point, child);
+                        child.drawableHotspotChanged(point[0], point[1]);
                         child.setPressed(true);
                         setPressed(true);
                         layoutChildren();
@@ -3758,10 +3778,10 @@
                 }
                 // Otherwise, check containment within list bounds. If we're
                 // outside bounds, cancel any active presses.
+                final View motionView = getChildAt(mMotionPosition - mFirstPosition);
                 final float x = ev.getX(pointerIndex);
                 if (!pointInView(x, y, mTouchSlop)) {
                     setPressed(false);
-                    final View motionView = getChildAt(mMotionPosition - mFirstPosition);
                     if (motionView != null) {
                         motionView.setPressed(false);
                     }
@@ -3769,6 +3789,13 @@
                             mPendingCheckForTap : mPendingCheckForLongPress);
                     mTouchMode = TOUCH_MODE_DONE_WAITING;
                     updateSelectorState();
+                } else if (motionView != null) {
+                    // Still within bounds, update the hotspot.
+                    final float[] point = mTmpPoint;
+                    point[0] = x;
+                    point[1] = y;
+                    transformPointToViewLocal(point, motionView);
+                    motionView.drawableHotspotChanged(point[0], point[1]);
                 }
                 break;
             case TOUCH_MODE_SCROLL:
@@ -6418,6 +6445,8 @@
                     // Note:  We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views.
                     //        However, we will NOT place them into scrap views.
                     activeViews[i] = child;
+                    // Remember the position so that setupChild() doesn't reset state.
+                    lp.scrappedFromPosition = firstActivePosition + i;
                 }
             }
         }
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 13e3d54..9809606 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -556,11 +556,13 @@
         if (mRipple != null) {
             mRipple.cancel();
             mRipple = null;
+            mRippleActive = false;
         }
 
         if (mBackground != null) {
             mBackground.cancel();
             mBackground = null;
+            mBackgroundActive = false;
         }
 
         cancelExitingRipples();