resolved conflicts for merge of b39470b5 to master

Change-Id: If441c8684c576b6cbc485a37088d6869ad3fb23f
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ac65e09..97d58d2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1613,15 +1613,21 @@
     private static final int AWAKEN_SCROLL_BARS_ON_ATTACH = 0x08000000;
 
     /**
+     * Indicates that this view has a visible/touchable overlay.
+     * @hide
+     */
+    static final int HAS_OVERLAY = 0x10000000;
+
+    /**
      * Indicates that pivotX or pivotY were explicitly set and we should not assume the center
      * for transform operations
      *
      * @hide
      */
-    private static final int PIVOT_EXPLICITLY_SET = 0x10000000;
+    private static final int PIVOT_EXPLICITLY_SET = 0x20000000;
 
     /** {@hide} */
-    static final int ACTIVATED                    = 0x20000000;
+    static final int ACTIVATED                    = 0x40000000;
 
     /**
      * The parent this view is attached to.
@@ -3034,6 +3040,57 @@
         resetPressedState();
     }
 
+    /**
+     * Enable or disable drawing overlays after a full drawing pass. This enables a view to
+     * draw on a topmost overlay layer after normal drawing completes and get right of first
+     * refusal for touch events in the window.
+     * 
+     * <em>Warning:</em> Views that use this feature should take care to disable/enable overlay
+     * appropriately when they are attached/detached from their window. All overlays should be
+     * disabled when detached.
+     * 
+     * @param enabled true if overlay drawing should be enabled for this view, false otherwise
+     * 
+     * @see #onDrawOverlay(Canvas)
+     * 
+     * @hide
+     */
+    protected void setOverlayEnabled(boolean enabled) {
+        final boolean oldValue = (mPrivateFlags & HAS_OVERLAY) == HAS_OVERLAY;
+        mPrivateFlags = (mPrivateFlags & ~HAS_OVERLAY) | (enabled ? HAS_OVERLAY : 0);
+        if (enabled != oldValue) {
+            final ViewParent parent = getParent();
+            if (parent != null) {
+                try {
+                    parent.childOverlayStateChanged(this);
+                } catch (AbstractMethodError e) {
+                    Log.e(VIEW_LOG_TAG, "Could not propagate hasOverlay state", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * @return true if this View has an overlay enabled.
+     * 
+     * @see #setOverlayEnabled(boolean)
+     * @see #onDrawOverlay(Canvas)
+     * 
+     * @hide
+     */
+    public boolean isOverlayEnabled() {
+        return (mPrivateFlags & HAS_OVERLAY) == HAS_OVERLAY;
+    }
+
+    /**
+     * Override this method to draw on an overlay layer above all other views in the window
+     * after the standard drawing pass is complete. This allows a view to draw outside its
+     * normal boundaries.
+     * @hide
+     */
+    public void onDrawOverlay(Canvas canvas) {
+    }
+
     private void resetPressedState() {
         if ((mViewFlags & ENABLED_MASK) == DISABLED) {
             return;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a18a977..abe66721 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -245,9 +245,14 @@
     protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
 
     /**
+     * When set, at least one child of this ViewGroup will return true from hasOverlay.
+     */
+    private static final int FLAG_CHILD_HAS_OVERLAY = 0x100000;
+
+    /**
      * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
      */
-    private static final int FLAG_SPLIT_MOTION_EVENTS = 0x100000;
+    private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
 
     /**
      * Indicates which types of drawing caches are to be kept in memory.
@@ -904,6 +909,34 @@
                 // who can handle it, start with the front-most child.
                 final View[] children = mChildren;
                 final int count = mChildrenCount;
+
+                // Check for children with overlays first. They don't rely on hit rects to determine
+                // if they can accept a new touch event.
+                if ((mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY) {
+                    for (int i = count - 1; i >= 0; i--) {
+                        final View child = children[i];
+                        // Don't let children respond to events as an overlay during an animation.
+                        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
+                                && child.getAnimation() == null
+                                && child.isOverlayEnabled()) {
+                            // offset the event to the view's coordinate system
+                            final float xc = scrolledXFloat - child.mLeft;
+                            final float yc = scrolledYFloat - child.mTop;
+                            ev.setLocation(xc, yc);
+                            child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
+                            if (child.dispatchTouchEvent(ev))  {
+                                // Event handled, we have a target now.
+                                mMotionTarget = child;
+                                return true;
+                            }
+                            // The event didn't get handled, try the next view.
+                            // Don't reset the event's location, it's not
+                            // necessary here.
+                        }
+                    }
+                }
+
+                // Now check views normally.
                 for (int i = count - 1; i >= 0; i--) {
                     final View child = children[i];
                     if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
@@ -2741,6 +2774,8 @@
         if (clearChildFocus != null) {
             clearChildFocus(clearChildFocus);
         }
+
+        mGroupFlags &= ~FLAG_CHILD_HAS_OVERLAY;
     }
 
     /**
@@ -2989,7 +3024,8 @@
                 final int left = mLeft;
                 final int top = mTop;
 
-                if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||
+                if ((mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY ||
+                        dirty.intersect(0, 0, mRight - left, mBottom - top) ||
                         (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
                     mPrivateFlags &= ~DRAWING_CACHE_VALID;
 
@@ -3977,6 +4013,69 @@
     }
 
     /**
+     * Called when a child's overlay state changes between enabled/disabled.
+     * @param child Child view whose state has changed or null
+     * @hide
+     */
+    public void childOverlayStateChanged(View child) {
+        boolean childHasOverlay = false;
+        if (child != null) {
+            childHasOverlay = child.isOverlayEnabled();
+        } else {
+            final int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                if (childHasOverlay |= getChildAt(i).isOverlayEnabled()) {
+                    break;
+                }
+            }
+        }
+        
+        final boolean hasChildWithOverlay = childHasOverlay ||
+                (mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY;
+
+        final boolean oldValue = isOverlayEnabled();
+        mGroupFlags = (mGroupFlags & ~FLAG_CHILD_HAS_OVERLAY) |
+                (hasChildWithOverlay ? FLAG_CHILD_HAS_OVERLAY : 0);
+        if (isOverlayEnabled() != oldValue) {
+            final ViewParent parent = getParent();
+            if (parent != null) {
+                try {
+                    parent.childOverlayStateChanged(this);
+                } catch (AbstractMethodError e) {
+                    Log.e("ViewGroup", "Could not propagate hasOverlay state", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public boolean isOverlayEnabled() {
+        return super.isOverlayEnabled() ||
+                ((mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onDrawOverlay(Canvas canvas) {
+        if ((mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY) {
+            final int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                final View child = getChildAt(i);
+                if (child.isOverlayEnabled()) {
+                    canvas.translate(child.mLeft + child.mScrollX, child.mTop + child.mScrollY);
+                    child.onDrawOverlay(canvas);
+                    canvas.translate(-(child.mLeft + child.mScrollX),
+                            -(child.mTop + child.mScrollY));
+                }
+            }
+        }
+    }
+
+    /**
      * LayoutParams are used by views to tell their parents how they want to be
      * laid out. See
      * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index d7d4c3f..d2907da 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -222,4 +222,11 @@
      */
     public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
             boolean immediate);
+
+    /**
+     * Called when a child view's overlay state changes between enabled/disabled.
+     * @param child Child view whose state changed or null.
+     * @hide
+     */
+    public void childOverlayStateChanged(View child);
 }
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 7b20b8b..cb9f84d 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -207,6 +207,8 @@
 
     private final int mDensity;
     
+    private boolean mHasOverlay;
+
     public static IWindowSession getWindowSession(Looper mainLooper) {
         synchronized (mStaticInit) {
             if (!mInitialized) {
@@ -1357,6 +1359,9 @@
                         canvas.setScreenDensity(scalingRequired
                                 ? DisplayMetrics.DENSITY_DEVICE : 0);
                         mView.draw(canvas);
+                        if (mHasOverlay) {
+                            mView.onDrawOverlay(canvas);
+                        }
                     } finally {
                         mAttachInfo.mIgnoreDirtyState = false;
                     }
@@ -2739,6 +2744,19 @@
         return scrollToRectOrFocus(rectangle, immediate);
     }
 
+    /**
+     * @hide
+     */
+    public void childOverlayStateChanged(View child) {
+        final boolean oldState = mHasOverlay;
+        mHasOverlay = child.isOverlayEnabled();
+        // Invalidate the whole thing when we change overlay states just in case
+        // something left chunks of data drawn someplace it shouldn't have.
+        if (mHasOverlay != oldState) {
+            child.invalidate();
+        }
+    }
+
     class TakenSurfaceHolder extends BaseSurfaceHolder {
         @Override
         public boolean onAllowLockCanvas() {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2d550e4..93e87e6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -290,6 +290,10 @@
     }
     InputMethodState mInputMethodState;
 
+    private int mTextSelectHandleLeftRes;
+    private int mTextSelectHandleRightRes;
+    private int mTextSelectHandleRes;
+
     /*
      * Kick-start the font cache for the zygote process (to pay the cost of
      * initializing freetype for our default font only once).
@@ -710,6 +714,18 @@
                     Log.w(LOG_TAG, "Failure reading input extras", e);
                 }
                 break;
+
+            case com.android.internal.R.styleable.TextView_textSelectHandleLeft:
+                mTextSelectHandleLeftRes = a.getResourceId(attr, 0);
+                break;
+
+            case com.android.internal.R.styleable.TextView_textSelectHandleRight:
+                mTextSelectHandleRightRes = a.getResourceId(attr, 0);
+                break;
+
+            case com.android.internal.R.styleable.TextView_textSelectHandle:
+                mTextSelectHandleRes = a.getResourceId(attr, 0);
+                break;
             }
         }
         a.recycle();
@@ -3763,6 +3779,8 @@
             showError();
             mShowErrorAfterAttach = false;
         }
+        
+        updateOverlay();
     }
 
     @Override
@@ -3780,6 +3798,8 @@
         if (mError != null) {
             hideError();
         }
+        
+        setOverlayEnabled(false);
     }
 
     @Override
@@ -4147,7 +4167,13 @@
         */
 
         canvas.restore();
+    }
 
+    /**
+     * @hide
+     */
+    @Override
+    public void onDrawOverlay(Canvas canvas) {
         if (mInsertionPointCursorController != null) {
             mInsertionPointCursorController.draw(canvas);
         }
@@ -6742,10 +6768,31 @@
     public boolean onTouchEvent(MotionEvent event) {
         final int action = event.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN) {
+            // Check to see if we're testing for our anchor overlay.
+            boolean handled = false;
+            final float x = event.getX();
+            final float y = event.getY();
+            if (x < 0 || x >= mRight - mLeft || y < 0 || y >= mBottom - mTop) {
+                if (mInsertionPointCursorController != null) {
+                    handled |= mInsertionPointCursorController.onTouchEvent(event);
+                }
+                if (mSelectionModifierCursorController != null) {
+                    handled |= mSelectionModifierCursorController.onTouchEvent(event);
+                }
+
+                if (!handled) {
+                    return false;
+                }
+            }
+
             // Reset this state; it will be re-set if super.onTouchEvent
             // causes focus to move to the view.
             mTouchFocusSelected = false;
             mScrolled = false;
+
+            if (handled) {
+                return true;
+            }
         }
 
         final boolean superResult = super.onTouchEvent(event);
@@ -7658,6 +7705,17 @@
         }
     }
 
+    private void updateOverlay() {
+        boolean enableOverlay = false;
+        if (mSelectionModifierCursorController != null) {
+            enableOverlay |= mSelectionModifierCursorController.isShowing();
+        }
+        if (mInsertionPointCursorController != null) {
+            enableOverlay |= mInsertionPointCursorController.isShowing();
+        }
+        setOverlayEnabled(enableOverlay);
+    }
+
     /**
      * A CursorController instance can be used to control a cursor in the text.
      *
@@ -7681,6 +7739,11 @@
         public void hide();
 
         /**
+         * @return true if the CursorController is currently visible
+         */
+        public boolean isShowing();
+
+        /**
          * Update the controller's position.
          */
         public void updatePosition(int x, int y);
@@ -7702,7 +7765,7 @@
          * a chance to become active and/or visible.
          * @param event The touch event
          */
-        public void onTouchEvent(MotionEvent event);
+        public boolean onTouchEvent(MotionEvent event);
 
         /**
          * Draws a visual representation of the controller on the canvas.
@@ -7736,10 +7799,10 @@
             final Rect bounds = sCursorControllerTempRect;
             bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - drawableWidth / 2.0)
                 + mScrollX;
-            bounds.top = (bottom ? lineBottom : lineTop) - drawableHeight / 2 + mScrollY;
+            bounds.top = (bottom ? lineBottom : lineTop) + mScrollY;
 
             mTopExtension = bottom ? 0 : drawableHeight / 2;
-            mBottomExtension = drawableHeight;
+            mBottomExtension = 0; //drawableHeight / 4;
 
             // Extend touch region up when editing the last line of text (or a single line) so that
             // it is easier to grab.
@@ -7797,7 +7860,7 @@
 
         InsertionPointCursorController() {
             Resources res = mContext.getResources();
-            mHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle));
+            mHandle = new Handle(res.getDrawable(mTextSelectHandleRes));
         }
 
         public void show() {
@@ -7805,6 +7868,7 @@
             // Has to be done after updateDrawablePosition, so that previous position invalidate
             // in only done if necessary.
             mIsVisible = true;
+            updateOverlay();
         }
 
         public void hide() {
@@ -7818,6 +7882,10 @@
             }
         }
 
+        public boolean isShowing() {
+            return mIsVisible;
+        }
+
         public void draw(Canvas canvas) {
             if (mIsVisible) {
                 int time = (int) (System.currentTimeMillis() - mFadeOutTimerStart);
@@ -7833,6 +7901,7 @@
                     } else {
                         mHandle.mDrawable.setAlpha(0);
                         mIsVisible = false;
+                        updateOverlay();
                     }
                 }
                 mHandle.mDrawable.draw(canvas);
@@ -7861,6 +7930,7 @@
                 // Should never happen, safety check.
                 Log.w(LOG_TAG, "Update cursor controller position called with no cursor");
                 mIsVisible = false;
+                updateOverlay();
                 return;
             }
 
@@ -7870,7 +7940,7 @@
             mHandle.mDrawable.setAlpha(255);
         }
 
-        public void onTouchEvent(MotionEvent event) {
+        public boolean onTouchEvent(MotionEvent event) {
             if (isFocused() && isTextEditable() && mIsVisible) {
                 switch (event.getActionMasked()) {
                     case MotionEvent.ACTION_DOWN : {
@@ -7898,8 +7968,9 @@
                             mOffsetY += viewportToContentVerticalOffset();
 
                             mOnDownTimerStart = event.getEventTime();
+                            return true;
                         }
-                        break;
+                        return false;
                     }
 
                     case MotionEvent.ACTION_UP : {
@@ -7917,6 +7988,7 @@
                     }
                 }
             }
+            return false;
         }
 
         public float getOffsetX() {
@@ -7944,8 +8016,8 @@
 
         SelectionModifierCursorController() {
             Resources res = mContext.getResources();
-            mStartHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle));
-            mEndHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle));
+            mStartHandle = new Handle(res.getDrawable(mTextSelectHandleLeftRes));
+            mEndHandle = new Handle(res.getDrawable(mTextSelectHandleRightRes));
         }
 
         public void show() {
@@ -7953,6 +8025,7 @@
             // Has to be done after updateDrawablePositions, so that previous position invalidate
             // in only done if necessary.
             mIsVisible = true;
+            updateOverlay();
             mFadeOutTimerStart = -1;
             hideInsertionPointCursorController();
         }
@@ -7965,8 +8038,13 @@
             }
         }
 
+        public boolean isShowing() {
+            return mIsVisible;
+        }
+
         public void cancelFadeOutAnimation() {
             mIsVisible = false;
+            updateOverlay();
             mStartHandle.postInvalidate();
             mEndHandle.postInvalidate();
         }
@@ -7985,6 +8063,7 @@
                         mStartHandle.mDrawable.setAlpha(0);
                         mEndHandle.mDrawable.setAlpha(0);
                         mIsVisible = false;
+                        updateOverlay();
                     }
                 }
                 mStartHandle.mDrawable.draw(canvas);
@@ -8042,6 +8121,7 @@
                 // Should never happen, safety check.
                 Log.w(LOG_TAG, "Update selection controller position called with no cursor");
                 mIsVisible = false;
+                updateOverlay();
                 return;
             }
 
@@ -8054,7 +8134,7 @@
             mEndHandle.mDrawable.setAlpha(255);
         }
 
-        public void onTouchEvent(MotionEvent event) {
+        public boolean onTouchEvent(MotionEvent event) {
             if (isTextEditable()) {
                 switch (event.getActionMasked()) {
                     case MotionEvent.ACTION_DOWN:
@@ -8088,6 +8168,7 @@
                                     mOffsetY += viewportToContentVerticalOffset();
 
                                     ((ArrowKeyMovementMethod)mMovement).setCursorController(this);
+                                    return true;
                                 }
                             }
                         }
@@ -8104,6 +8185,7 @@
                         break;
                 }
             }
+            return false;
         }
 
         /**