542 lines
20 KiB
Java
542 lines
20 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.AnyThread;
|
||
|
import android.annotation.CallbackExecutor;
|
||
|
import android.annotation.IntRange;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.graphics.RectF;
|
||
|
import android.os.Bundle;
|
||
|
import android.os.CancellationSignal;
|
||
|
import android.os.Handler;
|
||
|
import android.util.Log;
|
||
|
import android.view.KeyEvent;
|
||
|
import android.view.inputmethod.CompletionInfo;
|
||
|
import android.view.inputmethod.CorrectionInfo;
|
||
|
import android.view.inputmethod.ExtractedText;
|
||
|
import android.view.inputmethod.ExtractedTextRequest;
|
||
|
import android.view.inputmethod.HandwritingGesture;
|
||
|
import android.view.inputmethod.InputConnection;
|
||
|
import android.view.inputmethod.InputContentInfo;
|
||
|
import android.view.inputmethod.PreviewableHandwritingGesture;
|
||
|
import android.view.inputmethod.SurroundingText;
|
||
|
import android.view.inputmethod.TextAttribute;
|
||
|
import android.view.inputmethod.TextBoundsInfoResult;
|
||
|
|
||
|
import com.android.internal.inputmethod.CancellationGroup;
|
||
|
import com.android.internal.inputmethod.CompletableFutureUtil;
|
||
|
import com.android.internal.inputmethod.IRemoteInputConnection;
|
||
|
import com.android.internal.inputmethod.ImeTracing;
|
||
|
import com.android.internal.inputmethod.InputConnectionProtoDumper;
|
||
|
|
||
|
import java.lang.ref.WeakReference;
|
||
|
import java.util.concurrent.CompletableFuture;
|
||
|
import java.util.concurrent.Executor;
|
||
|
import java.util.function.Consumer;
|
||
|
import java.util.function.IntConsumer;
|
||
|
|
||
|
/**
|
||
|
* Takes care of remote method invocations of {@link InputConnection} in the IME side.
|
||
|
*
|
||
|
* <p>This class works as a proxy to forward API calls on {@link InputConnection} to
|
||
|
* {@link android.view.inputmethod.RemoteInputConnectionImpl} running on the IME client
|
||
|
* (editor app) process then waits replies as needed.</p>
|
||
|
*
|
||
|
* <p>See also {@link IRemoteInputConnection} for the actual {@link android.os.Binder} IPC protocols
|
||
|
* under the hood.</p>
|
||
|
*/
|
||
|
final class RemoteInputConnection implements InputConnection {
|
||
|
private static final String TAG = "RemoteInputConnection";
|
||
|
|
||
|
private static final int MAX_WAIT_TIME_MILLIS = 2000;
|
||
|
|
||
|
@NonNull
|
||
|
private final IRemoteInputConnectionInvoker mInvoker;
|
||
|
|
||
|
private static final class InputMethodServiceInternalHolder {
|
||
|
@NonNull
|
||
|
private final WeakReference<InputMethodServiceInternal> mServiceRef;
|
||
|
|
||
|
private InputMethodServiceInternalHolder(
|
||
|
@NonNull WeakReference<InputMethodServiceInternal> ims) {
|
||
|
mServiceRef = ims;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
@Nullable
|
||
|
public InputMethodServiceInternal getAndWarnIfNull() {
|
||
|
final InputMethodServiceInternal ims = mServiceRef.get();
|
||
|
if (ims == null) {
|
||
|
Log.e(TAG, "InputMethodService is already destroyed. InputConnection instances"
|
||
|
+ " cannot be used beyond InputMethodService lifetime.", new Throwable());
|
||
|
}
|
||
|
return ims;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
private final InputMethodServiceInternalHolder mImsInternal;
|
||
|
|
||
|
/**
|
||
|
* Signaled when the system decided to take away IME focus from the target app.
|
||
|
*
|
||
|
* <p>This is expected to be signaled immediately when the IME process receives
|
||
|
* {@link com.android.internal.inputmethod.IInputMethod#unbindInput()}.</p>
|
||
|
*/
|
||
|
@NonNull
|
||
|
private final CancellationGroup mCancellationGroup;
|
||
|
|
||
|
RemoteInputConnection(
|
||
|
@NonNull WeakReference<InputMethodServiceInternal> inputMethodService,
|
||
|
IRemoteInputConnection inputConnection, @NonNull CancellationGroup cancellationGroup) {
|
||
|
mImsInternal = new InputMethodServiceInternalHolder(inputMethodService);
|
||
|
mInvoker = IRemoteInputConnectionInvoker.create(inputConnection);
|
||
|
mCancellationGroup = cancellationGroup;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean isSameConnection(@NonNull IRemoteInputConnection inputConnection) {
|
||
|
return mInvoker.isSameConnection(inputConnection);
|
||
|
}
|
||
|
|
||
|
RemoteInputConnection(@NonNull RemoteInputConnection original, int sessionId) {
|
||
|
mImsInternal = original.mImsInternal;
|
||
|
mInvoker = original.mInvoker.cloneWithSessionId(sessionId);
|
||
|
mCancellationGroup = original.mCancellationGroup;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* See {@link InputConnection#getTextAfterCursor(int, int)}.
|
||
|
*/
|
||
|
@Nullable
|
||
|
@AnyThread
|
||
|
public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) {
|
||
|
if (length < 0) {
|
||
|
// TODO: Should we throw an InvalidParameterException() based on targetSdkVersion?
|
||
|
Log.e(TAG, "length=" + length + " is invalid and always results in null result.");
|
||
|
}
|
||
|
if (mCancellationGroup.isCanceled()) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
final CompletableFuture<CharSequence> value = mInvoker.getTextAfterCursor(length, flags);
|
||
|
final CharSequence result = CompletableFutureUtil.getResultOrNull(
|
||
|
value, TAG, "getTextAfterCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
|
||
|
|
||
|
final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
|
||
|
if (imsInternal != null && ImeTracing.getInstance().isEnabled()) {
|
||
|
final byte[] icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(length,
|
||
|
flags, result);
|
||
|
imsInternal.triggerServiceDump(TAG + "#getTextAfterCursor", icProto);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* See {@link InputConnection#getTextBeforeCursor(int, int)}.
|
||
|
*/
|
||
|
@Nullable
|
||
|
@AnyThread
|
||
|
public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) {
|
||
|
if (length < 0) {
|
||
|
// TODO: Should we throw an InvalidParameterException() based on targetSdkVersion?
|
||
|
Log.e(TAG, "length=" + length + " is invalid and always results in null result.");
|
||
|
}
|
||
|
if (mCancellationGroup.isCanceled()) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
final CompletableFuture<CharSequence> value = mInvoker.getTextBeforeCursor(length, flags);
|
||
|
final CharSequence result = CompletableFutureUtil.getResultOrNull(
|
||
|
value, TAG, "getTextBeforeCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
|
||
|
|
||
|
final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
|
||
|
if (imsInternal != null && ImeTracing.getInstance().isEnabled()) {
|
||
|
final byte[] icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(length,
|
||
|
flags, result);
|
||
|
imsInternal.triggerServiceDump(TAG + "#getTextBeforeCursor", icProto);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public CharSequence getSelectedText(int flags) {
|
||
|
if (mCancellationGroup.isCanceled()) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
final CompletableFuture<CharSequence> value = mInvoker.getSelectedText(flags);
|
||
|
final CharSequence result = CompletableFutureUtil.getResultOrNull(
|
||
|
value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
|
||
|
|
||
|
final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
|
||
|
if (imsInternal != null && ImeTracing.getInstance().isEnabled()) {
|
||
|
final byte[] icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(flags,
|
||
|
result);
|
||
|
imsInternal.triggerServiceDump(TAG + "#getSelectedText", icProto);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get {@link SurroundingText} around the current cursor, with <var>beforeLength</var>
|
||
|
* characters of text before the cursor, <var>afterLength</var> characters of text after the
|
||
|
* cursor, and all of the selected text.
|
||
|
* @param beforeLength The expected length of the text before the cursor
|
||
|
* @param afterLength The expected length of the text after the cursor
|
||
|
* @param flags Supplies additional options controlling how the text is returned. May be either
|
||
|
* 0 or {@link #GET_TEXT_WITH_STYLES}.
|
||
|
* @return the surrounding text around the cursor position; the length of the returned text
|
||
|
* might be less than requested. It could also be {@code null} when the editor or system could
|
||
|
* not support this protocol.
|
||
|
*/
|
||
|
@AnyThread
|
||
|
public SurroundingText getSurroundingText(
|
||
|
@IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
|
||
|
if (beforeLength < 0) {
|
||
|
// TODO: Should we throw an InvalidParameterException() based on targetSdkVersion?
|
||
|
Log.e(TAG, "beforeLength=" + beforeLength
|
||
|
+ " is invalid and always results in null result.");
|
||
|
}
|
||
|
if (afterLength < 0) {
|
||
|
// TODO: Should we throw an InvalidParameterException() based on targetSdkVersion?
|
||
|
Log.e(TAG, "afterLength=" + afterLength
|
||
|
+ " is invalid and always results in null result.");
|
||
|
}
|
||
|
if (mCancellationGroup.isCanceled()) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
final CompletableFuture<SurroundingText> value = mInvoker.getSurroundingText(beforeLength,
|
||
|
afterLength, flags);
|
||
|
final SurroundingText result = CompletableFutureUtil.getResultOrNull(
|
||
|
value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
|
||
|
|
||
|
final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
|
||
|
if (imsInternal != null && ImeTracing.getInstance().isEnabled()) {
|
||
|
final byte[] icProto = InputConnectionProtoDumper.buildGetSurroundingTextProto(
|
||
|
beforeLength, afterLength, flags, result);
|
||
|
imsInternal.triggerServiceDump(TAG + "#getSurroundingText", icProto);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public int getCursorCapsMode(int reqModes) {
|
||
|
if (mCancellationGroup.isCanceled()) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
final CompletableFuture<Integer> value = mInvoker.getCursorCapsMode(reqModes);
|
||
|
final int result = CompletableFutureUtil.getResultOrZero(
|
||
|
value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
|
||
|
|
||
|
final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
|
||
|
if (imsInternal != null && ImeTracing.getInstance().isEnabled()) {
|
||
|
final byte[] icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(
|
||
|
reqModes, result);
|
||
|
imsInternal.triggerServiceDump(TAG + "#getCursorCapsMode", icProto);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
|
||
|
if (mCancellationGroup.isCanceled()) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
final CompletableFuture<ExtractedText> value = mInvoker.getExtractedText(request, flags);
|
||
|
final ExtractedText result = CompletableFutureUtil.getResultOrNull(
|
||
|
value, TAG, "getExtractedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
|
||
|
|
||
|
final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
|
||
|
if (imsInternal != null && ImeTracing.getInstance().isEnabled()) {
|
||
|
final byte[] icProto = InputConnectionProtoDumper.buildGetExtractedTextProto(
|
||
|
request, flags, result);
|
||
|
imsInternal.triggerServiceDump(TAG + "#getExtractedText", icProto);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean commitText(CharSequence text, int newCursorPosition) {
|
||
|
final boolean handled = mInvoker.commitText(text, newCursorPosition);
|
||
|
if (handled) {
|
||
|
notifyUserActionIfNecessary();
|
||
|
}
|
||
|
return handled;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean commitText(@NonNull CharSequence text, int newCursorPosition,
|
||
|
@Nullable TextAttribute textAttribute) {
|
||
|
final boolean handled =
|
||
|
mInvoker.commitText(text, newCursorPosition, textAttribute);
|
||
|
if (handled) {
|
||
|
notifyUserActionIfNecessary();
|
||
|
}
|
||
|
return handled;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
private void notifyUserActionIfNecessary() {
|
||
|
final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
|
||
|
if (imsInternal == null) {
|
||
|
return;
|
||
|
}
|
||
|
imsInternal.notifyUserActionIfNecessary();
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean commitCompletion(CompletionInfo text) {
|
||
|
return mInvoker.commitCompletion(text);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean commitCorrection(CorrectionInfo correctionInfo) {
|
||
|
return mInvoker.commitCorrection(correctionInfo);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean setSelection(int start, int end) {
|
||
|
return mInvoker.setSelection(start, end);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean performEditorAction(int actionCode) {
|
||
|
return mInvoker.performEditorAction(actionCode);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean performContextMenuAction(int id) {
|
||
|
return mInvoker.performContextMenuAction(id);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean setComposingRegion(int start, int end) {
|
||
|
return mInvoker.setComposingRegion(start, end);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
|
||
|
return mInvoker.setComposingRegion(start, end, textAttribute);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean setComposingText(CharSequence text, int newCursorPosition) {
|
||
|
final boolean handled = mInvoker.setComposingText(text, newCursorPosition);
|
||
|
if (handled) {
|
||
|
notifyUserActionIfNecessary();
|
||
|
}
|
||
|
return handled;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean setComposingText(CharSequence text, int newCursorPosition,
|
||
|
@Nullable TextAttribute textAttribute) {
|
||
|
final boolean handled = mInvoker.setComposingText(text, newCursorPosition, textAttribute);
|
||
|
if (handled) {
|
||
|
notifyUserActionIfNecessary();
|
||
|
}
|
||
|
return handled;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean finishComposingText() {
|
||
|
return mInvoker.finishComposingText();
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean beginBatchEdit() {
|
||
|
return mInvoker.beginBatchEdit();
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean endBatchEdit() {
|
||
|
return mInvoker.endBatchEdit();
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean sendKeyEvent(KeyEvent event) {
|
||
|
final boolean handled = mInvoker.sendKeyEvent(event);
|
||
|
if (handled) {
|
||
|
notifyUserActionIfNecessary();
|
||
|
}
|
||
|
return handled;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean clearMetaKeyStates(int states) {
|
||
|
return mInvoker.clearMetaKeyStates(states);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
|
||
|
return mInvoker.deleteSurroundingText(beforeLength, afterLength);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
|
||
|
return mInvoker.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean reportFullscreenMode(boolean enabled) {
|
||
|
// Nothing should happen when called from input method.
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean performSpellCheck() {
|
||
|
return mInvoker.performSpellCheck();
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean performPrivateCommand(String action, Bundle data) {
|
||
|
return mInvoker.performPrivateCommand(action, data);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public void performHandwritingGesture(
|
||
|
@NonNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor,
|
||
|
@Nullable IntConsumer consumer) {
|
||
|
mInvoker.performHandwritingGesture(gesture, executor, consumer);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean previewHandwritingGesture(
|
||
|
@NonNull PreviewableHandwritingGesture gesture,
|
||
|
@Nullable CancellationSignal cancellationSignal) {
|
||
|
if (cancellationSignal != null && cancellationSignal.isCanceled()) {
|
||
|
return false; // cancelled.
|
||
|
}
|
||
|
|
||
|
return mInvoker.previewHandwritingGesture(gesture, cancellationSignal);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean requestCursorUpdates(int cursorUpdateMode) {
|
||
|
if (mCancellationGroup.isCanceled()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
final InputMethodServiceInternal ims = mImsInternal.getAndWarnIfNull();
|
||
|
if (ims == null) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
final int displayId = ims.getContext().getDisplayId();
|
||
|
final CompletableFuture<Boolean> value =
|
||
|
mInvoker.requestCursorUpdates(cursorUpdateMode, displayId);
|
||
|
return CompletableFutureUtil.getResultOrFalse(value, TAG, "requestCursorUpdates()",
|
||
|
mCancellationGroup, MAX_WAIT_TIME_MILLIS);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
@AnyThread
|
||
|
public boolean requestCursorUpdates(@CursorUpdateMode int cursorUpdateMode,
|
||
|
@CursorUpdateFilter int cursorUpdateFilter) {
|
||
|
if (mCancellationGroup.isCanceled()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
final InputMethodServiceInternal ims = mImsInternal.getAndWarnIfNull();
|
||
|
if (ims == null) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
final int displayId = ims.getContext().getDisplayId();
|
||
|
final CompletableFuture<Boolean> value =
|
||
|
mInvoker.requestCursorUpdates(cursorUpdateMode, cursorUpdateFilter, displayId);
|
||
|
return CompletableFutureUtil.getResultOrFalse(value, TAG, "requestCursorUpdates()",
|
||
|
mCancellationGroup, MAX_WAIT_TIME_MILLIS);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public void requestTextBoundsInfo(
|
||
|
@NonNull RectF bounds, @NonNull @CallbackExecutor Executor executor,
|
||
|
@NonNull Consumer<TextBoundsInfoResult> consumer) {
|
||
|
mInvoker.requestTextBoundsInfo(bounds, executor, consumer);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public Handler getHandler() {
|
||
|
// Nothing should happen when called from input method.
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public void closeConnection() {
|
||
|
// Nothing should happen when called from input method.
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
|
||
|
if (mCancellationGroup.isCanceled()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
|
||
|
final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
|
||
|
if (imsInternal == null) {
|
||
|
return false;
|
||
|
}
|
||
|
imsInternal.exposeContent(inputContentInfo, this);
|
||
|
}
|
||
|
|
||
|
final CompletableFuture<Boolean> value =
|
||
|
mInvoker.commitContent(inputContentInfo, flags, opts);
|
||
|
return CompletableFutureUtil.getResultOrFalse(
|
||
|
value, TAG, "commitContent()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* See {@link InputConnection#setImeConsumesInput(boolean)}.
|
||
|
*/
|
||
|
@AnyThread
|
||
|
public boolean setImeConsumesInput(boolean imeConsumesInput) {
|
||
|
return mInvoker.setImeConsumesInput(imeConsumesInput);
|
||
|
}
|
||
|
|
||
|
/** See {@link InputConnection#replaceText(int, int, CharSequence, int, TextAttribute)}. */
|
||
|
@AnyThread
|
||
|
public boolean replaceText(
|
||
|
int start,
|
||
|
int end,
|
||
|
@NonNull CharSequence text,
|
||
|
int newCursorPosition,
|
||
|
@Nullable TextAttribute textAttribute) {
|
||
|
return mInvoker.replaceText(start, end, text, newCursorPosition, textAttribute);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
return "RemoteInputConnection{idHash=#"
|
||
|
+ Integer.toHexString(System.identityHashCode(this)) + "}";
|
||
|
}
|
||
|
}
|