557 lines
23 KiB
Java
557 lines
23 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2008 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 android.annotation.BinderThread;
|
||
|
import android.annotation.DurationMillisLong;
|
||
|
import android.annotation.MainThread;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.compat.annotation.UnsupportedAppUsage;
|
||
|
import android.content.Context;
|
||
|
import android.content.pm.PackageManager;
|
||
|
import android.os.Binder;
|
||
|
import android.os.IBinder;
|
||
|
import android.os.Message;
|
||
|
import android.os.RemoteException;
|
||
|
import android.os.ResultReceiver;
|
||
|
import android.util.Log;
|
||
|
import android.view.InputChannel;
|
||
|
import android.view.MotionEvent;
|
||
|
import android.view.inputmethod.CursorAnchorInfo;
|
||
|
import android.view.inputmethod.ImeTracker;
|
||
|
import android.view.inputmethod.InputBinding;
|
||
|
import android.view.inputmethod.InputConnection;
|
||
|
import android.view.inputmethod.InputMethod;
|
||
|
import android.view.inputmethod.InputMethodSession;
|
||
|
import android.view.inputmethod.InputMethodSubtype;
|
||
|
|
||
|
import com.android.internal.inputmethod.CancellationGroup;
|
||
|
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
|
||
|
import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
|
||
|
import com.android.internal.inputmethod.IInputMethod;
|
||
|
import com.android.internal.inputmethod.IInputMethodSession;
|
||
|
import com.android.internal.inputmethod.IInputMethodSessionCallback;
|
||
|
import com.android.internal.inputmethod.IRemoteInputConnection;
|
||
|
import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
|
||
|
import com.android.internal.inputmethod.InputMethodNavButtonFlags;
|
||
|
import com.android.internal.os.HandlerCaller;
|
||
|
import com.android.internal.os.SomeArgs;
|
||
|
|
||
|
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;
|
||
|
|
||
|
/**
|
||
|
* Implements the internal IInputMethod interface to convert incoming calls
|
||
|
* on to it back to calls on the public InputMethod interface, scheduling
|
||
|
* them on the main thread of the process.
|
||
|
*/
|
||
|
class IInputMethodWrapper extends IInputMethod.Stub
|
||
|
implements HandlerCaller.Callback {
|
||
|
private static final String TAG = "InputMethodWrapper";
|
||
|
|
||
|
private static final int DO_DUMP = 1;
|
||
|
private static final int DO_INITIALIZE_INTERNAL = 10;
|
||
|
private static final int DO_SET_INPUT_CONTEXT = 20;
|
||
|
private static final int DO_UNSET_INPUT_CONTEXT = 30;
|
||
|
private static final int DO_START_INPUT = 32;
|
||
|
private static final int DO_ON_NAV_BUTTON_FLAGS_CHANGED = 35;
|
||
|
private static final int DO_CREATE_SESSION = 40;
|
||
|
private static final int DO_SET_SESSION_ENABLED = 45;
|
||
|
private static final int DO_SHOW_SOFT_INPUT = 60;
|
||
|
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;
|
||
|
private static final int DO_INIT_INK_WINDOW = 120;
|
||
|
private static final int DO_FINISH_STYLUS_HANDWRITING = 130;
|
||
|
private static final int DO_UPDATE_TOOL_TYPE = 140;
|
||
|
private static final int DO_REMOVE_STYLUS_HANDWRITING_WINDOW = 150;
|
||
|
private static final int DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT = 160;
|
||
|
private static final int DO_COMMIT_HANDWRITING_DELEGATION_TEXT_IF_AVAILABLE = 170;
|
||
|
private static final int DO_DISCARD_HANDWRITING_DELEGATION_TEXT = 180;
|
||
|
|
||
|
final WeakReference<InputMethodServiceInternal> mTarget;
|
||
|
final Context mContext;
|
||
|
@UnsupportedAppUsage
|
||
|
final HandlerCaller mCaller;
|
||
|
final WeakReference<InputMethod> mInputMethod;
|
||
|
final int mTargetSdkVersion;
|
||
|
|
||
|
/**
|
||
|
* This is not {@code null} only between {@link #bindInput(InputBinding)} and
|
||
|
* {@link #unbindInput()} so that {@link RemoteInputConnection} can query if
|
||
|
* {@link #unbindInput()} has already been called or not, mainly to avoid unnecessary
|
||
|
* blocking operations.
|
||
|
*
|
||
|
* <p>This field must be set and cleared only from the binder thread(s), where the system
|
||
|
* guarantees that {@link #bindInput(InputBinding)},
|
||
|
* {@link #startInput(IInputMethod.StartInputParams)}, and {@link #unbindInput()} are called
|
||
|
* with the same order as the original calls in
|
||
|
* {@link com.android.server.inputmethod.InputMethodManagerService}.
|
||
|
* See {@link IBinder#FLAG_ONEWAY} for detailed semantics.</p>
|
||
|
*/
|
||
|
@Nullable
|
||
|
CancellationGroup mCancellationGroup = null;
|
||
|
|
||
|
// NOTE: we should have a cache of these.
|
||
|
static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
|
||
|
final Context mContext;
|
||
|
final InputChannel mChannel;
|
||
|
final IInputMethodSessionCallback mCb;
|
||
|
|
||
|
InputMethodSessionCallbackWrapper(Context context, InputChannel channel,
|
||
|
IInputMethodSessionCallback cb) {
|
||
|
mContext = context;
|
||
|
mChannel = channel;
|
||
|
mCb = cb;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void sessionCreated(InputMethodSession session) {
|
||
|
try {
|
||
|
if (session != null) {
|
||
|
IInputMethodSessionWrapper wrap =
|
||
|
new IInputMethodSessionWrapper(mContext, session, mChannel);
|
||
|
mCb.sessionCreated(wrap);
|
||
|
} else {
|
||
|
if (mChannel != null) {
|
||
|
mChannel.dispose();
|
||
|
}
|
||
|
mCb.sessionCreated(null);
|
||
|
}
|
||
|
} catch (RemoteException e) {
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IInputMethodWrapper(InputMethodServiceInternal imsInternal, InputMethod inputMethod) {
|
||
|
mTarget = new WeakReference<>(imsInternal);
|
||
|
mContext = imsInternal.getContext().getApplicationContext();
|
||
|
mCaller = new HandlerCaller(mContext, null, this, true /*asyncHandler*/);
|
||
|
mInputMethod = new WeakReference<>(inputMethod);
|
||
|
mTargetSdkVersion = imsInternal.getContext().getApplicationInfo().targetSdkVersion;
|
||
|
}
|
||
|
|
||
|
@MainThread
|
||
|
@Override
|
||
|
public void executeMessage(Message msg) {
|
||
|
final InputMethod inputMethod = mInputMethod.get();
|
||
|
final InputMethodServiceInternal target = mTarget.get();
|
||
|
switch (msg.what) {
|
||
|
case DO_DUMP: {
|
||
|
SomeArgs args = (SomeArgs) msg.obj;
|
||
|
if (isValid(inputMethod, target, "DO_DUMP")) {
|
||
|
final FileDescriptor fd = (FileDescriptor) args.arg1;
|
||
|
final PrintWriter fout = (PrintWriter) args.arg2;
|
||
|
final String[] dumpArgs = (String[]) args.arg3;
|
||
|
final CountDownLatch latch = (CountDownLatch) args.arg4;
|
||
|
try {
|
||
|
target.dump(fd, fout, dumpArgs);
|
||
|
} catch (RuntimeException e) {
|
||
|
fout.println("Exception: " + e);
|
||
|
} finally {
|
||
|
latch.countDown();
|
||
|
}
|
||
|
}
|
||
|
args.recycle();
|
||
|
return;
|
||
|
}
|
||
|
case DO_INITIALIZE_INTERNAL:
|
||
|
if (isValid(inputMethod, target, "DO_INITIALIZE_INTERNAL")) {
|
||
|
inputMethod.initializeInternal((IInputMethod.InitParams) msg.obj);
|
||
|
}
|
||
|
return;
|
||
|
case DO_SET_INPUT_CONTEXT: {
|
||
|
if (isValid(inputMethod, target, "DO_SET_INPUT_CONTEXT")) {
|
||
|
inputMethod.bindInput((InputBinding) msg.obj);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
case DO_UNSET_INPUT_CONTEXT:
|
||
|
if (isValid(inputMethod, target, "DO_UNSET_INPUT_CONTEXT")) {
|
||
|
inputMethod.unbindInput();
|
||
|
}
|
||
|
return;
|
||
|
case DO_START_INPUT: {
|
||
|
final SomeArgs args = (SomeArgs) msg.obj;
|
||
|
if (isValid(inputMethod, target, "DO_START_INPUT")) {
|
||
|
final InputConnection inputConnection = (InputConnection) args.arg1;
|
||
|
final IInputMethod.StartInputParams params =
|
||
|
(IInputMethod.StartInputParams) args.arg2;
|
||
|
inputMethod.dispatchStartInput(inputConnection, params);
|
||
|
}
|
||
|
args.recycle();
|
||
|
return;
|
||
|
}
|
||
|
case DO_ON_NAV_BUTTON_FLAGS_CHANGED:
|
||
|
if (isValid(inputMethod, target, "DO_ON_NAV_BUTTON_FLAGS_CHANGED")) {
|
||
|
inputMethod.onNavButtonFlagsChanged(msg.arg1);
|
||
|
}
|
||
|
return;
|
||
|
case DO_CREATE_SESSION: {
|
||
|
SomeArgs args = (SomeArgs) msg.obj;
|
||
|
if (isValid(inputMethod, target, "DO_CREATE_SESSION")) {
|
||
|
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
|
||
|
mContext, (InputChannel) args.arg1,
|
||
|
(IInputMethodSessionCallback) args.arg2));
|
||
|
}
|
||
|
args.recycle();
|
||
|
return;
|
||
|
}
|
||
|
case DO_SET_SESSION_ENABLED:
|
||
|
if (isValid(inputMethod, target, "DO_SET_SESSION_ENABLED")) {
|
||
|
inputMethod.setSessionEnabled((InputMethodSession) msg.obj, msg.arg1 != 0);
|
||
|
}
|
||
|
return;
|
||
|
case DO_SHOW_SOFT_INPUT: {
|
||
|
final SomeArgs args = (SomeArgs) msg.obj;
|
||
|
final ImeTracker.Token statsToken = (ImeTracker.Token) args.arg3;
|
||
|
if (isValid(inputMethod, target, "DO_SHOW_SOFT_INPUT")) {
|
||
|
ImeTracker.forLogging().onProgress(
|
||
|
statsToken, ImeTracker.PHASE_IME_WRAPPER_DISPATCH);
|
||
|
inputMethod.showSoftInputWithToken(
|
||
|
msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1, statsToken);
|
||
|
} else {
|
||
|
ImeTracker.forLogging().onFailed(
|
||
|
statsToken, ImeTracker.PHASE_IME_WRAPPER_DISPATCH);
|
||
|
}
|
||
|
args.recycle();
|
||
|
return;
|
||
|
}
|
||
|
case DO_HIDE_SOFT_INPUT: {
|
||
|
final SomeArgs args = (SomeArgs) msg.obj;
|
||
|
final ImeTracker.Token statsToken = (ImeTracker.Token) args.arg3;
|
||
|
if (isValid(inputMethod, target, "DO_HIDE_SOFT_INPUT")) {
|
||
|
ImeTracker.forLogging().onProgress(
|
||
|
statsToken, ImeTracker.PHASE_IME_WRAPPER_DISPATCH);
|
||
|
inputMethod.hideSoftInputWithToken(msg.arg1, (ResultReceiver) args.arg2,
|
||
|
(IBinder) args.arg1, statsToken);
|
||
|
} else {
|
||
|
ImeTracker.forLogging().onFailed(
|
||
|
statsToken, ImeTracker.PHASE_IME_WRAPPER_DISPATCH);
|
||
|
}
|
||
|
args.recycle();
|
||
|
return;
|
||
|
}
|
||
|
case DO_CHANGE_INPUTMETHOD_SUBTYPE:
|
||
|
if (isValid(inputMethod, target, "DO_CHANGE_INPUTMETHOD_SUBTYPE")) {
|
||
|
inputMethod.changeInputMethodSubtype((InputMethodSubtype) msg.obj);
|
||
|
}
|
||
|
return;
|
||
|
case DO_CREATE_INLINE_SUGGESTIONS_REQUEST: {
|
||
|
final SomeArgs args = (SomeArgs) msg.obj;
|
||
|
if (isValid(inputMethod, target, "DO_CREATE_INLINE_SUGGESTIONS_REQUEST")) {
|
||
|
inputMethod.onCreateInlineSuggestionsRequest(
|
||
|
(InlineSuggestionsRequestInfo) args.arg1,
|
||
|
(IInlineSuggestionsRequestCallback) args.arg2);
|
||
|
}
|
||
|
args.recycle();
|
||
|
return;
|
||
|
}
|
||
|
case DO_CAN_START_STYLUS_HANDWRITING: {
|
||
|
final SomeArgs args = (SomeArgs) msg.obj;
|
||
|
if (isValid(inputMethod, target, "DO_CAN_START_STYLUS_HANDWRITING")) {
|
||
|
inputMethod.canStartStylusHandwriting(msg.arg1,
|
||
|
(IConnectionlessHandwritingCallback) args.arg1,
|
||
|
(CursorAnchorInfo) args.arg2, msg.arg2 != 0);
|
||
|
}
|
||
|
args.recycle();
|
||
|
return;
|
||
|
}
|
||
|
case DO_UPDATE_TOOL_TYPE: {
|
||
|
if (isValid(inputMethod, target, "DO_UPDATE_TOOL_TYPE")) {
|
||
|
inputMethod.updateEditorToolType(msg.arg1);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
case DO_START_STYLUS_HANDWRITING: {
|
||
|
final SomeArgs args = (SomeArgs) msg.obj;
|
||
|
if (isValid(inputMethod, target, "DO_START_STYLUS_HANDWRITING")) {
|
||
|
inputMethod.startStylusHandwriting(msg.arg1, (InputChannel) args.arg1,
|
||
|
(List<MotionEvent>) args.arg2);
|
||
|
}
|
||
|
args.recycle();
|
||
|
return;
|
||
|
}
|
||
|
case DO_INIT_INK_WINDOW: {
|
||
|
if (isValid(inputMethod, target, "DO_INIT_INK_WINDOW")) {
|
||
|
inputMethod.initInkWindow();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
case DO_FINISH_STYLUS_HANDWRITING: {
|
||
|
if (isValid(inputMethod, target, "DO_FINISH_STYLUS_HANDWRITING")) {
|
||
|
inputMethod.finishStylusHandwriting();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
case DO_REMOVE_STYLUS_HANDWRITING_WINDOW: {
|
||
|
if (isValid(inputMethod, target, "DO_REMOVE_STYLUS_HANDWRITING_WINDOW")) {
|
||
|
inputMethod.removeStylusHandwritingWindow();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
case DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT: {
|
||
|
if (isValid(inputMethod, target, "DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT")) {
|
||
|
inputMethod.setStylusWindowIdleTimeoutForTest((long) msg.obj);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
case DO_COMMIT_HANDWRITING_DELEGATION_TEXT_IF_AVAILABLE: {
|
||
|
if (isValid(inputMethod, target,
|
||
|
"DO_COMMIT_HANDWRITING_DELEGATION_TEXT_IF_AVAILABLE")) {
|
||
|
inputMethod.commitHandwritingDelegationTextIfAvailable();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
case DO_DISCARD_HANDWRITING_DELEGATION_TEXT: {
|
||
|
if (isValid(inputMethod, target, "DO_DISCARD_HANDWRITING_DELEGATION_TEXT")) {
|
||
|
inputMethod.discardHandwritingDelegationText();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
Log.w(TAG, "Unhandled message code: " + msg.what);
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
|
||
|
InputMethodServiceInternal target = mTarget.get();
|
||
|
if (target == null) {
|
||
|
return;
|
||
|
}
|
||
|
if (target.getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
|
||
|
!= PackageManager.PERMISSION_GRANTED) {
|
||
|
|
||
|
fout.println("Permission Denial: can't dump InputMethodManager from from pid="
|
||
|
+ Binder.getCallingPid()
|
||
|
+ ", uid=" + Binder.getCallingUid());
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CountDownLatch latch = new CountDownLatch(1);
|
||
|
mCaller.getHandler().sendMessageAtFrontOfQueue(mCaller.obtainMessageOOOO(DO_DUMP,
|
||
|
fd, fout, args, latch));
|
||
|
try {
|
||
|
if (!latch.await(5, TimeUnit.SECONDS)) {
|
||
|
fout.println("Timeout waiting for dump");
|
||
|
}
|
||
|
} catch (InterruptedException e) {
|
||
|
fout.println("Interrupted waiting for dump");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void initializeInternal(@NonNull IInputMethod.InitParams params) {
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_INITIALIZE_INTERNAL, params));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo,
|
||
|
IInlineSuggestionsRequestCallback cb) {
|
||
|
mCaller.executeOrSendMessage(
|
||
|
mCaller.obtainMessageOO(DO_CREATE_INLINE_SUGGESTIONS_REQUEST, requestInfo, cb));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void bindInput(InputBinding binding) {
|
||
|
if (mCancellationGroup != null) {
|
||
|
Log.e(TAG, "bindInput must be paired with unbindInput.");
|
||
|
}
|
||
|
mCancellationGroup = new CancellationGroup();
|
||
|
InputConnection ic = new RemoteInputConnection(mTarget,
|
||
|
IRemoteInputConnection.Stub.asInterface(binding.getConnectionToken()),
|
||
|
mCancellationGroup);
|
||
|
InputBinding nu = new InputBinding(ic, binding);
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void unbindInput() {
|
||
|
if (mCancellationGroup != null) {
|
||
|
// Signal the flag then forget it.
|
||
|
mCancellationGroup.cancelAll();
|
||
|
mCancellationGroup = null;
|
||
|
} else {
|
||
|
Log.e(TAG, "unbindInput must be paired with bindInput.");
|
||
|
}
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void startInput(@NonNull IInputMethod.StartInputParams params) {
|
||
|
if (mCancellationGroup == null) {
|
||
|
Log.e(TAG, "startInput must be called after bindInput.");
|
||
|
mCancellationGroup = new CancellationGroup();
|
||
|
}
|
||
|
|
||
|
params.editorInfo.makeCompatible(mTargetSdkVersion);
|
||
|
|
||
|
final InputConnection ic = params.remoteInputConnection == null ? null
|
||
|
: new RemoteInputConnection(mTarget, params.remoteInputConnection,
|
||
|
mCancellationGroup);
|
||
|
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT, ic, params));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
|
||
|
mCaller.executeOrSendMessage(
|
||
|
mCaller.obtainMessageI(DO_ON_NAV_BUTTON_FLAGS_CHANGED, navButtonFlags));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void createSession(InputChannel channel, IInputMethodSessionCallback callback) {
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION,
|
||
|
channel, callback));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void setSessionEnabled(IInputMethodSession session, boolean enabled) {
|
||
|
try {
|
||
|
InputMethodSession ls = ((IInputMethodSessionWrapper)
|
||
|
session).getInternalInputMethodSession();
|
||
|
if (ls == null) {
|
||
|
Log.w(TAG, "Session is already finished: " + session);
|
||
|
return;
|
||
|
}
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
|
||
|
DO_SET_SESSION_ENABLED, enabled ? 1 : 0, ls));
|
||
|
} catch (ClassCastException e) {
|
||
|
Log.w(TAG, "Incoming session not of correct type: " + session, e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void showSoftInput(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
|
||
|
@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
|
||
|
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_SHOW_SOFT_INPUT,
|
||
|
flags, showInputToken, resultReceiver, statsToken));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void hideSoftInput(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
|
||
|
int flags, ResultReceiver resultReceiver) {
|
||
|
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_HIDE_SOFT_INPUT,
|
||
|
flags, hideInputToken, resultReceiver, statsToken));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void changeInputMethodSubtype(InputMethodSubtype subtype) {
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE,
|
||
|
subtype));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void canStartStylusHandwriting(int requestId,
|
||
|
IConnectionlessHandwritingCallback connectionlessCallback,
|
||
|
CursorAnchorInfo cursorAnchorInfo, boolean isConnectionlessForDelegation)
|
||
|
throws RemoteException {
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessageIIOO(DO_CAN_START_STYLUS_HANDWRITING,
|
||
|
requestId, isConnectionlessForDelegation ? 1 : 0, connectionlessCallback,
|
||
|
cursorAnchorInfo));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void updateEditorToolType(int toolType)
|
||
|
throws RemoteException {
|
||
|
mCaller.executeOrSendMessage(
|
||
|
mCaller.obtainMessageI(DO_UPDATE_TOOL_TYPE, toolType));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void startStylusHandwriting(int requestId, @NonNull InputChannel channel,
|
||
|
@Nullable List<MotionEvent> stylusEvents)
|
||
|
throws RemoteException {
|
||
|
mCaller.executeOrSendMessage(
|
||
|
mCaller.obtainMessageIOO(DO_START_STYLUS_HANDWRITING, requestId, channel,
|
||
|
stylusEvents));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void commitHandwritingDelegationTextIfAvailable() {
|
||
|
mCaller.executeOrSendMessage(
|
||
|
mCaller.obtainMessage(DO_COMMIT_HANDWRITING_DELEGATION_TEXT_IF_AVAILABLE));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void discardHandwritingDelegationText() {
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_DISCARD_HANDWRITING_DELEGATION_TEXT));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void initInkWindow() {
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_INIT_INK_WINDOW));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void finishStylusHandwriting() {
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_STYLUS_HANDWRITING));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void removeStylusHandwritingWindow() {
|
||
|
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_STYLUS_HANDWRITING_WINDOW));
|
||
|
}
|
||
|
|
||
|
@BinderThread
|
||
|
@Override
|
||
|
public void setStylusWindowIdleTimeoutForTest(@DurationMillisLong long timeout) {
|
||
|
mCaller.executeOrSendMessage(
|
||
|
mCaller.obtainMessageO(DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT, timeout));
|
||
|
}
|
||
|
|
||
|
private static boolean isValid(InputMethod inputMethod, InputMethodServiceInternal target,
|
||
|
String msg) {
|
||
|
if (inputMethod != null && target != null && !target.isServiceDestroyed()) {
|
||
|
return true;
|
||
|
} else {
|
||
|
Log.w(TAG, "Ignoring " + msg + ", InputMethod:" + inputMethod
|
||
|
+ ", InputMethodServiceInternal:" + target);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|