diff options
| author | Gilles Debunne <debunne@google.com> | 2010-09-07 19:07:17 -0700 |
|---|---|---|
| committer | Gilles Debunne <debunne@google.com> | 2010-09-08 09:38:47 -0700 |
| commit | 64e54a6e1fcb4a9603b3b86e1fb883ce49eadc92 (patch) | |
| tree | 12ee3d5b98fa0d1c0eef3650fc1ef0e0b860227b /core/java/android/widget/TextView.java | |
| parent | 789a47488c0e834eb80fe6b596dcc3e0ba7f1344 (diff) | |
| download | frameworks_base-64e54a6e1fcb4a9603b3b86e1fb883ce49eadc92.zip frameworks_base-64e54a6e1fcb4a9603b3b86e1fb883ce49eadc92.tar.gz frameworks_base-64e54a6e1fcb4a9603b3b86e1fb883ce49eadc92.tar.bz2 | |
Made text selection work in ExtractEditText. Manual merge of Gingerbread 64947.
Change-Id: I2bbd4cd7abbc77212fb4df256c345f95232de127
Diffstat (limited to 'core/java/android/widget/TextView.java')
| -rw-r--r-- | core/java/android/widget/TextView.java | 201 |
1 files changed, 109 insertions, 92 deletions
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 9961438..dabe7ba 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -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,18 +3705,21 @@ 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()) { - curs = getSelectionStart(); - } + if (selectionController != null && selectionController.isSelectionStartDragged()) { + curs = getSelectionStart(); } /* @@ -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(); - terminateSelectionActionMode(); + 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 } } + private void onTapUpEvent(int prevStart, int prevEnd) { + final int start = getSelectionStart(); + final int end = getSelectionEnd(); + + 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; - private final int mNewStart, mNewEnd; - public CommitSelectionReceiver(int mPrevStart, int mPrevEnd, int mNewStart, int mNewEnd) { + public CommitSelectionReceiver(int prevStart, int prevEnd) { super(getHandler()); - this.mPrevStart = mPrevStart; - this.mPrevEnd = mPrevEnd; - this.mNewStart = mNewStart; - this.mNewEnd = mNewEnd; + mPrevStart = prevStart; + mPrevEnd = prevEnd; } @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 this tap was actually used to show the IMM, leave cursor or selection unchanged + // by restoring its previous position. 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); - return; - } else { - // Tapping outside stops selection mode, if any - stopSelectionActionMode(); - } - } - - if (mInsertionPointCursorController != null) { + final int len = mText.length(); + 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(); } } - - final int len = mText.length(); - if (start > len) { - start = len; - } - 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); } } @@ -7313,10 +7327,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onTextContextMenuItem(int id) { int min = 0; 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) { @@ -7649,7 +7663,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Handle(Drawable drawable) { mDrawable = drawable; } - + void positionAtCursor(final int offset, boolean bottom) { final int drawableWidth = mDrawable.getIntrinsicWidth(); final int drawableHeight = mDrawable.getIntrinsicHeight(); @@ -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; @@ -7669,7 +7684,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Extend touch region up when editing the last line of text (or a single line) so that // it is easier to grab. if (line == mLayout.getLineCount() - 1) { - mTopExtension = (lineBottom - lineTop) - drawableHeight / 2; + mTopExtension = (lineBottom - lineTop) - drawableHeight / 2; } bounds.right = bounds.left + drawableWidth; @@ -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(); |
