A unified data class to represent all the InputConnection call
This CL introduces a new parcelable class InputConnectionCommand so
that any of existing and future InputConnection APIs can be marshalled
into a standard Java object, which itself can be a subject of other
useful operations.
Historically each InputConnection API has had a corresponding IPC
definition in IInputContext binder interface, which is indeed a widely
used design pattern in the Android OS [1]. Instead of continuing
doing so, this CL reimplements everything by a single unified IPC
method with a single unified Parcelable data object existing and
future InputConnection APIs. Although doing so would introduce some
complexity, it also gives us huge flexibility and opportunities to
abstract out common data structure and operations across all the
InputConnection API calls.
For instance, here are code snippets we can now simply write.
ArrayList<InputConnectionCommand> pendingTasks;
void dumpToProto(@NonNull InputConnectionCommand command) {
...
}
void cancelPendingTasks(Predicate<InputConnectionCommand> cond) {
...
}
to do more complex abstractions as needed.
Other than rewriting InputConnection remote execution in a different
manner, this CL changes nothing. There should be no developer/user
visible behavior changed in this CL, which can also be verified with
CtsInputMethodTestCases:InputConnectionEndToEndTest.
[1]: This is not a hard requiement though. In fact there is already
android.os.Messenger that has been used in several places in the
system. What android.os.Messenger does is semantically the same
as what IInputConnectionWrapper is going to do with this CL.
Fix: 194151409
Test: atest CtsInputMethodTestCases:InputConnectionEndToEndTest
Test: atest FrameworksCoreTests:InputMethodDebugTest
Change-Id: I86eba7185b4b0664c1b0b3da794dfc5eeddc725c
This commit is contained in:
@@ -17,7 +17,11 @@
|
||||
package com.android.internal.inputmethod;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.SurroundingText;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.IntSupplier;
|
||||
@@ -137,4 +141,143 @@ public final class CallbackUtils {
|
||||
callback.onResult(result);
|
||||
} catch (RemoteException ignored) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method to reply associated with {@link InputConnectionCommand}.
|
||||
*
|
||||
* @param command {@link InputConnectionCommand} to be replied.
|
||||
* @param result a {@link String} value to be replied.
|
||||
* @param tag tag name to be used for debug output when the invocation fails.
|
||||
*/
|
||||
public static void onResult(@NonNull InputConnectionCommand command, boolean result,
|
||||
@Nullable String tag) {
|
||||
if (command.mResultCallbackType != InputConnectionCommand.ResultCallbackType.BOOLEAN) {
|
||||
if (tag != null) {
|
||||
Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
|
||||
+ ": Failed to return result=" + result + " due to callback type mismatch."
|
||||
+ " expected=String actual=" + command.mResultCallbackType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
IBooleanResultCallback.Stub.asInterface(command.mResultCallback).onResult(result);
|
||||
} catch (Throwable e) {
|
||||
if (tag != null) {
|
||||
Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
|
||||
+ ": Failed to return result=" + result, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method to reply associated with {@link InputConnectionCommand}.
|
||||
*
|
||||
* @param command {@link InputConnectionCommand} to be replied.
|
||||
* @param result an int result value to be replied.
|
||||
* @param tag tag name to be used for debug output when the invocation fails.
|
||||
*/
|
||||
public static void onResult(@NonNull InputConnectionCommand command, int result,
|
||||
@Nullable String tag) {
|
||||
if (command.mResultCallbackType != InputConnectionCommand.ResultCallbackType.INT) {
|
||||
if (tag != null) {
|
||||
Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
|
||||
+ ": Failed to return result=" + result + " due to callback type mismatch."
|
||||
+ " expected=int actual=" + command.mResultCallbackType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
IIntResultCallback.Stub.asInterface(command.mResultCallback).onResult(result);
|
||||
} catch (Throwable e) {
|
||||
if (tag != null) {
|
||||
Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
|
||||
+ ": Failed to return result=" + result, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method to reply associated with {@link InputConnectionCommand}.
|
||||
*
|
||||
* @param command {@link InputConnectionCommand} to be replied.
|
||||
* @param result a {@link CharSequence} result value to be replied.
|
||||
* @param tag tag name to be used for debug output when the invocation fails.
|
||||
*/
|
||||
public static void onResult(@NonNull InputConnectionCommand command,
|
||||
@Nullable CharSequence result, @Nullable String tag) {
|
||||
if (command.mResultCallbackType
|
||||
!= InputConnectionCommand.ResultCallbackType.CHAR_SEQUENCE) {
|
||||
if (tag != null) {
|
||||
Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
|
||||
+ ": Failed to return result=" + result + " due to callback type mismatch."
|
||||
+ " expected=CharSequence actual=" + command.mResultCallbackType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ICharSequenceResultCallback.Stub.asInterface(command.mResultCallback).onResult(result);
|
||||
} catch (Throwable e) {
|
||||
if (tag != null) {
|
||||
Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
|
||||
+ ": Failed to return result=" + result, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method to reply associated with {@link InputConnectionCommand}.
|
||||
*
|
||||
* @param command {@link InputConnectionCommand} to be replied.
|
||||
* @param result a {@link ExtractedText} result value to be replied.
|
||||
* @param tag tag name to be used for debug output when the invocation fails.
|
||||
*/
|
||||
public static void onResult(@NonNull InputConnectionCommand command,
|
||||
@Nullable ExtractedText result, @Nullable String tag) {
|
||||
if (command.mResultCallbackType
|
||||
!= InputConnectionCommand.ResultCallbackType.EXTRACTED_TEXT) {
|
||||
if (tag != null) {
|
||||
Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
|
||||
+ ": Failed to return result=" + result + " due to callback type mismatch."
|
||||
+ " expected=ExtractedText actual=" + command.mResultCallbackType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
IExtractedTextResultCallback.Stub.asInterface(command.mResultCallback).onResult(result);
|
||||
} catch (Throwable e) {
|
||||
if (tag != null) {
|
||||
Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
|
||||
+ ": Failed to return result=" + result, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method to reply associated with {@link InputConnectionCommand}.
|
||||
*
|
||||
* @param command {@link InputConnectionCommand} to be replied.
|
||||
* @param result a {@link SurroundingText} result value to be replied.
|
||||
* @param tag tag name to be used for debug output when the invocation fails.
|
||||
*/
|
||||
public static void onResult(@NonNull InputConnectionCommand command,
|
||||
@Nullable SurroundingText result, @Nullable String tag) {
|
||||
if (command.mResultCallbackType
|
||||
!= InputConnectionCommand.ResultCallbackType.SURROUNDING_TEXT) {
|
||||
if (tag != null) {
|
||||
Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
|
||||
+ ": Failed to return result=" + result + " due to callback type mismatch."
|
||||
+ " expected=SurroundingText actual=" + command.mResultCallbackType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ISurroundingTextResultCallback.Stub.asInterface(command.mResultCallback)
|
||||
.onResult(result);
|
||||
} catch (Throwable e) {
|
||||
if (tag != null) {
|
||||
Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
|
||||
+ ": Failed to return result=" + result, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.internal.inputmethod;
|
||||
|
||||
import android.annotation.AnyThread;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.view.KeyEvent;
|
||||
@@ -32,7 +33,7 @@ import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A stateless wrapper of {@link com.android.internal.view.IInputContext} to encapsulate boilerplate
|
||||
* code around {@link Completable} and {@link RemoteException}.
|
||||
* code around {@link InputConnectionCommand}, {@link Completable} and {@link RemoteException}.
|
||||
*/
|
||||
public final class IInputContextInvoker {
|
||||
|
||||
@@ -55,8 +56,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#getTextAfterCursor(int, int,
|
||||
* com.android.internal.inputmethod.ICharSequenceResultCallback)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#getTextAfterCursor(int, int)}.
|
||||
*
|
||||
* @param length {@code length} parameter to be passed.
|
||||
* @param flags {@code flags} parameter to be passed.
|
||||
@@ -67,8 +67,9 @@ public final class IInputContextInvoker {
|
||||
@NonNull
|
||||
public Completable.CharSequence getTextAfterCursor(int length, int flags) {
|
||||
final Completable.CharSequence value = Completable.createCharSequence();
|
||||
final InputConnectionCommand command = Commands.getTextAfterCursor(length, flags, value);
|
||||
try {
|
||||
mIInputContext.getTextAfterCursor(length, flags, ResultCallbacks.of(value));
|
||||
mIInputContext.doEdit(command);
|
||||
} catch (RemoteException e) {
|
||||
value.onError(ThrowableHolder.of(e));
|
||||
}
|
||||
@@ -76,7 +77,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#getTextBeforeCursor(int, int, ICharSequenceResultCallback)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#getTextBeforeCursor(int, int)}.
|
||||
*
|
||||
* @param length {@code length} parameter to be passed.
|
||||
* @param flags {@code flags} parameter to be passed.
|
||||
@@ -87,8 +88,9 @@ public final class IInputContextInvoker {
|
||||
@NonNull
|
||||
public Completable.CharSequence getTextBeforeCursor(int length, int flags) {
|
||||
final Completable.CharSequence value = Completable.createCharSequence();
|
||||
final InputConnectionCommand command = Commands.getTextBeforeCursor(length, flags, value);
|
||||
try {
|
||||
mIInputContext.getTextBeforeCursor(length, flags, ResultCallbacks.of(value));
|
||||
mIInputContext.doEdit(command);
|
||||
} catch (RemoteException e) {
|
||||
value.onError(ThrowableHolder.of(e));
|
||||
}
|
||||
@@ -96,7 +98,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#getSelectedText(int, ICharSequenceResultCallback)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#getSelectedText(int)}.
|
||||
*
|
||||
* @param flags {@code flags} parameter to be passed.
|
||||
* @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
|
||||
@@ -106,8 +108,9 @@ public final class IInputContextInvoker {
|
||||
@NonNull
|
||||
public Completable.CharSequence getSelectedText(int flags) {
|
||||
final Completable.CharSequence value = Completable.createCharSequence();
|
||||
final InputConnectionCommand command = Commands.getSelectedText(flags, value);
|
||||
try {
|
||||
mIInputContext.getSelectedText(flags, ResultCallbacks.of(value));
|
||||
mIInputContext.doEdit(command);
|
||||
} catch (RemoteException e) {
|
||||
value.onError(ThrowableHolder.of(e));
|
||||
}
|
||||
@@ -115,8 +118,8 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes
|
||||
* {@link IInputContext#getSurroundingText(int, int, int, ISurroundingTextResultCallback)}.
|
||||
* Implements
|
||||
* {@link android.view.inputmethod.InputConnection#getSurroundingText(int, int, int)}.
|
||||
*
|
||||
* @param beforeLength {@code beforeLength} parameter to be passed.
|
||||
* @param afterLength {@code afterLength} parameter to be passed.
|
||||
@@ -129,9 +132,10 @@ public final class IInputContextInvoker {
|
||||
public Completable.SurroundingText getSurroundingText(int beforeLength, int afterLength,
|
||||
int flags) {
|
||||
final Completable.SurroundingText value = Completable.createSurroundingText();
|
||||
final InputConnectionCommand command =
|
||||
Commands.getSurroundingText(beforeLength, afterLength, flags, value);
|
||||
try {
|
||||
mIInputContext.getSurroundingText(beforeLength, afterLength, flags,
|
||||
ResultCallbacks.of(value));
|
||||
mIInputContext.doEdit(command);
|
||||
} catch (RemoteException e) {
|
||||
value.onError(ThrowableHolder.of(e));
|
||||
}
|
||||
@@ -139,7 +143,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#getCursorCapsMode(int, IIntResultCallback)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#getCursorCapsMode(int)}.
|
||||
*
|
||||
* @param reqModes {@code reqModes} parameter to be passed.
|
||||
* @return {@link Completable.Int} that can be used to retrieve the invocation result.
|
||||
@@ -149,8 +153,9 @@ public final class IInputContextInvoker {
|
||||
@NonNull
|
||||
public Completable.Int getCursorCapsMode(int reqModes) {
|
||||
final Completable.Int value = Completable.createInt();
|
||||
final InputConnectionCommand command = Commands.getCursorCapsMode(reqModes, value);
|
||||
try {
|
||||
mIInputContext.getCursorCapsMode(reqModes, ResultCallbacks.of(value));
|
||||
mIInputContext.doEdit(command);
|
||||
} catch (RemoteException e) {
|
||||
value.onError(ThrowableHolder.of(e));
|
||||
}
|
||||
@@ -158,8 +163,8 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#getExtractedText(ExtractedTextRequest, int,
|
||||
* IExtractedTextResultCallback)}.
|
||||
* Implements
|
||||
* {@link android.view.inputmethod.InputConnection#getExtractedText(ExtractedTextRequest, int)}.
|
||||
*
|
||||
* @param request {@code request} parameter to be passed.
|
||||
* @param flags {@code flags} parameter to be passed.
|
||||
@@ -170,8 +175,9 @@ public final class IInputContextInvoker {
|
||||
@NonNull
|
||||
public Completable.ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
|
||||
final Completable.ExtractedText value = Completable.createExtractedText();
|
||||
final InputConnectionCommand command = Commands.getExtractedText(request, flags, value);
|
||||
try {
|
||||
mIInputContext.getExtractedText(request, flags, ResultCallbacks.of(value));
|
||||
mIInputContext.doEdit(command);
|
||||
} catch (RemoteException e) {
|
||||
value.onError(ThrowableHolder.of(e));
|
||||
}
|
||||
@@ -179,7 +185,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#commitText(CharSequence, int)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#commitText(CharSequence, int)}.
|
||||
*
|
||||
* @param text {@code text} parameter to be passed.
|
||||
* @param newCursorPosition {@code newCursorPosition} parameter to be passed.
|
||||
@@ -188,8 +194,9 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean commitText(CharSequence text, int newCursorPosition) {
|
||||
final InputConnectionCommand command = Commands.commitText(text, newCursorPosition);
|
||||
try {
|
||||
mIInputContext.commitText(text, newCursorPosition);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -197,7 +204,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#commitCompletion(CompletionInfo)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#commitCompletion(CompletionInfo)}.
|
||||
*
|
||||
* @param text {@code text} parameter to be passed.
|
||||
* @return {@code true} if the invocation is completed without {@link RemoteException}.
|
||||
@@ -205,8 +212,9 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean commitCompletion(CompletionInfo text) {
|
||||
final InputConnectionCommand command = Commands.commitCompletion(text);
|
||||
try {
|
||||
mIInputContext.commitCompletion(text);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -214,7 +222,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#commitCorrection(CorrectionInfo)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#commitCorrection(CorrectionInfo)}.
|
||||
*
|
||||
* @param correctionInfo {@code correctionInfo} parameter to be passed.
|
||||
* @return {@code true} if the invocation is completed without {@link RemoteException}.
|
||||
@@ -222,8 +230,9 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean commitCorrection(CorrectionInfo correctionInfo) {
|
||||
final InputConnectionCommand command = Commands.commitCorrection(correctionInfo);
|
||||
try {
|
||||
mIInputContext.commitCorrection(correctionInfo);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -231,7 +240,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#setSelection(int, int)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#setSelection(int, int)}.
|
||||
*
|
||||
* @param start {@code start} parameter to be passed.
|
||||
* @param end {@code start} parameter to be passed.
|
||||
@@ -240,8 +249,9 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean setSelection(int start, int end) {
|
||||
final InputConnectionCommand command = Commands.setSelection(start, end);
|
||||
try {
|
||||
mIInputContext.setSelection(start, end);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -249,7 +259,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#performEditorAction(int)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#performEditorAction(int)}.
|
||||
*
|
||||
* @param actionCode {@code start} parameter to be passed.
|
||||
* @return {@code true} if the invocation is completed without {@link RemoteException}.
|
||||
@@ -257,8 +267,9 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean performEditorAction(int actionCode) {
|
||||
final InputConnectionCommand command = Commands.performEditorAction(actionCode);
|
||||
try {
|
||||
mIInputContext.performEditorAction(actionCode);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -266,7 +277,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#performContextMenuAction(id)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#performContextMenuAction(int)}.
|
||||
*
|
||||
* @param id {@code id} parameter to be passed.
|
||||
* @return {@code true} if the invocation is completed without {@link RemoteException}.
|
||||
@@ -274,8 +285,9 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean performContextMenuAction(int id) {
|
||||
final InputConnectionCommand command = Commands.performContextMenuAction(id);
|
||||
try {
|
||||
mIInputContext.performContextMenuAction(id);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -283,7 +295,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#setComposingRegion(int, int)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#setComposingRegion(int, int)}.
|
||||
*
|
||||
* @param start {@code id} parameter to be passed.
|
||||
* @param end {@code id} parameter to be passed.
|
||||
@@ -292,8 +304,9 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean setComposingRegion(int start, int end) {
|
||||
final InputConnectionCommand command = Commands.setComposingRegion(start, end);
|
||||
try {
|
||||
mIInputContext.setComposingRegion(start, end);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -301,7 +314,8 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#setComposingText(CharSequence, int)}.
|
||||
* Implements
|
||||
* {@link android.view.inputmethod.InputConnection#setComposingText(CharSequence, int)}.
|
||||
*
|
||||
* @param text {@code text} parameter to be passed.
|
||||
* @param newCursorPosition {@code newCursorPosition} parameter to be passed.
|
||||
@@ -310,8 +324,9 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean setComposingText(CharSequence text, int newCursorPosition) {
|
||||
final InputConnectionCommand command = Commands.setComposingText(text, newCursorPosition);
|
||||
try {
|
||||
mIInputContext.setComposingText(text, newCursorPosition);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -319,15 +334,16 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#finishComposingText()}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#finishComposingText()}.
|
||||
*
|
||||
* @return {@code true} if the invocation is completed without {@link RemoteException}.
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean finishComposingText() {
|
||||
final InputConnectionCommand command = Commands.finishComposingText();
|
||||
try {
|
||||
mIInputContext.finishComposingText();
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -335,15 +351,16 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#beginBatchEdit()}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#beginBatchEdit()}.
|
||||
*
|
||||
* @return {@code true} if the invocation is completed without {@link RemoteException}.
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean beginBatchEdit() {
|
||||
final InputConnectionCommand command = Commands.beginBatchEdit();
|
||||
try {
|
||||
mIInputContext.beginBatchEdit();
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -351,15 +368,16 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#endBatchEdit()}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#endBatchEdit()}.
|
||||
*
|
||||
* @return {@code true} if the invocation is completed without {@link RemoteException}.
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean endBatchEdit() {
|
||||
final InputConnectionCommand command = Commands.endBatchEdit();
|
||||
try {
|
||||
mIInputContext.endBatchEdit();
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -367,7 +385,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#sendKeyEvent(KeyEvent)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#sendKeyEvent(KeyEvent)}.
|
||||
*
|
||||
* @param event {@code event} parameter to be passed.
|
||||
* @return {@code true} if the invocation is completed without {@link RemoteException}.
|
||||
@@ -375,8 +393,9 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean sendKeyEvent(KeyEvent event) {
|
||||
final InputConnectionCommand command = Commands.sendKeyEvent(event);
|
||||
try {
|
||||
mIInputContext.sendKeyEvent(event);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -384,7 +403,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#clearMetaKeyStates(int)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#clearMetaKeyStates(int)}.
|
||||
*
|
||||
* @param states {@code states} parameter to be passed.
|
||||
* @return {@code true} if the invocation is completed without {@link RemoteException}.
|
||||
@@ -392,8 +411,9 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean clearMetaKeyStates(int states) {
|
||||
final InputConnectionCommand command = Commands.clearMetaKeyStates(states);
|
||||
try {
|
||||
mIInputContext.clearMetaKeyStates(states);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -401,7 +421,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#deleteSurroundingText(int, int)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#deleteSurroundingText(int, int)}.
|
||||
*
|
||||
* @param beforeLength {@code beforeLength} parameter to be passed.
|
||||
* @param afterLength {@code afterLength} parameter to be passed.
|
||||
@@ -410,8 +430,10 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
|
||||
final InputConnectionCommand command =
|
||||
Commands.deleteSurroundingText(beforeLength, afterLength);
|
||||
try {
|
||||
mIInputContext.deleteSurroundingText(beforeLength, afterLength);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -419,7 +441,8 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#deleteSurroundingTextInCodePoints(int, int)}.
|
||||
* Implements
|
||||
* {@link android.view.inputmethod.InputConnection#deleteSurroundingTextInCodePoints(int, int)}.
|
||||
*
|
||||
* @param beforeLength {@code beforeLength} parameter to be passed.
|
||||
* @param afterLength {@code afterLength} parameter to be passed.
|
||||
@@ -428,8 +451,10 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
|
||||
final InputConnectionCommand command =
|
||||
Commands.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
|
||||
try {
|
||||
mIInputContext.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -437,15 +462,16 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#performSpellCheck()}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#performSpellCheck()}.
|
||||
*
|
||||
* @return {@code true} if the invocation is completed without {@link RemoteException}.
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean performSpellCheck() {
|
||||
final InputConnectionCommand command = Commands.performSpellCheck();
|
||||
try {
|
||||
mIInputContext.performSpellCheck();
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -453,7 +479,8 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#performPrivateCommand(String, Bundle)}.
|
||||
* Implements
|
||||
* {@link android.view.inputmethod.InputConnection#performPrivateCommand(String, Bundle)}.
|
||||
*
|
||||
* @param action {@code action} parameter to be passed.
|
||||
* @param data {@code data} parameter to be passed.
|
||||
@@ -462,8 +489,9 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean performPrivateCommand(String action, Bundle data) {
|
||||
final InputConnectionCommand command = Commands.performPrivateCommand(action, data);
|
||||
try {
|
||||
mIInputContext.performPrivateCommand(action, data);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
@@ -471,7 +499,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#requestCursorUpdates(int, IIntResultCallback)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#requestCursorUpdates(int)}.
|
||||
*
|
||||
* @param cursorUpdateMode {@code cursorUpdateMode} parameter to be passed.
|
||||
* @return {@link Completable.Boolean} that can be used to retrieve the invocation result.
|
||||
@@ -481,8 +509,10 @@ public final class IInputContextInvoker {
|
||||
@NonNull
|
||||
public Completable.Boolean requestCursorUpdates(int cursorUpdateMode) {
|
||||
final Completable.Boolean value = Completable.createBoolean();
|
||||
final InputConnectionCommand command =
|
||||
Commands.requestCursorUpdates(cursorUpdateMode, value);
|
||||
try {
|
||||
mIInputContext.requestCursorUpdates(cursorUpdateMode, ResultCallbacks.of(value));
|
||||
mIInputContext.doEdit(command);
|
||||
} catch (RemoteException e) {
|
||||
value.onError(ThrowableHolder.of(e));
|
||||
}
|
||||
@@ -490,8 +520,8 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes
|
||||
* {@link IInputContext#commitContent(InputContentInfo, int, Bundle, IIntResultCallback)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#commitContent(InputContentInfo,
|
||||
* int, Bundle)}.
|
||||
*
|
||||
* @param inputContentInfo {@code inputContentInfo} parameter to be passed.
|
||||
* @param flags {@code flags} parameter to be passed.
|
||||
@@ -504,8 +534,10 @@ public final class IInputContextInvoker {
|
||||
public Completable.Boolean commitContent(InputContentInfo inputContentInfo, int flags,
|
||||
Bundle opts) {
|
||||
final Completable.Boolean value = Completable.createBoolean();
|
||||
final InputConnectionCommand command =
|
||||
Commands.commitContent(inputContentInfo, flags, opts, value);
|
||||
try {
|
||||
mIInputContext.commitContent(inputContentInfo, flags, opts, ResultCallbacks.of(value));
|
||||
mIInputContext.doEdit(command);
|
||||
} catch (RemoteException e) {
|
||||
value.onError(ThrowableHolder.of(e));
|
||||
}
|
||||
@@ -513,7 +545,7 @@ public final class IInputContextInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link IInputContext#setImeConsumesInput(boolean)}.
|
||||
* Implements {@link android.view.inputmethod.InputConnection#setImeConsumesInput(boolean)}.
|
||||
*
|
||||
* @param imeConsumesInput {@code imeConsumesInput} parameter to be passed.
|
||||
* @return {@code true} if the invocation is completed without {@link RemoteException}.
|
||||
@@ -521,11 +553,341 @@ public final class IInputContextInvoker {
|
||||
*/
|
||||
@AnyThread
|
||||
public boolean setImeConsumesInput(boolean imeConsumesInput) {
|
||||
final InputConnectionCommand command = Commands.setImeConsumesInput(imeConsumesInput);
|
||||
try {
|
||||
mIInputContext.setImeConsumesInput(imeConsumesInput);
|
||||
mIInputContext.doEdit(command);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the data packing rules from {@link android.view.inputmethod.InputConnection} API
|
||||
* params into {@link InputConnectionCommand} fields.
|
||||
*
|
||||
* Rules need to be in sync with {@link com.android.internal.view.IInputConnectionWrapper} and
|
||||
* {@link InputMethodDebug#dumpInputConnectionCommand(InputConnectionCommand)}.
|
||||
*/
|
||||
private static final class Commands {
|
||||
/**
|
||||
* Not intended to be instantiated.
|
||||
*/
|
||||
private Commands() { }
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand getTextAfterCursor(int n, int flags,
|
||||
@NonNull Completable.CharSequence returnValue) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.GET_TEXT_AFTER_CURSOR,
|
||||
n,
|
||||
0,
|
||||
flags,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
InputConnectionCommand.ParcelableType.NULL,
|
||||
null,
|
||||
returnValue);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand getTextBeforeCursor(int n, int flags,
|
||||
@NonNull Completable.CharSequence returnValue) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.GET_TEXT_BEFORE_CURSOR,
|
||||
n,
|
||||
0,
|
||||
flags,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
InputConnectionCommand.ParcelableType.NULL,
|
||||
null,
|
||||
returnValue);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand getSelectedText(int flags,
|
||||
@NonNull Completable.CharSequence returnValue) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.GET_SELECTED_TEXT,
|
||||
0,
|
||||
0,
|
||||
flags,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
InputConnectionCommand.ParcelableType.NULL,
|
||||
null,
|
||||
returnValue);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand getSurroundingText(int beforeLength, int afterLength,
|
||||
int flags, @NonNull Completable.SurroundingText returnValue) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.GET_SURROUNDING_TEXT,
|
||||
beforeLength,
|
||||
afterLength,
|
||||
flags,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
InputConnectionCommand.ParcelableType.NULL,
|
||||
null,
|
||||
returnValue);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand getCursorCapsMode(int reqModes,
|
||||
@NonNull Completable.Int returnValue) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.GET_CURSOR_CAPS_MODE,
|
||||
reqModes,
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
InputConnectionCommand.ParcelableType.NULL,
|
||||
null,
|
||||
returnValue);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand getExtractedText(@Nullable ExtractedTextRequest request,
|
||||
int flags, @NonNull Completable.ExtractedText returnValue) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.GET_EXTRACTED_TEXT,
|
||||
0,
|
||||
0,
|
||||
flags,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
InputConnectionCommand.ParcelableType.EXTRACTED_TEXT_REQUEST,
|
||||
request,
|
||||
returnValue);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand commitText(@Nullable CharSequence text,
|
||||
int newCursorPosition) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.COMMIT_TEXT,
|
||||
newCursorPosition,
|
||||
0,
|
||||
0,
|
||||
text);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand commitCompletion(@Nullable CompletionInfo text) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.COMMIT_COMPLETION,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
InputConnectionCommand.ParcelableType.COMPLETION_INFO,
|
||||
text);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand commitCorrection(@Nullable CorrectionInfo correctionInfo) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.COMMIT_CORRECTION,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
InputConnectionCommand.ParcelableType.CORRECTION_INFO,
|
||||
correctionInfo);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand setSelection(int start, int end) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.SET_SELECTION,
|
||||
start,
|
||||
end);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand performEditorAction(int actionCode) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.PERFORM_EDITOR_ACTION,
|
||||
actionCode);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand performContextMenuAction(int id) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.PERFORM_CONTEXT_MENU_ACTION,
|
||||
id);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand setComposingRegion(int start, int end) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.SET_COMPOSING_REGION,
|
||||
start,
|
||||
end);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand setComposingText(@Nullable CharSequence text,
|
||||
int newCursorPosition) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.SET_COMPOSING_TEXT,
|
||||
newCursorPosition,
|
||||
0,
|
||||
0,
|
||||
text);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand finishComposingText() {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.FINISH_COMPOSING_TEXT);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand beginBatchEdit() {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.BEGIN_BATCH_EDIT);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand endBatchEdit() {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.END_BATCH_EDIT);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand sendKeyEvent(@Nullable KeyEvent event) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.SEND_KEY_EVENT,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
InputConnectionCommand.ParcelableType.KEY_EVENT,
|
||||
event);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand clearMetaKeyStates(int states) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.CLEAR_META_KEY_STATES,
|
||||
states);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand deleteSurroundingText(int beforeLength, int afterLength) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.DELETE_SURROUNDING_TEXT,
|
||||
beforeLength,
|
||||
afterLength);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand deleteSurroundingTextInCodePoints(int beforeLength,
|
||||
int afterLength) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
|
||||
beforeLength,
|
||||
afterLength);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand performSpellCheck() {
|
||||
return InputConnectionCommand.create(InputConnectionCommandType.PERFORM_SPELL_CHECK);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand performPrivateCommand(@Nullable String action,
|
||||
@Nullable Bundle data) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.PERFORM_PRIVATE_COMMAND,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
action,
|
||||
data);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand requestCursorUpdates(int cursorUpdateMode,
|
||||
@NonNull Completable.Boolean returnValue) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.REQUEST_CURSOR_UPDATES,
|
||||
cursorUpdateMode,
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
InputConnectionCommand.ParcelableType.NULL,
|
||||
null,
|
||||
returnValue);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand commitContent(@Nullable InputContentInfo inputContentInfo,
|
||||
int flags, @Nullable Bundle opts, @NonNull Completable.Boolean returnValue) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.COMMIT_CONTENT,
|
||||
0,
|
||||
0,
|
||||
flags,
|
||||
null,
|
||||
null,
|
||||
opts,
|
||||
InputConnectionCommand.ParcelableType.INPUT_CONTENT_INFO,
|
||||
inputContentInfo,
|
||||
returnValue);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
static InputConnectionCommand setImeConsumesInput(boolean imeConsumesInput) {
|
||||
return InputConnectionCommand.create(
|
||||
InputConnectionCommandType.SET_IME_CONSUMES_INPUT,
|
||||
imeConsumesInput ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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 com.android.internal.inputmethod;
|
||||
|
||||
parcelable InputConnectionCommand;
|
||||
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
* 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 com.android.internal.inputmethod;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
|
||||
import android.annotation.AnyThread;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
import android.view.inputmethod.CorrectionInfo;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputContentInfo;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
/**
|
||||
* Defines the command message to be used for IMEs to remotely invoke
|
||||
* {@link android.view.inputmethod.InputConnection} APIs in the IME client process then receive
|
||||
* results.
|
||||
*/
|
||||
public final class InputConnectionCommand implements Parcelable {
|
||||
private static final String TAG = "InputConnectionCommand";
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef(value = {
|
||||
ResultCallbackType.NULL,
|
||||
ResultCallbackType.BOOLEAN,
|
||||
ResultCallbackType.INT,
|
||||
ResultCallbackType.CHAR_SEQUENCE,
|
||||
ResultCallbackType.EXTRACTED_TEXT,
|
||||
ResultCallbackType.SURROUNDING_TEXT,
|
||||
})
|
||||
@interface ResultCallbackType {
|
||||
int NULL = 0;
|
||||
int BOOLEAN = 1;
|
||||
int INT = 2;
|
||||
int CHAR_SEQUENCE = 3;
|
||||
int EXTRACTED_TEXT = 4;
|
||||
int SURROUNDING_TEXT = 5;
|
||||
}
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef(value = {
|
||||
ParcelableType.NULL,
|
||||
ParcelableType.EXTRACTED_TEXT_REQUEST,
|
||||
ParcelableType.COMPLETION_INFO,
|
||||
ParcelableType.CORRECTION_INFO,
|
||||
ParcelableType.KEY_EVENT,
|
||||
ParcelableType.INPUT_CONTENT_INFO,
|
||||
})
|
||||
@interface ParcelableType {
|
||||
int NULL = 0;
|
||||
int EXTRACTED_TEXT_REQUEST = 1;
|
||||
int COMPLETION_INFO = 2;
|
||||
int CORRECTION_INFO = 3;
|
||||
int KEY_EVENT = 4;
|
||||
int INPUT_CONTENT_INFO = 5;
|
||||
}
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef(flag = true, value = {
|
||||
FieldMask.INT_ARG0,
|
||||
FieldMask.INT_ARG1,
|
||||
FieldMask.FLAGS,
|
||||
FieldMask.CHAR_SEQUENCE,
|
||||
FieldMask.STRING,
|
||||
FieldMask.BUNDLE,
|
||||
FieldMask.PARCELABLE,
|
||||
FieldMask.CALLBACK,
|
||||
})
|
||||
@interface FieldMask {
|
||||
int INT_ARG0 = 1 << 0;
|
||||
int INT_ARG1 = 1 << 1;
|
||||
int FLAGS = 1 << 2;
|
||||
int CHAR_SEQUENCE = 1 << 3;
|
||||
int STRING = 1 << 4;
|
||||
int BUNDLE = 1 << 5;
|
||||
int PARCELABLE = 1 << 6;
|
||||
int CALLBACK = 1 << 7;
|
||||
}
|
||||
|
||||
@IntRange(from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType
|
||||
public final int mCommandType;
|
||||
public final int mIntArg0;
|
||||
public final int mIntArg1;
|
||||
public final int mFlags;
|
||||
public final CharSequence mCharSequence;
|
||||
public final String mString;
|
||||
public final Bundle mBundle;
|
||||
@ParcelableType
|
||||
public final int mParcelableType;
|
||||
public final Parcelable mParcelable;
|
||||
@ResultCallbackType
|
||||
public final int mResultCallbackType;
|
||||
public final IBinder mResultCallback;
|
||||
|
||||
private InputConnectionCommand(
|
||||
@IntRange(
|
||||
from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType int type, int intArg0, int intArg1, int flags,
|
||||
@Nullable CharSequence charSequence, @Nullable String string, @Nullable Bundle bundle,
|
||||
@ParcelableType int parcelableType, @Nullable Parcelable parcelable,
|
||||
@ResultCallbackType int resultCallbackType, @Nullable IBinder resultCallback) {
|
||||
if (type < InputConnectionCommandType.FIRST_COMMAND
|
||||
|| InputConnectionCommandType.LAST_COMMAND < type) {
|
||||
throw new IllegalArgumentException("Unknown type=" + type);
|
||||
}
|
||||
mCommandType = type;
|
||||
mIntArg0 = intArg0;
|
||||
mIntArg1 = intArg1;
|
||||
mFlags = flags;
|
||||
mCharSequence = charSequence;
|
||||
mString = string;
|
||||
mBundle = bundle;
|
||||
mParcelableType = parcelableType;
|
||||
mParcelable = parcelable;
|
||||
mResultCallbackType = resultCallbackType;
|
||||
mResultCallback = resultCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link InputConnectionCommand} with the given {@link InputConnectionCommandType}.
|
||||
*
|
||||
* @param type {@link InputConnectionCommandType} to be set.
|
||||
* @return An {@link InputConnectionCommand} that is initialized with {@code type}.
|
||||
*/
|
||||
@NonNull
|
||||
public static InputConnectionCommand create(
|
||||
@IntRange(
|
||||
from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType int type) {
|
||||
return create(type, 0);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static InputConnectionCommand create(
|
||||
@IntRange(
|
||||
from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType int type, int intArg0) {
|
||||
return create(type, intArg0, 0);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static InputConnectionCommand create(
|
||||
@IntRange(
|
||||
from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType int type, int intArg0, int intArg1) {
|
||||
return create(type, intArg0, intArg1, 0, null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static InputConnectionCommand create(
|
||||
@IntRange(
|
||||
from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType int type, int intArg0,
|
||||
int intArg1, int flags, @Nullable CharSequence charSequence) {
|
||||
return create(type, intArg0, intArg1, flags, charSequence, null, null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static InputConnectionCommand create(
|
||||
@IntRange(
|
||||
from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType int type,
|
||||
int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
|
||||
@Nullable String string, @Nullable Bundle bundle) {
|
||||
return create(type, intArg0, intArg1, flags, charSequence, string,
|
||||
bundle, ParcelableType.NULL, null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static InputConnectionCommand create(
|
||||
@IntRange(
|
||||
from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType int type,
|
||||
int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
|
||||
@Nullable String string, @Nullable Bundle bundle,
|
||||
@ParcelableType int parcelableType, @Nullable Parcelable parcelable) {
|
||||
return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
|
||||
bundle, parcelableType, parcelable, ResultCallbackType.NULL, null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static InputConnectionCommand create(
|
||||
@IntRange(
|
||||
from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType int type,
|
||||
int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
|
||||
@Nullable String string, @Nullable Bundle bundle,
|
||||
@ParcelableType int parcelableType, @Nullable Parcelable parcelable,
|
||||
@NonNull Completable.Boolean returnValue) {
|
||||
return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
|
||||
bundle, parcelableType, parcelable,
|
||||
ResultCallbackType.BOOLEAN, ResultCallbacks.of(returnValue));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static InputConnectionCommand create(
|
||||
@IntRange(
|
||||
from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType int type,
|
||||
int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
|
||||
@Nullable String string, @Nullable Bundle bundle,
|
||||
@ParcelableType int parcelableType, @Nullable Parcelable parcelable,
|
||||
@NonNull Completable.Int returnValue) {
|
||||
return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
|
||||
bundle, parcelableType, parcelable,
|
||||
ResultCallbackType.INT, ResultCallbacks.of(returnValue));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static InputConnectionCommand create(
|
||||
@IntRange(
|
||||
from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType int type,
|
||||
int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
|
||||
@Nullable String string, @Nullable Bundle bundle,
|
||||
@ParcelableType int parcelableType, @Nullable Parcelable parcelable,
|
||||
@NonNull Completable.CharSequence returnValue) {
|
||||
return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
|
||||
bundle, parcelableType, parcelable,
|
||||
ResultCallbackType.CHAR_SEQUENCE, ResultCallbacks.of(returnValue));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static InputConnectionCommand create(
|
||||
@IntRange(
|
||||
from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType int type,
|
||||
int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
|
||||
@Nullable String string, @Nullable Bundle bundle,
|
||||
@ParcelableType int parcelableType, @Nullable Parcelable parcelable,
|
||||
@NonNull Completable.ExtractedText returnValue) {
|
||||
return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
|
||||
bundle, parcelableType, parcelable,
|
||||
ResultCallbackType.EXTRACTED_TEXT, ResultCallbacks.of(returnValue));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static InputConnectionCommand create(
|
||||
@IntRange(
|
||||
from = InputConnectionCommandType.FIRST_COMMAND,
|
||||
to = InputConnectionCommandType.LAST_COMMAND)
|
||||
@InputConnectionCommandType int type,
|
||||
int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
|
||||
@Nullable String string, @Nullable Bundle bundle,
|
||||
@ParcelableType int parcelableType, @Nullable Parcelable parcelable,
|
||||
@NonNull Completable.SurroundingText returnValue) {
|
||||
return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
|
||||
bundle, parcelableType, parcelable,
|
||||
ResultCallbackType.SURROUNDING_TEXT, ResultCallbacks.of(returnValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@AnyThread
|
||||
@Override
|
||||
public int describeContents() {
|
||||
int result = 0;
|
||||
if (mBundle != null) {
|
||||
result |= mBundle.describeContents();
|
||||
}
|
||||
if (mParcelable != null) {
|
||||
result |= mParcelable.describeContents();
|
||||
}
|
||||
// Here we assume other objects will never contain FDs to be parcelled.
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@AnyThread
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mCommandType);
|
||||
|
||||
@FieldMask final int fieldMask = getFieldMask();
|
||||
dest.writeInt(fieldMask);
|
||||
if ((fieldMask & FieldMask.INT_ARG0) != 0) {
|
||||
dest.writeInt(mIntArg0);
|
||||
}
|
||||
if ((fieldMask & FieldMask.INT_ARG1) != 0) {
|
||||
dest.writeInt(mIntArg1);
|
||||
}
|
||||
if ((fieldMask & FieldMask.FLAGS) != 0) {
|
||||
dest.writeInt(mFlags);
|
||||
}
|
||||
if ((fieldMask & FieldMask.CHAR_SEQUENCE) != 0) {
|
||||
TextUtils.writeToParcel(mCharSequence, dest, flags);
|
||||
}
|
||||
if ((fieldMask & FieldMask.STRING) != 0) {
|
||||
dest.writeString(mString);
|
||||
}
|
||||
if ((fieldMask & FieldMask.BUNDLE) != 0) {
|
||||
dest.writeBundle(mBundle);
|
||||
}
|
||||
if ((fieldMask & FieldMask.PARCELABLE) != 0) {
|
||||
dest.writeInt(mParcelableType);
|
||||
dest.writeTypedObject(mParcelable, flags);
|
||||
}
|
||||
if ((fieldMask & FieldMask.CALLBACK) != 0) {
|
||||
dest.writeInt(mResultCallbackType);
|
||||
dest.writeStrongBinder(mResultCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@FieldMask
|
||||
@AnyThread
|
||||
private int getFieldMask() {
|
||||
return (mIntArg0 != 0 ? FieldMask.INT_ARG0 : 0)
|
||||
| (mIntArg1 != 0 ? FieldMask.INT_ARG1 : 0)
|
||||
| (mFlags != 0 ? FieldMask.FLAGS : 0)
|
||||
| (mCharSequence != null ? FieldMask.CHAR_SEQUENCE : 0)
|
||||
| (mString != null ? FieldMask.STRING : 0)
|
||||
| (mBundle != null ? FieldMask.BUNDLE : 0)
|
||||
| (mParcelableType != ParcelableType.NULL ? FieldMask.PARCELABLE : 0)
|
||||
| (mResultCallbackType != ResultCallbackType.NULL ? FieldMask.CALLBACK : 0);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@Nullable
|
||||
private static InputConnectionCommand createFromParcel(@NonNull Parcel source) {
|
||||
final int type = source.readInt();
|
||||
if (type < InputConnectionCommandType.FIRST_COMMAND
|
||||
|| InputConnectionCommandType.LAST_COMMAND < type) {
|
||||
Log.e(TAG, "Invalid InputConnectionCommand type=" + type);
|
||||
return null;
|
||||
}
|
||||
|
||||
@FieldMask final int fieldMask = source.readInt();
|
||||
final int intArg0 = (fieldMask & FieldMask.INT_ARG0) != 0 ? source.readInt() : 0;
|
||||
final int intArg1 = (fieldMask & FieldMask.INT_ARG1) != 0 ? source.readInt() : 0;
|
||||
final int flags = (fieldMask & FieldMask.FLAGS) != 0 ? source.readInt() : 0;
|
||||
final CharSequence charSequence = (fieldMask & FieldMask.CHAR_SEQUENCE) != 0
|
||||
? TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source) : null;
|
||||
final String string = (fieldMask & FieldMask.STRING) != 0 ? source.readString() : null;
|
||||
final Bundle bundle = (fieldMask & FieldMask.BUNDLE) != 0 ? source.readBundle() : null;
|
||||
|
||||
@ParcelableType final int parcelableType;
|
||||
final Parcelable parcelable;
|
||||
if ((fieldMask & FieldMask.PARCELABLE) != 0) {
|
||||
parcelableType = source.readInt();
|
||||
switch (parcelableType) {
|
||||
case ParcelableType.NULL:
|
||||
Log.e(TAG, "Unexpected ParcelableType=NULL");
|
||||
return null;
|
||||
case ParcelableType.EXTRACTED_TEXT_REQUEST:
|
||||
parcelable = source.readTypedObject(ExtractedTextRequest.CREATOR);
|
||||
break;
|
||||
case ParcelableType.COMPLETION_INFO:
|
||||
parcelable = source.readTypedObject(CompletionInfo.CREATOR);
|
||||
break;
|
||||
case ParcelableType.CORRECTION_INFO:
|
||||
parcelable = source.readTypedObject(CorrectionInfo.CREATOR);
|
||||
break;
|
||||
case ParcelableType.KEY_EVENT:
|
||||
parcelable = source.readTypedObject(KeyEvent.CREATOR);
|
||||
break;
|
||||
case ParcelableType.INPUT_CONTENT_INFO:
|
||||
parcelable = source.readTypedObject(InputContentInfo.CREATOR);
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unknown ParcelableType=" + parcelableType);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
parcelableType = ParcelableType.NULL;
|
||||
parcelable = null;
|
||||
}
|
||||
@ResultCallbackType final int resultCallbackType;
|
||||
final IBinder resultCallback;
|
||||
if ((fieldMask & FieldMask.CALLBACK) != 0) {
|
||||
resultCallbackType = source.readInt();
|
||||
switch (resultCallbackType) {
|
||||
case ResultCallbackType.NULL:
|
||||
Log.e(TAG, "Unexpected ResultCallbackType=NULL");
|
||||
return null;
|
||||
case ResultCallbackType.BOOLEAN:
|
||||
case ResultCallbackType.INT:
|
||||
case ResultCallbackType.CHAR_SEQUENCE:
|
||||
case ResultCallbackType.EXTRACTED_TEXT:
|
||||
case ResultCallbackType.SURROUNDING_TEXT:
|
||||
resultCallback = source.readStrongBinder();
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unknown ResultCallbackType=" + resultCallbackType);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
resultCallbackType = ResultCallbackType.NULL;
|
||||
resultCallback = null;
|
||||
}
|
||||
return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
|
||||
bundle, parcelableType, parcelable, resultCallbackType, resultCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to make this class parcelable.
|
||||
*/
|
||||
public static final Parcelable.Creator<InputConnectionCommand> CREATOR =
|
||||
new Parcelable.Creator<InputConnectionCommand>() {
|
||||
@AnyThread
|
||||
@Nullable
|
||||
@Override
|
||||
public InputConnectionCommand createFromParcel(Parcel source) {
|
||||
try {
|
||||
return InputConnectionCommand.createFromParcel(source);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Returning null due to exception.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@NonNull
|
||||
@Override
|
||||
public InputConnectionCommand[] newArray(int size) {
|
||||
return new InputConnectionCommand[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 com.android.internal.inputmethod;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef(value = {
|
||||
InputConnectionCommandType.BEGIN_BATCH_EDIT,
|
||||
InputConnectionCommandType.CLEAR_META_KEY_STATES,
|
||||
InputConnectionCommandType.COMMIT_COMPLETION,
|
||||
InputConnectionCommandType.COMMIT_CONTENT,
|
||||
InputConnectionCommandType.COMMIT_CORRECTION,
|
||||
InputConnectionCommandType.COMMIT_TEXT,
|
||||
InputConnectionCommandType.DELETE_SURROUNDING_TEXT,
|
||||
InputConnectionCommandType.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
|
||||
InputConnectionCommandType.END_BATCH_EDIT,
|
||||
InputConnectionCommandType.FINISH_COMPOSING_TEXT,
|
||||
InputConnectionCommandType.GET_CURSOR_CAPS_MODE,
|
||||
InputConnectionCommandType.GET_EXTRACTED_TEXT,
|
||||
InputConnectionCommandType.GET_SELECTED_TEXT,
|
||||
InputConnectionCommandType.GET_SURROUNDING_TEXT,
|
||||
InputConnectionCommandType.GET_TEXT_AFTER_CURSOR,
|
||||
InputConnectionCommandType.GET_TEXT_BEFORE_CURSOR,
|
||||
InputConnectionCommandType.PERFORM_CONTEXT_MENU_ACTION,
|
||||
InputConnectionCommandType.PERFORM_EDITOR_ACTION,
|
||||
InputConnectionCommandType.PERFORM_SPELL_CHECK,
|
||||
InputConnectionCommandType.REQUEST_CURSOR_UPDATES,
|
||||
InputConnectionCommandType.SEND_KEY_EVENT,
|
||||
InputConnectionCommandType.SET_COMPOSING_REGION,
|
||||
InputConnectionCommandType.SET_COMPOSING_TEXT,
|
||||
InputConnectionCommandType.SET_IME_CONSUMES_INPUT,
|
||||
InputConnectionCommandType.SET_SELECTION,
|
||||
})
|
||||
public @interface InputConnectionCommandType {
|
||||
int FIRST_COMMAND = 1;
|
||||
|
||||
int BEGIN_BATCH_EDIT = FIRST_COMMAND;
|
||||
int CLEAR_META_KEY_STATES = 2;
|
||||
int COMMIT_COMPLETION = 3;
|
||||
int COMMIT_CONTENT = 4;
|
||||
int COMMIT_CORRECTION = 5;
|
||||
int COMMIT_TEXT = 6;
|
||||
int DELETE_SURROUNDING_TEXT = 7;
|
||||
int DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 8;
|
||||
int END_BATCH_EDIT = 9;
|
||||
int FINISH_COMPOSING_TEXT = 10;
|
||||
int GET_CURSOR_CAPS_MODE = 11;
|
||||
int GET_EXTRACTED_TEXT = 12;
|
||||
int GET_SELECTED_TEXT = 13;
|
||||
int GET_SURROUNDING_TEXT = 14;
|
||||
int GET_TEXT_AFTER_CURSOR = 15;
|
||||
int GET_TEXT_BEFORE_CURSOR = 16;
|
||||
int PERFORM_CONTEXT_MENU_ACTION = 17;
|
||||
int PERFORM_EDITOR_ACTION = 18;
|
||||
int PERFORM_SPELL_CHECK = 19;
|
||||
int PERFORM_PRIVATE_COMMAND = 20;
|
||||
int REQUEST_CURSOR_UPDATES = 21;
|
||||
int SEND_KEY_EVENT = 22;
|
||||
int SET_COMPOSING_REGION = 23;
|
||||
int SET_COMPOSING_TEXT = 24;
|
||||
int SET_IME_CONSUMES_INPUT = 25;
|
||||
int SET_SELECTION = 26;
|
||||
|
||||
int LAST_COMMAND = SET_SELECTION;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.android.internal.inputmethod;
|
||||
|
||||
import android.annotation.AnyThread;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
|
||||
|
||||
@@ -241,6 +242,133 @@ public final class InputMethodDebug {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts {@link InputConnectionCommandType} to human readable {@link String}.
|
||||
*/
|
||||
public static String inputConnectionCommandTypeToString(@InputConnectionCommandType int type) {
|
||||
switch (type) {
|
||||
case InputConnectionCommandType.BEGIN_BATCH_EDIT:
|
||||
return "beginBatchEdit";
|
||||
case InputConnectionCommandType.CLEAR_META_KEY_STATES:
|
||||
return "clearMetaKeyStates";
|
||||
case InputConnectionCommandType.COMMIT_COMPLETION:
|
||||
return "commitCompletion";
|
||||
case InputConnectionCommandType.COMMIT_CONTENT:
|
||||
return "commitContent";
|
||||
case InputConnectionCommandType.COMMIT_CORRECTION:
|
||||
return "commitCorrection";
|
||||
case InputConnectionCommandType.COMMIT_TEXT:
|
||||
return "commitText";
|
||||
case InputConnectionCommandType.DELETE_SURROUNDING_TEXT:
|
||||
return "deleteSurroundingText";
|
||||
case InputConnectionCommandType.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS:
|
||||
return "deleteSurroundingTextInCodePoints";
|
||||
case InputConnectionCommandType.END_BATCH_EDIT:
|
||||
return "endBatchEdit";
|
||||
case InputConnectionCommandType.FINISH_COMPOSING_TEXT:
|
||||
return "finishComposingText";
|
||||
case InputConnectionCommandType.GET_CURSOR_CAPS_MODE:
|
||||
return "getCursorCapsMode";
|
||||
case InputConnectionCommandType.GET_EXTRACTED_TEXT:
|
||||
return "getExtractedText";
|
||||
case InputConnectionCommandType.GET_SELECTED_TEXT:
|
||||
return "getSelectedText";
|
||||
case InputConnectionCommandType.GET_SURROUNDING_TEXT:
|
||||
return "getSurroundingText";
|
||||
case InputConnectionCommandType.GET_TEXT_AFTER_CURSOR:
|
||||
return "getTextAfterCursor";
|
||||
case InputConnectionCommandType.GET_TEXT_BEFORE_CURSOR:
|
||||
return "getTextBeforeCursor";
|
||||
case InputConnectionCommandType.PERFORM_CONTEXT_MENU_ACTION:
|
||||
return "performContextMenuAction";
|
||||
case InputConnectionCommandType.PERFORM_EDITOR_ACTION:
|
||||
return "performEditorAction";
|
||||
case InputConnectionCommandType.PERFORM_SPELL_CHECK:
|
||||
return "performSpellCheck";
|
||||
case InputConnectionCommandType.REQUEST_CURSOR_UPDATES:
|
||||
return "requestCursorUpdates";
|
||||
case InputConnectionCommandType.SEND_KEY_EVENT:
|
||||
return "sendKeyEvent";
|
||||
case InputConnectionCommandType.SET_COMPOSING_REGION:
|
||||
return "setComposingRegion";
|
||||
case InputConnectionCommandType.SET_COMPOSING_TEXT:
|
||||
return "setComposingText";
|
||||
case InputConnectionCommandType.SET_IME_CONSUMES_INPUT:
|
||||
return "setImeConsumesInput";
|
||||
case InputConnectionCommandType.SET_SELECTION:
|
||||
return "setSelection";
|
||||
default:
|
||||
return "Unknown=" + type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts {@link InputConnectionCommand} to human readable {@link String}.
|
||||
*/
|
||||
@NonNull
|
||||
public static String dumpInputConnectionCommand(@Nullable InputConnectionCommand command) {
|
||||
if (command == null) {
|
||||
return "null";
|
||||
}
|
||||
switch (command.mCommandType) {
|
||||
case InputConnectionCommandType.BEGIN_BATCH_EDIT:
|
||||
return "beginBatchEdit()";
|
||||
case InputConnectionCommandType.CLEAR_META_KEY_STATES:
|
||||
return "clearMetaKeyStates(" + command.mIntArg0 + ")";
|
||||
case InputConnectionCommandType.COMMIT_COMPLETION:
|
||||
return "commitCompletion(" + command.mParcelable + ")";
|
||||
case InputConnectionCommandType.COMMIT_CONTENT:
|
||||
return "commitContent(" + command.mParcelable + ", " + command.mFlags + ", "
|
||||
+ command.mBundle + ")";
|
||||
case InputConnectionCommandType.COMMIT_CORRECTION:
|
||||
return "commitCorrection(" + command.mParcelable + ")";
|
||||
case InputConnectionCommandType.COMMIT_TEXT:
|
||||
return "commitText(" + command.mCharSequence + ", " + command.mIntArg0 + ")";
|
||||
case InputConnectionCommandType.DELETE_SURROUNDING_TEXT:
|
||||
return "deleteSurroundingText(" + command.mIntArg0 + ", " + command.mIntArg1 + ")";
|
||||
case InputConnectionCommandType.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS:
|
||||
return "deleteSurroundingTextInCodePoints(" + command.mIntArg0 + ", "
|
||||
+ command.mIntArg1 + ")";
|
||||
case InputConnectionCommandType.END_BATCH_EDIT:
|
||||
return "endBatchEdit()";
|
||||
case InputConnectionCommandType.FINISH_COMPOSING_TEXT:
|
||||
return "finishComposingText()";
|
||||
case InputConnectionCommandType.GET_CURSOR_CAPS_MODE:
|
||||
return "getCursorCapsMode(" + command.mIntArg0 + ")";
|
||||
case InputConnectionCommandType.GET_EXTRACTED_TEXT:
|
||||
return "getExtractedText(" + command.mParcelable + ", " + command.mFlags + ")";
|
||||
case InputConnectionCommandType.GET_SELECTED_TEXT:
|
||||
return "getSelectedText(" + command.mFlags + ")";
|
||||
case InputConnectionCommandType.GET_SURROUNDING_TEXT:
|
||||
return "getSurroundingText(" + command.mIntArg0 + ", " + command.mIntArg1 + ", "
|
||||
+ command.mFlags + ")";
|
||||
case InputConnectionCommandType.GET_TEXT_AFTER_CURSOR:
|
||||
return "getTextAfterCursor(" + command.mIntArg0 + ", " + command.mFlags + ")";
|
||||
case InputConnectionCommandType.GET_TEXT_BEFORE_CURSOR:
|
||||
return "getTextBeforeCursor(" + command.mIntArg0 + ", " + command.mFlags + ")";
|
||||
case InputConnectionCommandType.PERFORM_CONTEXT_MENU_ACTION:
|
||||
return "performContextMenuAction(" + command.mIntArg0 + ")";
|
||||
case InputConnectionCommandType.PERFORM_EDITOR_ACTION:
|
||||
return "performEditorAction(" + command.mIntArg0 + ")";
|
||||
case InputConnectionCommandType.PERFORM_SPELL_CHECK:
|
||||
return "performSpellCheck()";
|
||||
case InputConnectionCommandType.REQUEST_CURSOR_UPDATES:
|
||||
return "requestCursorUpdates(" + command.mIntArg0 + ")";
|
||||
case InputConnectionCommandType.SEND_KEY_EVENT:
|
||||
return "sendKeyEvent(" + command.mParcelable + ")";
|
||||
case InputConnectionCommandType.SET_COMPOSING_REGION:
|
||||
return "setComposingRegion(" + command.mIntArg0 + ", " + command.mIntArg1 + ")";
|
||||
case InputConnectionCommandType.SET_COMPOSING_TEXT:
|
||||
return "setComposingText(" + command.mCharSequence + ", " + command.mIntArg0 + ")";
|
||||
case InputConnectionCommandType.SET_IME_CONSUMES_INPUT:
|
||||
return "setImeConsumesInput(" + (command.mIntArg0 != 0) + ")";
|
||||
case InputConnectionCommandType.SET_SELECTION:
|
||||
return "setSelection(" + command.mIntArg0 + ", " + command.mIntArg1 + ")";
|
||||
default:
|
||||
return "unknown(type=" + command.mCommandType + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a fixed size string of the object.
|
||||
* TODO(b/151575861): Take & return with StringBuilder to make more memory efficient.
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
|
||||
package com.android.internal.view;
|
||||
|
||||
import android.annotation.AnyThread;
|
||||
import android.annotation.BinderThread;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Trace;
|
||||
import android.util.Log;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
@@ -41,14 +42,12 @@ import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.inputmethod.SurroundingText;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.inputmethod.IBooleanResultCallback;
|
||||
import com.android.internal.inputmethod.ICharSequenceResultCallback;
|
||||
import com.android.internal.inputmethod.IExtractedTextResultCallback;
|
||||
import com.android.internal.inputmethod.IIntResultCallback;
|
||||
import com.android.internal.inputmethod.ISurroundingTextResultCallback;
|
||||
import com.android.internal.inputmethod.CallbackUtils;
|
||||
import com.android.internal.inputmethod.ImeTracing;
|
||||
import com.android.internal.inputmethod.InputConnectionCommand;
|
||||
import com.android.internal.inputmethod.InputConnectionCommandType;
|
||||
import com.android.internal.inputmethod.InputConnectionProtoDumper;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
import com.android.internal.inputmethod.InputMethodDebug;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
@@ -56,34 +55,8 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
|
||||
private static final String TAG = "IInputConnectionWrapper";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
|
||||
private static final int DO_GET_TEXT_BEFORE_CURSOR = 20;
|
||||
private static final int DO_GET_SELECTED_TEXT = 25;
|
||||
private static final int DO_GET_CURSOR_CAPS_MODE = 30;
|
||||
private static final int DO_GET_EXTRACTED_TEXT = 40;
|
||||
private static final int DO_COMMIT_TEXT = 50;
|
||||
private static final int DO_COMMIT_COMPLETION = 55;
|
||||
private static final int DO_COMMIT_CORRECTION = 56;
|
||||
private static final int DO_SET_SELECTION = 57;
|
||||
private static final int DO_PERFORM_EDITOR_ACTION = 58;
|
||||
private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
|
||||
private static final int DO_SET_COMPOSING_TEXT = 60;
|
||||
private static final int DO_SET_COMPOSING_REGION = 63;
|
||||
private static final int DO_FINISH_COMPOSING_TEXT = 65;
|
||||
private static final int DO_SEND_KEY_EVENT = 70;
|
||||
private static final int DO_DELETE_SURROUNDING_TEXT = 80;
|
||||
private static final int DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 81;
|
||||
private static final int DO_BEGIN_BATCH_EDIT = 90;
|
||||
private static final int DO_END_BATCH_EDIT = 95;
|
||||
private static final int DO_PERFORM_SPELL_CHECK = 110;
|
||||
private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
|
||||
private static final int DO_CLEAR_META_KEY_STATES = 130;
|
||||
private static final int DO_REQUEST_CURSOR_UPDATES = 140;
|
||||
private static final int DO_CLOSE_CONNECTION = 150;
|
||||
private static final int DO_COMMIT_CONTENT = 160;
|
||||
private static final int DO_GET_SURROUNDING_TEXT = 41;
|
||||
private static final int DO_SET_IME_CONSUMES_INPUT = 170;
|
||||
|
||||
private static final int DO_EDIT = 10;
|
||||
private static final int DO_CLOSE_CONNECTION = 20;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@Nullable
|
||||
@@ -147,7 +120,7 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
|
||||
// reportFinish() will take effect.
|
||||
return;
|
||||
}
|
||||
closeConnection();
|
||||
dispatchMessage(mH.obtainMessage(DO_CLOSE_CONNECTION));
|
||||
|
||||
// Notify the app that the InputConnection was closed.
|
||||
final View servedView = mServedView.get();
|
||||
@@ -192,146 +165,33 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) {
|
||||
dispatchMessage(mH.obtainMessage(DO_GET_TEXT_AFTER_CURSOR, length, flags, callback));
|
||||
}
|
||||
|
||||
public void getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback) {
|
||||
dispatchMessage(mH.obtainMessage(DO_GET_TEXT_BEFORE_CURSOR, length, flags, callback));
|
||||
}
|
||||
|
||||
public void getSelectedText(int flags, ICharSequenceResultCallback callback) {
|
||||
dispatchMessage(mH.obtainMessage(DO_GET_SELECTED_TEXT, flags, 0 /* unused */, callback));
|
||||
@BinderThread
|
||||
@Override
|
||||
public void doEdit(@Nullable InputConnectionCommand command) {
|
||||
if (command == null) {
|
||||
// As long as everything is working as expected, we should never see any null object
|
||||
// here. If we are seeing null object, it means that either the sender or
|
||||
// InputConnectionCommand.CREATOR#createFromParcel() returned null for whatever
|
||||
// unexpected reasons. Note that InputConnectionCommand.CREATOR#createFromParcel() does
|
||||
// some data verifications. Hence failing to pass the verification is one of the
|
||||
// reasons to see null here.
|
||||
Log.w(TAG, "Ignoring invalid InputConnectionCommand.");
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "incoming: " + InputMethodDebug.dumpInputConnectionCommand(command));
|
||||
}
|
||||
dispatchMessage(mH.obtainMessage(DO_EDIT, command));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the request for retrieving surrounding text.
|
||||
*
|
||||
* <p>See {@link InputConnection#getSurroundingText(int, int, int)}.
|
||||
* Exposed for {@link InputMethodManager} to trigger
|
||||
* {@link InputConnection#finishComposingText()}.
|
||||
*/
|
||||
public void getSurroundingText(int beforeLength, int afterLength, int flags,
|
||||
ISurroundingTextResultCallback callback) {
|
||||
final SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = beforeLength;
|
||||
args.arg2 = afterLength;
|
||||
args.arg3 = flags;
|
||||
args.arg4 = callback;
|
||||
dispatchMessage(mH.obtainMessage(DO_GET_SURROUNDING_TEXT, flags, 0 /* unused */, args));
|
||||
}
|
||||
|
||||
public void getCursorCapsMode(int reqModes, IIntResultCallback callback) {
|
||||
dispatchMessage(
|
||||
mH.obtainMessage(DO_GET_CURSOR_CAPS_MODE, reqModes, 0 /* unused */, callback));
|
||||
}
|
||||
|
||||
public void getExtractedText(ExtractedTextRequest request, int flags,
|
||||
IExtractedTextResultCallback callback) {
|
||||
final SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = request;
|
||||
args.arg2 = callback;
|
||||
dispatchMessage(mH.obtainMessage(DO_GET_EXTRACTED_TEXT, flags, 0 /* unused */, args));
|
||||
}
|
||||
|
||||
public void commitText(CharSequence text, int newCursorPosition) {
|
||||
dispatchMessage(obtainMessageIO(DO_COMMIT_TEXT, newCursorPosition, text));
|
||||
}
|
||||
|
||||
public void commitCompletion(CompletionInfo text) {
|
||||
dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text));
|
||||
}
|
||||
|
||||
public void commitCorrection(CorrectionInfo info) {
|
||||
dispatchMessage(obtainMessageO(DO_COMMIT_CORRECTION, info));
|
||||
}
|
||||
|
||||
public void setSelection(int start, int end) {
|
||||
dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end));
|
||||
}
|
||||
|
||||
public void performEditorAction(int id) {
|
||||
dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0));
|
||||
}
|
||||
|
||||
public void performContextMenuAction(int id) {
|
||||
dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0));
|
||||
}
|
||||
|
||||
public void setComposingRegion(int start, int end) {
|
||||
dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end));
|
||||
}
|
||||
|
||||
public void setComposingText(CharSequence text, int newCursorPosition) {
|
||||
dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text));
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
public void finishComposingText() {
|
||||
dispatchMessage(obtainMessage(DO_FINISH_COMPOSING_TEXT));
|
||||
}
|
||||
|
||||
public void sendKeyEvent(KeyEvent event) {
|
||||
dispatchMessage(obtainMessageO(DO_SEND_KEY_EVENT, event));
|
||||
}
|
||||
|
||||
public void clearMetaKeyStates(int states) {
|
||||
dispatchMessage(obtainMessageII(DO_CLEAR_META_KEY_STATES, states, 0));
|
||||
}
|
||||
|
||||
public void deleteSurroundingText(int beforeLength, int afterLength) {
|
||||
dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT,
|
||||
beforeLength, afterLength));
|
||||
}
|
||||
|
||||
public void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
|
||||
dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
|
||||
beforeLength, afterLength));
|
||||
}
|
||||
|
||||
public void beginBatchEdit() {
|
||||
dispatchMessage(obtainMessage(DO_BEGIN_BATCH_EDIT));
|
||||
}
|
||||
|
||||
public void endBatchEdit() {
|
||||
dispatchMessage(obtainMessage(DO_END_BATCH_EDIT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the request for performing spell check.
|
||||
*
|
||||
* @see InputConnection#performSpellCheck()
|
||||
*/
|
||||
public void performSpellCheck() {
|
||||
dispatchMessage(obtainMessage(DO_PERFORM_SPELL_CHECK));
|
||||
}
|
||||
|
||||
public void performPrivateCommand(String action, Bundle data) {
|
||||
dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
|
||||
}
|
||||
|
||||
public void requestCursorUpdates(int cursorUpdateMode, IBooleanResultCallback callback) {
|
||||
dispatchMessage(mH.obtainMessage(DO_REQUEST_CURSOR_UPDATES, cursorUpdateMode,
|
||||
0 /* unused */, callback));
|
||||
}
|
||||
|
||||
public void closeConnection() {
|
||||
dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION));
|
||||
}
|
||||
|
||||
public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts,
|
||||
IBooleanResultCallback callback) {
|
||||
final SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = inputContentInfo;
|
||||
args.arg2 = opts;
|
||||
args.arg3 = callback;
|
||||
dispatchMessage(mH.obtainMessage(DO_COMMIT_CONTENT, flags, 0 /* unused */, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the request for setting ime consumes input.
|
||||
*
|
||||
* <p>See {@link InputConnection#setImeConsumesInput(boolean)}.
|
||||
*/
|
||||
public void setImeConsumesInput(boolean imeConsumesInput) {
|
||||
dispatchMessage(obtainMessageB(DO_SET_IME_CONSUMES_INPUT, imeConsumesInput));
|
||||
dispatchMessage(mH.obtainMessage(DO_EDIT, InputConnectionCommand.create(
|
||||
InputConnectionCommandType.FINISH_COMPOSING_TEXT)));
|
||||
}
|
||||
|
||||
void dispatchMessage(Message msg) {
|
||||
@@ -347,108 +207,131 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
|
||||
mH.sendMessage(msg);
|
||||
}
|
||||
|
||||
void executeMessage(Message msg) {
|
||||
byte[] icProto;
|
||||
private void executeMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case DO_GET_TEXT_AFTER_CURSOR: {
|
||||
case DO_EDIT:
|
||||
doEditMain((InputConnectionCommand) msg.obj);
|
||||
break;
|
||||
case DO_CLOSE_CONNECTION:
|
||||
// Note that we do not need to worry about race condition here, because 1) mFinished
|
||||
// is updated only inside this block, and 2) the code here is running on a Handler
|
||||
// hence we assume multiple DO_CLOSE_CONNECTION messages will not be handled at the
|
||||
// same time.
|
||||
if (isFinished()) {
|
||||
return;
|
||||
}
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection");
|
||||
try {
|
||||
InputConnection ic = getInputConnection();
|
||||
// Note we do NOT check isActive() here, because this is safe
|
||||
// for an IME to call at any time, and we need to allow it
|
||||
// through to clean up our state after the IME has switched to
|
||||
// another client.
|
||||
if (ic == null) {
|
||||
return;
|
||||
}
|
||||
@MissingMethodFlags final int missingMethods =
|
||||
InputConnectionInspector.getMissingMethodFlags(ic);
|
||||
if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) {
|
||||
ic.closeConnection();
|
||||
}
|
||||
} finally {
|
||||
synchronized (mLock) {
|
||||
mInputConnection = null;
|
||||
mFinished = true;
|
||||
}
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void doEditMain(@NonNull InputConnectionCommand command) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "handling: " + InputMethodDebug.dumpInputConnectionCommand(command));
|
||||
}
|
||||
byte[] icProto;
|
||||
switch (command.mCommandType) {
|
||||
case InputConnectionCommandType.GET_TEXT_AFTER_CURSOR: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor");
|
||||
try {
|
||||
final ICharSequenceResultCallback callback =
|
||||
(ICharSequenceResultCallback) msg.obj;
|
||||
final int n = command.mIntArg0;
|
||||
final int flags = command.mFlags;
|
||||
final InputConnection ic = getInputConnection();
|
||||
final CharSequence result;
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
|
||||
result = null;
|
||||
} else {
|
||||
result = ic.getTextAfterCursor(msg.arg1, msg.arg2);
|
||||
result = ic.getTextAfterCursor(n, flags);
|
||||
}
|
||||
if (ImeTracing.getInstance().isEnabled()) {
|
||||
icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(msg.arg1,
|
||||
msg.arg2, result);
|
||||
icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(n, flags,
|
||||
result);
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
|
||||
}
|
||||
try {
|
||||
callback.onResult(result);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to return the result to getTextAfterCursor()."
|
||||
+ " result=" + result, e);
|
||||
}
|
||||
CallbackUtils.onResult(command, result, TAG);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_GET_TEXT_BEFORE_CURSOR: {
|
||||
case InputConnectionCommandType.GET_TEXT_BEFORE_CURSOR: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor");
|
||||
try {
|
||||
final ICharSequenceResultCallback callback =
|
||||
(ICharSequenceResultCallback) msg.obj;
|
||||
final int n = command.mIntArg0;
|
||||
final int flags = command.mFlags;
|
||||
final InputConnection ic = getInputConnection();
|
||||
final CharSequence result;
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
|
||||
result = null;
|
||||
} else {
|
||||
result = ic.getTextBeforeCursor(msg.arg1, msg.arg2);
|
||||
result = ic.getTextBeforeCursor(n, flags);
|
||||
}
|
||||
if (ImeTracing.getInstance().isEnabled()) {
|
||||
icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(msg.arg1,
|
||||
msg.arg2, result);
|
||||
icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(n, flags,
|
||||
result);
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
|
||||
}
|
||||
try {
|
||||
callback.onResult(result);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to return the result to getTextBeforeCursor()."
|
||||
+ " result=" + result, e);
|
||||
}
|
||||
CallbackUtils.onResult(command, result, TAG);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_GET_SELECTED_TEXT: {
|
||||
case InputConnectionCommandType.GET_SELECTED_TEXT: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText");
|
||||
try {
|
||||
final ICharSequenceResultCallback callback =
|
||||
(ICharSequenceResultCallback) msg.obj;
|
||||
final int flags = command.mFlags;
|
||||
final InputConnection ic = getInputConnection();
|
||||
final CharSequence result;
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "getSelectedText on inactive InputConnection");
|
||||
result = null;
|
||||
} else {
|
||||
result = ic.getSelectedText(msg.arg1);
|
||||
result = ic.getSelectedText(flags);
|
||||
}
|
||||
if (ImeTracing.getInstance().isEnabled()) {
|
||||
icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(msg.arg1,
|
||||
icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(flags,
|
||||
result);
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
TAG + "#getSelectedText", mParentInputMethodManager, icProto);
|
||||
}
|
||||
try {
|
||||
callback.onResult(result);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to return the result to getSelectedText()."
|
||||
+ " result=" + result, e);
|
||||
}
|
||||
CallbackUtils.onResult(command, result, TAG);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_GET_SURROUNDING_TEXT: {
|
||||
final SomeArgs args = (SomeArgs) msg.obj;
|
||||
case InputConnectionCommandType.GET_SURROUNDING_TEXT: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText");
|
||||
try {
|
||||
int beforeLength = (int) args.arg1;
|
||||
int afterLength = (int) args.arg2;
|
||||
int flags = (int) args.arg3;
|
||||
final ISurroundingTextResultCallback callback =
|
||||
(ISurroundingTextResultCallback) args.arg4;
|
||||
final int beforeLength = command.mIntArg0;
|
||||
final int afterLength = command.mIntArg1;
|
||||
final int flags = command.mFlags;
|
||||
final InputConnection ic = getInputConnection();
|
||||
final SurroundingText result;
|
||||
if (ic == null || !isActive()) {
|
||||
@@ -463,193 +346,186 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
|
||||
}
|
||||
try {
|
||||
callback.onResult(result);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to return the result to getSurroundingText()."
|
||||
+ " result=" + result, e);
|
||||
}
|
||||
CallbackUtils.onResult(command, result, TAG);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
args.recycle();
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_GET_CURSOR_CAPS_MODE: {
|
||||
case InputConnectionCommandType.GET_CURSOR_CAPS_MODE: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode");
|
||||
try {
|
||||
final IIntResultCallback callback = (IIntResultCallback) msg.obj;
|
||||
final int reqModes = command.mIntArg0;
|
||||
final InputConnection ic = getInputConnection();
|
||||
final int result;
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
|
||||
result = 0;
|
||||
} else {
|
||||
result = ic.getCursorCapsMode(msg.arg1);
|
||||
result = ic.getCursorCapsMode(reqModes);
|
||||
}
|
||||
if (ImeTracing.getInstance().isEnabled()) {
|
||||
icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(msg.arg1,
|
||||
icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(reqModes,
|
||||
result);
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
|
||||
}
|
||||
try {
|
||||
callback.onResult(result);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to return the result to getCursorCapsMode()."
|
||||
+ " result=" + result, e);
|
||||
}
|
||||
CallbackUtils.onResult(command, result, TAG);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_GET_EXTRACTED_TEXT: {
|
||||
final SomeArgs args = (SomeArgs) msg.obj;
|
||||
case InputConnectionCommandType.GET_EXTRACTED_TEXT: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText");
|
||||
try {
|
||||
final ExtractedTextRequest request = (ExtractedTextRequest) args.arg1;
|
||||
final IExtractedTextResultCallback callback =
|
||||
(IExtractedTextResultCallback) args.arg2;
|
||||
final ExtractedTextRequest request = (ExtractedTextRequest) command.mParcelable;
|
||||
final int flags = command.mFlags;
|
||||
final InputConnection ic = getInputConnection();
|
||||
final ExtractedText result;
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "getExtractedText on inactive InputConnection");
|
||||
result = null;
|
||||
} else {
|
||||
result = ic.getExtractedText(request, msg.arg1);
|
||||
result = ic.getExtractedText(request, flags);
|
||||
}
|
||||
if (ImeTracing.getInstance().isEnabled()) {
|
||||
icProto = InputConnectionProtoDumper.buildGetExtractedTextProto(request,
|
||||
msg.arg1, result);
|
||||
flags, result);
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
TAG + "#getExtractedText", mParentInputMethodManager, icProto);
|
||||
}
|
||||
try {
|
||||
callback.onResult(result);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to return the result to getExtractedText()."
|
||||
+ " result=" + result, e);
|
||||
}
|
||||
CallbackUtils.onResult(command, result, TAG);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
args.recycle();
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_COMMIT_TEXT: {
|
||||
case InputConnectionCommandType.COMMIT_TEXT: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitText");
|
||||
try {
|
||||
final CharSequence text = command.mCharSequence;
|
||||
final int newCursorPosition = command.mIntArg0;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "commitText on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.commitText((CharSequence) msg.obj, msg.arg1);
|
||||
ic.commitText(text, newCursorPosition);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_SET_SELECTION: {
|
||||
case InputConnectionCommandType.SET_SELECTION: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setSelection");
|
||||
try {
|
||||
final int start = command.mIntArg0;
|
||||
final int end = command.mIntArg1;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "setSelection on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.setSelection(msg.arg1, msg.arg2);
|
||||
ic.setSelection(start, end);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_PERFORM_EDITOR_ACTION: {
|
||||
case InputConnectionCommandType.PERFORM_EDITOR_ACTION: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performEditorAction");
|
||||
try {
|
||||
final int editorAction = command.mIntArg0;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "performEditorAction on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.performEditorAction(msg.arg1);
|
||||
ic.performEditorAction(editorAction);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_PERFORM_CONTEXT_MENU_ACTION: {
|
||||
case InputConnectionCommandType.PERFORM_CONTEXT_MENU_ACTION: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performContextMenuAction");
|
||||
try {
|
||||
final int id = command.mIntArg0;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "performContextMenuAction on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.performContextMenuAction(msg.arg1);
|
||||
ic.performContextMenuAction(id);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_COMMIT_COMPLETION: {
|
||||
case InputConnectionCommandType.COMMIT_COMPLETION: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCompletion");
|
||||
try {
|
||||
final CompletionInfo text = (CompletionInfo) command.mParcelable;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "commitCompletion on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.commitCompletion((CompletionInfo) msg.obj);
|
||||
ic.commitCompletion(text);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_COMMIT_CORRECTION: {
|
||||
case InputConnectionCommandType.COMMIT_CORRECTION: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCorrection");
|
||||
try {
|
||||
final CorrectionInfo correctionInfo = (CorrectionInfo) command.mParcelable;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "commitCorrection on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.commitCorrection((CorrectionInfo) msg.obj);
|
||||
ic.commitCorrection(correctionInfo);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_SET_COMPOSING_TEXT: {
|
||||
case InputConnectionCommandType.SET_COMPOSING_TEXT: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingText");
|
||||
try {
|
||||
final CharSequence text = command.mCharSequence;
|
||||
final int newCursorPosition = command.mIntArg0;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "setComposingText on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.setComposingText((CharSequence) msg.obj, msg.arg1);
|
||||
ic.setComposingText(text, newCursorPosition);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_SET_COMPOSING_REGION: {
|
||||
case InputConnectionCommandType.SET_COMPOSING_REGION: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingRegion");
|
||||
try {
|
||||
final int start = command.mIntArg0;
|
||||
final int end = command.mIntArg1;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "setComposingRegion on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.setComposingRegion(msg.arg1, msg.arg2);
|
||||
ic.setComposingRegion(start, end);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_FINISH_COMPOSING_TEXT: {
|
||||
case InputConnectionCommandType.FINISH_COMPOSING_TEXT: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#finishComposingText");
|
||||
try {
|
||||
if (isFinished()) {
|
||||
@@ -675,64 +551,70 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_SEND_KEY_EVENT: {
|
||||
case InputConnectionCommandType.SEND_KEY_EVENT: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#sendKeyEvent");
|
||||
try {
|
||||
final KeyEvent event = (KeyEvent) command.mParcelable;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "sendKeyEvent on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.sendKeyEvent((KeyEvent) msg.obj);
|
||||
ic.sendKeyEvent(event);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_CLEAR_META_KEY_STATES: {
|
||||
case InputConnectionCommandType.CLEAR_META_KEY_STATES: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#clearMetaKeyStates");
|
||||
try {
|
||||
final int states = command.mIntArg0;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.clearMetaKeyStates(msg.arg1);
|
||||
ic.clearMetaKeyStates(states);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_DELETE_SURROUNDING_TEXT: {
|
||||
case InputConnectionCommandType.DELETE_SURROUNDING_TEXT: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#deleteSurroundingText");
|
||||
try {
|
||||
final int beforeLength = command.mIntArg0;
|
||||
final int afterLength = command.mIntArg1;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.deleteSurroundingText(msg.arg1, msg.arg2);
|
||||
ic.deleteSurroundingText(beforeLength, afterLength);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: {
|
||||
case InputConnectionCommandType.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT,
|
||||
"InputConnection#deleteSurroundingTextInCodePoints");
|
||||
try {
|
||||
final int beforeLength = command.mIntArg0;
|
||||
final int afterLength = command.mIntArg1;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2);
|
||||
ic.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_BEGIN_BATCH_EDIT: {
|
||||
case InputConnectionCommandType.BEGIN_BATCH_EDIT: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#beginBatchEdit");
|
||||
try {
|
||||
InputConnection ic = getInputConnection();
|
||||
@@ -746,7 +628,7 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_END_BATCH_EDIT: {
|
||||
case InputConnectionCommandType.END_BATCH_EDIT: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#endBatchEdit");
|
||||
try {
|
||||
InputConnection ic = getInputConnection();
|
||||
@@ -760,7 +642,7 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_PERFORM_SPELL_CHECK: {
|
||||
case InputConnectionCommandType.PERFORM_SPELL_CHECK: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performSpellCheck");
|
||||
try {
|
||||
InputConnection ic = getInputConnection();
|
||||
@@ -774,12 +656,11 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_PERFORM_PRIVATE_COMMAND: {
|
||||
final SomeArgs args = (SomeArgs) msg.obj;
|
||||
case InputConnectionCommandType.PERFORM_PRIVATE_COMMAND: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performPrivateCommand");
|
||||
try {
|
||||
final String action = (String) args.arg1;
|
||||
final Bundle data = (Bundle) args.arg2;
|
||||
final String action = command.mString;
|
||||
final Bundle data = command.mBundle;
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "performPrivateCommand on inactive InputConnection");
|
||||
@@ -788,142 +669,71 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
|
||||
ic.performPrivateCommand(action, data);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
args.recycle();
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_REQUEST_CURSOR_UPDATES: {
|
||||
case InputConnectionCommandType.REQUEST_CURSOR_UPDATES: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates");
|
||||
try {
|
||||
final IBooleanResultCallback callback = (IBooleanResultCallback) msg.obj;
|
||||
final int cursorUpdateMode = command.mIntArg0;
|
||||
final InputConnection ic = getInputConnection();
|
||||
final boolean result;
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
|
||||
result = false;
|
||||
} else {
|
||||
result = ic.requestCursorUpdates(msg.arg1);
|
||||
}
|
||||
try {
|
||||
callback.onResult(result);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to return the result to requestCursorUpdates()."
|
||||
+ " result=" + result, e);
|
||||
result = ic.requestCursorUpdates(cursorUpdateMode);
|
||||
}
|
||||
CallbackUtils.onResult(command, result, TAG);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_CLOSE_CONNECTION: {
|
||||
// Note that we do not need to worry about race condition here, because 1) mFinished
|
||||
// is updated only inside this block, and 2) the code here is running on a Handler
|
||||
// hence we assume multiple DO_CLOSE_CONNECTION messages will not be handled at the
|
||||
// same time.
|
||||
if (isFinished()) {
|
||||
return;
|
||||
}
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection");
|
||||
try {
|
||||
InputConnection ic = getInputConnection();
|
||||
// Note we do NOT check isActive() here, because this is safe
|
||||
// for an IME to call at any time, and we need to allow it
|
||||
// through to clean up our state after the IME has switched to
|
||||
// another client.
|
||||
if (ic == null) {
|
||||
return;
|
||||
}
|
||||
@MissingMethodFlags
|
||||
final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic);
|
||||
if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) {
|
||||
ic.closeConnection();
|
||||
}
|
||||
} finally {
|
||||
synchronized (mLock) {
|
||||
mInputConnection = null;
|
||||
mFinished = true;
|
||||
}
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_COMMIT_CONTENT: {
|
||||
final int flags = msg.arg1;
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
case InputConnectionCommandType.COMMIT_CONTENT: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent");
|
||||
try {
|
||||
final IBooleanResultCallback callback = (IBooleanResultCallback) args.arg3;
|
||||
final InputContentInfo inputContentInfo =
|
||||
(InputContentInfo) command.mParcelable;
|
||||
final int flags = command.mFlags;
|
||||
final Bundle opts = command.mBundle;
|
||||
final InputConnection ic = getInputConnection();
|
||||
final boolean result;
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG, "commitContent on inactive InputConnection");
|
||||
result = false;
|
||||
} else {
|
||||
final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1;
|
||||
if (inputContentInfo == null || !inputContentInfo.validate()) {
|
||||
Log.w(TAG, "commitContent with invalid inputContentInfo="
|
||||
+ inputContentInfo);
|
||||
result = false;
|
||||
} else {
|
||||
result = ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2);
|
||||
result = ic.commitContent(inputContentInfo, flags, opts);
|
||||
}
|
||||
}
|
||||
try {
|
||||
callback.onResult(result);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to return the result to commitContent()."
|
||||
+ " result=" + result, e);
|
||||
}
|
||||
CallbackUtils.onResult(command, result, TAG);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
args.recycle();
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_SET_IME_CONSUMES_INPUT: {
|
||||
case InputConnectionCommandType.SET_IME_CONSUMES_INPUT: {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT,
|
||||
"InputConnection#setImeConsumesInput");
|
||||
try {
|
||||
final boolean imeConsumesInput = (command.mIntArg0 != 0);
|
||||
InputConnection ic = getInputConnection();
|
||||
if (ic == null || !isActive()) {
|
||||
Log.w(TAG,
|
||||
"setImeConsumesInput on inactive InputConnection");
|
||||
return;
|
||||
}
|
||||
ic.setImeConsumesInput(msg.arg1 == 1);
|
||||
ic.setImeConsumesInput(imeConsumesInput);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
Log.w(TAG, "Unhandled message code: " + msg.what);
|
||||
}
|
||||
|
||||
Message obtainMessage(int what) {
|
||||
return mH.obtainMessage(what);
|
||||
}
|
||||
|
||||
Message obtainMessageII(int what, int arg1, int arg2) {
|
||||
return mH.obtainMessage(what, arg1, arg2);
|
||||
}
|
||||
|
||||
Message obtainMessageO(int what, Object arg1) {
|
||||
return mH.obtainMessage(what, 0, 0, arg1);
|
||||
}
|
||||
|
||||
Message obtainMessageIO(int what, int arg1, Object arg2) {
|
||||
return mH.obtainMessage(what, arg1, 0, arg2);
|
||||
}
|
||||
|
||||
Message obtainMessageOO(int what, Object arg1, Object arg2) {
|
||||
final SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = arg1;
|
||||
args.arg2 = arg2;
|
||||
return mH.obtainMessage(what, 0, 0, args);
|
||||
}
|
||||
|
||||
Message obtainMessageB(int what, boolean arg1) {
|
||||
return mH.obtainMessage(what, arg1 ? 1 : 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,76 +16,13 @@
|
||||
|
||||
package com.android.internal.view;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
import android.view.inputmethod.CorrectionInfo;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputContentInfo;
|
||||
|
||||
import com.android.internal.inputmethod.IBooleanResultCallback;
|
||||
import com.android.internal.inputmethod.ICharSequenceResultCallback;
|
||||
import com.android.internal.inputmethod.IExtractedTextResultCallback;
|
||||
import com.android.internal.inputmethod.IIntResultCallback;
|
||||
import com.android.internal.inputmethod.ISurroundingTextResultCallback;
|
||||
import com.android.internal.inputmethod.InputConnectionCommand;
|
||||
|
||||
/**
|
||||
* Interface from an input method to the application, allowing it to perform
|
||||
* edits on the current input field and other interactions with the application.
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IInputContext {
|
||||
void getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback);
|
||||
|
||||
void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback);
|
||||
|
||||
void getCursorCapsMode(int reqModes, IIntResultCallback callback);
|
||||
|
||||
void getExtractedText(in ExtractedTextRequest request, int flags,
|
||||
IExtractedTextResultCallback callback);
|
||||
|
||||
void deleteSurroundingText(int beforeLength, int afterLength);
|
||||
void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
|
||||
|
||||
void setComposingText(CharSequence text, int newCursorPosition);
|
||||
|
||||
void finishComposingText();
|
||||
|
||||
void commitText(CharSequence text, int newCursorPosition);
|
||||
|
||||
void commitCompletion(in CompletionInfo completion);
|
||||
|
||||
void commitCorrection(in CorrectionInfo correction);
|
||||
|
||||
void setSelection(int start, int end);
|
||||
|
||||
void performEditorAction(int actionCode);
|
||||
|
||||
void performContextMenuAction(int id);
|
||||
|
||||
void beginBatchEdit();
|
||||
|
||||
void endBatchEdit();
|
||||
|
||||
void sendKeyEvent(in KeyEvent event);
|
||||
|
||||
void clearMetaKeyStates(int states);
|
||||
|
||||
void performSpellCheck();
|
||||
|
||||
void performPrivateCommand(String action, in Bundle data);
|
||||
|
||||
void setComposingRegion(int start, int end);
|
||||
|
||||
void getSelectedText(int flags, ICharSequenceResultCallback callback);
|
||||
|
||||
void requestCursorUpdates(int cursorUpdateMode, IBooleanResultCallback callback);
|
||||
|
||||
void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts,
|
||||
IBooleanResultCallback callback);
|
||||
|
||||
void getSurroundingText(int beforeLength, int afterLength, int flags,
|
||||
ISurroundingTextResultCallback callback);
|
||||
|
||||
void setImeConsumesInput(boolean imeConsumesInput);
|
||||
oneway interface IInputContext {
|
||||
void doEdit(in InputConnectionCommand command);
|
||||
}
|
||||
|
||||
@@ -73,4 +73,13 @@ public class InputMethodDebugTest {
|
||||
InputMethodDebug.softInputDisplayReasonToString(
|
||||
SoftInputShowHideReason.HIDE_REMOVE_CLIENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDumpInputConnectionCommand() {
|
||||
// TODO: add more tests
|
||||
assertEquals("null", InputMethodDebug.dumpInputConnectionCommand(null));
|
||||
assertEquals("endBatchEdit()",
|
||||
InputMethodDebug.dumpInputConnectionCommand(
|
||||
InputConnectionCommand.create(InputConnectionCommandType.END_BATCH_EDIT)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user