IME API: InputConnection#getSurroudingText(int, int, int)

Introduce a new class SurroundingText and a new API in InputConnection
to support retrieving surrounding text as an atomic request.

SurroundingText is the class for wrapping the text and sggestion info.
InputConnection#getSurroudingText() will return an SurroudingText object
if the protocol is supported.

Test: atest FrameworksCoreTests:SurroundingTextTest
Test: atest CtsInputMethodTestCases:BaseInputConnectionTest

BUG: 167947745
Change-Id: I2eb9ef5ba61a0e033007089da80f81548108621e
This commit is contained in:
Lan Wei
2020-09-24 12:05:21 +08:00
parent 4ecec9c521
commit 02b50d4944
16 changed files with 575 additions and 7 deletions

View File

@@ -57319,6 +57319,7 @@ package android.view.inputmethod {
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method public CharSequence getTextAfterCursor(int, int);
method public CharSequence getTextBeforeCursor(int, int);
method public boolean performContextMenuAction(int);
@@ -57527,6 +57528,17 @@ package android.view.inputmethod {
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeNameResId(int);
}
public final class SurroundingText implements android.os.Parcelable {
ctor public SurroundingText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
method public int describeContents();
method @IntRange(from=0xffffffff) public int getOffset();
method @IntRange(from=0) public int getSelectionEnd();
method @IntRange(from=0) public int getSelectionStart();
method @NonNull public CharSequence getText();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
}
}
package android.view.inspector {

View File

@@ -19,6 +19,8 @@ package android.view.inputmethod;
import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD;
import android.annotation.CallSuper;
import android.annotation.IntRange;
import android.annotation.Nullable;
import android.content.ClipData;
import android.content.Context;
import android.content.res.TypedArray;
@@ -584,6 +586,48 @@ public class BaseInputConnection implements InputConnection {
return TextUtils.substring(content, b, b + length);
}
/**
* The default implementation returns the given amount of text around the current cursor
* position in the buffer.
*/
@Nullable
public SurroundingText getSurroundingText(
@IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
final Editable content = getEditable();
if (content == null) return null;
int selStart = Selection.getSelectionStart(content);
int selEnd = Selection.getSelectionEnd(content);
// Guard against the case where the cursor has not been positioned yet.
if (selStart < 0 || selEnd < 0) {
return null;
}
if (selStart > selEnd) {
int tmp = selStart;
selStart = selEnd;
selEnd = tmp;
}
int contentLength = content.length();
int startPos = selStart - beforeLength;
int endPos = selEnd + afterLength;
// Guards the start and end pos within range [0, contentLength].
startPos = Math.max(0, startPos);
endPos = Math.min(contentLength, endPos);
CharSequence surroundingText;
if ((flags & GET_TEXT_WITH_STYLES) != 0) {
surroundingText = content.subSequence(startPos, endPos);
} else {
surroundingText = TextUtils.substring(content, startPos, endPos);
}
return new SurroundingText(
surroundingText, selStart - startPos, selEnd - startPos, startPos);
}
/**
* The default implementation turns this into the enter key.
*/

View File

@@ -16,14 +16,20 @@
package android.view.inputmethod;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* The InputConnection interface is the communication channel from an
* {@link InputMethod} back to the application that is receiving its
@@ -122,14 +128,20 @@ import android.view.KeyEvent;
* of each other, and the IME may use them however they see fit.</p>
*/
public interface InputConnection {
/** @hide */
@IntDef(flag = true, prefix = { "GET_TEXT_" }, value = {
GET_TEXT_WITH_STYLES,
})
@Retention(RetentionPolicy.SOURCE)
@interface GetTextType {}
/**
* Flag for use with {@link #getTextAfterCursor} and
* {@link #getTextBeforeCursor} to have style information returned
* along with the text. If not set, {@link #getTextAfterCursor}
* sends only the raw text, without style or other spans. If set,
* it may return a complex CharSequence of both text and style
* spans. <strong>Editor authors</strong>: you should strive to
* send text with styles if possible, but it is not required.
* Flag for use with {@link #getTextAfterCursor}, {@link #getTextBeforeCursor} and
* {@link #getSurroundingText} to have style information returned along with the text. If not
* set, {@link #getTextAfterCursor} sends only the raw text, without style or other spans. If
* set, it may return a complex CharSequence of both text and style spans.
* <strong>Editor authors</strong>: you should strive to send text with styles if possible, but
* it is not required.
*/
int GET_TEXT_WITH_STYLES = 0x0001;
@@ -263,6 +275,61 @@ public interface InputConnection {
*/
CharSequence getSelectedText(int flags);
/**
* Gets the surrounding text around the current cursor, with <var>beforeLength</var> characters
* of text before the cursor (start of the selection), <var>afterLength</var> characters of text
* after the cursor (end of the selection), and all of the selected text.
*
* <p>This method may fail either if the input connection has become invalid (such as its
* process crashing), or the client is taking too long to respond with the text (it is given a
* couple seconds to return), or the protocol is not supported. In any of these cases, null is
* returned.
*
* <p>This method does not affect the text in the editor in any way, nor does it affect the
* selection or composing spans.</p>
*
* <p>If {@link #GET_TEXT_WITH_STYLES} is supplied as flags, the editor should return a
* {@link android.text.Spanned} with all the spans set on the text.</p>
*
* <p><strong>IME authors:</strong> please consider this will trigger an IPC round-trip that
* will take some time. Assume this method consumes a lot of time. If you are using this to get
* the initial surrounding text around the cursor, you may consider using
* {@link EditorInfo#getInitialTextBeforeCursor(int, int)},
* {@link EditorInfo#getInitialSelectedText(int)}, and
* {@link EditorInfo#getInitialTextAfterCursor(int, int)} to prevent IPC costs.</p>
*
* @param beforeLength The expected length of the text before the cursor.
* @param afterLength The expected length of the text after the cursor.
* @param flags Supplies additional options controlling how the text is returned. Defined by the
* constants.
* @return an {@link android.view.inputmethod.SurroundingText} object describing the surrounding
* text and state of selection, or null if the input connection is no longer valid, or the
* editor can't comply with the request for some reason, or the application does not implement
* this method. The length of the returned text might be less than the sum of
* <var>beforeLength</var> and <var>afterLength</var> .
*/
@Nullable
default SurroundingText getSurroundingText(
@IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength,
@GetTextType int flags) {
CharSequence textBeforeCursor = getTextBeforeCursor(beforeLength, flags);
if (textBeforeCursor == null) {
textBeforeCursor = "";
}
CharSequence selectedText = getSelectedText(flags);
if (selectedText == null) {
selectedText = "";
}
CharSequence textAfterCursor = getTextAfterCursor(afterLength, flags);
if (textAfterCursor == null) {
textAfterCursor = "";
}
CharSequence surroundingText =
TextUtils.concat(textBeforeCursor, selectedText, textAfterCursor);
return new SurroundingText(surroundingText, textBeforeCursor.length(),
textBeforeCursor.length() + selectedText.length(), -1);
}
/**
* Retrieve the current capitalization mode in effect at the
* current cursor position in the text. See

View File

@@ -44,6 +44,7 @@ public final class InputConnectionInspector {
MissingMethodFlags.GET_HANDLER,
MissingMethodFlags.CLOSE_CONNECTION,
MissingMethodFlags.COMMIT_CONTENT,
MissingMethodFlags.GET_SURROUNDING_TEXT
})
public @interface MissingMethodFlags {
/**
@@ -86,6 +87,11 @@ public final class InputConnectionInspector {
* {@link android.os.Build.VERSION_CODES#N} MR-1 and later.
*/
int COMMIT_CONTENT = 1 << 7;
/**
* {@link InputConnection#getSurroundingText(int, int, int)} is available in
* {@link android.os.Build.VERSION_CODES#S} and later.
*/
int GET_SURROUNDING_TEXT = 1 << 8;
}
private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
@@ -138,6 +144,9 @@ public final class InputConnectionInspector {
if (!hasCommitContent(clazz)) {
flags |= MissingMethodFlags.COMMIT_CONTENT;
}
if (!hasGetSurroundingText(clazz)) {
flags |= MissingMethodFlags.GET_SURROUNDING_TEXT;
}
sMissingMethodsMap.put(clazz, flags);
return flags;
}
@@ -216,6 +225,16 @@ public final class InputConnectionInspector {
}
}
private static boolean hasGetSurroundingText(@NonNull final Class clazz) {
try {
final Method method = clazz.getMethod("getSurroundingText", int.class, int.class,
int.class);
return !Modifier.isAbstract(method.getModifiers());
} catch (NoSuchMethodException e) {
return false;
}
}
public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
final StringBuilder sb = new StringBuilder();
boolean isEmpty = true;

View File

@@ -16,6 +16,7 @@
package android.view.inputmethod;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyEvent;
@@ -97,6 +98,16 @@ public class InputConnectionWrapper implements InputConnection {
return mTarget.getSelectedText(flags);
}
/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
@Nullable
@Override
public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
return mTarget.getSurroundingText(beforeLength, afterLength, flags);
}
/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2020 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 SurroundingText;

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2020 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.IntRange;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
/**
* Information about the surrounding text around the cursor for use by an input method.
*
* <p>This contains information about the text and the selection relative to the text. </p>
*/
public final class SurroundingText implements Parcelable {
/**
* The surrounding text around the cursor.
*/
@NonNull
private final CharSequence mText;
/**
* The text offset of the start of the selection in the surrounding text.
*
* <p>This needs to be the position relative to the {@link #mText} instead of the real position
* in the editor.</p>
*/
@IntRange(from = 0)
private final int mSelectionStart;
/**
* The text offset of the end of the selection in the surrounding text.
*
* <p>This needs to be the position relative to the {@link #mText} instead of the real position
* in the editor.</p>
*/
@IntRange(from = 0)
private final int mSelectionEnd;
/**
* The text offset between the start of the editor's text and the start of the surrounding text.
*
* <p>-1 indicates the offset information is unknown.</p>
*/
@IntRange(from = -1)
private final int mOffset;
/**
* Constructor.
*
* @param text The surrounding text.
* @param selectionStart The text offset of the start of the selection in the surrounding text.
* Reversed selection is allowed.
* @param selectionEnd The text offset of the end of the selection in the surrounding text.
* Reversed selection is allowed.
* @param offset The text offset between the start of the editor's text and the start of the
* surrounding text. -1 indicates the offset is unknown.
*/
public SurroundingText(@NonNull final CharSequence text,
@IntRange(from = 0) int selectionStart, @IntRange(from = 0) int selectionEnd,
@IntRange(from = -1) int offset) {
mText = text;
mSelectionStart = selectionStart;
mSelectionEnd = selectionEnd;
mOffset = offset;
}
/**
* Returns the surrounding text around the cursor.
*/
@NonNull
public CharSequence getText() {
return mText;
}
/**
* Returns the text offset of the start of the selection in the surrounding text.
*/
@IntRange(from = 0)
public int getSelectionStart() {
return mSelectionStart;
}
/**
* Returns the text offset of the end of the selection in the surrounding text.
*/
@IntRange(from = 0)
public int getSelectionEnd() {
return mSelectionEnd;
}
/**
* Returns text offset between the start of the editor's text and the start of the surrounding
* text.
*
* <p>-1 indicates the offset information is unknown.</p>
*/
@IntRange(from = -1)
public int getOffset() {
return mOffset;
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
TextUtils.writeToParcel(mText, out, flags);
out.writeInt(mSelectionStart);
out.writeInt(mSelectionEnd);
out.writeInt(mOffset);
}
@Override
public int describeContents() {
return 0;
}
@NonNull
public static final Parcelable.Creator<SurroundingText> CREATOR =
new Parcelable.Creator<SurroundingText>() {
@NonNull
public SurroundingText createFromParcel(Parcel in) {
final CharSequence text =
TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
final int selectionHead = in.readInt();
final int selectionEnd = in.readInt();
final int offset = in.readInt();
return new SurroundingText(
text == null ? "" : text, selectionHead, selectionEnd, offset);
}
@NonNull
public SurroundingText[] newArray(int size) {
return new SurroundingText[size];
}
};
}

View File

@@ -82,6 +82,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.SurroundingText;
import android.view.inspector.InspectableProperty;
import android.view.inspector.InspectableProperty.EnumEntry;
import android.widget.RemoteViews.OnClickHandler;
@@ -5996,6 +5997,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return mTarget.getSelectedText(flags);
}
@Override
public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
if (mTarget == null) return null;
return mTarget.getSurroundingText(beforeLength, afterLength, flags);
}
@Override
public int getCursorCapsMode(int reqModes) {
if (mTarget == null) return InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;

View File

@@ -263,6 +263,16 @@ public final class CancellationGroup {
super(factory);
}
}
/**
* Completable object of {@link android.view.inputmethod.SurroundingText}.
*/
public static final class SurroundingText
extends Values<android.view.inputmethod.SurroundingText> {
private SurroundingText(@NonNull CancellationGroup factory) {
super(factory);
}
}
}
/**
@@ -292,6 +302,16 @@ public final class CancellationGroup {
return new Completable.ExtractedText(this);
}
/**
* @return an instance of {@link Completable.SurroundingText} that is associated with this
* {@link CancellationGroup}.
*/
@AnyThread
public Completable.SurroundingText createCompletableSurroundingText() {
return new Completable.SurroundingText(this);
}
@AnyThread
private boolean registerLatch(@NonNull CountDownLatch latch) {
synchronized (mLock) {

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2020 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 android.view.inputmethod.SurroundingText;
oneway interface ISurroundingTextResultCallback {
void onResult(in SurroundingText result);
}

View File

@@ -129,4 +129,32 @@ public final class ResultCallbacks {
}
};
}
/**
* Creates {@link ISurroundingTextResultCallback.Stub} that is to set
* {@link CancellationGroup.Completable.SurroundingText} when receiving the result.
*
* @param value {@link CancellationGroup.Completable.SurroundingText} to be set when receiving
* the result.
* @return {@link ISurroundingTextResultCallback.Stub} that can be passed as a binder IPC
* parameter.
*/
@AnyThread
public static ISurroundingTextResultCallback.Stub of(
@NonNull CancellationGroup.Completable.SurroundingText value) {
final AtomicReference<WeakReference<CancellationGroup.Completable.SurroundingText>>
atomicRef = new AtomicReference<>(new WeakReference<>(value));
return new ISurroundingTextResultCallback.Stub() {
@BinderThread
@Override
public void onResult(android.view.inputmethod.SurroundingText result) {
final CancellationGroup.Completable.SurroundingText value = unwrap(atomicRef);
if (value == null) {
return;
}
value.onComplete(result);
}
};
}
}

View File

@@ -35,11 +35,13 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.SurroundingText;
import com.android.internal.annotations.GuardedBy;
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.os.SomeArgs;
public abstract class IInputConnectionWrapper extends IInputContext.Stub {
@@ -70,6 +72,8 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 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;
@GuardedBy("mLock")
@Nullable
@@ -127,6 +131,21 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
dispatchMessage(mH.obtainMessage(DO_GET_SELECTED_TEXT, flags, 0 /* unused */, callback));
}
/**
* Dispatches the request for retrieving surrounding text.
*
* <p>See {@link InputConnection#getSurroundingText(int, int, int)}.
*/
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));
@@ -293,6 +312,33 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
}
return;
}
case DO_GET_SURROUNDING_TEXT: {
final SomeArgs args = (SomeArgs) msg.obj;
try {
int beforeLength = (int) args.arg1;
int afterLength = (int) args.arg2;
int flags = (int) args.arg3;
final ISurroundingTextResultCallback callback =
(ISurroundingTextResultCallback) args.arg4;
final InputConnection ic = getInputConnection();
final SurroundingText result;
if (ic == null || !isActive()) {
Log.w(TAG, "getSurroundingText on inactive InputConnection");
result = null;
} else {
result = ic.getSurroundingText(beforeLength, afterLength, flags);
}
try {
callback.onResult(result);
} catch (RemoteException e) {
Log.w(TAG, "Failed to return the result to getSurroundingText()."
+ " result=" + result, e);
}
} finally {
args.recycle();
}
return;
}
case DO_GET_CURSOR_CAPS_MODE: {
final IIntResultCallback callback = (IIntResultCallback) msg.obj;
final InputConnection ic = getInputConnection();

View File

@@ -26,6 +26,7 @@ import android.view.inputmethod.InputContentInfo;
import com.android.internal.inputmethod.ICharSequenceResultCallback;
import com.android.internal.inputmethod.IExtractedTextResultCallback;
import com.android.internal.inputmethod.IIntResultCallback;
import com.android.internal.inputmethod.ISurroundingTextResultCallback;
/**
* Interface from an input method to the application, allowing it to perform
@@ -79,4 +80,7 @@ import com.android.internal.inputmethod.IIntResultCallback;
void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts,
IIntResultCallback callback);
void getSurroundingText(int beforeLength, int afterLength, int flags,
ISurroundingTextResultCallback callback);
}

View File

@@ -33,6 +33,7 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.SurroundingText;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.ResultCallbacks;
@@ -157,6 +158,38 @@ public class InputConnectionWrapper implements InputConnection {
return getResultOrNull(value, "getSelectedText()");
}
/**
* Get {@link SurroundingText} around the current cursor, with <var>beforeLength</var>
* characters of text before the cursor, <var>afterLength</var> characters of text after the
* cursor, and all of the selected text.
* @param beforeLength The expected length of the text before the cursor
* @param afterLength The expected length of the text after the cursor
* @param flags Supplies additional options controlling how the text is returned. May be either
* 0 or {@link #GET_TEXT_WITH_STYLES}.
* @return the surrounding text around the cursor position; the length of the returned text
* might be less than requested. It could also be {@code null} when the editor or system could
* not support this protocol.
*/
@AnyThread
public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
if (mCancellationGroup.isCanceled()) {
return null;
}
if (isMethodMissing(MissingMethodFlags.GET_SURROUNDING_TEXT)) {
// This method is not implemented.
return null;
}
final CancellationGroup.Completable.SurroundingText value =
mCancellationGroup.createCompletableSurroundingText();
try {
mIInputContext.getSurroundingText(beforeLength, afterLength, flags,
ResultCallbacks.of(value));
} catch (RemoteException e) {
return null;
}
return getResultOrNull(value, "getSurroundingText()");
}
@AnyThread
public int getCursorCapsMode(int reqModes) {
if (mCancellationGroup.isCanceled()) {

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2020 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 static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SurroundingTextTest {
@Test
public void testSurroundingTextBasicCreation() {
SurroundingText surroundingText1 = new SurroundingText("test", 0, 0, 0);
assertThat(surroundingText1.getText(), is("test"));
assertThat(surroundingText1.getSelectionStart(), is(0));
assertThat(surroundingText1.getSelectionEnd(), is(0));
assertThat(surroundingText1.getOffset(), is(0));
SurroundingText surroundingText2 = new SurroundingText("", -1, -1, -1);
assertThat(surroundingText2.getText(), is(""));
assertThat(surroundingText2.getSelectionStart(), is(-1));
assertThat(surroundingText2.getSelectionEnd(), is(-1));
assertThat(surroundingText2.getOffset(), is(-1));
SurroundingText surroundingText3 = new SurroundingText("hello", 0, 5, 0);
assertThat(surroundingText3.getText(), is("hello"));
assertThat(surroundingText3.getSelectionStart(), is(0));
assertThat(surroundingText3.getSelectionEnd(), is(5));
assertThat(surroundingText3.getOffset(), is(0));
}
@Test
public void testSurroundingTextWriteToParcel() {
SurroundingText surroundingText = new SurroundingText("text", 0, 1, 2);
Parcel parcel = Parcel.obtain();
surroundingText.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
SurroundingText surroundingTextFromParcel =
SurroundingText.CREATOR.createFromParcel(parcel);
assertThat(surroundingText.getText(), is("text"));
assertThat(surroundingText.getSelectionStart(), is(0));
assertThat(surroundingText.getSelectionEnd(), is(1));
assertThat(surroundingText.getOffset(), is(2));
assertThat(surroundingTextFromParcel.getText(), is("text"));
assertThat(surroundingTextFromParcel.getSelectionStart(), is(0));
assertThat(surroundingTextFromParcel.getSelectionEnd(), is(1));
assertThat(surroundingTextFromParcel.getOffset(), is(2));
}
}

View File

@@ -55449,6 +55449,7 @@ package android.view.inputmethod {
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method public CharSequence getTextAfterCursor(int, int);
method public CharSequence getTextBeforeCursor(int, int);
method public boolean performContextMenuAction(int);
@@ -55657,6 +55658,17 @@ package android.view.inputmethod {
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeNameResId(int);
}
public final class SurroundingText implements android.os.Parcelable {
ctor public SurroundingText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
method public int describeContents();
method @IntRange(from=0xffffffff) public int getOffset();
method @IntRange(from=0) public int getSelectionEnd();
method @IntRange(from=0) public int getSelectionStart();
method @NonNull public CharSequence getText();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
}
}
package android.view.inspector {