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:
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user