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

Commit 8c13cc3a authored by Justin Ghan's avatar Justin Ghan Committed by Android (Google) Code Review
Browse files

Merge "Set cursor position in handwriting initiation" into udc-qpr-dev

parents 0d4d8cda 88f4cc5d
Loading
Loading
Loading
Loading
+32 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;

import com.android.internal.annotations.VisibleForTesting;
@@ -81,6 +82,8 @@ public class HandwritingInitiator {
    private int mConnectionCount = 0;
    private final InputMethodManager mImm;

    private final int[] mTempLocation = new int[2];

    private final Rect mTempRect = new Rect();

    private final RectF mTempRectF = new RectF();
@@ -429,7 +432,19 @@ public class HandwritingInitiator {
        return null;
    }

    private static void requestFocusWithoutReveal(View view) {
    private void requestFocusWithoutReveal(View view) {
        if (view instanceof EditText editText && !mState.mStylusDownWithinEditorBounds) {
            // If the stylus down point was inside the EditText's bounds, then the EditText will
            // automatically set its cursor position nearest to the stylus down point when it
            // gains focus. If the stylus down point was outside the EditText's bounds (within
            // the extended handwriting bounds), then we must calculate and set the cursor
            // position manually.
            view.getLocationInWindow(mTempLocation);
            int offset = editText.getOffsetForPosition(
                    mState.mStylusDownX - mTempLocation[0],
                    mState.mStylusDownY - mTempLocation[1]);
            editText.setSelection(offset);
        }
        if (view.getRevealOnFocusHint()) {
            view.setRevealOnFocusHint(false);
            view.requestFocus();
@@ -457,6 +472,10 @@ public class HandwritingInitiator {
            if (getViewHandwritingArea(connectedView, handwritingArea)
                    && isInHandwritingArea(handwritingArea, x, y, connectedView, isHover)
                    && shouldTriggerStylusHandwritingForView(connectedView)) {
                if (!isHover && mState != null) {
                    mState.mStylusDownWithinEditorBounds =
                            contains(handwritingArea, x, y, 0f, 0f, 0f, 0f);
                }
                return connectedView;
            }
        }
@@ -475,7 +494,12 @@ public class HandwritingInitiator {
            }

            final float distance = distance(handwritingArea, x, y);
            if (distance == 0f) return view;
            if (distance == 0f) {
                if (!isHover && mState != null) {
                    mState.mStylusDownWithinEditorBounds = true;
                }
                return view;
            }
            if (distance < minDistance) {
                minDistance = distance;
                bestCandidate = view;
@@ -657,6 +681,12 @@ public class HandwritingInitiator {
         */
        private boolean mExceedHandwritingSlop;

        /**
         * Whether the stylus down point of the MotionEvent sequence was within the editor's bounds
         * (not including the extended handwriting bounds).
         */
        private boolean mStylusDownWithinEditorBounds;

        /**
         * A view which has requested focus and is pending input connection creation. When an input
         * connection is created for the view, a handwriting session should be started for the view.
+19 −2
Original line number Diff line number Diff line
@@ -88,8 +88,8 @@ public class HandwritingInitiatorTest {
    }

    private HandwritingInitiator mHandwritingInitiator;
    private View mTestView1;
    private View mTestView2;
    private EditText mTestView1;
    private EditText mTestView2;
    private Context mContext;

    @Before
@@ -123,6 +123,9 @@ public class HandwritingInitiatorTest {

    @Test
    public void onTouchEvent_startHandwriting_when_stylusMoveOnce_withinHWArea() {
        mTestView1.setText("hello");
        when(mTestView1.getOffsetForPosition(anyFloat(), anyFloat())).thenReturn(4);

        mHandwritingInitiator.onInputConnectionCreated(mTestView1);
        final int x1 = (sHwArea1.left + sHwArea1.right) / 2;
        final int y1 = (sHwArea1.top + sHwArea1.bottom) / 2;
@@ -141,6 +144,9 @@ public class HandwritingInitiatorTest {
        // After IMM.startHandwriting is triggered, onTouchEvent should return true for ACTION_MOVE
        // events so that the events are not dispatched to the view tree.
        assertThat(onTouchEventResult2).isTrue();
        // Since the stylus down point was inside the TextView's bounds, the handwriting initiator
        // does not need to set the cursor position.
        verify(mTestView1, never()).setSelection(anyInt());
    }

    @Test
@@ -185,6 +191,9 @@ public class HandwritingInitiatorTest {

    @Test
    public void onTouchEvent_startHandwriting_when_stylusMove_withinExtendedHWArea() {
        mTestView1.setText("hello");
        when(mTestView1.getOffsetForPosition(anyFloat(), anyFloat())).thenReturn(4);

        mHandwritingInitiator.onInputConnectionCreated(mTestView1);
        final int x1 = sHwArea1.left - HW_BOUNDS_OFFSETS_LEFT_PX / 2;
        final int y1 = sHwArea1.top - HW_BOUNDS_OFFSETS_TOP_PX / 2;
@@ -199,6 +208,9 @@ public class HandwritingInitiatorTest {

        // Stylus movement within extended HandwritingArea should trigger IMM.startHandwriting once.
        verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView1);
        // Since the stylus down point was outside the TextView's bounds, the handwriting initiator
        // sets the cursor position.
        verify(mTestView1).setSelection(4);
    }

    @Test
@@ -221,6 +233,8 @@ public class HandwritingInitiatorTest {

    @Test
    public void onTouchEvent_startHandwriting_inputConnectionBuilt_stylusMoveInExtendedHWArea() {
        mTestView1.setText("hello");
        when(mTestView1.getOffsetForPosition(anyFloat(), anyFloat())).thenReturn(4);
        // The stylus down point is between mTestView1 and  mTestView2, but it is within the
        // extended handwriting area of both views. It is closer to mTestView1.
        final int x1 = sHwArea1.right + HW_BOUNDS_OFFSETS_RIGHT_PX / 2;
@@ -241,6 +255,9 @@ public class HandwritingInitiatorTest {
        // the stylus down point is closest to this view.
        mHandwritingInitiator.onInputConnectionCreated(mTestView1);
        verify(mHandwritingInitiator).startHandwriting(mTestView1);
        // Since the stylus down point was outside the TextView's bounds, the handwriting initiator
        // sets the cursor position.
        verify(mTestView1).setSelection(4);
    }

    @Test
+15 −4
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package android.view.stylus;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

@@ -26,22 +29,23 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;

import androidx.test.platform.app.InstrumentationRegistry;

public class HandwritingTestUtil {
    public static View createView(Rect handwritingArea) {
    public static EditText createView(Rect handwritingArea) {
        return createView(handwritingArea, true /* autoHandwritingEnabled */,
                true /* isStylusHandwritingAvailable */);
    }

    public static View createView(Rect handwritingArea, boolean autoHandwritingEnabled,
    public static EditText createView(Rect handwritingArea, boolean autoHandwritingEnabled,
            boolean isStylusHandwritingAvailable) {
        return createView(handwritingArea, autoHandwritingEnabled, isStylusHandwritingAvailable,
                0, 0, 0, 0);
    }

    public static View createView(Rect handwritingArea, boolean autoHandwritingEnabled,
    public static EditText createView(Rect handwritingArea, boolean autoHandwritingEnabled,
            boolean isStylusHandwritingAvailable,
            float handwritingBoundsOffsetLeft, float handwritingBoundsOffsetTop,
            float handwritingBoundsOffsetRight, float handwritingBoundsOffsetBottom) {
@@ -68,7 +72,7 @@ public class HandwritingTestUtil {
            }
        };

        View view = spy(new View(context));
        EditText view = spy(new EditText(context));
        when(view.isAttachedToWindow()).thenReturn(true);
        when(view.isAggregatedVisible()).thenReturn(true);
        when(view.isStylusHandwritingAvailable()).thenReturn(isStylusHandwritingAvailable);
@@ -77,6 +81,13 @@ public class HandwritingTestUtil {
        when(view.getHandwritingBoundsOffsetTop()).thenReturn(handwritingBoundsOffsetTop);
        when(view.getHandwritingBoundsOffsetRight()).thenReturn(handwritingBoundsOffsetRight);
        when(view.getHandwritingBoundsOffsetBottom()).thenReturn(handwritingBoundsOffsetBottom);
        doAnswer(invocation -> {
            int[] outLocation = invocation.getArgument(0);
            outLocation[0] = handwritingArea.left;
            outLocation[1] = handwritingArea.top;
            return null;
        }).when(view).getLocationInWindow(any());
        when(view.getOffsetForPosition(anyFloat(), anyFloat())).thenReturn(0);
        view.setAutoHandwritingEnabled(autoHandwritingEnabled);
        parent.addView(view);
        return view;