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

Commit 1c0dacd2 authored by Justin Ghan's avatar Justin Ghan
Browse files

Store pending connected view when requesting focus

Before this change, HandwritingInitiator would request focus for a
handwriting view when it detected stylus movement on the view, and
expect an InputConnection to be created for that view.

However, in some apps, an InputConnection could be created for a
different view, before the InputConnection for the focus requested view
is created (for example, see b/286289041). This would cause handwriting
initiation to fail.

To handle this case, HandwritingInitiator stores a reference to the view
when it requests focus, then waits for the InputConnection to be created
for that view, and ignores any other InputConnections created in the
meantime.

Bug: 286287855
Test: atest HandwritingInitiatorTest
Change-Id: I63ca718e1ac7221f28aa6f4c6dd1d66974e12118
parent 169e37cc
Loading
Loading
Loading
Loading
+11 −36
Original line number Diff line number Diff line
@@ -208,6 +208,7 @@ public class HandwritingInitiator {
                            candidateView.getHandwritingDelegatorCallback().run();
                            mState.mHasPreparedHandwritingDelegation = true;
                        } else {
                            mState.mPendingConnectedView = new WeakReference<>(candidateView);
                            requestFocusWithoutReveal(candidateView);
                        }
                    }
@@ -269,8 +270,9 @@ public class HandwritingInitiator {
                mShowHoverIconForConnectedView = false;
                return;
            }
            if (mState != null && mState.mShouldInitHandwriting) {
                tryStartHandwriting();
            if (mState != null && mState.mPendingConnectedView != null
                    && mState.mPendingConnectedView.get() == view) {
                startHandwriting(view);
            }
        }
    }
@@ -295,40 +297,6 @@ public class HandwritingInitiator {
        }
    }

    /**
     * Try to initiate handwriting. For this method to successfully send startHandwriting signal,
     * the following 3 conditions should meet:
     *   a) The stylus movement exceeds the touchSlop.
     *   b) A View has built InputConnection with IME.
     *   c) The stylus event lands into the connected View's boundary.
     * This method will immediately fail without any side effect if condition a or b is not met.
     * However, if both condition a and b are met but the condition c is not met, it will reset the
     * internal states. And HandwritingInitiator won't attempt to call startHandwriting until the
     * next ACTION_DOWN.
     */
    private void tryStartHandwriting() {
        if (!mState.mExceedHandwritingSlop) {
            return;
        }
        final View connectedView = getConnectedView();
        if (connectedView == null) {
            return;
        }

        if (!connectedView.isAutoHandwritingEnabled()) {
            clearConnectedView();
            return;
        }

        final Rect handwritingArea = getViewHandwritingArea(connectedView);
        if (isInHandwritingArea(
                handwritingArea, mState.mStylusDownX, mState.mStylusDownY, connectedView)) {
            startHandwriting(connectedView);
        } else {
            mState.mShouldInitHandwriting = false;
        }
    }

    /** Starts a stylus handwriting session for the view. */
    @VisibleForTesting
    public void startHandwriting(@NonNull View view) {
@@ -631,6 +599,7 @@ public class HandwritingInitiator {
        private boolean mHasInitiatedHandwriting;

        private boolean mHasPreparedHandwritingDelegation;

        /**
         * Whether the current ongoing stylus MotionEvent sequence already exceeds the
         * handwriting slop.
@@ -639,6 +608,12 @@ public class HandwritingInitiator {
         */
        private boolean mExceedHandwritingSlop;

        /**
         * 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.
         */
        private WeakReference<View> mPendingConnectedView = null;

        /** The pointer id of the stylus pointer that is being tracked. */
        private final int mStylusPointerId;
        /** The time stamp when the stylus pointer goes down. */
+10 −4
Original line number Diff line number Diff line
@@ -221,8 +221,10 @@ public class HandwritingInitiatorTest {

    @Test
    public void onTouchEvent_startHandwriting_inputConnectionBuilt_stylusMoveInExtendedHWArea() {
        // 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;
        final int y1 = sHwArea1.bottom + HW_BOUNDS_OFFSETS_BOTTOM_PX / 2;
        final int y1 = sHwArea1.bottom + (sHwArea2.top - sHwArea1.bottom) / 3;
        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
        mHandwritingInitiator.onTouchEvent(stylusEvent1);

@@ -231,10 +233,14 @@ public class HandwritingInitiatorTest {
        MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
        mHandwritingInitiator.onTouchEvent(stylusEvent2);

        // InputConnection is created after stylus movement.
        mHandwritingInitiator.onInputConnectionCreated(mTestView1);
        // First create InputConnection for mTestView2 and verify that handwriting is not started.
        mHandwritingInitiator.onInputConnectionCreated(mTestView2);
        verify(mHandwritingInitiator, never()).startHandwriting(mTestView2);

        verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView1);
        // Next create InputConnection for mTextView1. Handwriting is started for this view since
        // the stylus down point is closest to this view.
        mHandwritingInitiator.onInputConnectionCreated(mTestView1);
        verify(mHandwritingInitiator).startHandwriting(mTestView1);
    }

    @Test