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

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

Merge "Scribe in IMF: startStylusHandwriting & lifecycle 2/N"

parents 63706247 44dc2805
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -19740,6 +19740,7 @@ package android.inputmethodservice {
  @UiContext public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
    ctor public InputMethodService();
    method @Deprecated public boolean enableHardwareAcceleration();
    method public final void finishStylusHandwriting();
    method public int getBackDisposition();
    method public int getCandidatesHiddenVisibility();
    method public android.view.inputmethod.InputBinding getCurrentInputBinding();
@@ -19749,6 +19750,7 @@ package android.inputmethodservice {
    method @Deprecated public int getInputMethodWindowRecommendedHeight();
    method public android.view.LayoutInflater getLayoutInflater();
    method public int getMaxWidth();
    method @Nullable public final android.view.Window getStylusHandwritingWindow();
    method public CharSequence getTextForImeAction(int);
    method public android.app.Dialog getWindow();
    method public void hideStatusIcon();
@@ -19779,6 +19781,7 @@ package android.inputmethodservice {
    method public void onFinishCandidatesView(boolean);
    method public void onFinishInput();
    method public void onFinishInputView(boolean);
    method public void onFinishStylusHandwriting();
    method public void onInitializeInterface();
    method public boolean onInlineSuggestionsResponse(@NonNull android.view.inputmethod.InlineSuggestionsResponse);
    method public boolean onKeyDown(int, android.view.KeyEvent);
@@ -19789,6 +19792,7 @@ package android.inputmethodservice {
    method public void onStartCandidatesView(android.view.inputmethod.EditorInfo, boolean);
    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 onUnbindInput();
    method @Deprecated public void onUpdateCursor(android.graphics.Rect);
    method public void onUpdateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo);
@@ -53127,6 +53131,7 @@ package android.view.inputmethod {
    method public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver);
    method @Deprecated public void showSoftInputFromInputMethod(android.os.IBinder, int);
    method @Deprecated public void showStatusIcon(android.os.IBinder, String, @DrawableRes int);
    method public void startStylusHandwriting(@NonNull android.view.View);
    method @Deprecated public boolean switchToLastInputMethod(android.os.IBinder);
    method @Deprecated public boolean switchToNextInputMethod(android.os.IBinder, boolean);
    method @Deprecated public void toggleSoftInput(int, int);
+40 −4
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.inputmethodservice;

import android.annotation.BinderThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -29,6 +30,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.util.Log;
import android.view.InputChannel;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
@@ -50,6 +52,7 @@ import com.android.internal.view.InlineSuggestionsRequestInfo;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

@@ -74,6 +77,8 @@ class IInputMethodWrapper extends IInputMethod.Stub
    private static final int DO_HIDE_SOFT_INPUT = 70;
    private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
    private static final int DO_CREATE_INLINE_SUGGESTIONS_REQUEST = 90;
    private static final int DO_CAN_START_STYLUS_HANDWRITING = 100;
    private static final int DO_START_STYLUS_HANDWRITING = 110;

    final WeakReference<InputMethodServiceInternal> mTarget;
    final Context mContext;
@@ -169,7 +174,8 @@ class IInputMethodWrapper extends IInputMethod.Stub
                SomeArgs args = (SomeArgs) msg.obj;
                try {
                    inputMethod.initializeInternal((IBinder) args.arg1,
                            (IInputMethodPrivilegedOperations) args.arg2, msg.arg1);
                            (IInputMethodPrivilegedOperations) args.arg2, msg.arg1,
                            (boolean) args.arg3);
                } finally {
                    args.recycle();
                }
@@ -229,13 +235,25 @@ class IInputMethodWrapper extends IInputMethod.Stub
            case DO_CHANGE_INPUTMETHOD_SUBTYPE:
                inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj);
                return;
            case DO_CREATE_INLINE_SUGGESTIONS_REQUEST:
            case DO_CREATE_INLINE_SUGGESTIONS_REQUEST: {
                final SomeArgs args = (SomeArgs) msg.obj;
                inputMethod.onCreateInlineSuggestionsRequest(
                        (InlineSuggestionsRequestInfo) args.arg1,
                        (IInlineSuggestionsRequestCallback) args.arg2);
                args.recycle();
                return;
            }
            case DO_CAN_START_STYLUS_HANDWRITING: {
                inputMethod.canStartStylusHandwriting(msg.arg1);
                return;
            }
            case DO_START_STYLUS_HANDWRITING: {
                final SomeArgs args = (SomeArgs) msg.obj;
                inputMethod.startStylusHandwriting((InputChannel) args.arg1,
                        (List<MotionEvent>) args.arg2);
                args.recycle();
                return;
            }

        }
        Log.w(TAG, "Unhandled message code: " + msg.what);
@@ -272,9 +290,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
    @BinderThread
    @Override
    public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
            int configChanges) {
            int configChanges, boolean stylusHwSupported) {
        mCaller.executeOrSendMessage(
                mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, configChanges, token, privOps));
                mCaller.obtainMessageIOOO(
                        DO_INITIALIZE_INTERNAL, configChanges, token, privOps, stylusHwSupported));
    }

    @BinderThread
@@ -383,4 +402,21 @@ class IInputMethodWrapper extends IInputMethod.Stub
        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE,
                subtype));
    }

    @BinderThread
    @Override
    public void canStartStylusHandwriting(int requestId)
            throws RemoteException {
        mCaller.executeOrSendMessage(
                mCaller.obtainMessageI(DO_CAN_START_STYLUS_HANDWRITING, requestId));
    }

    @BinderThread
    @Override
    public void startStylusHandwriting(@NonNull InputChannel channel,
            @Nullable List<MotionEvent> stylusEvents)
            throws RemoteException {
        mCaller.executeOrSendMessage(
                mCaller.obtainMessageOO(DO_START_STYLUS_HANDWRITING, channel, stylusEvents));
    }
}
+88 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 android.inputmethodservice;

import static android.view.WindowManager.LayoutParams;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

import android.annotation.NonNull;
import android.content.Context;
import android.os.IBinder;
import android.util.Slog;
import android.view.View;
import android.view.WindowManager;

import com.android.internal.policy.PhoneWindow;

/**
 * Window of type {@code LayoutParams.TYPE_INPUT_METHOD_DIALOG} for drawing
 * Handwriting Ink on screen.
 * @hide
 */
final class InkWindow extends PhoneWindow {

    private final WindowManager mWindowManager;

    public InkWindow(@NonNull Context context) {
        super(context);

        setType(LayoutParams.TYPE_INPUT_METHOD);
        final LayoutParams attrs = getAttributes();
        attrs.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
        attrs.setFitInsetsTypes(0);
        setAttributes(attrs);
        // Ink window is not touchable with finger.
        addFlags(FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_NO_LIMITS | FLAG_NOT_TOUCHABLE
                | FLAG_NOT_FOCUSABLE);
        setBackgroundDrawableResource(android.R.color.transparent);
        setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        mWindowManager = context.getSystemService(WindowManager.class);
    }

    /**
     * Method to show InkWindow on screen.
     * Emulates internal behavior similar to Dialog.show().
     */
    void show() {
        if (getDecorView() == null) {
            Slog.i(InputMethodService.TAG, "DecorView is not set for InkWindow. show() failed.");
            return;
        }
        getDecorView().setVisibility(View.VISIBLE);
        mWindowManager.addView(getDecorView(), getAttributes());
    }

    /**
     * Method to hide InkWindow from screen.
     * Emulates internal behavior similar to Dialog.hide().
     * @param remove set {@code true} to remove InkWindow surface completely.
     */
    void hide(boolean remove) {
        if (getDecorView() != null) {
            getDecorView().setVisibility(remove ? View.GONE : View.INVISIBLE);
        }
    }

    void setToken(@NonNull IBinder token) {
        WindowManager.LayoutParams lp = getAttributes();
        lp.token = token;
        setAttributes(lp);
    }
}
+132 −1
Original line number Diff line number Diff line
@@ -94,6 +94,7 @@ import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.proto.ProtoOutputStream;
import android.view.Gravity;
import android.view.InputChannel;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -119,6 +120,7 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodServiceTraceProto;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.FrameLayout;
@@ -142,6 +144,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
@@ -520,10 +523,14 @@ public class InputMethodService extends AbstractInputMethodService {

    private boolean mAutomotiveHideNavBarForKeyboard;
    private boolean mIsAutomotive;
    private boolean mHandwritingStarted;
    private Handler mHandler;
    private boolean mImeSurfaceScheduledForRemoval;
    private ImsConfigurationTracker mConfigTracker = new ImsConfigurationTracker();

    /** Stylus handwriting Ink window.  */
    private InkWindow mInkWindow;

    /**
     * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
     * The original app window token is passed from client app window.
@@ -598,11 +605,15 @@ public class InputMethodService extends AbstractInputMethodService {
        @MainThread
        @Override
        public final void initializeInternal(@NonNull IBinder token,
                IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
                IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
                boolean stylusHwSupported) {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
            mConfigTracker.onInitialize(configChanges);
            mPrivOps.set(privilegedOperations);
            InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
            if (stylusHwSupported) {
                mInkWindow = new InkWindow(mWindow.getContext());
            }
            attachToken(token);
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
@@ -635,6 +646,9 @@ public class InputMethodService extends AbstractInputMethodService {
            attachToWindowToken(token);
            mToken = token;
            mWindow.setToken(token);
            if (mInkWindow != null) {
                mInkWindow.setToken(token);
            }
        }

        /**
@@ -816,6 +830,49 @@ public class InputMethodService extends AbstractInputMethodService {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }

        /**
         * {@inheritDoc}
         * @hide
         */
        @Override
        public void canStartStylusHandwriting(int requestId) {
            if (DEBUG) Log.v(TAG, "canStartStylusHandwriting()");
            if (mHandwritingStarted) {
                Log.d(TAG, "There is an ongoing Handwriting session. ignoring.");
                return;
            }
            if (!mInputStarted) {
                Log.d(TAG, "Input should have started before starting Stylus handwriting.");
                return;
            }
            if (onStartStylusHandwriting()) {
                mPrivOps.onStylusHandwritingReady(requestId);
            } else {
                Log.i(TAG, "IME is not ready. Can't start Stylus Handwriting");
            }
        }

        /**
         * {@inheritDoc}
         * @hide
         */
        @MainThread
        @Override
        public void startStylusHandwriting(
                @NonNull InputChannel channel, @Nullable List<MotionEvent> stylusEvents) {
            if (DEBUG) Log.v(TAG, "startStylusHandwriting()");
            if (mHandwritingStarted) {
                return;
            }

            mHandwritingStarted = true;
            mShowInputRequested = false;

            mInkWindow.show();
            // TODO: deliver previous @param stylusEvents
            // TODO: create spy receiver for @param channel
        }

        /**
         * {@inheritDoc}
         */
@@ -2181,6 +2238,77 @@ public class InputMethodService extends AbstractInputMethodService {
        }
    }
    
    /**
     * Called when an app requests stylus handwriting
     * {@link InputMethodManager#startStylusHandwriting(View)}.
     *
     * This will always be preceded by {@link #onStartInput(EditorInfo, boolean)} for the
     * {@link EditorInfo} and {@link InputConnection} for which stylus handwriting is being
     * requested.
     *
     * 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()}.
     * @return {@code true} if IME can honor the request, {@code false} if IME cannot at this time.
     */
    public boolean onStartStylusHandwriting() {
        // Intentionally empty
        return false;
    }

    /**
     * Called when the current stylus handwriting session was finished (either by the system or
     * via {@link #finishStylusHandwriting()}.
     *
     * When this is called, the ink window has been made invisible, and the IME no longer
     * intercepts handwriting-related {@code MotionEvent}s.
     */
    public void onFinishStylusHandwriting() {
        // Intentionally empty
    }

    /**
     * Returns the stylus handwriting inking window.
     * IMEs supporting stylus input are expected to attach their inking views to this
     * window (e.g. with {@link Window#setContentView(View)} )). Handwriting-related
     * {@link MotionEvent}s are dispatched to the attached view hierarchy.
     *
     * Note: This returns {@code null} if IME doesn't support stylus handwriting
     *   i.e. if {@link InputMethodInfo#supportsStylusHandwriting()} is false.
     *   This method should be called after {@link #onStartStylusHandwriting()}.
     * @see #onStartStylusHandwriting()
     */
    @Nullable
    public final Window getStylusHandwritingWindow() {
        return mInkWindow;
    }

    /**
     * Finish the current stylus handwriting session.
     *
     * This dismisses the {@link #getStylusHandwritingWindow ink window} and stops intercepting
     * stylus {@code MotionEvent}s.
     *
     * Note for IME developers: Call this method at any time to finish current handwriting session.
     * Generally, this should be invoked after a short timeout, giving the user enough time
     * to start the next stylus stroke, if any.
     *
     * Handwriting session will be finished by framework on next {@link #onFinishInput()}.
     */
    public final void finishStylusHandwriting() {
        if (DEBUG) Log.v(TAG, "finishStylusHandwriting()");
        if (mInkWindow == null) {
            return;
        }
        if (!mHandwritingStarted) {
            return;
        }

        mHandwritingStarted = false;
        mInkWindow.hide(false /* remove */);
        onFinishStylusHandwriting();
    }

    /**
     * The system has decided that it may be time to show your input method.
     * This is called due to a corresponding call to your
@@ -2450,6 +2578,9 @@ public class InputMethodService extends AbstractInputMethodService {
        mInputStarted = false;
        mStartedInputConnection = null;
        mCurCompletions = null;
        if (mInkWindow != null) {
            finishStylusHandwriting();
        }
    }

    void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
+21 −1
Original line number Diff line number Diff line
@@ -26,12 +26,16 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.util.Log;
import android.view.InputChannel;
import android.view.MotionEvent;
import android.view.View;

import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.InlineSuggestionsRequestInfo;

import java.util.List;

/**
 * The InputMethod interface represents an input method which can generate key
 * events and text, such as digital, email addresses, CJK characters, other
@@ -100,11 +104,13 @@ public interface InputMethod {
     *                             operations that are allowed only to the
     *                             current IME.
     * @param configChanges {@link InputMethodInfo#getConfigChanges()} declared by IME.
     * @param stylusHwSupported {@link InputMethodInfo#supportsStylusHandwriting()} declared by IME.
     * @hide
     */
    @MainThread
    default void initializeInternal(IBinder token,
            IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
            IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
            boolean stylusHwSupported) {
        attachToken(token);
    }

@@ -384,4 +390,18 @@ public interface InputMethod {
     */
    public void setCurrentHideInputToken(IBinder hideInputToken);

    /**
     * Checks if IME is ready to start stylus handwriting session.
     * If yes, {@link #startStylusHandwriting(InputChannel, List)} is called.
     * @param requestId
     * @hide
     */
    void canStartStylusHandwriting(int requestId);

    /**
     * Start stylus handwriting session.
     * @hide
     */
    void startStylusHandwriting(@NonNull InputChannel channel, @Nullable List<MotionEvent> events);

}
Loading