Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cc32bd83 authored by Sujith Ramakrishnan's avatar Sujith Ramakrishnan
Browse files

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
parent 7de699fb
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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);
+29 −13
Original line number Diff line number Diff line
@@ -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,11 +242,13 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme

        if (widget.isFocused() && !widget.didTouchFocusSelect()) {
            if (action == MotionEvent.ACTION_DOWN) {
              if (isSelecting(buffer)) {
                // 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
@@ -248,7 +256,16 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
                    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;
    }

+49 −3
Original line number Diff line number Diff line
@@ -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;
@@ -161,7 +172,9 @@ 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()) {
@@ -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;
+18 −0
Original line number Diff line number Diff line
@@ -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) {