blob: 24739bf8f3b3ed56d294b4a9109ecb78d3921862 [file] [log] [blame]
Yohei Yukawac2ddd602014-05-06 21:22:49 +09001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package android.view.inputmethod;
18
Yohei Yukawab7edebc2016-04-11 01:38:23 -070019import android.annotation.NonNull;
Yohei Yukawac2ddd602014-05-06 21:22:49 +090020import android.graphics.Matrix;
21import android.graphics.RectF;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.text.Layout;
Yohei Yukawaa1fda002014-06-27 12:24:20 +090025import android.text.SpannedString;
26import android.text.TextUtils;
Yohei Yukawac2ddd602014-05-06 21:22:49 +090027import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
28
Yohei Yukawab7edebc2016-04-11 01:38:23 -070029import java.util.Arrays;
Yohei Yukawac2ddd602014-05-06 21:22:49 +090030import java.util.Objects;
31
32/**
33 * Positional information about the text insertion point and characters in the composition string.
34 *
35 * <p>This class encapsulates locations of the text insertion point and the composition string in
36 * the screen coordinates so that IMEs can render their UI components near where the text is
37 * actually inserted.</p>
38 */
39public final class CursorAnchorInfo implements Parcelable {
Yohei Yukawa5f183f02014-09-02 14:18:40 -070040 /**
Yohei Yukawab7edebc2016-04-11 01:38:23 -070041 * The pre-computed hash code.
42 */
43 private final int mHashCode;
44
45 /**
Yohei Yukawa5f183f02014-09-02 14:18:40 -070046 * The index of the first character of the selected text (inclusive). {@code -1} when there is
47 * no text selection.
48 */
Yohei Yukawac2ddd602014-05-06 21:22:49 +090049 private final int mSelectionStart;
Yohei Yukawa5f183f02014-09-02 14:18:40 -070050 /**
51 * The index of the first character of the selected text (exclusive). {@code -1} when there is
52 * no text selection.
53 */
Yohei Yukawac2ddd602014-05-06 21:22:49 +090054 private final int mSelectionEnd;
Yohei Yukawa81f4cb32014-05-13 22:20:35 +090055
Yohei Yukawa5f183f02014-09-02 14:18:40 -070056 /**
57 * The index of the first character of the composing text (inclusive). {@code -1} when there is
58 * no composing text.
59 */
Yohei Yukawa81f4cb32014-05-13 22:20:35 +090060 private final int mComposingTextStart;
61 /**
62 * The text, tracked as a composing region.
63 */
Yohei Yukawaa1fda002014-06-27 12:24:20 +090064 private final CharSequence mComposingText;
Yohei Yukawac2ddd602014-05-06 21:22:49 +090065
66 /**
Yohei Yukawacc24e2b2014-08-29 20:21:10 -070067 * Flags of the insertion marker. See {@link #FLAG_HAS_VISIBLE_REGION} for example.
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +090068 */
Yohei Yukawacc24e2b2014-08-29 20:21:10 -070069 private final int mInsertionMarkerFlags;
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +090070 /**
Yohei Yukawac2ddd602014-05-06 21:22:49 +090071 * Horizontal position of the insertion marker, in the local coordinates that will be
72 * transformed with the transformation matrix when rendered on the screen. This should be
73 * calculated or compatible with {@link Layout#getPrimaryHorizontal(int)}. This can be
74 * {@code java.lang.Float.NaN} when no value is specified.
75 */
76 private final float mInsertionMarkerHorizontal;
77 /**
78 * Vertical position of the insertion marker, in the local coordinates that will be
79 * transformed with the transformation matrix when rendered on the screen. This should be
80 * calculated or compatible with {@link Layout#getLineTop(int)}. This can be
81 * {@code java.lang.Float.NaN} when no value is specified.
82 */
83 private final float mInsertionMarkerTop;
84 /**
85 * Vertical position of the insertion marker, in the local coordinates that will be
86 * transformed with the transformation matrix when rendered on the screen. This should be
87 * calculated or compatible with {@link Layout#getLineBaseline(int)}. This can be
88 * {@code java.lang.Float.NaN} when no value is specified.
89 */
90 private final float mInsertionMarkerBaseline;
91 /**
92 * Vertical position of the insertion marker, in the local coordinates that will be
93 * transformed with the transformation matrix when rendered on the screen. This should be
94 * calculated or compatible with {@link Layout#getLineBottom(int)}. This can be
95 * {@code java.lang.Float.NaN} when no value is specified.
96 */
97 private final float mInsertionMarkerBottom;
98
99 /**
100 * Container of rectangular position of characters, keyed with character index in a unit of
101 * Java chars, in the local coordinates that will be transformed with the transformation matrix
102 * when rendered on the screen.
103 */
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700104 private final SparseRectFArray mCharacterBoundsArray;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900105
106 /**
107 * Transformation matrix that is applied to any positional information of this class to
108 * transform local coordinates into screen coordinates.
109 */
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700110 @NonNull
111 private final float[] mMatrixValues;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900112
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700113 /**
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700114 * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700115 * insertion marker or character bounds have at least one visible region.
116 */
117 public static final int FLAG_HAS_VISIBLE_REGION = 0x01;
118
119 /**
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700120 * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700121 * insertion marker or character bounds have at least one invisible (clipped) region.
122 */
123 public static final int FLAG_HAS_INVISIBLE_REGION = 0x02;
124
125 /**
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700126 * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
127 * insertion marker or character bounds is placed at right-to-left (RTL) character.
128 */
129 public static final int FLAG_IS_RTL = 0x04;
130
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900131 public CursorAnchorInfo(final Parcel source) {
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700132 mHashCode = source.readInt();
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900133 mSelectionStart = source.readInt();
134 mSelectionEnd = source.readInt();
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900135 mComposingTextStart = source.readInt();
Yohei Yukawaa1fda002014-06-27 12:24:20 +0900136 mComposingText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700137 mInsertionMarkerFlags = source.readInt();
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900138 mInsertionMarkerHorizontal = source.readFloat();
139 mInsertionMarkerTop = source.readFloat();
140 mInsertionMarkerBaseline = source.readFloat();
141 mInsertionMarkerBottom = source.readFloat();
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700142 mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader());
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700143 mMatrixValues = source.createFloatArray();
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900144 }
145
146 /**
147 * Used to package this object into a {@link Parcel}.
148 *
149 * @param dest The {@link Parcel} to be written.
150 * @param flags The flags used for parceling.
151 */
152 @Override
153 public void writeToParcel(Parcel dest, int flags) {
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700154 dest.writeInt(mHashCode);
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900155 dest.writeInt(mSelectionStart);
156 dest.writeInt(mSelectionEnd);
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900157 dest.writeInt(mComposingTextStart);
Yohei Yukawaa1fda002014-06-27 12:24:20 +0900158 TextUtils.writeToParcel(mComposingText, dest, flags);
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700159 dest.writeInt(mInsertionMarkerFlags);
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900160 dest.writeFloat(mInsertionMarkerHorizontal);
161 dest.writeFloat(mInsertionMarkerTop);
162 dest.writeFloat(mInsertionMarkerBaseline);
163 dest.writeFloat(mInsertionMarkerBottom);
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700164 dest.writeParcelable(mCharacterBoundsArray, flags);
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700165 dest.writeFloatArray(mMatrixValues);
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900166 }
167
168 @Override
169 public int hashCode(){
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700170 return mHashCode;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900171 }
172
Yohei Yukawaeea0b8b2014-07-08 19:46:11 +0900173 /**
174 * Compares two float values. Returns {@code true} if {@code a} and {@code b} are
175 * {@link Float#NaN} at the same time.
176 */
177 private static boolean areSameFloatImpl(final float a, final float b) {
178 if (Float.isNaN(a) && Float.isNaN(b)) {
179 return true;
180 }
181 return a == b;
182 }
183
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900184 @Override
185 public boolean equals(Object obj){
186 if (obj == null) {
187 return false;
188 }
189 if (this == obj) {
190 return true;
191 }
192 if (!(obj instanceof CursorAnchorInfo)) {
193 return false;
194 }
195 final CursorAnchorInfo that = (CursorAnchorInfo) obj;
196 if (hashCode() != that.hashCode()) {
197 return false;
198 }
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700199
200 // Check fields that are not covered by hashCode() first.
201
Yohei Yukawaeea0b8b2014-07-08 19:46:11 +0900202 if (mSelectionStart != that.mSelectionStart || mSelectionEnd != that.mSelectionEnd) {
203 return false;
204 }
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700205
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700206 if (mInsertionMarkerFlags != that.mInsertionMarkerFlags
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900207 || !areSameFloatImpl(mInsertionMarkerHorizontal, that.mInsertionMarkerHorizontal)
Yohei Yukawaeea0b8b2014-07-08 19:46:11 +0900208 || !areSameFloatImpl(mInsertionMarkerTop, that.mInsertionMarkerTop)
209 || !areSameFloatImpl(mInsertionMarkerBaseline, that.mInsertionMarkerBaseline)
210 || !areSameFloatImpl(mInsertionMarkerBottom, that.mInsertionMarkerBottom)) {
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900211 return false;
212 }
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700213
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700214 if (!Objects.equals(mCharacterBoundsArray, that.mCharacterBoundsArray)) {
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900215 return false;
216 }
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700217
218 // Following fields are (partially) covered by hashCode().
219
220 if (mComposingTextStart != that.mComposingTextStart
221 || !Objects.equals(mComposingText, that.mComposingText)) {
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900222 return false;
223 }
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700224
225 // We do not use Arrays.equals(float[], float[]) to keep the previous behavior regarding
226 // NaN, 0.0f, and -0.0f.
227 if (mMatrixValues.length != that.mMatrixValues.length) {
228 return false;
229 }
230 for (int i = 0; i < mMatrixValues.length; ++i) {
231 if (mMatrixValues[i] != that.mMatrixValues[i]) {
232 return false;
233 }
234 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900235 return true;
236 }
237
238 @Override
239 public String toString() {
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700240 return "CursorAnchorInfo{mHashCode=" + mHashCode
241 + " mSelection=" + mSelectionStart + "," + mSelectionEnd
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900242 + " mComposingTextStart=" + mComposingTextStart
243 + " mComposingText=" + Objects.toString(mComposingText)
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700244 + " mInsertionMarkerFlags=" + mInsertionMarkerFlags
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900245 + " mInsertionMarkerHorizontal=" + mInsertionMarkerHorizontal
246 + " mInsertionMarkerTop=" + mInsertionMarkerTop
247 + " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline
248 + " mInsertionMarkerBottom=" + mInsertionMarkerBottom
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700249 + " mCharacterBoundsArray=" + Objects.toString(mCharacterBoundsArray)
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700250 + " mMatrix=" + Arrays.toString(mMatrixValues)
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900251 + "}";
252 }
253
254 /**
255 * Builder for {@link CursorAnchorInfo}. This class is not designed to be thread-safe.
256 */
Yohei Yukawac46b5f02014-06-10 12:26:34 +0900257 public static final class Builder {
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700258 private int mSelectionStart = -1;
259 private int mSelectionEnd = -1;
260 private int mComposingTextStart = -1;
261 private CharSequence mComposingText = null;
262 private float mInsertionMarkerHorizontal = Float.NaN;
263 private float mInsertionMarkerTop = Float.NaN;
264 private float mInsertionMarkerBaseline = Float.NaN;
265 private float mInsertionMarkerBottom = Float.NaN;
266 private int mInsertionMarkerFlags = 0;
267 private SparseRectFArrayBuilder mCharacterBoundsArrayBuilder = null;
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700268 private float[] mMatrixValues = null;
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700269 private boolean mMatrixInitialized = false;
270
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900271 /**
272 * Sets the text range of the selection. Calling this can be skipped if there is no
273 * selection.
274 */
Yohei Yukawac46b5f02014-06-10 12:26:34 +0900275 public Builder setSelectionRange(final int newStart, final int newEnd) {
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900276 mSelectionStart = newStart;
277 mSelectionEnd = newEnd;
278 return this;
279 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900280
281 /**
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900282 * Sets the text range of the composing text. Calling this can be skipped if there is
283 * no composing text.
Yohei Yukawabb9f6242014-07-12 01:11:45 +0900284 * @param composingTextStart index where the composing text starts.
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900285 * @param composingText the entire composing text.
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900286 */
Yohei Yukawabb9f6242014-07-12 01:11:45 +0900287 public Builder setComposingText(final int composingTextStart,
288 final CharSequence composingText) {
289 mComposingTextStart = composingTextStart;
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900290 if (composingText == null) {
291 mComposingText = null;
292 } else {
Yohei Yukawaa1fda002014-06-27 12:24:20 +0900293 // Make a snapshot of the given char sequence.
294 mComposingText = new SpannedString(composingText);
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900295 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900296 return this;
297 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900298
299 /**
300 * Sets the location of the text insertion point (zero width cursor) as a rectangle in
301 * local coordinates. Calling this can be skipped when there is no text insertion point;
302 * however if there is an insertion point, editors must call this method.
303 * @param horizontalPosition horizontal position of the insertion marker, in the local
304 * coordinates that will be transformed with the transformation matrix when rendered on the
305 * screen. This should be calculated or compatible with
306 * {@link Layout#getPrimaryHorizontal(int)}.
307 * @param lineTop vertical position of the insertion marker, in the local coordinates that
308 * will be transformed with the transformation matrix when rendered on the screen. This
309 * should be calculated or compatible with {@link Layout#getLineTop(int)}.
310 * @param lineBaseline vertical position of the insertion marker, in the local coordinates
311 * that will be transformed with the transformation matrix when rendered on the screen. This
312 * should be calculated or compatible with {@link Layout#getLineBaseline(int)}.
313 * @param lineBottom vertical position of the insertion marker, in the local coordinates
314 * that will be transformed with the transformation matrix when rendered on the screen. This
315 * should be calculated or compatible with {@link Layout#getLineBottom(int)}.
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700316 * @param flags flags of the insertion marker. See {@link #FLAG_HAS_VISIBLE_REGION} for
317 * example.
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900318 */
Yohei Yukawac46b5f02014-06-10 12:26:34 +0900319 public Builder setInsertionMarkerLocation(final float horizontalPosition,
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900320 final float lineTop, final float lineBaseline, final float lineBottom,
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700321 final int flags){
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900322 mInsertionMarkerHorizontal = horizontalPosition;
323 mInsertionMarkerTop = lineTop;
324 mInsertionMarkerBaseline = lineBaseline;
325 mInsertionMarkerBottom = lineBottom;
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700326 mInsertionMarkerFlags = flags;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900327 return this;
328 }
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700329
330 /**
331 * Adds the bounding box of the character specified with the index.
332 *
333 * @param index index of the character in Java chars units. Must be specified in
334 * ascending order across successive calls.
335 * @param left x coordinate of the left edge of the character in local coordinates.
336 * @param top y coordinate of the top edge of the character in local coordinates.
337 * @param right x coordinate of the right edge of the character in local coordinates.
338 * @param bottom y coordinate of the bottom edge of the character in local coordinates.
339 * @param flags flags for this character bounds. See {@link #FLAG_HAS_VISIBLE_REGION},
340 * {@link #FLAG_HAS_INVISIBLE_REGION} and {@link #FLAG_IS_RTL}. These flags must be
341 * specified when necessary.
342 * @throws IllegalArgumentException If the index is a negative value, or not greater than
343 * all of the previously called indices.
344 */
345 public Builder addCharacterBounds(final int index, final float left, final float top,
346 final float right, final float bottom, final int flags) {
347 if (index < 0) {
348 throw new IllegalArgumentException("index must not be a negative integer.");
349 }
350 if (mCharacterBoundsArrayBuilder == null) {
351 mCharacterBoundsArrayBuilder = new SparseRectFArrayBuilder();
352 }
353 mCharacterBoundsArrayBuilder.append(index, left, top, right, bottom, flags);
354 return this;
355 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900356
357 /**
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900358 * Sets the matrix that transforms local coordinates into screen coordinates.
359 * @param matrix transformation matrix from local coordinates into screen coordinates. null
360 * is interpreted as an identity matrix.
361 */
Yohei Yukawac46b5f02014-06-10 12:26:34 +0900362 public Builder setMatrix(final Matrix matrix) {
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700363 if (mMatrixValues == null) {
364 mMatrixValues = new float[9];
365 }
366 (matrix != null ? matrix : Matrix.IDENTITY_MATRIX).getValues(mMatrixValues);
Yohei Yukawab5268dc2014-06-27 16:16:47 +0900367 mMatrixInitialized = true;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900368 return this;
369 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900370
371 /**
Yohei Yukawab5268dc2014-06-27 16:16:47 +0900372 * @return {@link CursorAnchorInfo} using parameters in this {@link Builder}.
373 * @throws IllegalArgumentException if one or more positional parameters are specified but
374 * the coordinate transformation matrix is not provided via {@link #setMatrix(Matrix)}.
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900375 */
376 public CursorAnchorInfo build() {
Yohei Yukawab5268dc2014-06-27 16:16:47 +0900377 if (!mMatrixInitialized) {
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700378 // Coordinate transformation matrix is mandatory when at least one positional
379 // parameter is specified.
380 final boolean hasCharacterBounds = (mCharacterBoundsArrayBuilder != null
381 && !mCharacterBoundsArrayBuilder.isEmpty());
382 if (hasCharacterBounds
383 || !Float.isNaN(mInsertionMarkerHorizontal)
384 || !Float.isNaN(mInsertionMarkerTop)
385 || !Float.isNaN(mInsertionMarkerBaseline)
386 || !Float.isNaN(mInsertionMarkerBottom)) {
Yohei Yukawab5268dc2014-06-27 16:16:47 +0900387 throw new IllegalArgumentException("Coordinate transformation matrix is " +
388 "required when positional parameters are specified.");
389 }
390 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900391 return new CursorAnchorInfo(this);
392 }
393
394 /**
395 * Resets the internal state so that this instance can be reused to build another
396 * instance of {@link CursorAnchorInfo}.
397 */
398 public void reset() {
399 mSelectionStart = -1;
400 mSelectionEnd = -1;
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900401 mComposingTextStart = -1;
402 mComposingText = null;
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700403 mInsertionMarkerFlags = 0;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900404 mInsertionMarkerHorizontal = Float.NaN;
405 mInsertionMarkerTop = Float.NaN;
406 mInsertionMarkerBaseline = Float.NaN;
407 mInsertionMarkerBottom = Float.NaN;
Yohei Yukawab5268dc2014-06-27 16:16:47 +0900408 mMatrixInitialized = false;
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700409 if (mCharacterBoundsArrayBuilder != null) {
410 mCharacterBoundsArrayBuilder.reset();
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900411 }
412 }
413 }
414
Yohei Yukawac46b5f02014-06-10 12:26:34 +0900415 private CursorAnchorInfo(final Builder builder) {
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900416 mSelectionStart = builder.mSelectionStart;
417 mSelectionEnd = builder.mSelectionEnd;
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900418 mComposingTextStart = builder.mComposingTextStart;
419 mComposingText = builder.mComposingText;
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700420 mInsertionMarkerFlags = builder.mInsertionMarkerFlags;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900421 mInsertionMarkerHorizontal = builder.mInsertionMarkerHorizontal;
422 mInsertionMarkerTop = builder.mInsertionMarkerTop;
423 mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline;
424 mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700425 mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null ?
426 builder.mCharacterBoundsArrayBuilder.build() : null;
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700427 mMatrixValues = new float[9];
428 if (builder.mMatrixInitialized) {
429 System.arraycopy(builder.mMatrixValues, 0, mMatrixValues, 0, 9);
430 } else {
431 Matrix.IDENTITY_MATRIX.getValues(mMatrixValues);
432 }
433
434 // To keep hash function simple, we only use some complex objects for hash.
435 int hash = Objects.hashCode(mComposingText);
436 hash *= 31;
437 hash += Arrays.hashCode(mMatrixValues);
438 mHashCode = hash;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900439 }
440
441 /**
442 * Returns the index where the selection starts.
Yohei Yukawaeea0b8b2014-07-08 19:46:11 +0900443 * @return {@code -1} if there is no selection.
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900444 */
445 public int getSelectionStart() {
446 return mSelectionStart;
447 }
448
449 /**
450 * Returns the index where the selection ends.
Yohei Yukawaeea0b8b2014-07-08 19:46:11 +0900451 * @return {@code -1} if there is no selection.
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900452 */
453 public int getSelectionEnd() {
454 return mSelectionEnd;
455 }
456
457 /**
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900458 * Returns the index where the composing text starts.
Yohei Yukawaeea0b8b2014-07-08 19:46:11 +0900459 * @return {@code -1} if there is no composing text.
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900460 */
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900461 public int getComposingTextStart() {
462 return mComposingTextStart;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900463 }
464
465 /**
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900466 * Returns the entire composing text.
Yohei Yukawaeea0b8b2014-07-08 19:46:11 +0900467 * @return {@code null} if there is no composition.
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900468 */
Yohei Yukawaa1fda002014-06-27 12:24:20 +0900469 public CharSequence getComposingText() {
Yohei Yukawa81f4cb32014-05-13 22:20:35 +0900470 return mComposingText;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900471 }
472
473 /**
Yohei Yukawacc24e2b2014-08-29 20:21:10 -0700474 * Returns the flag of the insertion marker.
475 * @return the flag of the insertion marker. {@code 0} if no flag is specified.
476 */
477 public int getInsertionMarkerFlags() {
478 return mInsertionMarkerFlags;
479 }
480
481 /**
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900482 * Returns the horizontal start of the insertion marker, in the local coordinates that will
483 * be transformed with {@link #getMatrix()} when rendered on the screen.
484 * @return x coordinate that is compatible with {@link Layout#getPrimaryHorizontal(int)}.
485 * Pay special care to RTL/LTR handling.
486 * {@code java.lang.Float.NaN} if not specified.
487 * @see Layout#getPrimaryHorizontal(int)
488 */
489 public float getInsertionMarkerHorizontal() {
490 return mInsertionMarkerHorizontal;
491 }
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900492
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900493 /**
494 * Returns the vertical top position of the insertion marker, in the local coordinates that
495 * will be transformed with {@link #getMatrix()} when rendered on the screen.
496 * @return y coordinate that is compatible with {@link Layout#getLineTop(int)}.
497 * {@code java.lang.Float.NaN} if not specified.
498 */
499 public float getInsertionMarkerTop() {
500 return mInsertionMarkerTop;
501 }
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900502
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900503 /**
504 * Returns the vertical baseline position of the insertion marker, in the local coordinates
505 * that will be transformed with {@link #getMatrix()} when rendered on the screen.
506 * @return y coordinate that is compatible with {@link Layout#getLineBaseline(int)}.
507 * {@code java.lang.Float.NaN} if not specified.
508 */
509 public float getInsertionMarkerBaseline() {
510 return mInsertionMarkerBaseline;
511 }
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900512
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900513 /**
514 * Returns the vertical bottom position of the insertion marker, in the local coordinates
515 * that will be transformed with {@link #getMatrix()} when rendered on the screen.
516 * @return y coordinate that is compatible with {@link Layout#getLineBottom(int)}.
517 * {@code java.lang.Float.NaN} if not specified.
518 */
519 public float getInsertionMarkerBottom() {
520 return mInsertionMarkerBottom;
521 }
522
523 /**
524 * Returns a new instance of {@link RectF} that indicates the location of the character
525 * specified with the index.
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700526 * @param index index of the character in a Java chars.
527 * @return the character bounds in local coordinates as a new instance of {@link RectF}.
528 */
529 public RectF getCharacterBounds(final int index) {
530 if (mCharacterBoundsArray == null) {
531 return null;
532 }
533 return mCharacterBoundsArray.get(index);
534 }
535
536 /**
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700537 * Returns the flags associated with the character bounds specified with the index.
538 * @param index index of the character in a Java chars.
539 * @return {@code 0} if no flag is specified.
540 */
541 public int getCharacterBoundsFlags(final int index) {
542 if (mCharacterBoundsArray == null) {
543 return 0;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900544 }
Yohei Yukawa5f183f02014-09-02 14:18:40 -0700545 return mCharacterBoundsArray.getFlags(index, 0);
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900546 }
547
548 /**
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900549 * Returns a new instance of {@link android.graphics.Matrix} that indicates the transformation
550 * matrix that is to be applied other positional data in this class.
551 * @return a new instance (copy) of the transformation matrix.
552 */
553 public Matrix getMatrix() {
Yohei Yukawab7edebc2016-04-11 01:38:23 -0700554 final Matrix matrix = new Matrix();
555 matrix.setValues(mMatrixValues);
556 return matrix;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900557 }
558
559 /**
560 * Used to make this class parcelable.
561 */
562 public static final Parcelable.Creator<CursorAnchorInfo> CREATOR
563 = new Parcelable.Creator<CursorAnchorInfo>() {
564 @Override
565 public CursorAnchorInfo createFromParcel(Parcel source) {
566 return new CursorAnchorInfo(source);
567 }
568
569 @Override
570 public CursorAnchorInfo[] newArray(int size) {
571 return new CursorAnchorInfo[size];
572 }
573 };
574
575 @Override
576 public int describeContents() {
577 return 0;
578 }
579}