Add support for mouse-based text selection.
Incorporate patch from Logitech (donated under AOSP license) to the framework to add mouse-based text selection to ArrowKeyMovementMethod. Bug: 14652753 Change-Id: Iab264bb954b72ccedfada763eba8f13ef37a4578
This commit is contained in:
@@ -32297,6 +32297,7 @@ package android.view {
|
||||
method public final float getY();
|
||||
method public final float getY(int);
|
||||
method public final float getYPrecision();
|
||||
method public final boolean isButtonPressed(int);
|
||||
method public static android.view.MotionEvent obtain(long, long, int, int, android.view.MotionEvent.PointerProperties[], android.view.MotionEvent.PointerCoords[], int, int, float, float, int, int, int, int);
|
||||
method public static deprecated android.view.MotionEvent obtain(long, long, int, int, int[], android.view.MotionEvent.PointerCoords[], int, float, float, int, int, int, int);
|
||||
method public static android.view.MotionEvent obtain(long, long, int, float, float, float, float, int, float, float, int, int);
|
||||
|
||||
@@ -20,6 +20,7 @@ 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;
|
||||
@@ -221,11 +222,16 @@ 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);
|
||||
@@ -236,19 +242,30 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
|
||||
|
||||
if (widget.isFocused() && !widget.didTouchFocusSelect()) {
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
if (isSelecting(buffer)) {
|
||||
int offset = widget.getOffsetForPosition(event.getX(), event.getY());
|
||||
|
||||
buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
|
||||
|
||||
// Disallow intercepting of the touch events, so that
|
||||
// users can scroll and select at the same time.
|
||||
// without this, users would get booted out of select
|
||||
// mode once the view detected it needed to scroll.
|
||||
widget.getParent().requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
// 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)) {
|
||||
int offset = widget.getOffsetForPosition(event.getX(), event.getY());
|
||||
buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
|
||||
// Disallow intercepting of the touch events, so that
|
||||
// users can scroll and select at the same time.
|
||||
// without this, users would get booted out of select
|
||||
// mode once the view detected it needed to scroll.
|
||||
widget.getParent().requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
} else if (action == MotionEvent.ACTION_MOVE) {
|
||||
if (isSelecting(buffer) && handled) {
|
||||
|
||||
// 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 (isMouse && Touch.isSelectionStarted(buffer)) {
|
||||
int offset = buffer.getSpanStart(LAST_TAP_DOWN);
|
||||
Selection.setSelection(buffer, offset);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -277,7 +294,7 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
|
||||
}
|
||||
|
||||
int offset = widget.getOffsetForPosition(event.getX(), event.getY());
|
||||
if (isSelecting(buffer)) {
|
||||
if (isTouchSelecting(isMouse, buffer)) {
|
||||
buffer.removeSpan(LAST_TAP_DOWN);
|
||||
Selection.extendSelection(buffer, offset);
|
||||
}
|
||||
@@ -288,7 +305,6 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,12 +119,18 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,9 +141,14 @@ public class Touch {
|
||||
MetaKeyKeyListener.META_SHIFT_ON) == 1
|
||||
|| MetaKeyKeyListener.getMetaState(buffer,
|
||||
MetaKeyKeyListener.META_SELECTING) != 0;
|
||||
|
||||
if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
|
||||
ds[0].mIsActivelySelecting = false;
|
||||
}
|
||||
|
||||
float dx;
|
||||
float dy;
|
||||
if (cap) {
|
||||
if (cap && event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
|
||||
// if we're selecting, we want the scroll to go in
|
||||
// the direction of the drag
|
||||
dx = event.getX() - ds[0].mX;
|
||||
@@ -157,11 +168,13 @@ public class Touch {
|
||||
|
||||
ny = Math.min(ny, layout.getHeight() - (widget.getHeight() - padding));
|
||||
ny = Math.max(ny, 0);
|
||||
|
||||
|
||||
int oldX = widget.getScrollX();
|
||||
int oldY = widget.getScrollY();
|
||||
|
||||
scrollTo(widget, layout, nx, ny);
|
||||
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()) {
|
||||
@@ -194,6 +207,37 @@ 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;
|
||||
@@ -201,6 +245,8 @@ 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;
|
||||
|
||||
@@ -3131,6 +3131,24 @@ public final class MotionEvent extends InputEvent implements Parcelable {
|
||||
return symbolicName != null ? symbolicName : Integer.toString(toolType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a mouse or stylus button (or combination of buttons) is pressed.
|
||||
* @param button Button (or combination of buttons).
|
||||
* @return True if specified buttons are pressed.
|
||||
*
|
||||
* @see #BUTTON_PRIMARY
|
||||
* @see #BUTTON_SECONDARY
|
||||
* @see #BUTTON_TERTIARY
|
||||
* @see #BUTTON_FORWARD
|
||||
* @see #BUTTON_BACK
|
||||
*/
|
||||
public final boolean isButtonPressed(int button) {
|
||||
if (button == 0) {
|
||||
return false;
|
||||
}
|
||||
return (getButtonState() & button) == button;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<MotionEvent> CREATOR
|
||||
= new Parcelable.Creator<MotionEvent>() {
|
||||
public MotionEvent createFromParcel(Parcel in) {
|
||||
|
||||
Reference in New Issue
Block a user