Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d9b4c84a authored by Gilles Debunne's avatar Gilles Debunne Committed by Android (Google) Code Review
Browse files

Merge "Made text selection work in ExtractEditText. Manual merge of Gingerbread 64947."

parents c3a5cf07 64e54a6e
Loading
Loading
Loading
Loading
+109 −92
Original line number Diff line number Diff line
@@ -21,8 +21,8 @@ import com.android.internal.widget.EditableInputConnection;

import org.xmlpull.v1.XmlPullParserException;

import android.content.ClipboardManager;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
@@ -36,6 +36,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.ExtractEditText;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -3704,19 +3705,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

        boolean changed = false;

        SelectionModifierCursorController selectionController = null;
        if (mSelectionModifierCursorController != null) {
            selectionController = (SelectionModifierCursorController)
                mSelectionModifierCursorController;
        }


        if (mMovement != null) {
            /* This code also provides auto-scrolling when a cursor is moved using a
             * CursorController (insertion point or selection limits).
             * For selection, ensure start or end is visible depending on controller's state.
             */
            int curs = getSelectionEnd();
            if (mSelectionModifierCursorController != null) {
                SelectionModifierCursorController selectionController =
                    (SelectionModifierCursorController) mSelectionModifierCursorController;
                if (selectionController.isSelectionStartDragged()) {
            if (selectionController != null && selectionController.isSelectionStartDragged()) {
                curs = getSelectionStart();
            }
            }

            /*
             * TODO: This should really only keep the end in view if
@@ -3735,10 +3739,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            changed = bringTextIntoView();
        }

        if (mShouldStartSelectionActionMode) {
        // This has to be checked here since:
        // - onFocusChanged cannot start it when focus is given to a view with selected text (after
        //   a screen rotation) since layout is not yet initialized at that point.
        // - 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()) {
            startSelectionActionMode();
            mShouldStartSelectionActionMode = false;
        }

        mPreDrawState = PREDRAW_DONE;
        return !changed;
    }
@@ -4350,6 +4360,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                if (shouldAdvanceFocusOnEnter()) {
                    return 0;
                }
                break;

                // Has to be done on key down (and not on key up) to correctly be intercepted.
            case KeyEvent.KEYCODE_BACK:
                if (mSelectionActionMode != null) {
                    stopSelectionActionMode();
                    return -1;
                }
                break;
        }

        if (mInput != null) {
@@ -4503,6 +4522,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

                    return super.onKeyUp(keyCode, event);
                }
                break;
        }

        if (mInput != null)
@@ -6434,11 +6454,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after);
                mBeforeText = null;
            }

            // TODO. The cursor controller should hide as soon as text is typed.
            // But this method is also used for cosmetic changes (underline current word when
            // spell corrections are displayed. There is currently no way to make the difference
            // between these cosmetic changes and actual text modifications.
        }

        public void afterTextChanged(Editable buffer) {
@@ -6530,13 +6545,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            int selEnd = getSelectionEnd();

            if (!mFrozenWithFocus || (selStart < 0 || selEnd < 0)) {
                boolean selMoved = mSelectionMoved;

                if (mSelectionModifierCursorController != null) {
                    final int touchOffset = 
                        ((SelectionModifierCursorController) mSelectionModifierCursorController).
                        getMinTouchOffset();
                    Selection.setSelection((Spannable) mText, touchOffset);
                // Has to be done before onTakeFocus, which can be overloaded.
                if (mLastTouchOffset >= 0) {
                    Selection.setSelection((Spannable) mText, mLastTouchOffset);
                }

                if (mMovement != null) {
@@ -6547,7 +6558,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                    Selection.setSelection((Spannable) mText, 0, mText.length());
                }

                if (selMoved && selStart >= 0 && selEnd >= 0) {
                // The DecorView does not have focus when the 'Done' ExtractEditText button is
                // pressed. Since it is the ViewRoot's mView, it requests focus before
                // ExtractEditText clears focus, which gives focus to the ExtractEditText.
                // This special case ensure that we keep current selection in that case.
                // It would be better to know why the DecorView does not have focus at that time.
                if (((this instanceof ExtractEditText) || mSelectionMoved) && selStart >= 0 && selEnd >= 0) {
                    /*
                     * Someone intentionally set the selection, so let them
                     * do whatever it is that they wanted to do instead of
@@ -6557,7 +6573,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     * just setting the selection in theirs and we still
                     * need to go through that path.
                     */

                    Selection.setSelection((Spannable) mText, selStart, selEnd);
                }
                mTouchFocusSelected = true;
@@ -6576,13 +6591,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            if (mError != null) {
                showError();
            }

            // We cannot start the selection mode immediately. The layout may be null here and is
            // needed by the cursor controller. Layout creation is deferred up to drawing. The
            // selection action mode will be started in onPreDraw().
            if (selStart != selEnd) {
                mShouldStartSelectionActionMode = true;
            }
        } else {
            if (mError != null) {
                hideError();
@@ -6591,14 +6599,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            onEndBatchEdit();

            hideInsertionPointCursorController();
            if (this instanceof ExtractEditText) {
                // terminateTextSelectionMode removes selection, which we want to keep when
                // ExtractEditText goes out of focus.
                final int selStart = getSelectionStart();
                final int selEnd = getSelectionEnd();
                terminateSelectionActionMode();
                Selection.setSelection((Spannable) mText, selStart, selEnd);
            } else {
                terminateSelectionActionMode();
            }
        }

        startStopMarquee(focused);

        if (mTransformation != null) {
            mTransformation.onFocusChanged(this, mText, focused, direction,
                                           previouslyFocusedRect);
            mTransformation.onFocusChanged(this, mText, focused, direction, previouslyFocusedRect);
        }

        super.onFocusChanged(focused, direction, previouslyFocusedRect);
@@ -6657,59 +6673,57 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        }
    }

    class CommitSelectionReceiver extends ResultReceiver {
        private final int mPrevStart, mPrevEnd;
        private final int mNewStart, mNewEnd;
    private void onTapUpEvent(int prevStart, int prevEnd) {
        final int start = getSelectionStart();
        final int end = getSelectionEnd();

        public CommitSelectionReceiver(int mPrevStart, int mPrevEnd, int mNewStart, int mNewEnd) {
            super(getHandler());
            this.mPrevStart = mPrevStart;
            this.mPrevEnd = mPrevEnd;
            this.mNewStart = mNewStart;
            this.mNewEnd = mNewEnd;
        }

        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            int start = mNewStart;
            int end = mNewEnd;

            // Move the cursor to the new position, unless this tap was actually
            // use to show the IMM. Leave cursor unchanged in that case.
            if (resultCode == InputMethodManager.RESULT_SHOWN) {
                start = mPrevStart;
                end = mPrevEnd;
            } else {
                if ((mPrevStart != mPrevEnd) && (start == end)) {
                    if ((start >= mPrevStart) && (start <= mPrevEnd)) {
                        // Tapping inside the selection does nothing
                        Selection.setSelection((Spannable) mText, mPrevStart, mPrevEnd);
        if (start == end) {
            if (start >= prevStart && start < prevEnd) {
                // Tapping inside the selection displays the cut/copy/paste context menu.
                showContextMenu();
                return;
            } else {
                // Tapping outside stops selection mode, if any
                stopSelectionActionMode();
                    }
                }

                if (mInsertionPointCursorController != null) {
                    mInsertionPointCursorController.show();
                }
            }
        }
    }

    class CommitSelectionReceiver extends ResultReceiver {
        private final int mPrevStart, mPrevEnd;

        public CommitSelectionReceiver(int prevStart, int prevEnd) {
            super(getHandler());
            mPrevStart = prevStart;
            mPrevEnd = prevEnd;
        }

        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            // If this tap was actually used to show the IMM, leave cursor or selection unchanged
            // by restoring its previous position.
            if (resultCode == InputMethodManager.RESULT_SHOWN) {
                final int len = mText.length();
            if (start > len) {
                start = len;
                int start = Math.min(len, mPrevStart);
                int end = Math.min(len, mPrevEnd);
                Selection.setSelection((Spannable)mText, start, end);

                if (hasSelection()) {
                    startSelectionActionMode();
                } else if (mInsertionPointCursorController != null) {
                    mInsertionPointCursorController.show();
                }
            if (end > len) {
                end = len;
            }
            Selection.setSelection((Spannable)mText, start, end);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int action = event.getAction();
        final int action = event.getActionMasked();
        if (action == MotionEvent.ACTION_DOWN) {
            // Reset this state; it will be re-set if super.onTouchEvent
            // causes focus to move to the view.
@@ -6729,11 +6743,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            return superResult;
        }

        if ((mMovement != null || onCheckIsTextEditor()) &&
                mText instanceof Spannable && mLayout != null) {
            
            int oldSelStart = getSelectionStart();
            int oldSelEnd = getSelectionEnd();
        if ((mMovement != null || onCheckIsTextEditor()) && mText instanceof Spannable && mLayout != null) {

            if (mInsertionPointCursorController != null) {
                mInsertionPointCursorController.onTouchEvent(event);
@@ -6744,6 +6754,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

            boolean handled = false;

            // Save previous selection, in case this event is used to show the IME.
            int oldSelStart = getSelectionStart();
            int oldSelEnd = getSelectionEnd();

            if (mMovement != null) {
                handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
            }
@@ -6753,17 +6767,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                    InputMethodManager imm = (InputMethodManager)
                          getContext().getSystemService(Context.INPUT_METHOD_SERVICE);

                    final int newSelStart = getSelectionStart();
                    final int newSelEnd = getSelectionEnd();

                    CommitSelectionReceiver csr = null;
                    if (newSelStart != oldSelStart || newSelEnd != oldSelEnd ||
                    if (getSelectionStart() != oldSelStart || getSelectionEnd() != oldSelEnd ||
                            didTouchFocusSelect()) {
                        csr = new CommitSelectionReceiver(oldSelStart, oldSelEnd,
                                newSelStart, newSelEnd);
                        csr = new CommitSelectionReceiver(oldSelStart, oldSelEnd);
                    }

                    handled |= imm.showSoftInput(this, 0, csr) && (csr != null);

                    // Cannot be done by CommitSelectionReceiver, which might not always be called,
                    // for instance when dealing with an ExtractEditText.
                    onTapUpEvent(oldSelStart, oldSelEnd);
                }
            }

@@ -7315,8 +7329,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        int max = mText.length();

        if (isFocused()) {
            int selStart = getSelectionStart();
            int selEnd = getSelectionEnd();
            final int selStart = getSelectionStart();
            final int selEnd = getSelectionEnd();

            min = Math.max(0, Math.min(selStart, selEnd));
            max = Math.max(0, Math.max(selStart, selEnd));
        }
@@ -7326,7 +7341,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

        switch (id) {
            case ID_COPY_URL:
                MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);

                URLSpan[] urls = ((Spanned) mText).getSpans(min, max, URLSpan.class);
                if (urls.length >= 1) {
@@ -7660,8 +7674,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            mHotSpotVerticalPosition = lineTop;

            final Rect bounds = sCursorControllerTempRect;
            bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - drawableWidth / 2.0);
            bounds.top = (bottom ? lineBottom : lineTop) - drawableHeight / 2;
            bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - drawableWidth / 2.0)
                + mScrollX;
            bounds.top = (bottom ? lineBottom : lineTop) - drawableHeight / 2 + mScrollY;

            mTopExtension = bottom ? 0 : drawableHeight / 2;
            mBottomExtension = drawableHeight;
@@ -7692,6 +7707,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                           (int) (y - mBottomExtension),
                           (int) (x + drawableWidth / 2.0),
                           (int) (y + mTopExtension));
            fingerRect.offset(mScrollX, mScrollY);
            return Rect.intersects(mDrawable.getBounds(), fingerRect);
        }

@@ -7985,7 +8001,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                        final int y = (int) event.getY();

                        // Remember finger down position, to be able to start selection from there
                        mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
                        mMinTouchOffset = mMaxTouchOffset = mLastTouchOffset = getOffset(x, y);

                        if (mIsVisible) {
                            if (mMovement instanceof ArrowKeyMovementMethod) {
@@ -8001,7 +8017,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                                    // In case both controllers are under finger (very small
                                    // selection region), arbitrarily pick end controller.
                                    mStartIsDragged = !isOnEnd;
                                    final Handle draggedHandle = mStartIsDragged ? mStartHandle : mEndHandle;
                                    final Handle draggedHandle =
                                        mStartIsDragged ? mStartHandle : mEndHandle;
                                    final Rect bounds = draggedHandle.mDrawable.getBounds();
                                    mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
                                    mOffsetY = draggedHandle.mHotSpotVerticalPosition - y;
@@ -8166,8 +8183,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    // Cursor Controllers. Null when disabled.
    private CursorController        mInsertionPointCursorController;
    private CursorController        mSelectionModifierCursorController;
    private boolean                 mShouldStartSelectionActionMode = false;
    private ActionMode              mSelectionActionMode;
    private int                     mLastTouchOffset = -1;
    // Created once and shared by different CursorController helper methods.
    // Only one cursor controller is active at any time which prevent race conditions.
    private static Rect             sCursorControllerTempRect = new Rect();