• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.view.inputmethod;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.graphics.Matrix;
22 import android.graphics.RectF;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.text.Layout;
26 import android.text.SpannedString;
27 import android.text.TextUtils;
28 import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
29 
30 import java.util.Arrays;
31 import java.util.Objects;
32 
33 /**
34  * Positional information about the text insertion point and characters in the composition string.
35  *
36  * <p>This class encapsulates locations of the text insertion point and the composition string in
37  * the screen coordinates so that IMEs can render their UI components near where the text is
38  * actually inserted.</p>
39  */
40 public final class CursorAnchorInfo implements Parcelable {
41     /**
42      * The pre-computed hash code.
43      */
44     private final int mHashCode;
45 
46     /**
47      * The index of the first character of the selected text (inclusive). {@code -1} when there is
48      * no text selection.
49      */
50     private final int mSelectionStart;
51     /**
52      * The index of the first character of the selected text (exclusive). {@code -1} when there is
53      * no text selection.
54      */
55     private final int mSelectionEnd;
56 
57     /**
58      * The index of the first character of the composing text (inclusive). {@code -1} when there is
59      * no composing text.
60      */
61     private final int mComposingTextStart;
62     /**
63      * The text, tracked as a composing region.
64      */
65     private final CharSequence mComposingText;
66 
67     /**
68      * Flags of the insertion marker. See {@link #FLAG_HAS_VISIBLE_REGION} for example.
69      */
70     private final int mInsertionMarkerFlags;
71     /**
72      * Horizontal position of the insertion marker, in the local coordinates that will be
73      * transformed with the transformation matrix when rendered on the screen. This should be
74      * calculated or compatible with {@link Layout#getPrimaryHorizontal(int)}. This can be
75      * {@code java.lang.Float.NaN} when no value is specified.
76      */
77     private final float mInsertionMarkerHorizontal;
78     /**
79      * Vertical position of the insertion marker, in the local coordinates that will be
80      * transformed with the transformation matrix when rendered on the screen. This should be
81      * calculated or compatible with {@link Layout#getLineTop(int)}. This can be
82      * {@code java.lang.Float.NaN} when no value is specified.
83      */
84     private final float mInsertionMarkerTop;
85     /**
86      * Vertical position of the insertion marker, in the local coordinates that will be
87      * transformed with the transformation matrix when rendered on the screen. This should be
88      * calculated or compatible with {@link Layout#getLineBaseline(int)}. This can be
89      * {@code java.lang.Float.NaN} when no value is specified.
90      */
91     private final float mInsertionMarkerBaseline;
92     /**
93      * Vertical position of the insertion marker, in the local coordinates that will be
94      * transformed with the transformation matrix when rendered on the screen. This should be
95      * calculated or compatible with {@link Layout#getLineBottom(int)}. This can be
96      * {@code java.lang.Float.NaN} when no value is specified.
97      */
98     private final float mInsertionMarkerBottom;
99 
100     /**
101      * Container of rectangular position of characters, keyed with character index in a unit of
102      * Java chars, in the local coordinates that will be transformed with the transformation matrix
103      * when rendered on the screen.
104      */
105     private final SparseRectFArray mCharacterBoundsArray;
106 
107     /**
108      * Container of rectangular position of Editor in the local coordinates that will be transformed
109      * with the transformation matrix when rendered on the screen.
110      * @see {@link EditorBoundsInfo}.
111      */
112     private final EditorBoundsInfo mEditorBoundsInfo;
113 
114     /**
115      * Transformation matrix that is applied to any positional information of this class to
116      * transform local coordinates into screen coordinates.
117      */
118     @NonNull
119     private final float[] mMatrixValues;
120 
121     /**
122      * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
123      * insertion marker or character bounds have at least one visible region.
124      */
125     public static final int FLAG_HAS_VISIBLE_REGION = 0x01;
126 
127     /**
128      * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
129      * insertion marker or character bounds have at least one invisible (clipped) region.
130      */
131     public static final int FLAG_HAS_INVISIBLE_REGION = 0x02;
132 
133     /**
134      * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
135      * insertion marker or character bounds is placed at right-to-left (RTL) character.
136      */
137     public static final int FLAG_IS_RTL = 0x04;
138 
CursorAnchorInfo(final Parcel source)139     public CursorAnchorInfo(final Parcel source) {
140         mHashCode = source.readInt();
141         mSelectionStart = source.readInt();
142         mSelectionEnd = source.readInt();
143         mComposingTextStart = source.readInt();
144         mComposingText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
145         mInsertionMarkerFlags = source.readInt();
146         mInsertionMarkerHorizontal = source.readFloat();
147         mInsertionMarkerTop = source.readFloat();
148         mInsertionMarkerBaseline = source.readFloat();
149         mInsertionMarkerBottom = source.readFloat();
150         mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader(), android.view.inputmethod.SparseRectFArray.class);
151         mEditorBoundsInfo = source.readTypedObject(EditorBoundsInfo.CREATOR);
152         mMatrixValues = source.createFloatArray();
153     }
154 
155     /**
156      * Used to package this object into a {@link Parcel}.
157      *
158      * @param dest The {@link Parcel} to be written.
159      * @param flags The flags used for parceling.
160      */
161     @Override
writeToParcel(Parcel dest, int flags)162     public void writeToParcel(Parcel dest, int flags) {
163         dest.writeInt(mHashCode);
164         dest.writeInt(mSelectionStart);
165         dest.writeInt(mSelectionEnd);
166         dest.writeInt(mComposingTextStart);
167         TextUtils.writeToParcel(mComposingText, dest, flags);
168         dest.writeInt(mInsertionMarkerFlags);
169         dest.writeFloat(mInsertionMarkerHorizontal);
170         dest.writeFloat(mInsertionMarkerTop);
171         dest.writeFloat(mInsertionMarkerBaseline);
172         dest.writeFloat(mInsertionMarkerBottom);
173         dest.writeParcelable(mCharacterBoundsArray, flags);
174         dest.writeTypedObject(mEditorBoundsInfo, flags);
175         dest.writeFloatArray(mMatrixValues);
176     }
177 
178     @Override
hashCode()179     public int hashCode(){
180         return mHashCode;
181     }
182 
183     /**
184      * Compares two float values. Returns {@code true} if {@code a} and {@code b} are
185      * {@link Float#NaN} at the same time.
186      */
areSameFloatImpl(final float a, final float b)187     private static boolean areSameFloatImpl(final float a, final float b) {
188         if (Float.isNaN(a) && Float.isNaN(b)) {
189             return true;
190         }
191         return a == b;
192     }
193 
194     @Override
equals(@ullable Object obj)195     public boolean equals(@Nullable Object obj){
196         if (obj == null) {
197             return false;
198         }
199         if (this == obj) {
200             return true;
201         }
202         if (!(obj instanceof CursorAnchorInfo)) {
203             return false;
204         }
205         final CursorAnchorInfo that = (CursorAnchorInfo) obj;
206         if (hashCode() != that.hashCode()) {
207             return false;
208         }
209 
210         // Check fields that are not covered by hashCode() first.
211 
212         if (mSelectionStart != that.mSelectionStart || mSelectionEnd != that.mSelectionEnd) {
213             return false;
214         }
215 
216         if (mInsertionMarkerFlags != that.mInsertionMarkerFlags
217                 || !areSameFloatImpl(mInsertionMarkerHorizontal, that.mInsertionMarkerHorizontal)
218                 || !areSameFloatImpl(mInsertionMarkerTop, that.mInsertionMarkerTop)
219                 || !areSameFloatImpl(mInsertionMarkerBaseline, that.mInsertionMarkerBaseline)
220                 || !areSameFloatImpl(mInsertionMarkerBottom, that.mInsertionMarkerBottom)) {
221             return false;
222         }
223 
224         if (!Objects.equals(mCharacterBoundsArray, that.mCharacterBoundsArray)) {
225             return false;
226         }
227 
228         if (!Objects.equals(mEditorBoundsInfo, that.mEditorBoundsInfo)) {
229             return false;
230         }
231 
232         // Following fields are (partially) covered by hashCode().
233 
234         if (mComposingTextStart != that.mComposingTextStart
235                 || !Objects.equals(mComposingText, that.mComposingText)) {
236             return false;
237         }
238 
239         // We do not use Arrays.equals(float[], float[]) to keep the previous behavior regarding
240         // NaN, 0.0f, and -0.0f.
241         if (mMatrixValues.length != that.mMatrixValues.length) {
242             return false;
243         }
244         for (int i = 0; i < mMatrixValues.length; ++i) {
245             if (mMatrixValues[i] != that.mMatrixValues[i]) {
246                 return false;
247             }
248         }
249         return true;
250     }
251 
252     @Override
toString()253     public String toString() {
254         return "CursorAnchorInfo{mHashCode=" + mHashCode
255                 + " mSelection=" + mSelectionStart + "," + mSelectionEnd
256                 + " mComposingTextStart=" + mComposingTextStart
257                 + " mComposingText=" + Objects.toString(mComposingText)
258                 + " mInsertionMarkerFlags=" + mInsertionMarkerFlags
259                 + " mInsertionMarkerHorizontal=" + mInsertionMarkerHorizontal
260                 + " mInsertionMarkerTop=" + mInsertionMarkerTop
261                 + " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline
262                 + " mInsertionMarkerBottom=" + mInsertionMarkerBottom
263                 + " mCharacterBoundsArray=" + Objects.toString(mCharacterBoundsArray)
264                 + " mEditorBoundsInfo=" + mEditorBoundsInfo
265                 + " mMatrix=" + Arrays.toString(mMatrixValues)
266                 + "}";
267     }
268 
269     /**
270      * Builder for {@link CursorAnchorInfo}. This class is not designed to be thread-safe.
271      */
272     public static final class Builder {
273         private int mSelectionStart = -1;
274         private int mSelectionEnd = -1;
275         private int mComposingTextStart = -1;
276         private CharSequence mComposingText = null;
277         private float mInsertionMarkerHorizontal = Float.NaN;
278         private float mInsertionMarkerTop = Float.NaN;
279         private float mInsertionMarkerBaseline = Float.NaN;
280         private float mInsertionMarkerBottom = Float.NaN;
281         private int mInsertionMarkerFlags = 0;
282         private SparseRectFArrayBuilder mCharacterBoundsArrayBuilder = null;
283         private EditorBoundsInfo mEditorBoundsInfo = null;
284         private float[] mMatrixValues = null;
285         private boolean mMatrixInitialized = false;
286 
287         /**
288          * Sets the text range of the selection. Calling this can be skipped if there is no
289          * selection.
290          */
setSelectionRange(final int newStart, final int newEnd)291         public Builder setSelectionRange(final int newStart, final int newEnd) {
292             mSelectionStart = newStart;
293             mSelectionEnd = newEnd;
294             return this;
295         }
296 
297         /**
298          * Sets the text range of the composing text. Calling this can be skipped if there is
299          * no composing text.
300          * @param composingTextStart index where the composing text starts.
301          * @param composingText the entire composing text.
302          */
setComposingText(final int composingTextStart, final CharSequence composingText)303         public Builder setComposingText(final int composingTextStart,
304             final CharSequence composingText) {
305             mComposingTextStart = composingTextStart;
306             if (composingText == null) {
307                 mComposingText = null;
308             } else {
309                 // Make a snapshot of the given char sequence.
310                 mComposingText = new SpannedString(composingText);
311             }
312             return this;
313         }
314 
315         /**
316          * Sets the location of the text insertion point (zero width cursor) as a rectangle in
317          * local coordinates. Calling this can be skipped when there is no text insertion point;
318          * however if there is an insertion point, editors must call this method.
319          * @param horizontalPosition horizontal position of the insertion marker, in the local
320          * coordinates that will be transformed with the transformation matrix when rendered on the
321          * screen. This should be calculated or compatible with
322          * {@link Layout#getPrimaryHorizontal(int)}.
323          * @param lineTop vertical position of the insertion marker, in the local coordinates that
324          * will be transformed with the transformation matrix when rendered on the screen. This
325          * should be calculated or compatible with {@link Layout#getLineTop(int)}.
326          * @param lineBaseline vertical position of the insertion marker, in the local coordinates
327          * that will be transformed with the transformation matrix when rendered on the screen. This
328          * should be calculated or compatible with {@link Layout#getLineBaseline(int)}.
329          * @param lineBottom vertical position of the insertion marker, in the local coordinates
330          * that will be transformed with the transformation matrix when rendered on the screen. This
331          * should be calculated or compatible with {@link Layout#getLineBottom(int)}.
332          * @param flags flags of the insertion marker. See {@link #FLAG_HAS_VISIBLE_REGION} for
333          * example.
334          */
setInsertionMarkerLocation(final float horizontalPosition, final float lineTop, final float lineBaseline, final float lineBottom, final int flags)335         public Builder setInsertionMarkerLocation(final float horizontalPosition,
336                 final float lineTop, final float lineBaseline, final float lineBottom,
337                 final int flags){
338             mInsertionMarkerHorizontal = horizontalPosition;
339             mInsertionMarkerTop = lineTop;
340             mInsertionMarkerBaseline = lineBaseline;
341             mInsertionMarkerBottom = lineBottom;
342             mInsertionMarkerFlags = flags;
343             return this;
344         }
345 
346         /**
347          * Adds the bounding box of the character specified with the index.
348          *
349          * @param index index of the character in Java chars units. Must be specified in
350          * ascending order across successive calls.
351          * @param left x coordinate of the left edge of the character in local coordinates.
352          * @param top y coordinate of the top edge of the character in local coordinates.
353          * @param right x coordinate of the right edge of the character in local coordinates.
354          * @param bottom y coordinate of the bottom edge of the character in local coordinates.
355          * @param flags flags for this character bounds. See {@link #FLAG_HAS_VISIBLE_REGION},
356          * {@link #FLAG_HAS_INVISIBLE_REGION} and {@link #FLAG_IS_RTL}. These flags must be
357          * specified when necessary.
358          * @throws IllegalArgumentException If the index is a negative value, or not greater than
359          * all of the previously called indices.
360          */
addCharacterBounds(final int index, final float left, final float top, final float right, final float bottom, final int flags)361         public Builder addCharacterBounds(final int index, final float left, final float top,
362                 final float right, final float bottom, final int flags) {
363             if (index < 0) {
364                 throw new IllegalArgumentException("index must not be a negative integer.");
365             }
366             if (mCharacterBoundsArrayBuilder == null) {
367                 mCharacterBoundsArrayBuilder = new SparseRectFArrayBuilder();
368             }
369             mCharacterBoundsArrayBuilder.append(index, left, top, right, bottom, flags);
370             return this;
371         }
372 
373         /**
374          * Sets the current editor related bounds.
375          *
376          * @param bounds {@link EditorBoundsInfo} in local coordinates.
377          */
378         @NonNull
setEditorBoundsInfo(@ullable EditorBoundsInfo bounds)379         public Builder setEditorBoundsInfo(@Nullable EditorBoundsInfo bounds) {
380             mEditorBoundsInfo = bounds;
381             return this;
382         }
383 
384         /**
385          * Sets the matrix that transforms local coordinates into screen coordinates.
386          * @param matrix transformation matrix from local coordinates into screen coordinates. null
387          * is interpreted as an identity matrix.
388          */
setMatrix(final Matrix matrix)389         public Builder setMatrix(final Matrix matrix) {
390             if (mMatrixValues == null) {
391                 mMatrixValues = new float[9];
392             }
393             (matrix != null ? matrix : Matrix.IDENTITY_MATRIX).getValues(mMatrixValues);
394             mMatrixInitialized = true;
395             return this;
396         }
397 
398         /**
399          * @return {@link CursorAnchorInfo} using parameters in this {@link Builder}.
400          * @throws IllegalArgumentException if one or more positional parameters are specified but
401          * the coordinate transformation matrix is not provided via {@link #setMatrix(Matrix)}.
402          */
build()403         public CursorAnchorInfo build() {
404             if (!mMatrixInitialized) {
405                 // Coordinate transformation matrix is mandatory when at least one positional
406                 // parameter is specified.
407                 final boolean hasCharacterBounds = (mCharacterBoundsArrayBuilder != null
408                         && !mCharacterBoundsArrayBuilder.isEmpty());
409                 if (hasCharacterBounds
410                         || !Float.isNaN(mInsertionMarkerHorizontal)
411                         || !Float.isNaN(mInsertionMarkerTop)
412                         || !Float.isNaN(mInsertionMarkerBaseline)
413                         || !Float.isNaN(mInsertionMarkerBottom)) {
414                     throw new IllegalArgumentException("Coordinate transformation matrix is " +
415                             "required when positional parameters are specified.");
416                 }
417             }
418             return CursorAnchorInfo.create(this);
419         }
420 
421         /**
422          * Resets the internal state so that this instance can be reused to build another
423          * instance of {@link CursorAnchorInfo}.
424          */
reset()425         public void reset() {
426             mSelectionStart = -1;
427             mSelectionEnd = -1;
428             mComposingTextStart = -1;
429             mComposingText = null;
430             mInsertionMarkerFlags = 0;
431             mInsertionMarkerHorizontal = Float.NaN;
432             mInsertionMarkerTop = Float.NaN;
433             mInsertionMarkerBaseline = Float.NaN;
434             mInsertionMarkerBottom = Float.NaN;
435             mMatrixInitialized = false;
436             if (mCharacterBoundsArrayBuilder != null) {
437                 mCharacterBoundsArrayBuilder.reset();
438             }
439             mEditorBoundsInfo = null;
440         }
441     }
442 
create(Builder builder)443     private static CursorAnchorInfo create(Builder builder) {
444         final SparseRectFArray characterBoundsArray =
445                 builder.mCharacterBoundsArrayBuilder != null
446                         ? builder.mCharacterBoundsArrayBuilder.build()
447                         : null;
448         final float[] matrixValues = new float[9];
449         if (builder.mMatrixInitialized) {
450             System.arraycopy(builder.mMatrixValues, 0, matrixValues, 0, 9);
451         } else {
452             Matrix.IDENTITY_MATRIX.getValues(matrixValues);
453         }
454 
455         return new CursorAnchorInfo(builder.mSelectionStart, builder.mSelectionEnd,
456                 builder.mComposingTextStart, builder.mComposingText, builder.mInsertionMarkerFlags,
457                 builder.mInsertionMarkerHorizontal, builder.mInsertionMarkerTop,
458                 builder.mInsertionMarkerBaseline, builder.mInsertionMarkerBottom,
459                 characterBoundsArray, builder.mEditorBoundsInfo, matrixValues);
460     }
461 
CursorAnchorInfo(int selectionStart, int selectionEnd, int composingTextStart, @Nullable CharSequence composingText, int insertionMarkerFlags, float insertionMarkerHorizontal, float insertionMarkerTop, float insertionMarkerBaseline, float insertionMarkerBottom, @Nullable SparseRectFArray characterBoundsArray, @Nullable EditorBoundsInfo editorBoundsInfo, @NonNull float[] matrixValues)462     private CursorAnchorInfo(int selectionStart, int selectionEnd, int composingTextStart,
463             @Nullable CharSequence composingText, int insertionMarkerFlags,
464             float insertionMarkerHorizontal, float insertionMarkerTop,
465             float insertionMarkerBaseline, float insertionMarkerBottom,
466             @Nullable SparseRectFArray characterBoundsArray,
467             @Nullable EditorBoundsInfo editorBoundsInfo,
468             @NonNull float[] matrixValues) {
469         mSelectionStart = selectionStart;
470         mSelectionEnd = selectionEnd;
471         mComposingTextStart = composingTextStart;
472         mComposingText = composingText;
473         mInsertionMarkerFlags = insertionMarkerFlags;
474         mInsertionMarkerHorizontal = insertionMarkerHorizontal;
475         mInsertionMarkerTop = insertionMarkerTop;
476         mInsertionMarkerBaseline = insertionMarkerBaseline;
477         mInsertionMarkerBottom = insertionMarkerBottom;
478         mCharacterBoundsArray = characterBoundsArray;
479         mEditorBoundsInfo = editorBoundsInfo;
480         mMatrixValues = matrixValues;
481 
482         // To keep hash function simple, we only use some complex objects for hash.
483         int hashCode = Objects.hashCode(mComposingText);
484         hashCode *= 31;
485         hashCode += Arrays.hashCode(mMatrixValues);
486         mHashCode = hashCode;
487     }
488 
489     /**
490      * Creates a new instance of {@link CursorAnchorInfo} by applying {@code parentMatrix} to
491      * the coordinate transformation matrix.
492      *
493      * @param original     {@link CursorAnchorInfo} to be cloned from.
494      * @param parentMatrix {@link Matrix} to be applied to {@code original.getMatrix()}
495      * @return A new instance of {@link CursorAnchorInfo} whose {@link CursorAnchorInfo#getMatrix()}
496      *         returns {@code parentMatrix * original.getMatrix()}.
497      * @hide
498      */
createForAdditionalParentMatrix(CursorAnchorInfo original, @NonNull Matrix parentMatrix)499     public static CursorAnchorInfo createForAdditionalParentMatrix(CursorAnchorInfo original,
500             @NonNull Matrix parentMatrix) {
501         return new CursorAnchorInfo(original.mSelectionStart, original.mSelectionEnd,
502                 original.mComposingTextStart, original.mComposingText,
503                 original.mInsertionMarkerFlags, original.mInsertionMarkerHorizontal,
504                 original.mInsertionMarkerTop, original.mInsertionMarkerBaseline,
505                 original.mInsertionMarkerBottom, original.mCharacterBoundsArray,
506                 original.mEditorBoundsInfo, computeMatrixValues(parentMatrix, original));
507     }
508 
509     /**
510      * Returns a float array that represents {@link Matrix} elements for
511      * {@code parentMatrix * info.getMatrix()}.
512      *
513      * @param parentMatrix {@link Matrix} to be multiplied.
514      * @param info         {@link CursorAnchorInfo} to provide {@link Matrix} to be multiplied.
515      * @return {@code parentMatrix * info.getMatrix()}.
516      */
computeMatrixValues(@onNull Matrix parentMatrix, @NonNull CursorAnchorInfo info)517     private static float[] computeMatrixValues(@NonNull Matrix parentMatrix,
518             @NonNull CursorAnchorInfo info) {
519         if (parentMatrix.isIdentity()) {
520             return info.mMatrixValues;
521         }
522 
523         final Matrix newMatrix = new Matrix();
524         newMatrix.setValues(info.mMatrixValues);
525         newMatrix.postConcat(parentMatrix);
526 
527         final float[] matrixValues = new float[9];
528         newMatrix.getValues(matrixValues);
529         return matrixValues;
530     }
531 
532     /**
533      * Returns the index where the selection starts.
534      * @return {@code -1} if there is no selection.
535      */
getSelectionStart()536     public int getSelectionStart() {
537         return mSelectionStart;
538     }
539 
540     /**
541      * Returns the index where the selection ends.
542      * @return {@code -1} if there is no selection.
543      */
getSelectionEnd()544     public int getSelectionEnd() {
545         return mSelectionEnd;
546     }
547 
548     /**
549      * Returns the index where the composing text starts.
550      * @return {@code -1} if there is no composing text.
551      */
getComposingTextStart()552     public int getComposingTextStart() {
553         return mComposingTextStart;
554     }
555 
556     /**
557      * Returns the entire composing text.
558      * @return {@code null} if there is no composition.
559      */
getComposingText()560     public CharSequence getComposingText() {
561         return mComposingText;
562     }
563 
564     /**
565      * Returns the flag of the insertion marker.
566      * @return the flag of the insertion marker. {@code 0} if no flag is specified.
567      */
getInsertionMarkerFlags()568     public int getInsertionMarkerFlags() {
569         return mInsertionMarkerFlags;
570     }
571 
572     /**
573      * Returns the horizontal start of the insertion marker, in the local coordinates that will
574      * be transformed with {@link #getMatrix()} when rendered on the screen.
575      * @return x coordinate that is compatible with {@link Layout#getPrimaryHorizontal(int)}.
576      * Pay special care to RTL/LTR handling.
577      * {@code java.lang.Float.NaN} if not specified.
578      * @see Layout#getPrimaryHorizontal(int)
579      */
getInsertionMarkerHorizontal()580     public float getInsertionMarkerHorizontal() {
581         return mInsertionMarkerHorizontal;
582     }
583 
584     /**
585      * Returns the vertical top position of the insertion marker, in the local coordinates that
586      * will be transformed with {@link #getMatrix()} when rendered on the screen.
587      * @return y coordinate that is compatible with {@link Layout#getLineTop(int)}.
588      * {@code java.lang.Float.NaN} if not specified.
589      */
getInsertionMarkerTop()590     public float getInsertionMarkerTop() {
591         return mInsertionMarkerTop;
592     }
593 
594     /**
595      * Returns the vertical baseline position of the insertion marker, in the local coordinates
596      * that will be transformed with {@link #getMatrix()} when rendered on the screen.
597      * @return y coordinate that is compatible with {@link Layout#getLineBaseline(int)}.
598      * {@code java.lang.Float.NaN} if not specified.
599      */
getInsertionMarkerBaseline()600     public float getInsertionMarkerBaseline() {
601         return mInsertionMarkerBaseline;
602     }
603 
604     /**
605      * Returns the vertical bottom position of the insertion marker, in the local coordinates
606      * that will be transformed with {@link #getMatrix()} when rendered on the screen.
607      * @return y coordinate that is compatible with {@link Layout#getLineBottom(int)}.
608      * {@code java.lang.Float.NaN} if not specified.
609      */
getInsertionMarkerBottom()610     public float getInsertionMarkerBottom() {
611         return mInsertionMarkerBottom;
612     }
613 
614     /**
615      * Returns a new instance of {@link RectF} that indicates the location of the character
616      * specified with the index.
617      * @param index index of the character in a Java chars.
618      * @return the character bounds in local coordinates as a new instance of {@link RectF}.
619      */
getCharacterBounds(final int index)620     public RectF getCharacterBounds(final int index) {
621         if (mCharacterBoundsArray == null) {
622             return null;
623         }
624         return mCharacterBoundsArray.get(index);
625     }
626 
627     /**
628      * Returns the flags associated with the character bounds specified with the index.
629      * @param index index of the character in a Java chars.
630      * @return {@code 0} if no flag is specified.
631      */
getCharacterBoundsFlags(final int index)632     public int getCharacterBoundsFlags(final int index) {
633         if (mCharacterBoundsArray == null) {
634             return 0;
635         }
636         return mCharacterBoundsArray.getFlags(index, 0);
637     }
638 
639     /**
640      * Returns {@link EditorBoundsInfo} for the current editor, or {@code null} if IME is not
641      * subscribed with {@link InputConnection#CURSOR_UPDATE_FILTER_EDITOR_BOUNDS}
642      * or {@link InputConnection#CURSOR_UPDATE_MONITOR}.
643      */
644     @Nullable
getEditorBoundsInfo()645     public EditorBoundsInfo getEditorBoundsInfo() {
646         return mEditorBoundsInfo;
647     }
648 
649     /**
650      * Returns a new instance of {@link android.graphics.Matrix} that indicates the transformation
651      * matrix that is to be applied other positional data in this class.
652      * @return a new instance (copy) of the transformation matrix.
653      */
getMatrix()654     public Matrix getMatrix() {
655         final Matrix matrix = new Matrix();
656         matrix.setValues(mMatrixValues);
657         return matrix;
658     }
659 
660     /**
661      * Used to make this class parcelable.
662      */
663     public static final @android.annotation.NonNull Parcelable.Creator<CursorAnchorInfo> CREATOR
664             = new Parcelable.Creator<CursorAnchorInfo>() {
665         @Override
666         public CursorAnchorInfo createFromParcel(Parcel source) {
667             return new CursorAnchorInfo(source);
668         }
669 
670         @Override
671         public CursorAnchorInfo[] newArray(int size) {
672             return new CursorAnchorInfo[size];
673         }
674     };
675 
676     @Override
describeContents()677     public int describeContents() {
678         return 0;
679     }
680 }
681