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

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

Merge "Scribe initiation without InputConnection" into main

parents 43790ba5 26d404f7
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -21,8 +21,11 @@ import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.TOOL_TYPE_FINGER;
import static android.view.MotionEvent.TOOL_TYPE_STYLUS;
import static android.view.inputmethod.Flags.initiationWithoutInputConnection;


import static org.junit.Assume.assumeFalse;

import android.app.Instrumentation;
import android.content.Context;
import android.perftests.utils.BenchmarkState;
@@ -186,6 +189,7 @@ public class HandwritingInitiatorPerfTest {

    @Test
    public void onInputConnectionCreated() {
        assumeFalse(initiationWithoutInputConnection());
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        final View view = new View(mContext);
        final EditorInfo editorInfo = new EditorInfo();
@@ -199,6 +203,7 @@ public class HandwritingInitiatorPerfTest {

    @Test
    public void onInputConnectionClosed() {
        assumeFalse(initiationWithoutInputConnection());
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        final View view = new View(mContext);
        while (state.keepRunning()) {
+112 −21
Original line number Diff line number Diff line
@@ -23,8 +23,10 @@ import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.view.inputmethod.Flags;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Editor;
import android.widget.TextView;

import com.android.internal.annotations.VisibleForTesting;
@@ -80,6 +82,16 @@ public class HandwritingInitiator {
     * connections and only set mConnectedView to null when mConnectionCount is zero.
     */
    private int mConnectionCount = 0;

    /**
     * The reference to the View that currently has focus.
     * This replaces mConnecteView when {@code Flags#intitiationWithoutInputConnection()} is
     * enabled.
     */
    @Nullable
    @VisibleForTesting
    public WeakReference<View> mFocusedView = null;

    private final InputMethodManager mImm;

    private final int[] mTempLocation = new int[2];
@@ -112,9 +124,15 @@ public class HandwritingInitiator {
     *
     * If the stylus is hovering on an unconnected editor that supports handwriting, we always show
     * the hover icon.
     * TODO(b/308827131): Rename to FocusedView after Flag is flipped.
     */
    private boolean mShowHoverIconForConnectedView = true;

    /** When flag is enabled, touched editors don't wait for InputConnection for initiation.
     * However, delegation still waits for InputConnection.
     */
    private final boolean mInitiateWithoutConnection = Flags.initiationWithoutInputConnection();

    @VisibleForTesting
    public HandwritingInitiator(@NonNull ViewConfiguration viewConfiguration,
            @NonNull InputMethodManager inputMethodManager) {
@@ -201,8 +219,8 @@ public class HandwritingInitiator {
                    View candidateView = findBestCandidateView(mState.mStylusDownX,
                            mState.mStylusDownY, /* isHover */ false);
                    if (candidateView != null) {
                        if (candidateView == getConnectedView()) {
                            if (!candidateView.hasFocus()) {
                        if (candidateView == getConnectedOrFocusedView()) {
                            if (!mInitiateWithoutConnection && !candidateView.hasFocus()) {
                                requestFocusWithoutReveal(candidateView);
                            }
                            startHandwriting(candidateView);
@@ -217,9 +235,18 @@ public class HandwritingInitiator {
                            candidateView.getHandwritingDelegatorCallback().run();
                            mState.mHasPreparedHandwritingDelegation = true;
                        } else {
                            if (!mInitiateWithoutConnection) {
                                mState.mPendingConnectedView = new WeakReference<>(candidateView);
                            }
                            if (!candidateView.hasFocus()) {
                                requestFocusWithoutReveal(candidateView);
                            }
                            if (mInitiateWithoutConnection
                                    && updateFocusedView(candidateView,
                                            /* fromTouchEvent */ true)) {
                                startHandwriting(candidateView);
                            }
                        }
                    }
                }
                return mState.mHasInitiatedHandwriting || mState.mHasPreparedHandwritingDelegation;
@@ -244,11 +271,7 @@ public class HandwritingInitiator {
     */
    public void onDelegateViewFocused(@NonNull View view) {
        if (view == getConnectedView()) {
            if (tryAcceptStylusHandwritingDelegation(view)) {
                // A handwriting delegate view is accepted and handwriting starts; hide the
                // hover icon.
                mShowHoverIconForConnectedView = false;
            }
            tryAcceptStylusHandwritingDelegation(view);
        }
    }

@@ -260,6 +283,10 @@ public class HandwritingInitiator {
     * @see  #onInputConnectionClosed(View)
     */
    public void onInputConnectionCreated(@NonNull View view) {
        if (mInitiateWithoutConnection && !view.isHandwritingDelegate()) {
            // When flag is enabled, only delegation continues to wait for InputConnection.
            return;
        }
        if (!view.isAutoHandwritingEnabled()) {
            clearConnectedView();
            return;
@@ -274,12 +301,15 @@ public class HandwritingInitiator {
            // A new view just gain focus. By default, we should show hover icon for it.
            mShowHoverIconForConnectedView = true;
            if (view.isHandwritingDelegate() && tryAcceptStylusHandwritingDelegation(view)) {
                // A handwriting delegate view is accepted and handwriting starts; hide the
                // hover icon.
                // tryAcceptStylusHandwritingDelegation should set boolean below, however, we
                // cannot mock IMM to return true for acceptStylusDelegation().
                // TODO(b/324670412): we should move any dependent tests to integration and remove
                //  the assignment below.
                mShowHoverIconForConnectedView = false;
                return;
            }
            if (mState != null && mState.mPendingConnectedView != null
            if (!mInitiateWithoutConnection && mState != null
                    && mState.mPendingConnectedView != null
                    && mState.mPendingConnectedView.get() == view) {
                startHandwriting(view);
            }
@@ -293,6 +323,9 @@ public class HandwritingInitiator {
     * @param view the view that closed the InputConnection.
     */
    public void onInputConnectionClosed(@NonNull View view) {
        if (mInitiateWithoutConnection && !view.isHandwritingDelegate()) {
            return;
        }
        final View connectedView = getConnectedView();
        if (connectedView == null) return;
        if (connectedView == view) {
@@ -306,6 +339,48 @@ public class HandwritingInitiator {
        }
    }

    @Nullable
    private View getFocusedView() {
        if (mFocusedView == null) return null;
        return mFocusedView.get();
    }

    /**
     * Clear the tracked focused view tracked for handwriting initiation.
     * @param view the focused view.
     */
    public void clearFocusedView(View view) {
        if (view == null || mFocusedView == null) {
            return;
        }
        if (mFocusedView.get() == view) {
            mFocusedView = null;
        }
    }

    /**
     * Called when new {@link Editor} is focused.
     * @return {@code true} if handwriting can initiate for given view.
     */
    @VisibleForTesting
    public boolean updateFocusedView(@NonNull View view, boolean fromTouchEvent) {
        if (!view.shouldInitiateHandwriting()) {
            mFocusedView = null;
            return false;
        }

        final View focusedView = getFocusedView();
        if (focusedView != view) {
            mFocusedView = new WeakReference<>(view);
            // A new view just gain focus. By default, we should show hover icon for it.
            mShowHoverIconForConnectedView = true;
        }
        if (!fromTouchEvent) {
            tryAcceptStylusHandwritingDelegation(view);
        }
        return true;
    }

    /** Starts a stylus handwriting session for the view. */
    @VisibleForTesting
    public void startHandwriting(@NonNull View view) {
@@ -324,6 +399,9 @@ public class HandwritingInitiator {
     */
    @VisibleForTesting
    public boolean tryAcceptStylusHandwritingDelegation(@NonNull View view) {
        if (!view.isHandwritingDelegate() || (mState != null && mState.mHasInitiatedHandwriting)) {
            return false;
        }
        String delegatorPackageName =
                view.getAllowedHandwritingDelegatorPackageName();
        if (delegatorPackageName == null) {
@@ -337,6 +415,9 @@ public class HandwritingInitiator {
            if (view instanceof TextView) {
                ((TextView) view).hideHint();
            }
            // A handwriting delegate view is accepted and handwriting starts; hide the
            // hover icon.
            mShowHoverIconForConnectedView = false;
            return true;
        }
        return false;
@@ -377,16 +458,25 @@ public class HandwritingInitiator {
            return PointerIcon.getSystemIcon(context, PointerIcon.TYPE_HANDWRITING);
        }

        if (hoverView != getConnectedView()) {
        if (hoverView != getConnectedOrFocusedView()) {
            // The stylus is hovering on another view that supports handwriting. We should show
            // hover icon. Also reset the mShowHoverIconForConnectedView so that hover
            // icon is displayed again next time when the stylus hovers on connected view.
            // hover icon. Also reset the mShowHoverIconForFocusedView so that hover
            // icon is displayed again next time when the stylus hovers on focused view.
            mShowHoverIconForConnectedView = true;
            return PointerIcon.getSystemIcon(context, PointerIcon.TYPE_HANDWRITING);
        }
        return null;
    }

    // TODO(b/308827131): Remove once Flag is flipped.
    private View getConnectedOrFocusedView() {
        if (mInitiateWithoutConnection) {
            return mFocusedView == null ? null : mFocusedView.get();
        } else {
            return mConnectedView == null ? null : mConnectedView.get();
        }
    }

    private View getCachedHoverTarget() {
        if (mCachedHoverTarget == null) {
            return null;
@@ -458,20 +548,21 @@ public class HandwritingInitiator {
     */
    @Nullable
    private View findBestCandidateView(float x, float y, boolean isHover) {
        // TODO(b/308827131): Rename to FocusedView after Flag is flipped.
        // If the connectedView is not null and do not set any handwriting area, it will check
        // whether the connectedView's boundary contains the initial stylus position. If true,
        // directly return the connectedView.
        final View connectedView = getConnectedView();
        if (connectedView != null) {
        final View connectedOrFocusedView = getConnectedOrFocusedView();
        if (connectedOrFocusedView != null) {
            Rect handwritingArea = mTempRect;
            if (getViewHandwritingArea(connectedView, handwritingArea)
                    && isInHandwritingArea(handwritingArea, x, y, connectedView, isHover)
                    && shouldTriggerStylusHandwritingForView(connectedView)) {
            if (getViewHandwritingArea(connectedOrFocusedView, handwritingArea)
                    && isInHandwritingArea(handwritingArea, x, y, connectedOrFocusedView, isHover)
                    && shouldTriggerStylusHandwritingForView(connectedOrFocusedView)) {
                if (!isHover && mState != null) {
                    mState.mStylusDownWithinEditorBounds =
                            contains(handwritingArea, x, y, 0f, 0f, 0f, 0f);
                }
                return connectedView;
                return connectedOrFocusedView;
            }
        }

+14 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.view.inputmethod;

import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
import static android.view.inputmethod.Flags.initiationWithoutInputConnection;
import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_IMMEDIATE;
import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_MONITOR;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID;
@@ -1950,6 +1951,10 @@ public final class InputMethodManager {
        if (mServedView != null) {
            clearedView = mServedView;
            mServedView = null;
            if (initiationWithoutInputConnection() && clearedView.getViewRootImpl() != null) {
                clearedView.getViewRootImpl().getHandwritingInitiator()
                        .clearFocusedView(clearedView);
            }
        }
        if (clearedView != null) {
            if (DEBUG) {
@@ -2932,6 +2937,10 @@ public final class InputMethodManager {
            switch (res.result) {
                case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
                    mRestartOnNextWindowFocus = true;
                    if (initiationWithoutInputConnection()) {
                        mServedView.getViewRootImpl().getHandwritingInitiator().clearFocusedView(
                                mServedView);
                    }
                    mServedView = null;
                    break;
            }
@@ -3094,6 +3103,11 @@ public final class InputMethodManager {
            return false;
        }
        mServedView = mNextServedView;
        if (initiationWithoutInputConnection() && mServedView.onCheckIsTextEditor()
                && mServedView.isHandwritingDelegate()) {
            mServedView.getViewRootImpl().getHandwritingInitiator().onDelegateViewFocused(
                    mServedView);
        }
        if (mServedInputConnection != null) {
            mServedInputConnection.finishComposingTextFromImm();
        }
+8 −0
Original line number Diff line number Diff line
@@ -62,3 +62,11 @@ flag {
    bug: "311791923"
    is_fixed_read_only: true
}

flag {
    name: "initiation_without_input_connection"
    namespace: "input_method"
    description: "Feature flag for initiating handwriting without InputConnection"
    bug: "308827131"
    is_fixed_read_only: true
}
+91 −29

File changed.

Preview size limit exceeded, changes collapsed.