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

Commit e301746a authored by Mihai Popa's avatar Mihai Popa
Browse files

[Magnifier-31] Do not magnify outside current line

Currently, after the cursor is reaching the end of a line, the magnifier
keeps following the finger even if the cursor cannot move anymore.

This CL limits the movement of the magnifier, ensuring it stays between
the bounds of the text line. Also, when the finger gets too far from the
end of the line, we dismiss the magnifier. We consider it went too far
when the cursor is not visible anymore inside the magnifier.

Bug: 72314536
Test: manual testing (both English and Arabic)
Test: atest FrameworksCoreTests:android.widget.TextViewActivityTest
Test: atest CtsWidgetTestCases:android.widget.cts.TextViewTest
Change-Id: I8dafba1fc8e7b8e482526e818831ece2ee20ac27
(cherry picked from commit dfc752bc)
Merged-In: I8dafba1fc8e7b8e482526e818831ece2ee20ac27
parent 17ea3058
Loading
Loading
Loading
Loading
+48 −17
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
@@ -4648,10 +4649,14 @@ public class Editor {
            return 0;
        }

        protected final void showMagnifier(@NonNull final MotionEvent event) {
            if (mMagnifier == null) {
                return;
            }
        /**
         * Computes the position where the magnifier should be shown, relative to
         * {@code mTextView}, and writes them to {@code showPosInView}. Also decides
         * whether the magnifier should be shown or dismissed after this touch event.
         * @return Whether the magnifier should be shown at the computed coordinates or dismissed.
         */
        private boolean obtainMagnifierShowCoordinates(@NonNull final MotionEvent event,
                final PointF showPosInView) {

            final int trigger = getMagnifierHandleTrigger();
            final int offset;
@@ -4669,26 +4674,52 @@ public class Editor {
            }

            if (offset == -1) {
                dismissMagnifier();
                return false;
            }

            final Layout layout = mTextView.getLayout();
            final int lineNumber = layout.getLineForOffset(offset);
            // Horizontally move the magnifier smoothly.

            // Horizontally move the magnifier smoothly but clamp inside the current line.
            final int[] textViewLocationOnScreen = new int[2];
            mTextView.getLocationOnScreen(textViewLocationOnScreen);
            final float xPosInView = event.getRawX() - textViewLocationOnScreen[0];
            final float touchXInView = event.getRawX() - textViewLocationOnScreen[0];
            final float lineLeft = mTextView.getLayout().getLineLeft(lineNumber)
                    + mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
            final float lineRight = mTextView.getLayout().getLineRight(lineNumber)
                    + mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
            final float contentWidth = Math.round(mMagnifier.getWidth() / mMagnifier.getZoom());
            if (touchXInView < lineLeft - contentWidth / 2
                    || touchXInView > lineRight + contentWidth / 2) {
                // The touch is too out of the bounds of the current line, so hide the magnifier.
                return false;
            }
            showPosInView.x = Math.max(lineLeft, Math.min(lineRight, touchXInView));

            // Vertically snap to middle of current line.
            final float yPosInView = (mTextView.getLayout().getLineTop(lineNumber)
            showPosInView.y = (mTextView.getLayout().getLineTop(lineNumber)
                    + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f
                    + mTextView.getTotalPaddingTop() - mTextView.getScrollY();

            return true;
        }

        protected final void updateMagnifier(@NonNull final MotionEvent event) {
            if (mMagnifier == null) {
                return;
            }

            final PointF showPosInView = new PointF();
            final boolean shouldShow = obtainMagnifierShowCoordinates(event, showPosInView);
            if (shouldShow) {
                // Make the cursor visible and stop blinking.
                mRenderCursorRegardlessTiming = true;
                mTextView.invalidateCursorPath();
                suspendBlink();

            mMagnifier.show(xPosInView, yPosInView);
                mMagnifier.show(showPosInView.x, showPosInView.y);
            } else {
                dismissMagnifier();
            }
        }

        protected final void dismissMagnifier() {
@@ -4877,11 +4908,11 @@ public class Editor {
                case MotionEvent.ACTION_DOWN:
                    mDownPositionX = ev.getRawX();
                    mDownPositionY = ev.getRawY();
                    showMagnifier(ev);
                    updateMagnifier(ev);
                    break;

                case MotionEvent.ACTION_MOVE:
                    showMagnifier(ev);
                    updateMagnifier(ev);
                    break;

                case MotionEvent.ACTION_UP:
@@ -5235,11 +5266,11 @@ public class Editor {
                    // re-engages the handle.
                    mTouchWordDelta = 0.0f;
                    mPrevX = UNSET_X_VALUE;
                    showMagnifier(event);
                    updateMagnifier(event);
                    break;

                case MotionEvent.ACTION_MOVE:
                    showMagnifier(event);
                    updateMagnifier(event);
                    break;

                case MotionEvent.ACTION_UP: