Merge "Support IME to take extra information to editor"

This commit is contained in:
Calvin Pan
2021-11-11 02:26:21 +00:00
committed by Android (Google) Code Review
9 changed files with 429 additions and 1 deletions

View File

@@ -52258,6 +52258,7 @@ package android.view.inputmethod {
method public boolean commitContent(@NonNull android.view.inputmethod.InputContentInfo, int, @Nullable android.os.Bundle);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(CharSequence, int);
method public default boolean commitText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean deleteSurroundingText(int, int);
method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
@@ -52277,7 +52278,9 @@ package android.view.inputmethod {
method public boolean requestCursorUpdates(int);
method public boolean sendKeyEvent(android.view.KeyEvent);
method public boolean setComposingRegion(int, int);
method public default boolean setComposingRegion(int, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean setComposingText(CharSequence, int);
method public default boolean setComposingText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public default boolean setImeConsumesInput(boolean);
method public boolean setSelection(int, int);
method @Nullable public default android.view.inputmethod.TextSnapshot takeSnapshot();
@@ -52493,6 +52496,21 @@ package android.view.inputmethod {
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
}
public final class TextAttribute implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.os.PersistableBundle getExtras();
method @NonNull public java.util.List<java.lang.String> getTextConversionSuggestions();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.TextAttribute> CREATOR;
}
public static final class TextAttribute.TextAttributeBuilder {
ctor public TextAttribute.TextAttributeBuilder();
method @NonNull public android.view.inputmethod.TextAttribute build();
method @NonNull public android.view.inputmethod.TextAttribute.TextAttributeBuilder setExtras(@NonNull android.os.PersistableBundle);
method @NonNull public android.view.inputmethod.TextAttribute.TextAttributeBuilder setTextConversionSuggestions(@NonNull java.util.List<java.lang.String>);
}
public final class TextSnapshot {
ctor public TextSnapshot(@NonNull android.view.inputmethod.SurroundingText, @IntRange(from=0xffffffff) int, @IntRange(from=0xffffffff) int, int);
method @IntRange(from=0xffffffff) public int getCompositionEnd();

View File

@@ -31,6 +31,7 @@ import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.TextAttribute;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.CompletableFutureUtil;
@@ -271,6 +272,17 @@ final class RemoteInputConnection implements InputConnection {
return handled;
}
@AnyThread
public boolean commitText(@NonNull CharSequence text, int newCursorPosition,
@Nullable TextAttribute textAttribute) {
final boolean handled =
mInvoker.commitText(text, newCursorPosition, textAttribute);
if (handled) {
notifyUserActionIfNecessary();
}
return handled;
}
@AnyThread
private void notifyUserActionIfNecessary() {
final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
@@ -310,6 +322,11 @@ final class RemoteInputConnection implements InputConnection {
return mInvoker.setComposingRegion(start, end);
}
@AnyThread
public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
return mInvoker.setComposingRegion(start, end, textAttribute);
}
@AnyThread
public boolean setComposingText(CharSequence text, int newCursorPosition) {
final boolean handled = mInvoker.setComposingText(text, newCursorPosition);
@@ -319,6 +336,16 @@ final class RemoteInputConnection implements InputConnection {
return handled;
}
@AnyThread
public boolean setComposingText(CharSequence text, int newCursorPosition,
@Nullable TextAttribute textAttribute) {
final boolean handled = mInvoker.setComposingText(text, newCursorPosition, textAttribute);
if (handled) {
notifyUserActionIfNecessary();
}
return handled;
}
@AnyThread
public boolean finishComposingText() {
return mInvoker.finishComposingText();

View File

@@ -544,6 +544,33 @@ public interface InputConnection {
*/
boolean setComposingText(CharSequence text, int newCursorPosition);
/**
* The variant of {@link #setComposingText(CharSequence, int)}. This method is
* used to allow the IME to provide extra information while setting up composing text.
*
* @param text The composing text with styles if necessary. If no style
* object attached to the text, the default style for composing text
* is used. See {@link android.text.Spanned} for how to attach style
* object to the text. {@link android.text.SpannableString} and
* {@link android.text.SpannableStringBuilder} are two
* implementations of the interface {@link android.text.Spanned}.
* @param newCursorPosition The new cursor position around the text. If
* > 0, this is relative to the end of the text - 1; if <= 0, this
* is relative to the start of the text. So a value of 1 will
* always advance you to the position after the full text being
* inserted. Note that this means you can't position the cursor
* within the text, because the editor can make modifications to
* the text you are providing so it is not possible to correctly
* specify locations there.
* @param textAttribute The extra information about the text.
* @return true on success, false if the input connection is no longer
*
*/
default boolean setComposingText(@NonNull CharSequence text, int newCursorPosition,
@Nullable TextAttribute textAttribute) {
return setComposingText(text, newCursorPosition);
}
/**
* Mark a certain region of text as composing text. If there was a
* composing region, the characters are left as they were and the
@@ -578,6 +605,22 @@ public interface InputConnection {
*/
boolean setComposingRegion(int start, int end);
/**
* The variant of {@link InputConnection#setComposingRegion(int, int)}. This method is
* used to allow the IME to provide extra information while setting up text.
*
* @param start the position in the text at which the composing region begins
* @param end the position in the text at which the composing region ends
* @param textAttribute The extra information about the text.
* @return {@code true} on success, {@code false} if the input connection is no longer valid.
* Since Android {@link android.os.Build.VERSION_CODES#N} until
* {@link android.os.Build.VERSION_CODES#TIRAMISU}, this API returned {@code false} when
* the target application does not implement this method.
*/
default boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
return setComposingRegion(start, end);
}
/**
* Have the text editor finish whatever composing text is
* currently active. This simply leaves the text as-is, removing
@@ -633,6 +676,28 @@ public interface InputConnection {
*/
boolean commitText(CharSequence text, int newCursorPosition);
/**
* The variant of {@link InputConnection#commitText(CharSequence, int)}. This method is
* used to allow the IME to provide extra information while setting up text.
*
* @param text The text to commit. This may include styles.
* @param newCursorPosition The new cursor position around the text,
* in Java characters. If > 0, this is relative to the end
* of the text - 1; if <= 0, this is relative to the start
* of the text. So a value of 1 will always advance the cursor
* to the position after the full text being inserted. Note that
* this means you can't position the cursor within the text,
* because the editor can make modifications to the text
* you are providing so it is not possible to correctly specify
* locations there.
* @param textAttribute The extra information about the text.
* @return true on success, false if the input connection is no longer
*/
default boolean commitText(@NonNull CharSequence text, int newCursorPosition,
@Nullable TextAttribute textAttribute) {
return commitText(text, newCursorPosition);
}
/**
* Commit a completion the user has selected from the possible ones
* previously reported to {@link InputMethodSession#displayCompletions

View File

@@ -17,6 +17,7 @@
package android.view.inputmethod;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
@@ -153,6 +154,16 @@ public class InputConnectionWrapper implements InputConnection {
return mTarget.setComposingText(text, newCursorPosition);
}
/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
@Override
public boolean setComposingText(@NonNull CharSequence text,
int newCursorPosition, @Nullable TextAttribute textAttribute) {
return mTarget.setComposingText(text, newCursorPosition, textAttribute);
}
/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
@@ -162,6 +173,15 @@ public class InputConnectionWrapper implements InputConnection {
return mTarget.setComposingRegion(start, end);
}
/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
@Override
public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
return mTarget.setComposingRegion(start, end, textAttribute);
}
/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
@@ -180,6 +200,16 @@ public class InputConnectionWrapper implements InputConnection {
return mTarget.commitText(text, newCursorPosition);
}
/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
@Override
public boolean commitText(@NonNull CharSequence text, int newCursorPosition,
@Nullable TextAttribute textAttribute) {
return mTarget.commitText(text, newCursorPosition, textAttribute);
}
/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.

View File

@@ -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 android.view.inputmethod;
parcelable TextAttribute;

View File

@@ -0,0 +1,140 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view.inputmethod;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* The data class that IME can take extra information to applications when setting the text.
*
* See {@link InputConnection#commitText(CharSequence, int, TextAttribute)} and
* {@link InputConnection#setComposingRegion(int, int, TextAttribute)} and
* {@link InputConnection#setComposingText(CharSequence, int, TextAttribute)}
*/
public final class TextAttribute implements Parcelable {
private final @NonNull List<String> mTextConversionSuggestions;
private final @NonNull PersistableBundle mExtras;
private TextAttribute(TextAttributeBuilder builder) {
mTextConversionSuggestions = builder.mTextConversionSuggestions;
mExtras = builder.mExtras;
}
private TextAttribute(Parcel source) {
mTextConversionSuggestions = source.createStringArrayList();
mExtras = source.readPersistableBundle();
}
/**
* Get the list of text conversion suggestions. More text conversion details in
* {@link TextAttributeBuilder#setTextConversionSuggestions(List)}.
*
* @return List of text conversion suggestions. If the list is empty, it means that IME not set
* this field or IME didn't have suggestions for applications.
*/
public @NonNull List<String> getTextConversionSuggestions() {
return mTextConversionSuggestions;
}
/**
* Get the extras data. More extras data details in
* {@link TextAttributeBuilder#setExtras(PersistableBundle)}.
*
* @return Extras data. If the Bundle is empty, it means that IME not set this field or IME
* didn't have extras data.
*/
public @NonNull PersistableBundle getExtras() {
return mExtras;
}
/**
* Builder for creating a {@link TextAttribute}.
*/
public static final class TextAttributeBuilder {
private List<String> mTextConversionSuggestions = new ArrayList<>();
private PersistableBundle mExtras = new PersistableBundle();
/**
* Sets text conversion suggestions.
*
* <p>Text conversion suggestion is for some transliteration languages which has
* pronunciation characters and target characters. When the user is typing the pronunciation
* characters, the input method can insert possible target characters into this list so that
* the editor authors can provide suggestion before the user enters the complete
* pronunciation characters.</p>
*
* @param textConversionSuggestions The list of text conversion suggestions.
* @return This builder
*/
public @NonNull TextAttributeBuilder setTextConversionSuggestions(
@NonNull List<String> textConversionSuggestions) {
mTextConversionSuggestions = Collections.unmodifiableList(textConversionSuggestions);
return this;
}
/**
* Sets extras data.
*
* <p>Any extra data to supply to the applications. This field is for extended communication
* with IME if there is data not defined in framework.</p>
*
* @return This builder.
*/
public @NonNull TextAttributeBuilder setExtras(@NonNull PersistableBundle extras) {
mExtras = extras;
return this;
}
/**
* @return a new {@link TextAttribute}.
*/
public @NonNull TextAttribute build() {
return new TextAttribute(this);
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStringList(mTextConversionSuggestions);
dest.writePersistableBundle(mExtras);
}
public static final @NonNull Parcelable.Creator<TextAttribute> CREATOR =
new Parcelable.Creator<TextAttribute>() {
@Override
public TextAttribute createFromParcel(Parcel source) {
return new TextAttribute(source);
}
@Override
public TextAttribute[] newArray(int size) {
return new TextAttribute[size];
}
};
}

View File

@@ -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;
@@ -27,6 +28,7 @@ import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.TextAttribute;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.view.IInputContext;
@@ -210,6 +212,28 @@ public final class IInputContextInvoker {
}
}
/**
* Invokes {@link IInputContext#commitTextWithTextAttribute(InputConnectionCommandHeader, int,
* CharSequence)}.
*
* @param text {@code text} parameter to be passed.
* @param newCursorPosition {@code newCursorPosition} parameter to be passed.
* @param textAttribute The extra information about the text.
* @return {@code true} if the invocation is completed without {@link RemoteException}.
* {@code false} otherwise.
*/
@AnyThread
public boolean commitText(CharSequence text, int newCursorPosition,
@Nullable TextAttribute textAttribute) {
try {
mIInputContext.commitTextWithTextAttribute(
createHeader(), text, newCursorPosition, textAttribute);
return true;
} catch (RemoteException e) {
return false;
}
}
/**
* Invokes {@link IInputContext#commitCompletion(InputConnectionCommandHeader, CompletionInfo)}.
*
@@ -314,6 +338,27 @@ public final class IInputContextInvoker {
}
}
/**
* Invokes {@link IInputContext#setComposingRegionWithTextAttribute(
* InputConnectionCommandHeader, int, int, TextAttribute)}.
*
* @param start {@code id} parameter to be passed.
* @param end {@code id} parameter to be passed.
* @param textAttribute The extra information about the text.
* @return {@code true} if the invocation is completed without {@link RemoteException}.
* {@code false} otherwise.
*/
@AnyThread
public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
try {
mIInputContext.setComposingRegionWithTextAttribute(
createHeader(), start, end, textAttribute);
return true;
} catch (RemoteException e) {
return false;
}
}
/**
* Invokes
* {@link IInputContext#setComposingText(InputConnectionCommandHeader, CharSequence, int)}.
@@ -333,6 +378,28 @@ public final class IInputContextInvoker {
}
}
/**
* Invokes {@link IInputContext#setComposingTextWithTextAttribute(InputConnectionCommandHeader,
* CharSequence, int, TextAttribute)}.
*
* @param text {@code text} parameter to be passed.
* @param newCursorPosition {@code newCursorPosition} parameter to be passed.
* @param textAttribute The extra information about the text.
* @return {@code true} if the invocation is completed without {@link RemoteException}.
* {@code false} otherwise.
*/
@AnyThread
public boolean setComposingText(CharSequence text, int newCursorPosition,
@Nullable TextAttribute textAttribute) {
try {
mIInputContext.setComposingTextWithTextAttribute(
createHeader(), text, newCursorPosition, textAttribute);
return true;
} catch (RemoteException e) {
return false;
}
}
/**
* Invokes {@link IInputContext#finishComposingText(InputConnectionCommandHeader)}.
*

View File

@@ -42,6 +42,7 @@ import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.TextAttribute;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
@@ -383,6 +384,23 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
});
}
@Dispatching(cancellable = true)
@Override
public void commitTextWithTextAttribute(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("commitTextWithTextAttribute", () -> {
if (header.mSessionId != mCurrentSessionId.get()) {
return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "commitText on inactive InputConnection");
return;
}
ic.commitText(text, newCursorPosition, textAttribute);
});
}
@Dispatching(cancellable = true)
@Override
public void commitCompletion(InputConnectionCommandHeader header, CompletionInfo text) {
@@ -487,6 +505,23 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
});
}
@Dispatching(cancellable = true)
@Override
public void setComposingRegionWithTextAttribute(InputConnectionCommandHeader header, int start,
int end, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("setComposingRegionWithTextAttribute", () -> {
if (header.mSessionId != mCurrentSessionId.get()) {
return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "setComposingRegion on inactive InputConnection");
return;
}
ic.setComposingRegion(start, end, textAttribute);
});
}
@Dispatching(cancellable = true)
@Override
public void setComposingText(InputConnectionCommandHeader header, CharSequence text,
@@ -504,6 +539,23 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
});
}
@Dispatching(cancellable = true)
@Override
public void setComposingTextWithTextAttribute(InputConnectionCommandHeader header,
CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("setComposingTextWithTextAttribute", () -> {
if (header.mSessionId != mCurrentSessionId.get()) {
return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "setComposingText on inactive InputConnection");
return;
}
ic.setComposingText(text, newCursorPosition, textAttribute);
});
}
/**
* Dispatches {@link InputConnection#finishComposingText()}.
*

View File

@@ -22,6 +22,7 @@ import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.TextAttribute;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.InputConnectionCommandHeader;
@@ -52,10 +53,16 @@ import com.android.internal.inputmethod.InputConnectionCommandHeader;
void setComposingText(in InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition);
void setComposingTextWithTextAttribute(in InputConnectionCommandHeader header,
CharSequence text, int newCursorPosition, in TextAttribute textAttribute);
void finishComposingText(in InputConnectionCommandHeader header);
void commitText(in InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition);
int newCursorPosition);
void commitTextWithTextAttribute(in InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition, in TextAttribute textAttribute);
void commitCompletion(in InputConnectionCommandHeader header, in CompletionInfo completion);
@@ -82,6 +89,9 @@ import com.android.internal.inputmethod.InputConnectionCommandHeader;
void setComposingRegion(in InputConnectionCommandHeader header, int start, int end);
void setComposingRegionWithTextAttribute(in InputConnectionCommandHeader header, int start,
int end, in TextAttribute textAttribute);
void getSelectedText(in InputConnectionCommandHeader header, int flags,
in AndroidFuture future /* T=CharSequence */);