Merge "DO NOT MERGE Fix bug 3121030 - Text handles erroneously appear over extract mode IME window" into gingerbread
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index b30b6b1..41c9736 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3758,7 +3758,7 @@
         // - ExtractEditText does not call onFocus when it is displayed. Fixing this issue would
         //   allow to test for hasSelection in onFocusChanged, which would trigger a
         //   startTextSelectionMode here. TODO
-        if (selectionController != null && hasSelection()) {
+        if (this instanceof ExtractEditText && selectionController != null && hasSelection()) {
             startTextSelectionMode();
         }
 
@@ -4819,6 +4819,7 @@
     }
 
     public void beginBatchEdit() {
+        mInBatchEditControllers = true;
         final InputMethodState ims = mInputMethodState;
         if (ims != null) {
             int nesting = ++ims.mBatchEditNesting;
@@ -4841,6 +4842,7 @@
     }
     
     public void endBatchEdit() {
+        mInBatchEditControllers = false;
         final InputMethodState ims = mInputMethodState;
         if (ims != null) {
             int nesting = --ims.mBatchEditNesting;
@@ -6747,10 +6749,9 @@
                 // Restore previous selection
                 Selection.setSelection((Spannable)mText, prevStart, prevEnd);
 
-                if (mSelectionModifierCursorController != null &&
-                        !mSelectionModifierCursorController.isShowing()) {
+                if (hasSelectionController() && !getSelectionController().isShowing()) {
                     // If the anchors aren't showing, revive them.
-                    mSelectionModifierCursorController.show();
+                    getSelectionController().show();
                 } else {
                     // Tapping inside the selection displays the cut/copy/paste context menu
                     // as long as the anchors are already showing.
@@ -6761,12 +6762,12 @@
                 // Tapping outside stops selection mode, if any
                 stopTextSelectionMode();
 
-                if (mInsertionPointCursorController != null && mText.length() > 0) {
-                    mInsertionPointCursorController.show();
+                if (hasInsertionController() && mText.length() > 0) {
+                    getInsertionController().show();
                 }
             }
-        } else if (hasSelection() && mSelectionModifierCursorController != null) {
-            mSelectionModifierCursorController.show();
+        } else if (hasSelection() && hasSelectionController()) {
+            getSelectionController().show();
         }
     }
 
@@ -6800,11 +6801,11 @@
     public boolean onTouchEvent(MotionEvent event) {
         final int action = event.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN) {
-            if (mInsertionPointCursorController != null) {
-                mInsertionPointCursorController.onTouchEvent(event);
+            if (hasInsertionController()) {
+                getInsertionController().onTouchEvent(event);
             }
-            if (mSelectionModifierCursorController != null) {
-                mSelectionModifierCursorController.onTouchEvent(event);
+            if (hasSelectionController()) {
+                getSelectionController().onTouchEvent(event);
             }
 
             // Reset this state; it will be re-set if super.onTouchEvent
@@ -6826,11 +6827,11 @@
         }
 
         if ((mMovement != null || onCheckIsTextEditor()) && mText instanceof Spannable && mLayout != null) {
-            if (mInsertionPointCursorController != null) {
-                mInsertionPointCursorController.onTouchEvent(event);
+            if (hasInsertionController()) {
+                getInsertionController().onTouchEvent(event);
             }
-            if (mSelectionModifierCursorController != null) {
-                mSelectionModifierCursorController.onTouchEvent(event);
+            if (hasSelectionController()) {
+                getSelectionController().onTouchEvent(event);
             }
 
             boolean handled = false;
@@ -6892,19 +6893,15 @@
         }
 
         // TODO Add an extra android:cursorController flag to disable the controller?
-        if (windowSupportsHandles && mCursorVisible && mLayout != null) {
-            if (mInsertionPointCursorController == null) {
-                mInsertionPointCursorController = new InsertionPointCursorController();
-            }
-        } else {
+        mInsertionControllerEnabled = windowSupportsHandles && mCursorVisible && mLayout != null;
+        mSelectionControllerEnabled = windowSupportsHandles && textCanBeSelected() &&
+                mLayout != null;
+
+        if (!mInsertionControllerEnabled) {
             mInsertionPointCursorController = null;
         }
 
-        if (windowSupportsHandles && textCanBeSelected() && mLayout != null) {
-            if (mSelectionModifierCursorController == null) {
-                mSelectionModifierCursorController = new SelectionModifierCursorController();
-            }
-        } else {
+        if (!mSelectionControllerEnabled) {
             // Stop selection mode if the controller becomes unavailable.
             stopTextSelectionMode();
             mSelectionModifierCursorController = null;
@@ -7535,10 +7532,12 @@
             case ID_SELECT_ALL:
                 Selection.setSelection((Spannable) mText, 0, mText.length());
                 startTextSelectionMode();
+                getSelectionController().show();
                 return true;
 
             case ID_START_SELECTING_TEXT:
                 startTextSelectionMode();
+                getSelectionController().show();
                 return true;
 
             case ID_CUT:                
@@ -7648,7 +7647,7 @@
 
     private void startTextSelectionMode() {
         if (!mIsInTextSelectionMode) {
-            if (mSelectionModifierCursorController == null) {
+            if (!hasSelectionController()) {
                 Log.w(LOG_TAG, "TextView has no selection controller. Action mode cancelled.");
                 return;
             }
@@ -7658,7 +7657,6 @@
             }
 
             selectCurrentWord();
-            mSelectionModifierCursorController.show();
             mIsInTextSelectionMode = true;
         }
     }
@@ -7837,6 +7835,10 @@
                 return true;
             }
 
+            if (isInBatchEditMode()) {
+                return false;
+            }
+
             final int extendedPaddingTop = getExtendedPaddingTop();
             final int extendedPaddingBottom = getExtendedPaddingBottom();
             final int compoundPaddingLeft = getCompoundPaddingLeft();
@@ -7876,7 +7878,7 @@
             mPositionY = y - TextView.this.mScrollY;
             if (isPositionVisible()) {
                 int[] coords = null;
-                if (mContainer.isShowing()){
+                if (mContainer.isShowing()) {
                     coords = mTempCoords;
                     TextView.this.getLocationInWindow(coords);
                     mContainer.update(coords[0] + mPositionX, coords[1] + mPositionY,
@@ -8065,6 +8067,10 @@
         }
 
         public void show() {
+            if (isInBatchEditMode()) {
+                return;
+            }
+
             mIsShowing = true;
             updatePosition();
             mStartHandle.show();
@@ -8128,6 +8134,10 @@
         }
 
         public void updatePosition() {
+            if (!isShowing()) {
+                return;
+            }
+
             final int selectionStart = getSelectionStart();
             final int selectionEnd = getSelectionEnd();
 
@@ -8288,6 +8298,62 @@
         return getOffsetForHorizontal(line, x);
     }
 
+    /**
+     * @return True if this view supports insertion handles.
+     */
+    boolean hasInsertionController() {
+        return mInsertionControllerEnabled;
+    }
+
+    /**
+     * @return True if this view supports selection handles.
+     */
+    boolean hasSelectionController() {
+        return mSelectionControllerEnabled;
+    }
+
+    CursorController getInsertionController() {
+        if (!mInsertionControllerEnabled) {
+            return null;
+        }
+
+        if (mInsertionPointCursorController == null) {
+            mInsertionPointCursorController = new InsertionPointCursorController();
+
+            final ViewTreeObserver observer = getViewTreeObserver();
+            if (observer != null) {
+                observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
+            }
+        }
+
+        return mInsertionPointCursorController;
+    }
+
+    CursorController getSelectionController() {
+        if (!mSelectionControllerEnabled) {
+            return null;
+        }
+
+        if (mSelectionModifierCursorController == null) {
+            mSelectionModifierCursorController = new SelectionModifierCursorController();
+
+            final ViewTreeObserver observer = getViewTreeObserver();
+            if (observer != null) {
+                observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
+            }
+        }
+
+        return mSelectionModifierCursorController;
+    }
+
+    boolean isInBatchEditMode() {
+        final InputMethodState ims = mInputMethodState;
+        if (ims != null) {
+            return ims.mBatchEditNesting > 0;
+        }
+        return mInBatchEditControllers;
+    }
+
     @ViewDebug.ExportedProperty
     private CharSequence            mText;
     private CharSequence            mTransformed;
@@ -8319,6 +8385,9 @@
     // Cursor Controllers. Null when disabled.
     private CursorController        mInsertionPointCursorController;
     private CursorController        mSelectionModifierCursorController;
+    private boolean                 mInsertionControllerEnabled;
+    private boolean                 mSelectionControllerEnabled;
+    private boolean                 mInBatchEditControllers;
     private boolean                 mIsInTextSelectionMode = false;
     // These are needed to desambiguate a long click. If the long click comes from ones of these, we
     // select from the current cursor position. Otherwise, select from long pressed position.