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

Commit 6e3f7a98 authored by Taran Singh's avatar Taran Singh Committed by Android (Google) Code Review
Browse files

Merge "Introduce new IMS method for Stylus events and dispatch" into tm-dev

parents 17feaf86 467fd168
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -18820,6 +18820,7 @@ package android.inputmethodservice {
    method public void onStartInput(android.view.inputmethod.EditorInfo, boolean);
    method public void onStartInputView(android.view.inputmethod.EditorInfo, boolean);
    method public boolean onStartStylusHandwriting();
    method public void onStylusHandwritingMotionEvent(@NonNull android.view.MotionEvent);
    method public void onUnbindInput();
    method @Deprecated public void onUpdateCursor(android.graphics.Rect);
    method public void onUpdateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo);
+78 −0
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import android.content.Context;
import android.os.IBinder;
import android.util.Slog;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;

import com.android.internal.policy.PhoneWindow;
@@ -40,6 +42,9 @@ final class InkWindow extends PhoneWindow {

    private final WindowManager mWindowManager;
    private boolean mIsViewAdded;
    private View mInkView;
    private InkVisibilityListener mInkViewVisibilityListener;
    private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener;

    public InkWindow(@NonNull Context context) {
        super(context);
@@ -102,4 +107,77 @@ final class InkWindow extends PhoneWindow {
        lp.token = token;
        setAttributes(lp);
    }

    @Override
    public void addContentView(View view, ViewGroup.LayoutParams params) {
        if (mInkView == null) {
            mInkView = view;
        } else if (mInkView != view) {
            throw new IllegalStateException("Only one Child Inking view is permitted.");
        }
        super.addContentView(view, params);
        initInkViewVisibilityListener();
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        mInkView = view;
        super.setContentView(view, params);
        initInkViewVisibilityListener();
    }

    @Override
    public void setContentView(View view) {
        mInkView = view;
        super.setContentView(view);
        initInkViewVisibilityListener();
    }

    @Override
    public void clearContentView() {
        if (mGlobalLayoutListener != null && mInkView != null) {
            mInkView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
        }
        mGlobalLayoutListener = null;
        mInkView = null;
        super.clearContentView();
    }

    /**
    * Listener used by InkWindow to time the dispatching of {@link MotionEvent}s to Ink view, once
    * it is visible to user.
    */
    interface InkVisibilityListener {
        void onInkViewVisible();
    }

    void setInkViewVisibilityListener(InkVisibilityListener listener) {
        mInkViewVisibilityListener = listener;
        initInkViewVisibilityListener();
    }

    void initInkViewVisibilityListener() {
        if (mInkView == null || mInkViewVisibilityListener == null
                || mGlobalLayoutListener != null) {
            return;
        }
        mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (mInkView.isVisibleToUser()) {
                    if (mInkViewVisibilityListener != null) {
                        mInkViewVisibilityListener.onInkViewVisible();
                    }
                    mInkView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    mGlobalLayoutListener = null;
                }
            }
        };
        mInkView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
    }

    boolean isInkViewVisible() {
        return getDecorView().getVisibility() == View.VISIBLE
                && mInkView != null && mInkView.isVisibleToUser();
    }
}
+45 −4
Original line number Diff line number Diff line
@@ -143,6 +143,7 @@ import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.util.RingBuffer;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInputContext;
import com.android.internal.view.InlineSuggestionsRequestInfo;
@@ -333,6 +334,17 @@ public class InputMethodService extends AbstractInputMethodService {
    private static final String PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS =
            "persist.sys.ime.can_render_gestural_nav_buttons";

    /**
     * Number of {@link MotionEvent} to buffer if IME is not ready with Ink view.
     * This number may be configured eventually based on device's touch sampling frequency.
     */
    private static final int MAX_EVENTS_BUFFER = 500;

    /**
     * A circular buffer of size MAX_EVENTS_BUFFER in case IME is taking too long to add ink view.
     **/
    private RingBuffer<MotionEvent> mPendingEvents;

    /**
     * Returns whether {@link InputMethodService} is responsible for rendering the back button and
     * the IME switcher button or not when the gestural navigation is enabled.
@@ -954,7 +966,8 @@ public class InputMethodService extends AbstractInputMethodService {
            mInkWindow.show();

            // deliver previous @param stylusEvents
            stylusEvents.forEach(mInkWindow.getDecorView()::dispatchTouchEvent);
            stylusEvents.forEach(InputMethodService.this::onStylusHandwritingMotionEvent);

            // create receiver for channel
            mHandwritingEventReceiver = new SimpleBatchedInputEventReceiver(
                    channel,
@@ -963,11 +976,11 @@ public class InputMethodService extends AbstractInputMethodService {
                        if (!(event instanceof MotionEvent)) {
                            return false;
                        }
                        return mInkWindow.getDecorView().dispatchTouchEvent((MotionEvent) event);
                        onStylusHandwritingMotionEvent((MotionEvent) event);
                        return true;
                    });
        }


        /**
         * {@inheritDoc}
         * @hide
@@ -2357,7 +2370,8 @@ public class InputMethodService extends AbstractInputMethodService {
     *
     * If the IME supports handwriting for the current input, it should return {@code true},
     * ensure its inking views are attached to the {@link #getStylusHandwritingWindow()}, and handle
     * stylus input received on the ink window via {@link #getCurrentInputConnection()}.
     * stylus input received from {@link #onStylusHandwritingMotionEvent(MotionEvent)} on the
     * {@link #getStylusHandwritingWindow()} via {@link #getCurrentInputConnection()}.
     * @return {@code true} if IME can honor the request, {@code false} if IME cannot at this time.
     */
    public boolean onStartStylusHandwriting() {
@@ -2365,6 +2379,33 @@ public class InputMethodService extends AbstractInputMethodService {
        return false;
    }

    /**
     * Called after {@link #onStartStylusHandwriting()} returns {@code true} for every Stylus
     * {@link MotionEvent}.
     * By default, this method forwards all {@link MotionEvent}s to the
     * {@link #getStylusHandwritingWindow()} once its visible, however IME can override it to
     * receive them sooner.
     * @param motionEvent {@link MotionEvent} from stylus.
     */
    public void onStylusHandwritingMotionEvent(@NonNull MotionEvent motionEvent) {
        if (mInkWindow.isInkViewVisible()) {
            mInkWindow.getDecorView().dispatchTouchEvent(motionEvent);
        } else {
            if (mPendingEvents == null) {
                mPendingEvents = new RingBuffer(MotionEvent.class, MAX_EVENTS_BUFFER);
            }
            mPendingEvents.append(motionEvent);
            mInkWindow.setInkViewVisibilityListener(() -> {
                if (mPendingEvents != null && !mPendingEvents.isEmpty()) {
                    for (MotionEvent event : mPendingEvents.toArray()) {
                        mInkWindow.getDecorView().dispatchTouchEvent(event);
                    }
                    mPendingEvents.clear();
                }
            });
        }
    }

    /**
     * Called when the current stylus handwriting session was finished (either by the system or
     * via {@link #finishStylusHandwriting()}.