Loading core/java/android/text/method/ArrowKeyMovementMethod.java +5 −21 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import android.graphics.Rect; import android.text.Layout; import android.text.Selection; import android.text.Spannable; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; Loading Loading @@ -222,34 +221,26 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme return lineEnd(widget, buffer); } private static boolean isTouchSelecting(boolean isMouse, Spannable buffer) { return isMouse ? Touch.isActivelySelecting(buffer) : isSelecting(buffer); } @Override public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { int initialScrollX = -1; int initialScrollY = -1; final int action = event.getAction(); final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE); if (action == MotionEvent.ACTION_UP) { initialScrollX = Touch.getInitialScrollX(widget, buffer); initialScrollY = Touch.getInitialScrollY(widget, buffer); } boolean wasTouchSelecting = isTouchSelecting(isMouse, buffer); boolean wasTouchSelecting = isSelecting(buffer); boolean handled = Touch.onTouchEvent(widget, buffer, event); if (widget.didTouchFocusSelect() && !isMouse) { if (widget.didTouchFocusSelect()) { return handled; } if (action == MotionEvent.ACTION_DOWN) { // Capture the mouse pointer down location to ensure selection starts // right under the mouse (and is not influenced by cursor location). // The code below needs to run for mouse events. // For touch events, the code should run only when selection is active. if (isMouse || isTouchSelecting(isMouse, buffer)) { if (isSelecting(buffer)) { if (!widget.isFocused()) { if (!widget.requestFocus()) { return handled; Loading @@ -265,15 +256,8 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme } } else if (widget.isFocused()) { if (action == MotionEvent.ACTION_MOVE) { // Cursor can be active at any location in the text while mouse pointer can start // selection from a totally different location. Use LAST_TAP_DOWN span to ensure // text selection will start from mouse pointer location. if (isSelecting(buffer) && handled) { final int startOffset = buffer.getSpanStart(LAST_TAP_DOWN); if (isMouse && Touch.isSelectionStarted(buffer)) { Selection.setSelection(buffer, startOffset); } if (isTouchSelecting(isMouse, buffer) && handled) { // Before selecting, make sure we've moved out of the "slop". // handled will be true, if we're in select mode AND we're // OUT of the slop Loading core/java/android/text/method/Touch.java +1 −49 Original line number Diff line number Diff line Loading @@ -119,18 +119,12 @@ public class Touch { ds = buffer.getSpans(0, buffer.length(), DragState.class); if (ds.length > 0) { ds[0].mIsSelectionStarted = false; if (ds[0].mFarEnough == false) { int slop = ViewConfiguration.get(widget.getContext()).getScaledTouchSlop(); if (Math.abs(event.getX() - ds[0].mX) >= slop || Math.abs(event.getY() - ds[0].mY) >= slop) { ds[0].mFarEnough = true; if (event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) { ds[0].mIsActivelySelecting = true; ds[0].mIsSelectionStarted = true; } } } Loading @@ -142,13 +136,9 @@ public class Touch { || MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0; if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) { ds[0].mIsActivelySelecting = false; } float dx; float dy; if (cap && event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) { if (cap) { // if we're selecting, we want the scroll to go in // the direction of the drag dx = event.getX() - ds[0].mX; Loading @@ -160,7 +150,6 @@ public class Touch { ds[0].mX = event.getX(); ds[0].mY = event.getY(); int nx = widget.getScrollX() + (int) dx; int ny = widget.getScrollY() + (int) dy; int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom(); Loading @@ -172,10 +161,6 @@ public class Touch { int oldX = widget.getScrollX(); int oldY = widget.getScrollY(); if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) { scrollTo(widget, layout, nx, ny); } // If we actually scrolled, then cancel the up action. if (oldX != widget.getScrollX() || oldY != widget.getScrollY()) { widget.cancelLongPress(); Loading Loading @@ -207,37 +192,6 @@ public class Touch { return ds.length > 0 ? ds[0].mScrollY : -1; } /** * Checks if selection is still active. * This is useful for extending Selection span on buffer. * @param buffer The text buffer. * @return true if buffer has been marked for selection. * * @hide */ static boolean isActivelySelecting(Spannable buffer) { DragState[] ds; ds = buffer.getSpans(0, buffer.length(), DragState.class); return ds.length > 0 && ds[0].mIsActivelySelecting; } /** * Checks if selection has begun (are we out of slop?). * Note: DragState.mIsSelectionStarted goes back to false with the very next event. * This is useful for starting Selection span on buffer. * @param buffer The text buffer. * @return true if selection has started on the buffer. * * @hide */ static boolean isSelectionStarted(Spannable buffer) { DragState[] ds; ds = buffer.getSpans(0, buffer.length(), DragState.class); return ds.length > 0 && ds[0].mIsSelectionStarted; } private static class DragState implements NoCopySpan { public float mX; public float mY; Loading @@ -245,8 +199,6 @@ public class Touch { public int mScrollY; public boolean mFarEnough; public boolean mUsed; public boolean mIsActivelySelecting; public boolean mIsSelectionStarted; public DragState(float x, float y, int scrollX, int scrollY) { mX = x; Loading core/java/android/widget/Editor.java +139 −82 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ import android.view.ActionMode.Callback; import android.view.DisplayListCanvas; import android.view.DragEvent; import android.view.Gravity; import android.view.InputDevice; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; Loading Loading @@ -805,8 +806,8 @@ public class Editor { final int maxOffset = TextUtils.unpackRangeEndFromLong(lastTouchOffsets); // Safety check in case standard touch event handling has been bypassed if (minOffset < 0 || minOffset >= mTextView.getText().length()) return false; if (maxOffset < 0 || maxOffset >= mTextView.getText().length()) return false; if (minOffset < 0 || minOffset > mTextView.getText().length()) return false; if (maxOffset < 0 || maxOffset > mTextView.getText().length()) return false; int selectionStart, selectionEnd; Loading Loading @@ -1773,7 +1774,8 @@ public class Editor { stopTextActionMode(); mPreserveDetachedSelection = false; getSelectionController().enterDrag(); getSelectionController().enterDrag( SelectionModifierCursorController.DRAG_ACCELERATOR_MODE_WORD); return true; } Loading Loading @@ -4492,14 +4494,22 @@ public class Editor { // Where the user first starts the drag motion. private int mStartOffset = -1; // Indicates whether the user is selecting text and using the drag accelerator. private boolean mDragAcceleratorActive; private boolean mHaventMovedEnoughToStartDrag; // The line that a selection happened most recently with the drag accelerator. private int mLineSelectionIsOn = -1; // Whether the drag accelerator has selected past the initial line. private boolean mSwitchedLines = false; // Indicates the drag accelerator mode that the user is currently using. private int mDragAcceleratorMode = DRAG_ACCELERATOR_MODE_INACTIVE; // Drag accelerator is inactive. private static final int DRAG_ACCELERATOR_MODE_INACTIVE = 0; // Character based selection by dragging. Only for mouse. private static final int DRAG_ACCELERATOR_MODE_CHARACTER = 1; // Word based selection by dragging. Enabled after long pressing or double tapping. private static final int DRAG_ACCELERATOR_MODE_WORD = 2; SelectionModifierCursorController() { resetTouchOffsets(); } Loading @@ -4510,7 +4520,6 @@ public class Editor { } initDrawables(); initHandles(); hideInsertionPointCursorController(); } private void initDrawables() { Loading Loading @@ -4548,10 +4557,10 @@ public class Editor { if (mEndHandle != null) mEndHandle.hide(); } public void enterDrag() { public void enterDrag(int dragAcceleratorMode) { // Just need to init the handles / hide insertion cursor. show(); mDragAcceleratorActive = true; mDragAcceleratorMode = dragAcceleratorMode; // Start location of selection. mStartOffset = mTextView.getOffsetForPosition(mLastDownPositionX, mLastDownPositionY); Loading @@ -4563,6 +4572,7 @@ public class Editor { // the user to continue dragging across the screen to select text; TextView will // scroll as necessary. mTextView.getParent().requestDisallowInterceptTouchEvent(true); mTextView.cancelLongPress(); } public void onTouchEvent(MotionEvent event) { Loading @@ -4570,6 +4580,7 @@ public class Editor { // selection and tap can move cursor from this tap position. final float eventX = event.getX(); final float eventY = event.getY(); final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: if (extractedTextModeWillBeStarted()) { Loading @@ -4593,7 +4604,7 @@ public class Editor { boolean stayedInArea = distanceSquared < doubleTapSlop * doubleTapSlop; if (stayedInArea && isPositionOnText(eventX, eventY)) { if (stayedInArea && (isMouse || isPositionOnText(eventX, eventY))) { selectCurrentWordAndStartDrag(); mDiscardNextActionUp = true; } Loading Loading @@ -4639,36 +4650,118 @@ public class Editor { } } if (isMouse && !isDragAcceleratorActive()) { final int offset = mTextView.getOffsetForPosition(eventX, eventY); if (mStartOffset != offset) { // Start character based drag accelerator. if (mTextActionMode != null) { mTextActionMode.finish(); } enterDrag(DRAG_ACCELERATOR_MODE_CHARACTER); mDiscardNextActionUp = true; mHaventMovedEnoughToStartDrag = false; } } if (mStartHandle != null && mStartHandle.isShowing()) { // Don't do the drag if the handles are showing already. break; } if (mStartOffset != -1 && mTextView.getLayout() != null) { if (!mHaventMovedEnoughToStartDrag) { updateSelection(event); break; case MotionEvent.ACTION_UP: if (!isDragAcceleratorActive()) { break; } updateSelection(event); // No longer dragging to select text, let the parent intercept events. mTextView.getParent().requestDisallowInterceptTouchEvent(false); int startOffset = mTextView.getSelectionStart(); int endOffset = mTextView.getSelectionEnd(); // Since we don't let drag handles pass once they're visible, we need to // make sure the start / end locations are correct because the user *can* // switch directions during the initial drag. if (endOffset < startOffset) { int tmp = endOffset; endOffset = startOffset; startOffset = tmp; // Also update the selection with the right offsets in this case. Selection.setSelection((Spannable) mTextView.getText(), startOffset, endOffset); } if (startOffset != endOffset) { mStartHandle.showAtLocation(startOffset); mEndHandle.showAtLocation(endOffset); startSelectionActionMode(); } // No longer the first dragging motion, reset. resetDragAcceleratorState(); break; } } private void updateSelection(MotionEvent event) { if (mTextView.getLayout() != null) { switch (mDragAcceleratorMode) { case DRAG_ACCELERATOR_MODE_CHARACTER: updateCharacterBasedSelection(event); break; case DRAG_ACCELERATOR_MODE_WORD: updateWordBasedSelection(event); break; } } } private void updateCharacterBasedSelection(MotionEvent event) { final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY()); Selection.setSelection((Spannable) mTextView.getText(), mStartOffset, offset); } private void updateWordBasedSelection(MotionEvent event) { if (mHaventMovedEnoughToStartDrag) { return; } final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE); final ViewConfiguration viewConfig = ViewConfiguration.get( mTextView.getContext()); final float eventX = event.getX(); final float eventY = event.getY(); final int currLine; if (isMouse) { // No need to offset the y coordinate for mouse input. currLine = mTextView.getLineAtCoordinate(eventY); } else { float y = eventY; if (mSwitchedLines) { // Offset the finger by the same vertical offset as the handles. // This improves visibility of the content being selected by // shifting the finger below the content, this is applied once // the user has switched lines. final int touchSlop = viewConfig.getScaledTouchSlop(); final float fingerOffset = (mStartHandle != null) ? mStartHandle.getIdealVerticalOffset() : touchSlop; y = eventY - fingerOffset; } final int currLine = getCurrentLineAdjustedForSlop( mTextView.getLayout(), mLineSelectionIsOn, y); currLine = getCurrentLineAdjustedForSlop(mTextView.getLayout(), mLineSelectionIsOn, y); if (!mSwitchedLines && currLine != mLineSelectionIsOn) { // Break early here, we want to offset the finger position from // the selection highlight, once the user moved their finger // to a different line we should apply the offset and *not* switch // lines until recomputing the position with the finger offset. mSwitchedLines = true; break; return; } } int startOffset; Loading @@ -4687,46 +4780,6 @@ public class Editor { Selection.setSelection((Spannable) mTextView.getText(), startOffset, offset); } } break; case MotionEvent.ACTION_UP: if (mDragAcceleratorActive) { // No longer dragging to select text, let the parent intercept events. mTextView.getParent().requestDisallowInterceptTouchEvent(false); show(); int startOffset = mTextView.getSelectionStart(); int endOffset = mTextView.getSelectionEnd(); // Since we don't let drag handles pass once they're visible, we need to // make sure the start / end locations are correct because the user *can* // switch directions during the initial drag. if (endOffset < startOffset) { int tmp = endOffset; endOffset = startOffset; startOffset = tmp; // Also update the selection with the right offsets in this case. Selection.setSelection((Spannable) mTextView.getText(), startOffset, endOffset); } // Need to do this to display the handles. mStartHandle.showAtLocation(startOffset); mEndHandle.showAtLocation(endOffset); // No longer the first dragging motion, reset. startSelectionActionMode(); mDragAcceleratorActive = false; mStartOffset = -1; mSwitchedLines = false; } break; } } /** * @param event */ Loading @@ -4749,8 +4802,12 @@ public class Editor { public void resetTouchOffsets() { mMinTouchOffset = mMaxTouchOffset = -1; resetDragAcceleratorState(); } private void resetDragAcceleratorState() { mStartOffset = -1; mDragAcceleratorActive = false; mDragAcceleratorMode = DRAG_ACCELERATOR_MODE_INACTIVE; mSwitchedLines = false; } Loading @@ -4765,7 +4822,7 @@ public class Editor { * @return true if the user is selecting text using the drag accelerator. */ public boolean isDragAcceleratorActive() { return mDragAcceleratorActive; return mDragAcceleratorMode != DRAG_ACCELERATOR_MODE_INACTIVE; } public void onTouchModeChanged(boolean isInTouchMode) { Loading Loading
core/java/android/text/method/ArrowKeyMovementMethod.java +5 −21 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import android.graphics.Rect; import android.text.Layout; import android.text.Selection; import android.text.Spannable; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; Loading Loading @@ -222,34 +221,26 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme return lineEnd(widget, buffer); } private static boolean isTouchSelecting(boolean isMouse, Spannable buffer) { return isMouse ? Touch.isActivelySelecting(buffer) : isSelecting(buffer); } @Override public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { int initialScrollX = -1; int initialScrollY = -1; final int action = event.getAction(); final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE); if (action == MotionEvent.ACTION_UP) { initialScrollX = Touch.getInitialScrollX(widget, buffer); initialScrollY = Touch.getInitialScrollY(widget, buffer); } boolean wasTouchSelecting = isTouchSelecting(isMouse, buffer); boolean wasTouchSelecting = isSelecting(buffer); boolean handled = Touch.onTouchEvent(widget, buffer, event); if (widget.didTouchFocusSelect() && !isMouse) { if (widget.didTouchFocusSelect()) { return handled; } if (action == MotionEvent.ACTION_DOWN) { // Capture the mouse pointer down location to ensure selection starts // right under the mouse (and is not influenced by cursor location). // The code below needs to run for mouse events. // For touch events, the code should run only when selection is active. if (isMouse || isTouchSelecting(isMouse, buffer)) { if (isSelecting(buffer)) { if (!widget.isFocused()) { if (!widget.requestFocus()) { return handled; Loading @@ -265,15 +256,8 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme } } else if (widget.isFocused()) { if (action == MotionEvent.ACTION_MOVE) { // Cursor can be active at any location in the text while mouse pointer can start // selection from a totally different location. Use LAST_TAP_DOWN span to ensure // text selection will start from mouse pointer location. if (isSelecting(buffer) && handled) { final int startOffset = buffer.getSpanStart(LAST_TAP_DOWN); if (isMouse && Touch.isSelectionStarted(buffer)) { Selection.setSelection(buffer, startOffset); } if (isTouchSelecting(isMouse, buffer) && handled) { // Before selecting, make sure we've moved out of the "slop". // handled will be true, if we're in select mode AND we're // OUT of the slop Loading
core/java/android/text/method/Touch.java +1 −49 Original line number Diff line number Diff line Loading @@ -119,18 +119,12 @@ public class Touch { ds = buffer.getSpans(0, buffer.length(), DragState.class); if (ds.length > 0) { ds[0].mIsSelectionStarted = false; if (ds[0].mFarEnough == false) { int slop = ViewConfiguration.get(widget.getContext()).getScaledTouchSlop(); if (Math.abs(event.getX() - ds[0].mX) >= slop || Math.abs(event.getY() - ds[0].mY) >= slop) { ds[0].mFarEnough = true; if (event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) { ds[0].mIsActivelySelecting = true; ds[0].mIsSelectionStarted = true; } } } Loading @@ -142,13 +136,9 @@ public class Touch { || MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0; if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) { ds[0].mIsActivelySelecting = false; } float dx; float dy; if (cap && event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) { if (cap) { // if we're selecting, we want the scroll to go in // the direction of the drag dx = event.getX() - ds[0].mX; Loading @@ -160,7 +150,6 @@ public class Touch { ds[0].mX = event.getX(); ds[0].mY = event.getY(); int nx = widget.getScrollX() + (int) dx; int ny = widget.getScrollY() + (int) dy; int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom(); Loading @@ -172,10 +161,6 @@ public class Touch { int oldX = widget.getScrollX(); int oldY = widget.getScrollY(); if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) { scrollTo(widget, layout, nx, ny); } // If we actually scrolled, then cancel the up action. if (oldX != widget.getScrollX() || oldY != widget.getScrollY()) { widget.cancelLongPress(); Loading Loading @@ -207,37 +192,6 @@ public class Touch { return ds.length > 0 ? ds[0].mScrollY : -1; } /** * Checks if selection is still active. * This is useful for extending Selection span on buffer. * @param buffer The text buffer. * @return true if buffer has been marked for selection. * * @hide */ static boolean isActivelySelecting(Spannable buffer) { DragState[] ds; ds = buffer.getSpans(0, buffer.length(), DragState.class); return ds.length > 0 && ds[0].mIsActivelySelecting; } /** * Checks if selection has begun (are we out of slop?). * Note: DragState.mIsSelectionStarted goes back to false with the very next event. * This is useful for starting Selection span on buffer. * @param buffer The text buffer. * @return true if selection has started on the buffer. * * @hide */ static boolean isSelectionStarted(Spannable buffer) { DragState[] ds; ds = buffer.getSpans(0, buffer.length(), DragState.class); return ds.length > 0 && ds[0].mIsSelectionStarted; } private static class DragState implements NoCopySpan { public float mX; public float mY; Loading @@ -245,8 +199,6 @@ public class Touch { public int mScrollY; public boolean mFarEnough; public boolean mUsed; public boolean mIsActivelySelecting; public boolean mIsSelectionStarted; public DragState(float x, float y, int scrollX, int scrollY) { mX = x; Loading
core/java/android/widget/Editor.java +139 −82 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ import android.view.ActionMode.Callback; import android.view.DisplayListCanvas; import android.view.DragEvent; import android.view.Gravity; import android.view.InputDevice; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; Loading Loading @@ -805,8 +806,8 @@ public class Editor { final int maxOffset = TextUtils.unpackRangeEndFromLong(lastTouchOffsets); // Safety check in case standard touch event handling has been bypassed if (minOffset < 0 || minOffset >= mTextView.getText().length()) return false; if (maxOffset < 0 || maxOffset >= mTextView.getText().length()) return false; if (minOffset < 0 || minOffset > mTextView.getText().length()) return false; if (maxOffset < 0 || maxOffset > mTextView.getText().length()) return false; int selectionStart, selectionEnd; Loading Loading @@ -1773,7 +1774,8 @@ public class Editor { stopTextActionMode(); mPreserveDetachedSelection = false; getSelectionController().enterDrag(); getSelectionController().enterDrag( SelectionModifierCursorController.DRAG_ACCELERATOR_MODE_WORD); return true; } Loading Loading @@ -4492,14 +4494,22 @@ public class Editor { // Where the user first starts the drag motion. private int mStartOffset = -1; // Indicates whether the user is selecting text and using the drag accelerator. private boolean mDragAcceleratorActive; private boolean mHaventMovedEnoughToStartDrag; // The line that a selection happened most recently with the drag accelerator. private int mLineSelectionIsOn = -1; // Whether the drag accelerator has selected past the initial line. private boolean mSwitchedLines = false; // Indicates the drag accelerator mode that the user is currently using. private int mDragAcceleratorMode = DRAG_ACCELERATOR_MODE_INACTIVE; // Drag accelerator is inactive. private static final int DRAG_ACCELERATOR_MODE_INACTIVE = 0; // Character based selection by dragging. Only for mouse. private static final int DRAG_ACCELERATOR_MODE_CHARACTER = 1; // Word based selection by dragging. Enabled after long pressing or double tapping. private static final int DRAG_ACCELERATOR_MODE_WORD = 2; SelectionModifierCursorController() { resetTouchOffsets(); } Loading @@ -4510,7 +4520,6 @@ public class Editor { } initDrawables(); initHandles(); hideInsertionPointCursorController(); } private void initDrawables() { Loading Loading @@ -4548,10 +4557,10 @@ public class Editor { if (mEndHandle != null) mEndHandle.hide(); } public void enterDrag() { public void enterDrag(int dragAcceleratorMode) { // Just need to init the handles / hide insertion cursor. show(); mDragAcceleratorActive = true; mDragAcceleratorMode = dragAcceleratorMode; // Start location of selection. mStartOffset = mTextView.getOffsetForPosition(mLastDownPositionX, mLastDownPositionY); Loading @@ -4563,6 +4572,7 @@ public class Editor { // the user to continue dragging across the screen to select text; TextView will // scroll as necessary. mTextView.getParent().requestDisallowInterceptTouchEvent(true); mTextView.cancelLongPress(); } public void onTouchEvent(MotionEvent event) { Loading @@ -4570,6 +4580,7 @@ public class Editor { // selection and tap can move cursor from this tap position. final float eventX = event.getX(); final float eventY = event.getY(); final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: if (extractedTextModeWillBeStarted()) { Loading @@ -4593,7 +4604,7 @@ public class Editor { boolean stayedInArea = distanceSquared < doubleTapSlop * doubleTapSlop; if (stayedInArea && isPositionOnText(eventX, eventY)) { if (stayedInArea && (isMouse || isPositionOnText(eventX, eventY))) { selectCurrentWordAndStartDrag(); mDiscardNextActionUp = true; } Loading Loading @@ -4639,36 +4650,118 @@ public class Editor { } } if (isMouse && !isDragAcceleratorActive()) { final int offset = mTextView.getOffsetForPosition(eventX, eventY); if (mStartOffset != offset) { // Start character based drag accelerator. if (mTextActionMode != null) { mTextActionMode.finish(); } enterDrag(DRAG_ACCELERATOR_MODE_CHARACTER); mDiscardNextActionUp = true; mHaventMovedEnoughToStartDrag = false; } } if (mStartHandle != null && mStartHandle.isShowing()) { // Don't do the drag if the handles are showing already. break; } if (mStartOffset != -1 && mTextView.getLayout() != null) { if (!mHaventMovedEnoughToStartDrag) { updateSelection(event); break; case MotionEvent.ACTION_UP: if (!isDragAcceleratorActive()) { break; } updateSelection(event); // No longer dragging to select text, let the parent intercept events. mTextView.getParent().requestDisallowInterceptTouchEvent(false); int startOffset = mTextView.getSelectionStart(); int endOffset = mTextView.getSelectionEnd(); // Since we don't let drag handles pass once they're visible, we need to // make sure the start / end locations are correct because the user *can* // switch directions during the initial drag. if (endOffset < startOffset) { int tmp = endOffset; endOffset = startOffset; startOffset = tmp; // Also update the selection with the right offsets in this case. Selection.setSelection((Spannable) mTextView.getText(), startOffset, endOffset); } if (startOffset != endOffset) { mStartHandle.showAtLocation(startOffset); mEndHandle.showAtLocation(endOffset); startSelectionActionMode(); } // No longer the first dragging motion, reset. resetDragAcceleratorState(); break; } } private void updateSelection(MotionEvent event) { if (mTextView.getLayout() != null) { switch (mDragAcceleratorMode) { case DRAG_ACCELERATOR_MODE_CHARACTER: updateCharacterBasedSelection(event); break; case DRAG_ACCELERATOR_MODE_WORD: updateWordBasedSelection(event); break; } } } private void updateCharacterBasedSelection(MotionEvent event) { final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY()); Selection.setSelection((Spannable) mTextView.getText(), mStartOffset, offset); } private void updateWordBasedSelection(MotionEvent event) { if (mHaventMovedEnoughToStartDrag) { return; } final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE); final ViewConfiguration viewConfig = ViewConfiguration.get( mTextView.getContext()); final float eventX = event.getX(); final float eventY = event.getY(); final int currLine; if (isMouse) { // No need to offset the y coordinate for mouse input. currLine = mTextView.getLineAtCoordinate(eventY); } else { float y = eventY; if (mSwitchedLines) { // Offset the finger by the same vertical offset as the handles. // This improves visibility of the content being selected by // shifting the finger below the content, this is applied once // the user has switched lines. final int touchSlop = viewConfig.getScaledTouchSlop(); final float fingerOffset = (mStartHandle != null) ? mStartHandle.getIdealVerticalOffset() : touchSlop; y = eventY - fingerOffset; } final int currLine = getCurrentLineAdjustedForSlop( mTextView.getLayout(), mLineSelectionIsOn, y); currLine = getCurrentLineAdjustedForSlop(mTextView.getLayout(), mLineSelectionIsOn, y); if (!mSwitchedLines && currLine != mLineSelectionIsOn) { // Break early here, we want to offset the finger position from // the selection highlight, once the user moved their finger // to a different line we should apply the offset and *not* switch // lines until recomputing the position with the finger offset. mSwitchedLines = true; break; return; } } int startOffset; Loading @@ -4687,46 +4780,6 @@ public class Editor { Selection.setSelection((Spannable) mTextView.getText(), startOffset, offset); } } break; case MotionEvent.ACTION_UP: if (mDragAcceleratorActive) { // No longer dragging to select text, let the parent intercept events. mTextView.getParent().requestDisallowInterceptTouchEvent(false); show(); int startOffset = mTextView.getSelectionStart(); int endOffset = mTextView.getSelectionEnd(); // Since we don't let drag handles pass once they're visible, we need to // make sure the start / end locations are correct because the user *can* // switch directions during the initial drag. if (endOffset < startOffset) { int tmp = endOffset; endOffset = startOffset; startOffset = tmp; // Also update the selection with the right offsets in this case. Selection.setSelection((Spannable) mTextView.getText(), startOffset, endOffset); } // Need to do this to display the handles. mStartHandle.showAtLocation(startOffset); mEndHandle.showAtLocation(endOffset); // No longer the first dragging motion, reset. startSelectionActionMode(); mDragAcceleratorActive = false; mStartOffset = -1; mSwitchedLines = false; } break; } } /** * @param event */ Loading @@ -4749,8 +4802,12 @@ public class Editor { public void resetTouchOffsets() { mMinTouchOffset = mMaxTouchOffset = -1; resetDragAcceleratorState(); } private void resetDragAcceleratorState() { mStartOffset = -1; mDragAcceleratorActive = false; mDragAcceleratorMode = DRAG_ACCELERATOR_MODE_INACTIVE; mSwitchedLines = false; } Loading @@ -4765,7 +4822,7 @@ public class Editor { * @return true if the user is selecting text using the drag accelerator. */ public boolean isDragAcceleratorActive() { return mDragAcceleratorActive; return mDragAcceleratorMode != DRAG_ACCELERATOR_MODE_INACTIVE; } public void onTouchModeChanged(boolean isInTouchMode) { Loading