diff options
author | Gilles Debunne <debunne@google.com> | 2010-09-07 15:21:14 -0700 |
---|---|---|
committer | Gilles Debunne <debunne@google.com> | 2010-09-07 18:07:30 -0700 |
commit | dbd25cdbc3dcad573aaeaf493bc186006bce3d8e (patch) | |
tree | 9dd10a772a2771fce5bcc54e1cd2702bcf059b6c | |
parent | d0f74ae081bac9a9c8f7faf9288305647735d743 (diff) | |
download | frameworks_base-dbd25cdbc3dcad573aaeaf493bc186006bce3d8e.zip frameworks_base-dbd25cdbc3dcad573aaeaf493bc186006bce3d8e.tar.gz frameworks_base-dbd25cdbc3dcad573aaeaf493bc186006bce3d8e.tar.bz2 |
Made text selection work in ExtractEditText. DO NOT MERGE
Change insertion point on tap is no longer handled by the CommitSelectionReceiver
(as it is not called by ExtractEditText).
Fixed a bug to handle drawing positions when the internal TextView scroller is used.
Change-Id: I87398c7109c5527d21dee6abbdb925848244d594
-rw-r--r-- | api/current.xml | 4 | ||||
-rw-r--r-- | core/java/android/inputmethodservice/ExtractEditText.java | 11 | ||||
-rw-r--r-- | core/java/android/widget/TextView.java | 206 |
3 files changed, 119 insertions, 102 deletions
diff --git a/api/current.xml b/api/current.xml index b4fc949..bbe9936 100644 --- a/api/current.xml +++ b/api/current.xml @@ -78402,7 +78402,7 @@ type="float" transient="false" volatile="false" - value="0.0010f" + value="0.001f" static="true" final="true" deprecated="not deprecated" @@ -225403,7 +225403,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="t" type="T"> +<parameter name="arg0" type="T"> </parameter> </method> </interface> diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java index 22968b0..8a52e40 100644 --- a/core/java/android/inputmethodservice/ExtractEditText.java +++ b/core/java/android/inputmethodservice/ExtractEditText.java @@ -18,6 +18,7 @@ package android.inputmethodservice; import android.content.Context; import android.util.AttributeSet; +import android.view.ContextMenu; import android.view.inputmethod.ExtractedText; import android.widget.EditText; @@ -28,6 +29,7 @@ import android.widget.EditText; public class ExtractEditText extends EditText { private InputMethodService mIME; private int mSettingExtractedText; + private boolean mContextMenuShouldBeHandledBySuper = false; public ExtractEditText(Context context) { super(context, null); @@ -97,12 +99,19 @@ public class ExtractEditText extends EditText { return false; } + @Override + protected void onCreateContextMenu(ContextMenu menu) { + super.onCreateContextMenu(menu); + mContextMenuShouldBeHandledBySuper = true; + } + @Override public boolean onTextContextMenuItem(int id) { - if (mIME != null) { + if (mIME != null && !mContextMenuShouldBeHandledBySuper) { if (mIME.onExtractTextContextMenuItem(id)) { return true; } } + mContextMenuShouldBeHandledBySuper = false; return super.onTextContextMenuItem(id); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 1e8023c..d0dd6cc 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -35,6 +35,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.inputmethodservice.ExtractEditText; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -3674,18 +3675,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(); } /* @@ -3705,10 +3709,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener changed = bringTextIntoView(); } - if (mShouldStartTextSelectionMode) { + // 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()) { startTextSelectionMode(); - mShouldStartTextSelectionMode = false; } + mPreDrawState = PREDRAW_DONE; return !changed; } @@ -6471,19 +6481,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mShowCursor = SystemClock.uptimeMillis(); ensureEndedBatchEdit(); - + if (focused) { int selStart = getSelectionStart(); 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) { @@ -6494,7 +6500,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 @@ -6504,7 +6515,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; @@ -6523,13 +6533,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) { - mShouldStartTextSelectionMode = true; - } } else { if (mError != null) { hideError(); @@ -6538,14 +6541,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener onEndBatchEdit(); hideInsertionPointCursorController(); - terminateTextSelectionMode(); + if (this instanceof ExtractEditText) { + // terminateTextSelectionMode would remove selection, which we want to keep when + // ExtractEditText goes out of focus. + mIsInTextSelectionMode = false; + } else { + terminateTextSelectionMode(); + } } startStopMarquee(focused); if (mTransformation != null) { - mTransformation.onFocusChanged(this, mText, focused, direction, - previouslyFocusedRect); + mTransformation.onFocusChanged(this, mText, focused, direction, previouslyFocusedRect); } super.onFocusChanged(focused, direction, previouslyFocusedRect); @@ -6604,60 +6612,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 + stopTextSelectionMode(); + + 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); - showContextMenu(); - return; - } else { - // Tapping outside stops selection mode, if any - stopTextSelectionMode(); - } - } - - 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()) { + startTextSelectionMode(); + } 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. @@ -6678,10 +6683,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if ((mMovement != null || onCheckIsTextEditor()) && mText instanceof Spannable && mLayout != null) { - - int oldSelStart = getSelectionStart(); - int oldSelEnd = getSelectionEnd(); - + if (mInsertionPointCursorController != null) { mInsertionPointCursorController.onTouchEvent(event); } @@ -6690,6 +6692,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); @@ -6699,18 +6705,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (action == MotionEvent.ACTION_UP && isFocused() && !mScrolled) { 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); } } @@ -7152,14 +7158,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private String getWordForDictionary() { - if (mSelectionModifierCursorController == null) { + if (mLastTouchOffset < 0) { return null; } - int offset = ((SelectionModifierCursorController) mSelectionModifierCursorController). - getMinTouchOffset(); - - long wordLimits = getWordLimitsAt(offset); + long wordLimits = getWordLimitsAt(mLastTouchOffset); if (wordLimits >= 0) { int start = (int) (wordLimits >>> 32); int end = (int) (wordLimits & 0x00000000FFFFFFFFL); @@ -7167,7 +7170,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } else { return null; } - } @Override @@ -7439,18 +7441,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void startTextSelectionMode() { - if (mSelectionModifierCursorController == null) { - Log.w(LOG_TAG, "TextView has no selection controller. Action mode cancelled."); - return; - } + if (!mIsInTextSelectionMode) { + if (mSelectionModifierCursorController == null) { + Log.w(LOG_TAG, "TextView has no selection controller. Action mode cancelled."); + return; + } - if (!requestFocus()) { - return; - } + if (!requestFocus()) { + return; + } - selectCurrentWord(); - mSelectionModifierCursorController.show(); - mIsInTextSelectionMode = true; + selectCurrentWord(); + mSelectionModifierCursorController.show(); + mIsInTextSelectionMode = true; + } } /** @@ -7555,8 +7559,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; @@ -7587,6 +7592,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); } @@ -7865,7 +7871,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } - boolean oneLineSelection = mLayout.getLineForOffset(selectionStart) == mLayout.getLineForOffset(selectionEnd); + boolean oneLineSelection = mLayout.getLineForOffset(selectionStart) == + mLayout.getLineForOffset(selectionEnd); mStartHandle.positionAtCursor(selectionStart, oneLineSelection); mEndHandle.positionAtCursor(selectionEnd, true); @@ -7881,7 +7888,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) { @@ -7897,7 +7904,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; @@ -8071,8 +8079,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Cursor Controllers. Null when disabled. private CursorController mInsertionPointCursorController; private CursorController mSelectionModifierCursorController; - private boolean mShouldStartTextSelectionMode = false; private boolean mIsInTextSelectionMode = false; + 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(); |