Loading core/api/current.txt +5 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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(); Loading Loading @@ -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); Loading @@ -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); Loading Loading @@ -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); core/java/android/inputmethodservice/IInputMethodWrapper.java +40 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(); } Loading Loading @@ -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); Loading Loading @@ -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 Loading Loading @@ -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)); } } core/java/android/inputmethodservice/InkWindow.java 0 → 100644 +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); } } core/java/android/inputmethodservice/InputMethodService.java +132 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; /** Loading Loading @@ -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. Loading Loading @@ -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); } Loading Loading @@ -635,6 +646,9 @@ public class InputMethodService extends AbstractInputMethodService { attachToWindowToken(token); mToken = token; mWindow.setToken(token); if (mInkWindow != null) { mInkWindow.setToken(token); } } /** Loading Loading @@ -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} */ Loading Loading @@ -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 Loading Loading @@ -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) { Loading core/java/android/view/inputmethod/InputMethod.java +21 −1 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); } Loading Loading @@ -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
core/api/current.txt +5 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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(); Loading Loading @@ -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); Loading @@ -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); Loading Loading @@ -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);
core/java/android/inputmethodservice/IInputMethodWrapper.java +40 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(); } Loading Loading @@ -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); Loading Loading @@ -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 Loading Loading @@ -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)); } }
core/java/android/inputmethodservice/InkWindow.java 0 → 100644 +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); } }
core/java/android/inputmethodservice/InputMethodService.java +132 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; /** Loading Loading @@ -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. Loading Loading @@ -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); } Loading Loading @@ -635,6 +646,9 @@ public class InputMethodService extends AbstractInputMethodService { attachToWindowToken(token); mToken = token; mWindow.setToken(token); if (mInkWindow != null) { mInkWindow.setToken(token); } } /** Loading Loading @@ -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} */ Loading Loading @@ -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 Loading Loading @@ -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) { Loading
core/java/android/view/inputmethod/InputMethod.java +21 −1 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); } Loading Loading @@ -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); }