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

Commit 97ae05c7 authored by Taran Singh's avatar Taran Singh
Browse files

Make handwriting surface ignore touch when stylus is down

With support of simultaneous touch and stylus feature, we now allow
user to simulataneous touch and stylus on different windows. However,
stylus handwriting is a special case (using spy surface) can receive
stylus and finger touch in the app, which can steal focus and end stylus
handwriting session abruptly.

This change makes the app and system areas untouchable while stylus is
down to prevent accidental touches. When stylus is UP, its reverted to
NOT_TOUCHABLE (i.e. lets the touch passthrough).

Bug: 332850063
Test: atest StylusHandwritingTest

Change-Id: I927cee4e8ebb727f157d24b81558ccbe55696d14
parent 964cb83b
Loading
Loading
Loading
Loading
+32 −3
Original line number Diff line number Diff line
@@ -765,6 +765,7 @@ public class InputMethodService extends AbstractInputMethodService {

        private boolean mSystemCallingShowSoftInput;
        private boolean mSystemCallingHideSoftInput;
        private boolean mSimultaneousStylusAndTouchEnabled;

        /**
         * {@inheritDoc}
@@ -1129,9 +1130,11 @@ public class InputMethodService extends AbstractInputMethodService {
            mShowInputRequested = false;

            mInkWindow.show();
            mSimultaneousStylusAndTouchEnabled =
                    com.android.input.flags.Flags.enableMultiDeviceInput();

            // deliver previous @param stylusEvents
            stylusEvents.forEach(InputMethodService.this::onStylusHandwritingMotionEvent);
            stylusEvents.forEach(this::deliverStylusHandwritingMotionEvent);

            // create receiver for channel
            mHandwritingEventReceiver = new InputEventReceiver(channel, Looper.getMainLooper()) {
@@ -1139,10 +1142,15 @@ public class InputMethodService extends AbstractInputMethodService {
                public void onInputEvent(InputEvent event) {
                    boolean handled = false;
                    try {
                        if (!(event instanceof MotionEvent)) {
                        if (!(event instanceof MotionEvent motionEvent)) {
                            return;
                        }
                        onStylusHandwritingMotionEvent((MotionEvent) event);
                        if (!motionEvent.isStylusPointer()) {
                            // Handwriting surface is touchable, we don't want these touch events
                            // to get to the IME.
                            return;
                        }
                        deliverStylusHandwritingMotionEvent(motionEvent);
                        scheduleHandwritingSessionTimeout();
                        handled = true;
                    } finally {
@@ -1153,6 +1161,27 @@ public class InputMethodService extends AbstractInputMethodService {
            scheduleHandwritingSessionTimeout();
        }

        private void deliverStylusHandwritingMotionEvent(MotionEvent motionEvent) {
            onStylusHandwritingMotionEvent(motionEvent);
            if (!mSimultaneousStylusAndTouchEnabled) {
                return;
            }
            switch (motionEvent.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    // Consume and ignore all touches while stylus is down to prevent
                    // accidental touches from going to the app while writing.
                    mPrivOps.setHandwritingSurfaceNotTouchable(false);
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    // Go back to only consuming stylus events so that the user
                    // can continue to interact with the app using touch
                    // when the stylus is not down.
                    mPrivOps.setHandwritingSurfaceNotTouchable(true);
                    break;
            }
        }

        /**
         * {@inheritDoc}
         * @hide
+1 −0
Original line number Diff line number Diff line
@@ -49,4 +49,5 @@ oneway interface IInputMethodPrivilegedOperations {
    void onStylusHandwritingReady(int requestId, int pid);
    void resetStylusHandwriting(int requestId);
    void switchKeyboardLayoutAsync(int direction);
    void setHandwritingSurfaceNotTouchable(boolean notTouchable);
}
+18 −0
Original line number Diff line number Diff line
@@ -143,6 +143,24 @@ public final class InputMethodPrivilegedOperations {
        }
    }

    /**
     * Calls {@link IInputMethodPrivilegedOperations#setHandwritingSurfaceNotTouchable(boolean)}.
     *
     * @param notTouchable {@code true} to make handwriting surface not-touchable (pass-through).
     */
    @AnyThread
    public void setHandwritingSurfaceNotTouchable(boolean notTouchable) {
        final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
        if (ops == null) {
            return;
        }
        try {
            ops.setHandwritingSurfaceNotTouchable(notTouchable);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String,
     * AndroidFuture)}.
+11 −0
Original line number Diff line number Diff line
@@ -85,6 +85,17 @@ final class HandwritingEventReceiverSurface {
        mIsIntercepting = true;
    }

    void setNotTouchable(boolean notTouchable) {
        if (notTouchable) {
            mWindowHandle.inputConfig |= InputConfig.NOT_TOUCHABLE;
        } else {
            mWindowHandle.inputConfig &=  ~InputConfig.NOT_TOUCHABLE;
        }
        new SurfaceControl.Transaction()
                .setInputWindowInfo(mInputSurface, mWindowHandle)
                .apply();
    }

    boolean isIntercepting() {
        return mIsIntercepting;
    }
+7 −0
Original line number Diff line number Diff line
@@ -159,6 +159,13 @@ final class HandwritingModeController {
        return OptionalInt.of(mCurrentRequestId);
    }

    void setNotTouchable(boolean notTouchable) {
        if (!getCurrentRequestId().isPresent()) {
            return;
        }
        mHandwritingSurface.setNotTouchable(notTouchable);
    }

    boolean isStylusGestureOngoing() {
        if (mRecordingGestureAfterStylusUp && !mHandwritingBuffer.isEmpty()) {
            // If it is less than AFTER_STYLUS_UP_ALLOW_PERIOD_MS after the stylus up event, return
Loading