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
This commit is contained in:
Gilles Debunne
2010-09-07 15:21:14 -07:00
parent d0f74ae081
commit dbd25cdbc3
3 changed files with 122 additions and 105 deletions

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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
}
}
class CommitSelectionReceiver extends ResultReceiver {
private final int mPrevStart, mPrevEnd;
private final int mNewStart, mNewEnd;
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;
private void onTapUpEvent(int prevStart, int prevEnd) {
final int start = getSelectionStart();
final int end = getSelectionEnd();
// 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;
if (start == end) {
if (start >= prevStart && start < prevEnd) {
// Tapping inside the selection displays the cut/copy/paste context menu.
showContextMenu();
return;
} 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();
}
}
// Tapping outside stops selection mode, if any
stopTextSelectionMode();
if (mInsertionPointCursorController != null) {
mInsertionPointCursorController.show();
}
}
}
}
final int len = mText.length();
if (start > len) {
start = len;
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();
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();
}
}
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();