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

Commit aa8d73bf authored by Gilles Debunne's avatar Gilles Debunne
Browse files

Added a touch up filter in text selection handles.

This filters the touch up event, so that in case the handles'
position is altered when the finger is lifted up, this unwanted
movement is discarded.

Bug 3282095

Change-Id: Ibfe8f49d979091ba49139449ecc13f47050608d9
parent 3d7a9707
Loading
Loading
Loading
Loading
+79 −17
Original line number Diff line number Diff line
@@ -7931,9 +7931,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            max = Math.max(0, Math.max(selStart, selEnd));
        }

        ClipboardManager clipboard = (ClipboardManager)getContext()
                .getSystemService(Context.CLIPBOARD_SERVICE);

        switch (id) {
            case ID_COPY_URL:
                URLSpan[] urls = ((Spanned) mText).getSpans(min, max, URLSpan.class);
@@ -8404,8 +8401,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         */
        public void updatePosition(HandleView handle, int x, int y);

        public void updateOffset(HandleView handle, int offset);

        public void updatePosition();

        public int getCurrentOffset(HandleView handle);

        /**
         * This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
         * a chance to become active and/or visible.
@@ -8422,7 +8423,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    }

    private class PastePopupMenu implements OnClickListener {
        private PopupWindow mContainer;
        private final PopupWindow mContainer;
        private int mPositionX;
        private int mPositionY;
        private View mPasteView, mNoPasteView;
@@ -8521,12 +8522,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    }

    private class HandleView extends View {
        private boolean mPositionOnTop = false;
        private Drawable mDrawable;
        private PopupWindow mContainer;
        private final PopupWindow mContainer;
        private int mPositionX;
        private int mPositionY;
        private CursorController mController;
        private final CursorController mController;
        private boolean mIsDragging;
        private float mTouchToWindowOffsetX;
        private float mTouchToWindowOffsetY;
@@ -8543,6 +8543,46 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        private PastePopupMenu mPastePopupWindow;
        private Runnable mLongPressCallback;

        // Touch-up filter: number of previous positions remembered
        private static final int HISTORY_SIZE = 5;
        private static final int TOUCH_UP_FILTER_DELAY = 150;
        private final long[] mPreviousOffsetsTimes = new long[HISTORY_SIZE];
        private final int[] mPreviousOffsets = new int[HISTORY_SIZE];
        private int mPreviousOffsetIndex = 0;
        private int mNumberPreviousOffsets = 0;

        public void startTouchUpFilter(int offset) {
            mNumberPreviousOffsets = 0;
            addPositionToTouchUpFilter(offset);
        }

        public void addPositionToTouchUpFilter(int offset) {
            if (mNumberPreviousOffsets > 0 &&
                    mPreviousOffsets[mPreviousOffsetIndex] == offset) {
                // Make sure only actual changes of position are recorded.
                return;
            }

            mPreviousOffsetIndex = (mPreviousOffsetIndex + 1) % HISTORY_SIZE;
            mPreviousOffsets[mPreviousOffsetIndex] = offset;
            mPreviousOffsetsTimes[mPreviousOffsetIndex] = SystemClock.uptimeMillis();
            mNumberPreviousOffsets++;
        }

        public void filterOnTouchUp() {
            final long now = SystemClock.uptimeMillis();
            int i = 0;
            int index = 0;
            final int iMax = Math.min(mNumberPreviousOffsets, HISTORY_SIZE);
            while (i < iMax) {
                index = (mPreviousOffsetIndex - i + HISTORY_SIZE) % HISTORY_SIZE;
                if ((now - mPreviousOffsetsTimes[index]) >= TOUCH_UP_FILTER_DELAY) break;
                i++;
            }

            mController.updateOffset(this, mPreviousOffsets[index]);
        }

        public static final int LEFT = 0;
        public static final int CENTER = 1;
        public static final int RIGHT = 2;
@@ -8738,20 +8778,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        @Override
        protected void onDraw(Canvas c) {
            mDrawable.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
            if (mPositionOnTop) {
                c.save();
                c.rotate(180, (mRight - mLeft) / 2, (mBottom - mTop) / 2);
                mDrawable.draw(c);
                c.restore();
            } else {
            mDrawable.draw(c);
        }
        }

        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN: {
                startTouchUpFilter(mController.getCurrentOffset(this));
                mDownPositionX = ev.getRawX();
                mDownPositionY = ev.getRawY();
                mTouchToWindowOffsetX = mDownPositionX - mPositionX;
@@ -8808,6 +8842,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                        }
                    }
                }
                filterOnTouchUp();
                mIsDragging = false;
                break;

@@ -8825,6 +8860,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        }

        void positionAtCursor(final int offset, boolean bottom) {
            addPositionToTouchUpFilter(offset);
            final int width = mDrawable.getIntrinsicWidth();
            final int height = mDrawable.getIntrinsicHeight();
            final int line = mLayout.getLineForOffset(offset);
@@ -8940,12 +8976,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            int offset = getHysteresisOffset(x, y, previousOffset);

            if (offset != previousOffset) {
                Selection.setSelection((Spannable) mText, offset);
                updatePosition();
                updateOffset(handle, offset);
            }
            hideDelayed();
        }

        public void updateOffset(HandleView handle, int offset) {
            Selection.setSelection((Spannable) mText, offset);
            updatePosition();
        }

        public void updatePosition() {
            final int offset = getSelectionStart();

@@ -8959,6 +8999,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            getHandle().positionAtCursor(offset, true);
        }

        public int getCurrentOffset(HandleView handle) {
            return getSelectionStart();
        }

        public boolean onTouchEvent(MotionEvent ev) {
            return false;
        }
@@ -9069,6 +9113,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            updatePosition();
        }

        public void updateOffset(HandleView handle, int offset) {
            int start = getSelectionStart();
            int end = getSelectionEnd();

            if (mStartHandle == handle) {
                start = offset;
            } else {
                end = offset;
            }

            Selection.setSelection((Spannable) mText, start, end);
            updatePosition();
        }

        public void updatePosition() {
            if (!isShowing()) {
                return;
@@ -9089,6 +9147,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            mEndHandle.positionAtCursor(selectionEnd, true);
        }

        public int getCurrentOffset(HandleView handle) {
            return mStartHandle == handle ? getSelectionStart() : getSelectionEnd();
        }

        public boolean onTouchEvent(MotionEvent event) {
            // This is done even when the View does not have focus, so that long presses can start
            // selection and tap can move cursor from this tap position.