summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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
commitdbd25cdbc3dcad573aaeaf493bc186006bce3d8e (patch)
tree9dd10a772a2771fce5bcc54e1cd2702bcf059b6c
parentd0f74ae081bac9a9c8f7faf9288305647735d743 (diff)
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.xml4
-rw-r--r--core/java/android/inputmethodservice/ExtractEditText.java11
-rw-r--r--core/java/android/widget/TextView.java206
3 files changed, 119 insertions, 102 deletions
diff --git a/api/current.xml b/api/current.xml
index b4fc949416c3..bbe9936e9444 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 22968b09dfd5..8a52e4025b72 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 1e8023c9803e..d0dd6cc751e1 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();