Add an @hide way to adjust CursorAnchorInfo for another Matrix

This is a preparation to support CursorAnchorInfo API in ActivityView.

In order to enable the system to automatically adjust CursorAnchorInfo
object between the IME client and IME, there needs to be an @hide
method to create a new CursorAnchorInfo instance with applying an
additional coordinate transformation Matrix, which is what this CL
does.

This CL also cleans up CursorAnchorInfoTest.java as most of test there
were already moved to CTS [1].

Anyway, this is a mechanical change.  There should be no behavior
change in existing methods in CursorAnchorInfo.

 [1]: Ib758bddff34b4722b39c94e7ad4e8f8da2bb8b92
      c4ef1d6ec22dc2af2ed339fd4e78075903783be0

Bug: 115693908
Test: atest CtsInputMethodTestCases:CursorAnchorInfoTest
Test: atest FrameworksCoreTests:CursorAnchorInfoTest
Change-Id: Ic7f9057623ffc61ec7a6121735dc39adecf4649d
This commit is contained in:
Yohei Yukawa
2019-04-25 18:49:16 -07:00
parent 090cb13600
commit e4410a135b
2 changed files with 102 additions and 450 deletions

View File

@@ -17,6 +17,7 @@
package android.view.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Parcel;
@@ -388,7 +389,7 @@ public final class CursorAnchorInfo implements Parcelable {
"required when positional parameters are specified.");
}
}
return new CursorAnchorInfo(this);
return CursorAnchorInfo.create(this);
}
/**
@@ -412,30 +413,90 @@ public final class CursorAnchorInfo implements Parcelable {
}
}
private CursorAnchorInfo(final Builder builder) {
mSelectionStart = builder.mSelectionStart;
mSelectionEnd = builder.mSelectionEnd;
mComposingTextStart = builder.mComposingTextStart;
mComposingText = builder.mComposingText;
mInsertionMarkerFlags = builder.mInsertionMarkerFlags;
mInsertionMarkerHorizontal = builder.mInsertionMarkerHorizontal;
mInsertionMarkerTop = builder.mInsertionMarkerTop;
mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline;
mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null ?
builder.mCharacterBoundsArrayBuilder.build() : null;
mMatrixValues = new float[9];
private static CursorAnchorInfo create(Builder builder) {
final SparseRectFArray characterBoundsArray =
builder.mCharacterBoundsArrayBuilder != null
? builder.mCharacterBoundsArrayBuilder.build()
: null;
final float[] matrixValues = new float[9];
if (builder.mMatrixInitialized) {
System.arraycopy(builder.mMatrixValues, 0, mMatrixValues, 0, 9);
System.arraycopy(builder.mMatrixValues, 0, matrixValues, 0, 9);
} else {
Matrix.IDENTITY_MATRIX.getValues(mMatrixValues);
Matrix.IDENTITY_MATRIX.getValues(matrixValues);
}
return new CursorAnchorInfo(builder.mSelectionStart, builder.mSelectionEnd,
builder.mComposingTextStart, builder.mComposingText, builder.mInsertionMarkerFlags,
builder.mInsertionMarkerHorizontal, builder.mInsertionMarkerTop,
builder.mInsertionMarkerBaseline, builder.mInsertionMarkerBottom,
characterBoundsArray, matrixValues);
}
private CursorAnchorInfo(int selectionStart, int selectionEnd, int composingTextStart,
@Nullable CharSequence composingText, int insertionMarkerFlags,
float insertionMarkerHorizontal, float insertionMarkerTop,
float insertionMarkerBaseline, float insertionMarkerBottom,
@Nullable SparseRectFArray characterBoundsArray, @NonNull float[] matrixValues) {
mSelectionStart = selectionStart;
mSelectionEnd = selectionEnd;
mComposingTextStart = composingTextStart;
mComposingText = composingText;
mInsertionMarkerFlags = insertionMarkerFlags;
mInsertionMarkerHorizontal = insertionMarkerHorizontal;
mInsertionMarkerTop = insertionMarkerTop;
mInsertionMarkerBaseline = insertionMarkerBaseline;
mInsertionMarkerBottom = insertionMarkerBottom;
mCharacterBoundsArray = characterBoundsArray;
mMatrixValues = matrixValues;
// To keep hash function simple, we only use some complex objects for hash.
int hash = Objects.hashCode(mComposingText);
hash *= 31;
hash += Arrays.hashCode(mMatrixValues);
mHashCode = hash;
int hashCode = Objects.hashCode(mComposingText);
hashCode *= 31;
hashCode += Arrays.hashCode(matrixValues);
mHashCode = hashCode;
}
/**
* Creates a new instance of {@link CursorAnchorInfo} by applying {@code parentMatrix} to
* the coordinate transformation matrix.
*
* @param original {@link CursorAnchorInfo} to be cloned from.
* @param parentMatrix {@link Matrix} to be applied to {@code original.getMatrix()}
* @return A new instance of {@link CursorAnchorInfo} whose {@link CursorAnchorInfo#getMatrix()}
* returns {@code parentMatrix * original.getMatrix()}.
* @hide
*/
public static CursorAnchorInfo createForAdditionalParentMatrix(CursorAnchorInfo original,
@NonNull Matrix parentMatrix) {
return new CursorAnchorInfo(original.mSelectionStart, original.mSelectionEnd,
original.mComposingTextStart, original.mComposingText,
original.mInsertionMarkerFlags, original.mInsertionMarkerHorizontal,
original.mInsertionMarkerTop, original.mInsertionMarkerBaseline,
original.mInsertionMarkerBottom, original.mCharacterBoundsArray,
computeMatrixValues(parentMatrix, original));
}
/**
* Returns a float array that represents {@link Matrix} elements for
* {@code parentMatrix * info.getMatrix()}.
*
* @param parentMatrix {@link Matrix} to be multiplied.
* @param info {@link CursorAnchorInfo} to provide {@link Matrix} to be multiplied.
* @return {@code parentMatrix * info.getMatrix()}.
*/
private static float[] computeMatrixValues(@NonNull Matrix parentMatrix,
@NonNull CursorAnchorInfo info) {
if (parentMatrix.isIdentity()) {
return info.mMatrixValues;
}
final Matrix newMatrix = new Matrix();
newMatrix.setValues(info.mMatrixValues);
newMatrix.postConcat(parentMatrix);
final float[] matrixValues = new float[9];
newMatrix.getValues(matrixValues);
return matrixValues;
}
/**

View File

@@ -16,19 +16,9 @@
package android.view.inputmethod;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Parcel;
import android.text.TextUtils;
import android.view.inputmethod.CursorAnchorInfo.Builder;
import androidx.test.filters.SmallTest;
@@ -37,437 +27,38 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Objects;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class CursorAnchorInfoTest {
private static final float EPSILON = 0.0000001f;
private static final RectF[] MANY_BOUNDS = new RectF[] {
new RectF(101.0f, 201.0f, 301.0f, 401.0f),
new RectF(102.0f, 202.0f, 302.0f, 402.0f),
new RectF(103.0f, 203.0f, 303.0f, 403.0f),
new RectF(104.0f, 204.0f, 304.0f, 404.0f),
new RectF(105.0f, 205.0f, 305.0f, 405.0f),
new RectF(106.0f, 206.0f, 306.0f, 406.0f),
new RectF(107.0f, 207.0f, 307.0f, 407.0f),
new RectF(108.0f, 208.0f, 308.0f, 408.0f),
new RectF(109.0f, 209.0f, 309.0f, 409.0f),
new RectF(110.0f, 210.0f, 310.0f, 410.0f),
new RectF(111.0f, 211.0f, 311.0f, 411.0f),
new RectF(112.0f, 212.0f, 312.0f, 412.0f),
new RectF(113.0f, 213.0f, 313.0f, 413.0f),
new RectF(114.0f, 214.0f, 314.0f, 414.0f),
new RectF(115.0f, 215.0f, 315.0f, 415.0f),
new RectF(116.0f, 216.0f, 316.0f, 416.0f),
new RectF(117.0f, 217.0f, 317.0f, 417.0f),
new RectF(118.0f, 218.0f, 318.0f, 418.0f),
new RectF(119.0f, 219.0f, 319.0f, 419.0f),
};
private static final int[] MANY_FLAGS_ARRAY = new int[] {
FLAG_HAS_INVISIBLE_REGION,
FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION,
FLAG_HAS_VISIBLE_REGION,
FLAG_HAS_VISIBLE_REGION,
FLAG_HAS_VISIBLE_REGION,
FLAG_HAS_VISIBLE_REGION,
FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
FLAG_HAS_VISIBLE_REGION,
FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
FLAG_HAS_VISIBLE_REGION,
FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
FLAG_HAS_VISIBLE_REGION,
FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
FLAG_HAS_VISIBLE_REGION,
FLAG_HAS_INVISIBLE_REGION,
FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
};
@Test
public void testBuilder() throws Exception {
final int SELECTION_START = 30;
final int SELECTION_END = 40;
final int COMPOSING_TEXT_START = 32;
final String COMPOSING_TEXT = "test";
final int INSERTION_MARKER_FLAGS =
FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
final float INSERTION_MARKER_HORIZONTAL = 10.5f;
final float INSERTION_MARKER_TOP = 100.1f;
final float INSERTION_MARKER_BASELINE = 110.4f;
final float INSERTION_MARKER_BOTOM = 111.0f;
Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
public void testCreateForAdditionalParentMatrix() {
final Matrix originalMatrix = new Matrix();
originalMatrix.setTranslate(10.0f, 20.0f);
final Builder builder = new Builder();
builder.setSelectionRange(SELECTION_START, SELECTION_END)
.setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT)
.setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS)
.setMatrix(TRANSFORM_MATRIX);
for (int i = 0; i < MANY_BOUNDS.length; i++) {
final RectF bounds = MANY_BOUNDS[i];
final int flags = MANY_FLAGS_ARRAY[i];
builder.addCharacterBounds(i, bounds.left, bounds.top, bounds.right, bounds.bottom,
flags);
}
builder.setMatrix(originalMatrix);
final CursorAnchorInfo info = builder.build();
assertEquals(SELECTION_START, info.getSelectionStart());
assertEquals(SELECTION_END, info.getSelectionEnd());
assertEquals(COMPOSING_TEXT_START, info.getComposingTextStart());
assertTrue(TextUtils.equals(COMPOSING_TEXT, info.getComposingText()));
assertEquals(INSERTION_MARKER_FLAGS, info.getInsertionMarkerFlags());
assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal(), EPSILON);
assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop(), EPSILON);
assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline(), EPSILON);
assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom(), EPSILON);
assertEquals(TRANSFORM_MATRIX, info.getMatrix());
for (int i = 0; i < MANY_BOUNDS.length; i++) {
final RectF expectedBounds = MANY_BOUNDS[i];
assertEquals(expectedBounds, info.getCharacterBounds(i));
}
assertNull(info.getCharacterBounds(-1));
assertNull(info.getCharacterBounds(MANY_BOUNDS.length + 1));
for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
final int expectedFlags = MANY_FLAGS_ARRAY[i];
assertEquals(expectedFlags, info.getCharacterBoundsFlags(i));
}
assertEquals(0, info.getCharacterBoundsFlags(-1));
assertEquals(0, info.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
final CursorAnchorInfo originalInstance = builder.build();
// Make sure that the builder can reproduce the same object.
final CursorAnchorInfo info2 = builder.build();
assertEquals(SELECTION_START, info2.getSelectionStart());
assertEquals(SELECTION_END, info2.getSelectionEnd());
assertEquals(COMPOSING_TEXT_START, info2.getComposingTextStart());
assertTrue(TextUtils.equals(COMPOSING_TEXT, info2.getComposingText()));
assertEquals(INSERTION_MARKER_FLAGS, info2.getInsertionMarkerFlags());
assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal(), EPSILON);
assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop(), EPSILON);
assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline(), EPSILON);
assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom(), EPSILON);
assertEquals(TRANSFORM_MATRIX, info2.getMatrix());
for (int i = 0; i < MANY_BOUNDS.length; i++) {
final RectF expectedBounds = MANY_BOUNDS[i];
assertEquals(expectedBounds, info2.getCharacterBounds(i));
}
assertNull(info2.getCharacterBounds(-1));
assertNull(info2.getCharacterBounds(MANY_BOUNDS.length + 1));
for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
final int expectedFlags = MANY_FLAGS_ARRAY[i];
assertEquals(expectedFlags, info2.getCharacterBoundsFlags(i));
}
assertEquals(0, info2.getCharacterBoundsFlags(-1));
assertEquals(0, info2.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
assertEquals(info, info2);
assertEquals(info.hashCode(), info2.hashCode());
assertEquals(originalMatrix, originalInstance.getMatrix());
// Make sure that object can be marshaled via {@link Parsel}.
final CursorAnchorInfo info3 = cloneViaParcel(info2);
assertEquals(SELECTION_START, info3.getSelectionStart());
assertEquals(SELECTION_END, info3.getSelectionEnd());
assertEquals(COMPOSING_TEXT_START, info3.getComposingTextStart());
assertTrue(TextUtils.equals(COMPOSING_TEXT, info3.getComposingText()));
assertEquals(INSERTION_MARKER_FLAGS, info3.getInsertionMarkerFlags());
assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal(), EPSILON);
assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop(), EPSILON);
assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline(), EPSILON);
assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom(), EPSILON);
assertEquals(TRANSFORM_MATRIX, info3.getMatrix());
for (int i = 0; i < MANY_BOUNDS.length; i++) {
final RectF expectedBounds = MANY_BOUNDS[i];
assertEquals(expectedBounds, info3.getCharacterBounds(i));
}
assertNull(info3.getCharacterBounds(-1));
assertNull(info3.getCharacterBounds(MANY_BOUNDS.length + 1));
for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
final int expectedFlags = MANY_FLAGS_ARRAY[i];
assertEquals(expectedFlags, info3.getCharacterBoundsFlags(i));
}
assertEquals(0, info3.getCharacterBoundsFlags(-1));
assertEquals(0, info3.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
assertEquals(info.hashCode(), info3.hashCode());
final Matrix additionalParentMatrix = new Matrix();
additionalParentMatrix.setTranslate(1.0f, 2.0f);
final Matrix newMatrix = new Matrix(originalMatrix);
newMatrix.postConcat(additionalParentMatrix);
builder.reset();
final CursorAnchorInfo uninitializedInfo = builder.build();
assertEquals(-1, uninitializedInfo.getSelectionStart());
assertEquals(-1, uninitializedInfo.getSelectionEnd());
assertEquals(-1, uninitializedInfo.getComposingTextStart());
assertNull(uninitializedInfo.getComposingText());
assertEquals(0, uninitializedInfo.getInsertionMarkerFlags());
assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal(), EPSILON);
assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop(), EPSILON);
assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline(), EPSILON);
assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom(), EPSILON);
assertEquals(Matrix.IDENTITY_MATRIX, uninitializedInfo.getMatrix());
}
builder.setMatrix(newMatrix);
// An instance created by the standard Builder class.
final CursorAnchorInfo newInstanceByBuilder = builder.build();
private static void assertNotEquals(final CursorAnchorInfo reference,
final CursorAnchorInfo actual) {
assertFalse(Objects.equals(reference, actual));
}
// An instance created by an @hide method.
final CursorAnchorInfo newInstanceByMethod =
CursorAnchorInfo.createForAdditionalParentMatrix(
originalInstance, additionalParentMatrix);
@Test
public void testEquality() throws Exception {
final Matrix MATRIX1 = new Matrix();
MATRIX1.setTranslate(10.0f, 20.0f);
final Matrix MATRIX2 = new Matrix();
MATRIX2.setTranslate(110.0f, 120.0f);
final Matrix NAN_MATRIX = new Matrix();
NAN_MATRIX.setValues(new float[]{
Float.NaN, Float.NaN, Float.NaN,
Float.NaN, Float.NaN, Float.NaN,
Float.NaN, Float.NaN, Float.NaN});
final int SELECTION_START1 = 2;
final int SELECTION_END1 = 7;
final String COMPOSING_TEXT1 = "0123456789";
final int COMPOSING_TEXT_START1 = 0;
final int INSERTION_MARKER_FLAGS1 = FLAG_HAS_VISIBLE_REGION;
final float INSERTION_MARKER_HORIZONTAL1 = 10.5f;
final float INSERTION_MARKER_TOP1 = 100.1f;
final float INSERTION_MARKER_BASELINE1 = 110.4f;
final float INSERTION_MARKER_BOTOM1 = 111.0f;
final int SELECTION_START2 = 4;
final int SELECTION_END2 = 8;
final String COMPOSING_TEXT2 = "9876543210";
final int COMPOSING_TEXT_START2 = 3;
final int INSERTION_MARKER_FLAGS2 =
FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
final float INSERTION_MARKER_HORIZONTAL2 = 14.5f;
final float INSERTION_MARKER_TOP2 = 200.1f;
final float INSERTION_MARKER_BASELINE2 = 210.4f;
final float INSERTION_MARKER_BOTOM2 = 211.0f;
// Default instance should be equal.
assertEquals(new Builder().build(), new Builder().build());
assertEquals(
new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build());
assertNotEquals(
new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
new Builder().setSelectionRange(SELECTION_START1, SELECTION_END2).build());
assertNotEquals(
new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
new Builder().setSelectionRange(SELECTION_START2, SELECTION_END1).build());
assertNotEquals(
new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
new Builder().setSelectionRange(SELECTION_START2, SELECTION_END2).build());
assertEquals(
new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build());
assertNotEquals(
new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
new Builder().setComposingText(COMPOSING_TEXT_START2, COMPOSING_TEXT1).build());
assertNotEquals(
new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT2).build());
assertNotEquals(
new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
new Builder().setComposingText(COMPOSING_TEXT_START2, COMPOSING_TEXT2).build());
// For insertion marker locations, {@link Float#NaN} is treated as if it was a number.
assertEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
Float.NaN, Float.NaN, Float.NaN, Float.NaN,
INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
Float.NaN, Float.NaN, Float.NaN, Float.NaN,
INSERTION_MARKER_FLAGS1).build());
// Check Matrix.
assertEquals(
new Builder().setMatrix(MATRIX1).build(),
new Builder().setMatrix(MATRIX1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).build(),
new Builder().setMatrix(MATRIX2).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).build(),
new Builder().setMatrix(NAN_MATRIX).build());
// Unlike insertion marker locations, {@link Float#NaN} in the matrix is treated as just a
// NaN as usual (NaN == NaN -> false).
assertNotEquals(
new Builder().setMatrix(NAN_MATRIX).build(),
new Builder().setMatrix(NAN_MATRIX).build());
assertEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
Float.NaN, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP2,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE2, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM2,
INSERTION_MARKER_FLAGS1).build());
assertNotEquals(
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS1).build(),
new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
INSERTION_MARKER_FLAGS2).build());
}
@Test
public void testMatrixIsCopied() throws Exception {
final Matrix MATRIX1 = new Matrix();
MATRIX1.setTranslate(10.0f, 20.0f);
final Matrix MATRIX2 = new Matrix();
MATRIX2.setTranslate(110.0f, 120.0f);
final Matrix MATRIX3 = new Matrix();
MATRIX3.setTranslate(210.0f, 220.0f);
final Matrix matrix = new Matrix();
final Builder builder = new Builder();
matrix.set(MATRIX1);
builder.setMatrix(matrix);
matrix.postRotate(90.0f);
final CursorAnchorInfo firstInstance = builder.build();
assertEquals(MATRIX1, firstInstance.getMatrix());
matrix.set(MATRIX2);
builder.setMatrix(matrix);
final CursorAnchorInfo secondInstance = builder.build();
assertEquals(MATRIX1, firstInstance.getMatrix());
assertEquals(MATRIX2, secondInstance.getMatrix());
matrix.set(MATRIX3);
assertEquals(MATRIX1, firstInstance.getMatrix());
assertEquals(MATRIX2, secondInstance.getMatrix());
}
@Test
public void testMatrixIsRequired() throws Exception {
final int SELECTION_START = 30;
final int SELECTION_END = 40;
final int COMPOSING_TEXT_START = 32;
final String COMPOSING_TEXT = "test";
final int INSERTION_MARKER_FLAGS = FLAG_HAS_VISIBLE_REGION;
final float INSERTION_MARKER_HORIZONTAL = 10.5f;
final float INSERTION_MARKER_TOP = 100.1f;
final float INSERTION_MARKER_BASELINE = 110.4f;
final float INSERTION_MARKER_BOTOM = 111.0f;
Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
final Builder builder = new Builder();
// Check twice to make sure if Builder#reset() works as expected.
for (int repeatCount = 0; repeatCount < 2; ++repeatCount) {
builder.setSelectionRange(SELECTION_START, SELECTION_END)
.setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT);
try {
// Should succeed as coordinate transformation matrix is not required if no
// positional information is specified.
builder.build();
} catch (IllegalArgumentException ex) {
assertTrue(false);
}
builder.setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS);
try {
// Coordinate transformation matrix is required if no positional information is
// specified.
builder.build();
assertTrue(false);
} catch (IllegalArgumentException ex) {
}
builder.setMatrix(TRANSFORM_MATRIX);
try {
// Should succeed as coordinate transformation matrix is required.
builder.build();
} catch (IllegalArgumentException ex) {
assertTrue(false);
}
builder.reset();
}
}
@Test
public void testBuilderAddCharacterBounds() throws Exception {
// A negative index should be rejected.
try {
new Builder().addCharacterBounds(-1, 0.0f, 0.0f, 0.0f, 0.0f, FLAG_HAS_VISIBLE_REGION);
assertTrue(false);
} catch (IllegalArgumentException ex) {
}
}
private static CursorAnchorInfo cloneViaParcel(final CursorAnchorInfo src) {
Parcel parcel = null;
try {
parcel = Parcel.obtain();
src.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
return new CursorAnchorInfo(parcel);
} finally {
if (parcel != null) {
parcel.recycle();
}
}
assertEquals(newMatrix, newInstanceByBuilder.getMatrix());
assertEquals(newMatrix, newInstanceByMethod.getMatrix());
assertEquals(newInstanceByBuilder.hashCode(), newInstanceByMethod.hashCode());
}
}