1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view.inputmethod; 18 19 import android.annotation.IntRange; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 import android.text.TextUtils; 25 26 /** 27 * Information about the surrounding text around the cursor for use by an input method. 28 * 29 * <p>This contains information about the text and the selection relative to the text. </p> 30 */ 31 public final class SurroundingText implements Parcelable { 32 /** 33 * The surrounding text around the cursor. 34 */ 35 @NonNull 36 private final CharSequence mText; 37 38 /** 39 * The text offset of the start of the selection in the surrounding text. 40 * 41 * <p>This needs to be the position relative to the {@link #mText} instead of the real position 42 * in the editor.</p> 43 */ 44 @IntRange(from = 0) 45 private final int mSelectionStart; 46 47 /** 48 * The text offset of the end of the selection in the surrounding text. 49 * 50 * <p>This needs to be the position relative to the {@link #mText} instead of the real position 51 * in the editor.</p> 52 */ 53 @IntRange(from = 0) 54 private final int mSelectionEnd; 55 56 /** 57 * The text offset between the start of the editor's text and the start of the surrounding text. 58 * 59 * <p>-1 indicates the offset information is unknown.</p> 60 */ 61 @IntRange(from = -1) 62 private final int mOffset; 63 64 /** 65 * Constructor. 66 * 67 * @param text The surrounding text. 68 * @param selectionStart The text offset of the start of the selection in the surrounding text. 69 * Reversed selection is allowed. 70 * @param selectionEnd The text offset of the end of the selection in the surrounding text. 71 * Reversed selection is allowed. 72 * @param offset The text offset between the start of the editor's text and the start of the 73 * surrounding text. -1 indicates the offset is unknown. 74 */ SurroundingText(@onNull final CharSequence text, @IntRange(from = 0) int selectionStart, @IntRange(from = 0) int selectionEnd, @IntRange(from = -1) int offset)75 public SurroundingText(@NonNull final CharSequence text, 76 @IntRange(from = 0) int selectionStart, @IntRange(from = 0) int selectionEnd, 77 @IntRange(from = -1) int offset) { 78 mText = copyWithParcelableSpans(text); 79 mSelectionStart = selectionStart; 80 mSelectionEnd = selectionEnd; 81 mOffset = offset; 82 } 83 84 /** 85 * Returns the surrounding text around the cursor. 86 */ 87 @NonNull getText()88 public CharSequence getText() { 89 return mText; 90 } 91 92 /** 93 * Returns the text offset of the start of the selection in the surrounding text. 94 * 95 * <p>A selection is the current range of the text that is selected by the user, or the current 96 * position of the cursor. A cursor is a selection where the start and end are at the same 97 * offset.<p> 98 */ 99 @IntRange(from = 0) getSelectionStart()100 public int getSelectionStart() { 101 return mSelectionStart; 102 } 103 104 /** 105 * Returns the text offset of the end of the selection in the surrounding text. 106 * 107 * <p>A selection is the current range of the text that is selected by the user, or the current 108 * position of the cursor. A cursor is a selection where the start and end are at the same 109 * offset.<p> 110 */ 111 @IntRange(from = 0) getSelectionEnd()112 public int getSelectionEnd() { 113 return mSelectionEnd; 114 } 115 116 /** 117 * Returns text offset between the start of the editor's text and the start of the surrounding 118 * text. 119 * 120 * <p>-1 indicates the offset information is unknown.</p> 121 */ 122 @IntRange(from = -1) getOffset()123 public int getOffset() { 124 return mOffset; 125 } 126 127 @Override writeToParcel(@onNull Parcel out, int flags)128 public void writeToParcel(@NonNull Parcel out, int flags) { 129 TextUtils.writeToParcel(mText, out, flags); 130 out.writeInt(mSelectionStart); 131 out.writeInt(mSelectionEnd); 132 out.writeInt(mOffset); 133 } 134 135 @Override describeContents()136 public int describeContents() { 137 return 0; 138 } 139 140 @NonNull 141 public static final Parcelable.Creator<SurroundingText> CREATOR = 142 new Parcelable.Creator<SurroundingText>() { 143 @NonNull 144 public SurroundingText createFromParcel(Parcel in) { 145 final CharSequence text = 146 TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 147 final int selectionHead = in.readInt(); 148 final int selectionEnd = in.readInt(); 149 final int offset = in.readInt(); 150 return new SurroundingText( 151 text == null ? "" : text, selectionHead, selectionEnd, offset); 152 } 153 154 @NonNull 155 public SurroundingText[] newArray(int size) { 156 return new SurroundingText[size]; 157 } 158 }; 159 160 /** 161 * Create a copy of the given {@link CharSequence} object, with completely copy 162 * {@link ParcelableSpan} instances. 163 * 164 * @param source the original {@link CharSequence} to be copied. 165 * @return the copied {@link CharSequence}. {@code null} if {@code source} is {@code null}. 166 */ 167 @Nullable copyWithParcelableSpans(@ullable CharSequence source)168 private static CharSequence copyWithParcelableSpans(@Nullable CharSequence source) { 169 if (source == null) { 170 return null; 171 } 172 Parcel parcel = null; 173 try { 174 parcel = Parcel.obtain(); 175 TextUtils.writeToParcel(source, parcel, /* parcelableFlags= */ 0); 176 parcel.setDataPosition(0); 177 return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 178 } finally { 179 if (parcel != null) { 180 parcel.recycle(); 181 } 182 } 183 } 184 185 /** @hide */ isEqualTo(@ullable SurroundingText that)186 public boolean isEqualTo(@Nullable SurroundingText that) { 187 if (that == null) return false; 188 if (this == that) return true; 189 return mSelectionStart == that.mSelectionStart 190 && mSelectionEnd == that.mSelectionEnd 191 && mOffset == that.mOffset 192 && TextUtils.equals(mText, that.mText); 193 } 194 } 195