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

Commit 1b75fb86 authored by Taran Singh's avatar Taran Singh
Browse files

Zero jank IMF 3/N

Introduce new async API method for accepting stylus delegation.

Bug: 293640003
Test: atest CtsInputMethodTestCase HandwritingInitiatorTest

Change-Id: I732ce7320a31223f3cb5abce5a05513b3a921138
parent 77c6fd2c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -56277,6 +56277,7 @@ package android.view.inputmethod {
  public final class InputMethodManager {
    method public boolean acceptStylusHandwritingDelegation(@NonNull android.view.View);
    method public boolean acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String);
    method @FlaggedApi("android.view.inputmethod.use_zero_jank_proxy") public void acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
    method @FlaggedApi("android.view.inputmethod.home_screen_handwriting_delegator") public boolean acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String, int);
    method public void dispatchKeyEventFromInputMethod(@Nullable android.view.View, @NonNull android.view.KeyEvent);
    method public void displayCompletions(android.view.View, android.view.inputmethod.CompletionInfo[]);
+40 −10
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.view;

import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -38,6 +39,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

/**
 * Initiates handwriting mode once it detects stylus movement in handwritable areas.
@@ -415,12 +417,43 @@ public class HandwritingInitiator {
     */
    @VisibleForTesting
    public boolean tryAcceptStylusHandwritingDelegation(@NonNull View view) {
        if (Flags.useZeroJankProxy()) {
            tryAcceptStylusHandwritingDelegationAsync(view);
        } else {
            return tryAcceptStylusHandwritingDelegationInternal(view);
        }
        return false;
    }

    private boolean tryAcceptStylusHandwritingDelegationInternal(@NonNull View view) {
        String delegatorPackageName =
                view.getAllowedHandwritingDelegatorPackageName();
        if (delegatorPackageName == null) {
            delegatorPackageName = view.getContext().getOpPackageName();
        }
        if (mImm.acceptStylusHandwritingDelegation(view, delegatorPackageName)) {
            onDelegationAccepted(view);
            return true;
        }
        return false;
    }

    @FlaggedApi(Flags.FLAG_USE_ZERO_JANK_PROXY)
    private void tryAcceptStylusHandwritingDelegationAsync(@NonNull View view) {
        String delegatorPackageName =
                view.getAllowedHandwritingDelegatorPackageName();
        if (delegatorPackageName == null) {
            delegatorPackageName = view.getContext().getOpPackageName();
        }
        Consumer<Boolean> consumer = delegationAccepted -> {
            if (delegationAccepted) {
                onDelegationAccepted(view);
            }
        };
        mImm.acceptStylusHandwritingDelegation(view, delegatorPackageName, view::post, consumer);
    }

    private void onDelegationAccepted(View view) {
        if (mState != null) {
            mState.mHasInitiatedHandwriting = true;
            mState.mShouldInitHandwriting = false;
@@ -431,9 +464,6 @@ public class HandwritingInitiator {
        // A handwriting delegate view is accepted and handwriting starts; hide the
        // hover icon.
        mShowHoverIconForConnectedView = false;
            return true;
        }
        return false;
    }

    /**
+23 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.view.WindowManager;
import android.window.ImeOnBackInvokedDispatcher;

import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInputMethodClient;
@@ -587,6 +588,28 @@ final class IInputMethodManagerGlobalInvoker {
        }
    }

    /** Returns {@code true} if method is invoked */
    @AnyThread
    static boolean acceptStylusHandwritingDelegationAsync(
            @NonNull IInputMethodClient client,
            @UserIdInt int userId,
            @NonNull String delegatePackageName,
            @NonNull String delegatorPackageName,
            @InputMethodManager.HandwritingDelegateFlags int flags,
            @NonNull IBooleanListener callback) {
        final IInputMethodManager service = getService();
        if (service == null) {
            return false;
        }
        try {
            service.acceptStylusHandwritingDelegationAsync(
                    client, userId, delegatePackageName, delegatorPackageName, flags, callback);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        return true;
    }

    @AnyThread
    @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
    static boolean isStylusHandwritingAvailableAsUser(
+86 −5
Original line number Diff line number Diff line
@@ -109,6 +109,7 @@ import android.window.WindowOnBackInvokedDispatcher;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IInputMethodSession;
@@ -2518,16 +2519,46 @@ public final class InputMethodManager {
                view, /* delegatorPackageName= */ null, /* handwritingDelegateFlags= */ 0);
    }

    private void startStylusHandwritingInternalAsync(
            @NonNull View view, @Nullable String delegatorPackageName,
            @HandwritingDelegateFlags int handwritingDelegateFlags,
            @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
        Objects.requireNonNull(view);
        Objects.requireNonNull(executor);
        Objects.requireNonNull(callback);

        startStylusHandwritingInternal(
                view, delegatorPackageName, handwritingDelegateFlags, executor, callback);
    }

    private void sendFailureCallback(@NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<Boolean> callback) {
        if (executor == null || callback == null) {
            return;
        }
        executor.execute(() -> callback.accept(false));
    }

    private boolean startStylusHandwritingInternal(
            @NonNull View view, @Nullable String delegatorPackageName,
            @HandwritingDelegateFlags int handwritingDelegateFlags) {
        return startStylusHandwritingInternal(
                view, delegatorPackageName, handwritingDelegateFlags,
                null /* executor */, null /* callback */);
    }

    private boolean startStylusHandwritingInternal(
            @NonNull View view, @Nullable String delegatorPackageName,
            @HandwritingDelegateFlags int handwritingDelegateFlags, Executor executor,
            Consumer<Boolean> callback) {
        Objects.requireNonNull(view);
        boolean useCallback = callback != null;

        // Re-dispatch if there is a context mismatch.
        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
        if (fallbackImm != null) {
            fallbackImm.startStylusHandwritingInternal(
                    view, delegatorPackageName, handwritingDelegateFlags);
                    view, delegatorPackageName, handwritingDelegateFlags, executor, callback);
        }

        boolean useDelegation = !TextUtils.isEmpty(delegatorPackageName);
@@ -2537,23 +2568,42 @@ public final class InputMethodManager {
            if (!hasServedByInputMethodLocked(view)) {
                Log.w(TAG,
                        "Ignoring startStylusHandwriting as view=" + view + " is not served.");
                sendFailureCallback(executor, callback);
                return false;
            }
            if (view.getViewRootImpl() != mCurRootView) {
                Log.w(TAG,
                        "Ignoring startStylusHandwriting: View's window does not have focus.");
                sendFailureCallback(executor, callback);
                return false;
            }
            if (useDelegation) {
                if (useCallback) {
                    IBooleanListener listener = new IBooleanListener.Stub() {
                        @Override
                        public void onResult(boolean value) {
                            executor.execute(() -> {
                                callback.accept(value);
                            });
                        }
                    };
                    if (!IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegationAsync(
                            mClient, UserHandle.myUserId(), view.getContext().getOpPackageName(),
                            delegatorPackageName, handwritingDelegateFlags, listener)) {
                        sendFailureCallback(executor, callback);
                    }
                    return true;
                } else {
                    return IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegation(
                            mClient, UserHandle.myUserId(), view.getContext().getOpPackageName(),
                            delegatorPackageName, handwritingDelegateFlags);
                }
            } else {
                IInputMethodManagerGlobalInvoker.startStylusHandwriting(mClient);
            }
                return false;
            }
        }
    }

    /**
     * Starts a connectionless stylus handwriting session. A connectionless session differs from a
@@ -2788,6 +2838,7 @@ public final class InputMethodManager {
     *     #prepareStylusHandwritingDelegation(View, String)} and delegation is accepted
     * @see #prepareStylusHandwritingDelegation(View, String)
     * @see #acceptStylusHandwritingDelegation(View)
     * TODO (b/293640003): deprecate this method once flag is enabled.
     */
    // TODO(b/300979854): Once connectionless APIs are finalised, update documentation to add:
    // <p>Otherwise, if the delegator view previously started delegation using {@link
@@ -2803,6 +2854,36 @@ public final class InputMethodManager {
                delegateView, delegatorPackageName, delegateView.getHandwritingDelegateFlags());
    }

    /**
     * Accepts and starts a stylus handwriting session on the delegate view, if handwriting
     * initiation delegation was previously requested using
     * {@link #prepareStylusHandwritingDelegation(View, String)} from the delegator and the view
     * belongs to a specified delegate package.
     *
     * @param delegateView delegate view capable of receiving input via {@link InputConnection}
     *  on which {@link #startStylusHandwriting(View)} will be called.
     * @param delegatorPackageName package name of the delegator that handled initial stylus stroke.
     * @param executor The executor to run the callback on.
     * @param callback Consumer callback that provides {@code true} if view belongs to allowed
     *                delegate package declared in
     *                {@link #prepareStylusHandwritingDelegation(View, String)} and handwriting
     *                session can start.
     * @see #prepareStylusHandwritingDelegation(View, String)
     * @see #acceptStylusHandwritingDelegation(View)
     */
    @FlaggedApi(Flags.FLAG_USE_ZERO_JANK_PROXY)
    public void acceptStylusHandwritingDelegation(
            @NonNull View delegateView, @NonNull String delegatorPackageName,
            @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
        Objects.requireNonNull(delegatorPackageName);
        int flags = 0;
        if (Flags.homeScreenHandwritingDelegator()) {
            flags = delegateView.getHandwritingDelegateFlags();
        }
        startStylusHandwritingInternalAsync(
                delegateView, delegatorPackageName, flags, executor, callback);
    }

    /**
     * Accepts and starts a stylus handwriting session on the delegate view, if handwriting
     * initiation delegation was previously requested using {@link
+25 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.inputmethod;

/**
 * Interface for providing a Boolean result.
 */
oneway interface IBooleanListener
{
    void onResult(boolean value);
}
 No newline at end of file
Loading