Chromium Code Reviews| Index: content/public/android/javatests/src/org/chromium/content/browser/input/CursorAnchorInfoControllerTest.java |
| diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/CursorAnchorInfoControllerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/CursorAnchorInfoControllerTest.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..bc20180ad67b374093fa77dd8b897d454a176d9d |
| --- /dev/null |
| +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/CursorAnchorInfoControllerTest.java |
| @@ -0,0 +1,607 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package org.chromium.content.browser.input; |
| + |
| +import android.annotation.TargetApi; |
| +import android.graphics.Matrix; |
| +import android.graphics.RectF; |
| +import android.os.Build; |
| +import android.test.InstrumentationTestCase; |
| +import android.test.suitebuilder.annotation.SmallTest; |
| +import android.text.TextUtils; |
| +import android.view.View; |
| +import android.view.inputmethod.CursorAnchorInfo; |
| +import android.view.inputmethod.InputConnection; |
| + |
| +import org.chromium.base.test.util.Feature; |
| +import org.chromium.base.test.util.MinAndroidSdkLevel; |
| +import org.chromium.content.browser.RenderCoordinates; |
| +import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper; |
| + |
| +/** |
| + * Test for {@link CursorAnchorInfoController}. |
| + */ |
| +@MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP) |
| +@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
| +public class CursorAnchorInfoControllerTest extends InstrumentationTestCase { |
| + private static RenderCoordinates createRenderCoordinates(float deviceScaleFactor, |
| + float contentOffsetYPix) { |
| + RenderCoordinates renderCoordinates = new RenderCoordinates(); |
| + renderCoordinates.setFrameInfoForTest(deviceScaleFactor, contentOffsetYPix); |
| + return renderCoordinates; |
| + } |
| + |
| + private static final class TestViewDelegate implements CursorAnchorInfoController.ViewDelegate { |
| + public int mLocationX; |
|
Ted C
2016/03/07 21:31:45
no leading m prefix for public fields.
kinaba
2016/03/10 06:24:12
Done.
|
| + public int mLocationY; |
| + @Override |
| + public void getLocationOnScreen(View view, int[] location) { |
| + location[0] = mLocationX; |
| + location[1] = mLocationY; |
| + } |
| + } |
| + |
| + private static final class TestComposingTextDelegate |
| + implements CursorAnchorInfoController.ComposingTextDelegate { |
| + private String mText; |
| + private int mSelectionStart = -1; |
| + private int mSelectionEnd = -1; |
| + private int mComposingTextStart = -1; |
| + private int mComposingTextEnd = -1; |
| + |
| + @Override |
| + public CharSequence getText() { |
| + return mText; |
| + } |
| + @Override |
| + public int getSelectionStart() { |
| + return mSelectionStart; |
| + } |
| + @Override |
| + public int getSelectionEnd() { |
| + return mSelectionEnd; |
| + } |
| + @Override |
| + public int getComposingTextStart() { |
| + return mComposingTextStart; |
| + } |
| + @Override |
| + public int getComposingTextEnd() { |
| + return mComposingTextEnd; |
| + } |
| + |
| + public void updateTextAndSelection(CursorAnchorInfoController controller, |
| + String text, int compositionStart, int compositionEnd, int selectionStart, |
| + int selectionEnd) { |
| + mText = text; |
| + mSelectionStart = selectionStart; |
| + mSelectionEnd = selectionEnd; |
| + mComposingTextStart = compositionStart; |
| + mComposingTextEnd = compositionEnd; |
| + controller.invalidateLastCursorAnchorInfo(); |
| + } |
| + |
| + public void clearTextAndSelection(CursorAnchorInfoController controller) { |
| + updateTextAndSelection(controller, null, -1, -1, -1, -1); |
| + } |
| + } |
| + |
| + private void assertScaleAndTranslate(float expectedScale, float expectedTranslateX, |
| + float expectedTranslateY, CursorAnchorInfo actual) { |
| + Matrix expectedMatrix = new Matrix(); |
| + expectedMatrix.setScale(expectedScale, expectedScale); |
| + expectedMatrix.postTranslate(expectedTranslateX, expectedTranslateY); |
| + float[] expectedMatrixValues = new float[12]; |
| + expectedMatrix.getValues(expectedMatrixValues); |
|
Ted C
2016/03/07 21:31:45
The documentation states it copies 9, is that inco
kinaba
2016/03/10 06:24:12
It's 9.
|
| + float[] actualMatrixValues = new float[12]; |
| + actual.getMatrix().getValues(actualMatrixValues); |
| + for (int i = 0; i < expectedMatrixValues.length; ++i) { |
| + assertEquals(expectedMatrixValues[i], actualMatrixValues[i]); |
|
Ted C
2016/03/07 21:31:45
why not use Matrix.equals instead of comparing flo
kinaba
2016/03/10 06:24:12
Done.
|
| + } |
| + } |
| + |
| + private void assertHasInsertionMarker(int expectedFlags, float expectedHorizontal, |
| + float expectedTop, float expectedBaseline, float expectedBottom, |
| + CursorAnchorInfo actual) { |
| + assertEquals(expectedFlags, actual.getInsertionMarkerFlags()); |
| + assertEquals(expectedHorizontal, actual.getInsertionMarkerHorizontal()); |
| + assertEquals(expectedTop, actual.getInsertionMarkerTop()); |
| + assertEquals(expectedBaseline, actual.getInsertionMarkerBaseline()); |
| + assertEquals(expectedBottom, actual.getInsertionMarkerBottom()); |
| + } |
| + |
| + private void assertHasNoInsertionMarker(CursorAnchorInfo actual) { |
| + assertEquals(0, actual.getInsertionMarkerFlags()); |
| + assertTrue(Float.isNaN(actual.getInsertionMarkerHorizontal())); |
| + assertTrue(Float.isNaN(actual.getInsertionMarkerTop())); |
| + assertTrue(Float.isNaN(actual.getInsertionMarkerBaseline())); |
| + assertTrue(Float.isNaN(actual.getInsertionMarkerBottom())); |
| + } |
| + |
| + private void assertComposingText(CharSequence expectedComposingText, |
| + int expectedComposingTextStart, CursorAnchorInfo actual) { |
| + assertTrue(TextUtils.equals(expectedComposingText, actual.getComposingText())); |
| + assertEquals(expectedComposingTextStart, actual.getComposingTextStart()); |
| + } |
| + |
| + private void assertSelection(int expecteSelectionStart, int expecteSelectionEnd, |
| + CursorAnchorInfo actual) { |
| + assertEquals(expecteSelectionStart, actual.getSelectionStart()); |
| + assertEquals(expecteSelectionEnd, actual.getSelectionEnd()); |
| + } |
| + |
| + private void assertEquals(RectF expectedRect, RectF actualRect) { |
|
Ted C
2016/03/07 21:31:45
same question as Matrix, why not use the .equals
kinaba
2016/03/10 06:24:12
Done.
|
| + if (expectedRect == null) { |
| + assertNull(actualRect); |
| + return; |
| + } |
| + assertNotNull(actualRect); |
| + assertEquals(actualRect.left, expectedRect.left); |
| + assertEquals(actualRect.top, expectedRect.top); |
| + assertEquals(actualRect.right, expectedRect.right); |
| + assertEquals(actualRect.bottom, expectedRect.bottom); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Input-Text-IME"}) |
| + public void testFocusedNodeChanged() { |
| + TestInputMethodManagerWrapper immw = new TestInputMethodManagerWrapper(null); |
| + TestViewDelegate viewDelegate = new TestViewDelegate(); |
| + TestComposingTextDelegate composingTextDelegate = new TestComposingTextDelegate(); |
| + CursorAnchorInfoController controller = CursorAnchorInfoController.createForTest( |
| + immw, composingTextDelegate, viewDelegate); |
| + View view = null; |
| + |
| + viewDelegate.mLocationX = 0; |
| + viewDelegate.mLocationY = 0; |
| + |
| + assertFalse( |
| + "IC#onRequestCursorUpdates() must be rejected if the focused node is not editable.", |
| + controller.onRequestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR, view)); |
| + |
| + // Make sure that the focused node is considered to be non-editable by default. |
| + controller.setCompositionCharacterBounds(new float[]{0.0f, 1.0f, 2.0f, 3.0f}); |
|
Ted C
2016/03/07 21:31:45
pretty sure there is supposed to be a space after
kinaba
2016/03/10 06:24:12
Done.
|
| + composingTextDelegate.updateTextAndSelection(controller, "0", 0, 1, 0, 1); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(0, immw.getUpdateCursorAnchorInfoCounter()); |
| + |
| + controller.focusedNodeChanged(false); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + |
| + // Make sure that the controller does not crash even if it is called while the focused node |
| + // is not editable. |
| + controller.setCompositionCharacterBounds(new float[]{30.0f, 1.0f, 32.0f, 3.0f}); |
| + composingTextDelegate.updateTextAndSelection(controller, "1", 0, 1, 0, 1); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 100.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(0, immw.getUpdateCursorAnchorInfoCounter()); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Input-Text-IME"}) |
| + public void testImmediateMode() { |
| + TestInputMethodManagerWrapper immw = new TestInputMethodManagerWrapper(null); |
| + TestViewDelegate viewDelegate = new TestViewDelegate(); |
| + TestComposingTextDelegate composingTextDelegate = new TestComposingTextDelegate(); |
| + CursorAnchorInfoController controller = CursorAnchorInfoController.createForTest( |
| + immw, composingTextDelegate, viewDelegate); |
| + View view = null; |
| + viewDelegate.mLocationX = 0; |
| + viewDelegate.mLocationY = 0; |
| + |
| + controller.focusedNodeChanged(true); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + |
| + // Make sure that #updateCursorAnchorInfo() is not be called until the matrix info becomes |
| + // available with #onUpdateFrameInfo(). |
| + assertTrue(controller.onRequestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE, |
| + view)); |
| + controller.setCompositionCharacterBounds(new float[]{0.0f, 1.0f, 2.0f, 3.0f}); |
| + composingTextDelegate.updateTextAndSelection(controller, "0", 0, 1, 0, 1); |
| + assertEquals(0, immw.getUpdateCursorAnchorInfoCounter()); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(1, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(1.0f, 0.0f, 0.0f, immw.getLastCursorAnchorInfo()); |
| + assertHasInsertionMarker(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, 2.0f, 0.0f, 3.0f, |
| + 3.0f, immw.getLastCursorAnchorInfo()); |
| + assertEquals(new RectF(0.0f, 1.0f, 2.0f, 3.0f), |
| + immw.getLastCursorAnchorInfo().getCharacterBounds(0)); |
| + assertEquals(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, |
| + immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(0)); |
| + assertComposingText("0", 0, immw.getLastCursorAnchorInfo()); |
| + assertSelection(0, 1, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // Make sure that 2nd call of #onUpdateFrameInfo() is ignored. |
| + controller.onUpdateFrameInfo(createRenderCoordinates(2.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(1, immw.getUpdateCursorAnchorInfoCounter()); |
| + |
| + // Make sure that #onUpdateFrameInfo() is immediately called because the matrix info is |
| + // already available. |
| + assertTrue(controller.onRequestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE, |
| + view)); |
| + assertEquals(2, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(2.0f, 0.0f, 0.0f, immw.getLastCursorAnchorInfo()); |
| + assertHasInsertionMarker(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, 2.0f, 0.0f, 3.0f, |
| + 3.0f, immw.getLastCursorAnchorInfo()); |
| + assertEquals(new RectF(0.0f, 1.0f, 2.0f, 3.0f), |
| + immw.getLastCursorAnchorInfo().getCharacterBounds(0)); |
| + assertEquals(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, |
| + immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(0)); |
| + assertComposingText("0", 0, immw.getLastCursorAnchorInfo()); |
| + assertSelection(0, 1, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // Make sure that CURSOR_UPDATE_IMMEDIATE and CURSOR_UPDATE_MONITOR can be specified at |
| + // the same time. |
| + assertTrue(controller.onRequestCursorUpdates( |
| + InputConnection.CURSOR_UPDATE_IMMEDIATE | InputConnection.CURSOR_UPDATE_MONITOR, |
| + view)); |
| + assertEquals(3, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(2.0f, 0.0f, 0.0f, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(4, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(1.0f, 0.0f, 0.0f, immw.getLastCursorAnchorInfo()); |
| + assertHasInsertionMarker(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, 2.0f, 0.0f, 3.0f, |
| + 3.0f, immw.getLastCursorAnchorInfo()); |
| + assertEquals(new RectF(0.0f, 1.0f, 2.0f, 3.0f), |
| + immw.getLastCursorAnchorInfo().getCharacterBounds(0)); |
| + assertEquals(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, |
| + immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(0)); |
| + assertComposingText("0", 0, immw.getLastCursorAnchorInfo()); |
| + assertSelection(0, 1, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // Make sure that CURSOR_UPDATE_IMMEDIATE is cleared if the focused node becomes |
| + // non-editable. |
| + controller.focusedNodeChanged(false); |
| + controller.focusedNodeChanged(true); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + assertTrue(controller.onRequestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE, |
| + view)); |
| + controller.focusedNodeChanged(false); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 100.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(4, immw.getUpdateCursorAnchorInfoCounter()); |
| + |
| + // Make sure that CURSOR_UPDATE_IMMEDIATE can be enabled again. |
| + controller.focusedNodeChanged(true); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + assertTrue(controller.onRequestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE, |
| + view)); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(5, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(1.0f, 0.0f, 0.0f, immw.getLastCursorAnchorInfo()); |
| + assertHasInsertionMarker(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, 2.0f, 0.0f, 3.0f, |
| + 3.0f, immw.getLastCursorAnchorInfo()); |
| + assertEquals(null, immw.getLastCursorAnchorInfo().getCharacterBounds(0)); |
| + assertEquals(0, immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(0)); |
| + assertComposingText(null, -1, immw.getLastCursorAnchorInfo()); |
| + assertSelection(-1, -1, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Input-Text-IME"}) |
| + public void testMonitorMode() { |
| + TestInputMethodManagerWrapper immw = new TestInputMethodManagerWrapper(null); |
| + TestViewDelegate viewDelegate = new TestViewDelegate(); |
| + TestComposingTextDelegate composingTextDelegate = new TestComposingTextDelegate(); |
| + CursorAnchorInfoController controller = CursorAnchorInfoController.createForTest( |
| + immw, composingTextDelegate, viewDelegate); |
| + View view = null; |
| + viewDelegate.mLocationX = 0; |
| + viewDelegate.mLocationY = 0; |
| + |
| + controller.focusedNodeChanged(true); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + |
| + // Make sure that #updateCursorAnchorInfo() is not be called until the matrix info becomes |
| + // available with #onUpdateFrameInfo(). |
| + assertTrue(controller.onRequestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR, view)); |
| + controller.setCompositionCharacterBounds(new float[]{0.0f, 1.0f, 2.0f, 3.0f}); |
| + composingTextDelegate.updateTextAndSelection(controller, "0", 0, 1, 0, 1); |
| + assertEquals(0, immw.getUpdateCursorAnchorInfoCounter()); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(1, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(1.0f, 0.0f, 0.0f, immw.getLastCursorAnchorInfo()); |
| + assertHasInsertionMarker(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, 2.0f, 0.0f, 3.0f, |
| + 3.0f, immw.getLastCursorAnchorInfo()); |
| + assertEquals(new RectF(0.0f, 1.0f, 2.0f, 3.0f), |
| + immw.getLastCursorAnchorInfo().getCharacterBounds(0)); |
| + assertEquals(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, |
| + immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(0)); |
| + assertComposingText("0", 0, immw.getLastCursorAnchorInfo()); |
| + assertSelection(0, 1, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // Make sure that #updateCursorAnchorInfo() is not be called if any coordinate parameter is |
| + // changed for better performance. |
| + controller.setCompositionCharacterBounds(new float[]{0.0f, 1.0f, 2.0f, 3.0f}); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(1, immw.getUpdateCursorAnchorInfoCounter()); |
| + |
| + // Make sure that #updateCursorAnchorInfo() is called if #setCompositionCharacterBounds() |
| + // is called with a different parameter. |
| + controller.setCompositionCharacterBounds(new float[]{30.0f, 1.0f, 32.0f, 3.0f}); |
| + assertEquals(1, immw.getUpdateCursorAnchorInfoCounter()); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(2, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(1.0f, 0.0f, 0.0f, immw.getLastCursorAnchorInfo()); |
| + assertHasInsertionMarker(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, 2.0f, 0.0f, 3.0f, |
| + 3.0f, immw.getLastCursorAnchorInfo()); |
| + assertEquals(new RectF(30.0f, 1.0f, 32.0f, 3.0f), |
| + immw.getLastCursorAnchorInfo().getCharacterBounds(0)); |
| + assertEquals(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, |
| + immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(0)); |
| + assertComposingText("0", 0, immw.getLastCursorAnchorInfo()); |
| + assertSelection(0, 1, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // Make sure that #updateCursorAnchorInfo() is called if #updateTextAndSelection() |
| + // is called with a different parameter. |
| + composingTextDelegate.updateTextAndSelection(controller, "1", 0, 1, 0, 1); |
| + assertEquals(2, immw.getUpdateCursorAnchorInfoCounter()); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(3, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(1.0f, 0.0f, 0.0f, immw.getLastCursorAnchorInfo()); |
| + assertHasInsertionMarker(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, 2.0f, 0.0f, 3.0f, |
| + 3.0f, immw.getLastCursorAnchorInfo()); |
| + assertEquals(new RectF(30.0f, 1.0f, 32.0f, 3.0f), |
| + immw.getLastCursorAnchorInfo().getCharacterBounds(0)); |
| + assertEquals(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, |
| + immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(0)); |
| + assertComposingText("1", 0, immw.getLastCursorAnchorInfo()); |
| + assertSelection(0, 1, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // Make sure that #updateCursorAnchorInfo() is called if #onUpdateFrameInfo() |
| + // is called with a different parameter. |
| + controller.onUpdateFrameInfo(createRenderCoordinates(2.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(4, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(2.0f, 0.0f, 0.0f, immw.getLastCursorAnchorInfo()); |
| + assertHasInsertionMarker(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, 2.0f, 0.0f, 3.0f, |
| + 3.0f, immw.getLastCursorAnchorInfo()); |
| + assertEquals(new RectF(30.0f, 1.0f, 32.0f, 3.0f), |
| + immw.getLastCursorAnchorInfo().getCharacterBounds(0)); |
| + assertEquals(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, |
| + immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(0)); |
| + assertComposingText("1", 0, immw.getLastCursorAnchorInfo()); |
| + assertSelection(0, 1, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // Make sure that #updateCursorAnchorInfo() is called when the view origin is changed. |
| + viewDelegate.mLocationX = 7; |
| + viewDelegate.mLocationY = 9; |
| + controller.onUpdateFrameInfo(createRenderCoordinates(2.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(5, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(2.0f, 7.0f, 9.0f, immw.getLastCursorAnchorInfo()); |
| + assertHasInsertionMarker(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, 2.0f, 0.0f, 3.0f, |
| + 3.0f, immw.getLastCursorAnchorInfo()); |
| + assertEquals(new RectF(30.0f, 1.0f, 32.0f, 3.0f), |
| + immw.getLastCursorAnchorInfo().getCharacterBounds(0)); |
| + assertEquals(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, |
| + immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(0)); |
| + assertComposingText("1", 0, immw.getLastCursorAnchorInfo()); |
| + assertSelection(0, 1, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // Make sure that CURSOR_UPDATE_IMMEDIATE is cleared if the focused node becomes |
| + // non-editable. |
| + controller.focusedNodeChanged(false); |
| + controller.focusedNodeChanged(true); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + assertTrue(controller.onRequestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR, view)); |
| + controller.focusedNodeChanged(false); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + controller.setCompositionCharacterBounds(new float[]{0.0f, 1.0f, 2.0f, 3.0f}); |
| + composingTextDelegate.updateTextAndSelection(controller, "0", 0, 1, 0, 1); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(5, immw.getUpdateCursorAnchorInfoCounter()); |
| + |
| + // Make sure that CURSOR_UPDATE_MONITOR can be enabled again. |
| + controller.focusedNodeChanged(true); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + assertTrue(controller.onRequestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR, view)); |
| + controller.setCompositionCharacterBounds(new float[]{0.0f, 1.0f, 2.0f, 3.0f}); |
| + composingTextDelegate.updateTextAndSelection(controller, "0", 0, 1, 0, 1); |
| + assertEquals(5, immw.getUpdateCursorAnchorInfoCounter()); |
| + viewDelegate.mLocationX = 0; |
| + viewDelegate.mLocationY = 0; |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + true, true, 2.0f, 0.0f, 3.0f, view); |
| + assertEquals(6, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(1.0f, 0.0f, 0.0f, immw.getLastCursorAnchorInfo()); |
| + assertHasInsertionMarker(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, 2.0f, 0.0f, 3.0f, |
| + 3.0f, immw.getLastCursorAnchorInfo()); |
| + assertEquals(new RectF(0.0f, 1.0f, 2.0f, 3.0f), |
| + immw.getLastCursorAnchorInfo().getCharacterBounds(0)); |
| + assertEquals(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, |
| + immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(0)); |
| + assertComposingText("0", 0, immw.getLastCursorAnchorInfo()); |
| + assertSelection(0, 1, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Input-Text-IME"}) |
| + public void testSetCompositionCharacterBounds() { |
| + TestInputMethodManagerWrapper immw = new TestInputMethodManagerWrapper(null); |
| + TestViewDelegate viewDelegate = new TestViewDelegate(); |
| + TestComposingTextDelegate composingTextDelegate = new TestComposingTextDelegate(); |
| + CursorAnchorInfoController controller = CursorAnchorInfoController.createForTest( |
| + immw, composingTextDelegate, viewDelegate); |
| + View view = null; |
| + |
| + viewDelegate.mLocationX = 0; |
| + viewDelegate.mLocationY = 0; |
| + |
| + controller.focusedNodeChanged(true); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + assertTrue(controller.onRequestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR, view)); |
| + |
| + composingTextDelegate.updateTextAndSelection(controller, "01234", 1, 3, 1, 1); |
| + controller.setCompositionCharacterBounds(new float[]{0.0f, 1.0f, 2.0f, 3.0f, |
| + 4.0f, 1.1f, 6.0f, 2.9f}); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + false, false, Float.NaN, Float.NaN, Float.NaN, view); |
| + assertEquals(1, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertEquals(null, immw.getLastCursorAnchorInfo().getCharacterBounds(0)); |
| + assertEquals(0, immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(0)); |
| + assertEquals(new RectF(0.0f, 1.0f, 2.0f, 3.0f), |
| + immw.getLastCursorAnchorInfo().getCharacterBounds(1)); |
| + assertEquals(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, |
| + immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(1)); |
| + assertEquals(new RectF(4.0f, 1.1f, 6.0f, 2.9f), |
| + immw.getLastCursorAnchorInfo().getCharacterBounds(2)); |
| + assertEquals(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, |
| + immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(2)); |
| + assertEquals(null, immw.getLastCursorAnchorInfo().getCharacterBounds(3)); |
| + assertEquals(0, immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(3)); |
| + assertComposingText("12", 1, immw.getLastCursorAnchorInfo()); |
| + assertSelection(1, 1, immw.getLastCursorAnchorInfo()); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Input-Text-IME"}) |
| + public void testUpdateTextAndSelection() { |
| + TestInputMethodManagerWrapper immw = new TestInputMethodManagerWrapper(null); |
| + TestViewDelegate viewDelegate = new TestViewDelegate(); |
| + TestComposingTextDelegate composingTextDelegate = new TestComposingTextDelegate(); |
| + CursorAnchorInfoController controller = CursorAnchorInfoController.createForTest( |
| + immw, composingTextDelegate, viewDelegate); |
| + View view = null; |
| + |
| + viewDelegate.mLocationX = 0; |
| + viewDelegate.mLocationY = 0; |
| + |
| + controller.focusedNodeChanged(true); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + assertTrue(controller.onRequestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR, view)); |
| + |
| + composingTextDelegate.updateTextAndSelection(controller, "01234", 3, 3, 1, 1); |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + false, false, Float.NaN, Float.NaN, Float.NaN, view); |
| + assertEquals(1, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertEquals(null, immw.getLastCursorAnchorInfo().getCharacterBounds(0)); |
| + assertEquals(0, immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(0)); |
| + assertEquals(null, immw.getLastCursorAnchorInfo().getCharacterBounds(1)); |
| + assertEquals(0, immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(1)); |
| + assertEquals(null, immw.getLastCursorAnchorInfo().getCharacterBounds(2)); |
| + assertEquals(0, immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(2)); |
| + assertEquals(null, immw.getLastCursorAnchorInfo().getCharacterBounds(3)); |
| + assertEquals(0, immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(3)); |
| + assertEquals(null, immw.getLastCursorAnchorInfo().getCharacterBounds(4)); |
| + assertEquals(0, immw.getLastCursorAnchorInfo().getCharacterBoundsFlags(4)); |
| + assertComposingText("", 3, immw.getLastCursorAnchorInfo()); |
| + assertSelection(1, 1, immw.getLastCursorAnchorInfo()); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Input-Text-IME"}) |
| + public void testInsertionMarker() { |
| + TestInputMethodManagerWrapper immw = new TestInputMethodManagerWrapper(null); |
| + TestViewDelegate viewDelegate = new TestViewDelegate(); |
| + TestComposingTextDelegate composingTextDelegate = new TestComposingTextDelegate(); |
| + CursorAnchorInfoController controller = CursorAnchorInfoController.createForTest( |
| + immw, composingTextDelegate, viewDelegate); |
| + View view = null; |
| + |
| + controller.focusedNodeChanged(true); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + assertTrue(controller.onRequestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR, view)); |
| + |
| + // Test no insertion marker. |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + false, false, Float.NaN, Float.NaN, Float.NaN, view); |
| + assertEquals(1, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertHasNoInsertionMarker(immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // Test a visible insertion marker. |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + true, true, 10.0f, 23.0f, 29.0f, view); |
| + assertEquals(2, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertHasInsertionMarker(CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION, |
| + 10.0f, 23.0f, 29.0f, 29.0f, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // Test a invisible insertion marker. |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + true, false, 10.0f, 23.0f, 29.0f, view); |
| + assertEquals(3, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertHasInsertionMarker(CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION, |
| + 10.0f, 23.0f, 29.0f, 29.0f, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Input-Text-IME"}) |
| + public void testMatrix() { |
| + TestInputMethodManagerWrapper immw = new TestInputMethodManagerWrapper(null); |
| + TestViewDelegate viewDelegate = new TestViewDelegate(); |
| + TestComposingTextDelegate composingTextDelegate = new TestComposingTextDelegate(); |
| + CursorAnchorInfoController controller = CursorAnchorInfoController.createForTest( |
| + immw, composingTextDelegate, viewDelegate); |
| + View view = null; |
| + |
| + controller.focusedNodeChanged(true); |
| + composingTextDelegate.clearTextAndSelection(controller); |
| + assertTrue(controller.onRequestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR, view)); |
| + |
| + // Test no transformation |
| + viewDelegate.mLocationX = 0; |
| + viewDelegate.mLocationY = 0; |
| + controller.onUpdateFrameInfo(createRenderCoordinates(1.0f, 0.0f), |
| + false, false, Float.NaN, Float.NaN, Float.NaN, view); |
| + assertEquals(1, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(1.0f, 0.0f, 0.0f, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // device scale factor == 2.0 |
| + viewDelegate.mLocationX = 0; |
| + viewDelegate.mLocationY = 0; |
| + controller.onUpdateFrameInfo(createRenderCoordinates(2.0f, 0.0f), |
| + false, false, Float.NaN, Float.NaN, Float.NaN, view); |
| + assertEquals(2, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(2.0f, 0.0f, 0.0f, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // device scale factor == 2.0 |
| + // view origin == (10, 141) |
| + viewDelegate.mLocationX = 10; |
| + viewDelegate.mLocationY = 141; |
| + controller.onUpdateFrameInfo(createRenderCoordinates(2.0f, 0.0f), |
| + false, false, Float.NaN, Float.NaN, Float.NaN, view); |
| + assertEquals(3, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(2.0f, 10.0f, 141.0f, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + |
| + // device scale factor == 2.0 |
| + // content offset Y = 40.0f |
| + // view origin == (10, 141) |
| + viewDelegate.mLocationX = 10; |
| + viewDelegate.mLocationY = 141; |
| + controller.onUpdateFrameInfo(createRenderCoordinates(2.0f, 40.0f), |
| + false, false, Float.NaN, Float.NaN, Float.NaN, view); |
| + assertEquals(4, immw.getUpdateCursorAnchorInfoCounter()); |
| + assertScaleAndTranslate(2.0f, 10.0f, 181.0f, immw.getLastCursorAnchorInfo()); |
| + immw.clearLastCursorAnchorInfo(); |
| + } |
| +} |