Loading core/java/android/widget/Editor.java +29 −32 Original line number Diff line number Diff line Loading @@ -1827,7 +1827,7 @@ public class Editor { return; } Layout layout = getActiveLayout(); Layout layout = mTextView.getLayout(); final int offset = mTextView.getSelectionStart(); final int line = layout.getLineForOffset(offset); final int top = layout.getLineTop(line); Loading Loading @@ -2192,27 +2192,35 @@ public class Editor { mCursorDrawable[cursorIndex] = mTextView.getContext().getDrawable( mTextView.mCursorDrawableRes); final Drawable drawable = mCursorDrawable[cursorIndex]; final int left = clampCursorHorizontalPosition(drawable, horizontal); final int left = clampHorizontalPosition(drawable, horizontal); final int width = drawable.getIntrinsicWidth(); drawable.setBounds(left, top - mTempRect.top, left + width, bottom + mTempRect.bottom); } /** * Return clamped position for the cursor. If the cursor is within the boundaries of the view, * then it is offset with the left padding of the cursor drawable. If the cursor is at * Return clamped position for the drawable. If the drawable is within the boundaries of the * view, then it is offset with the left padding of the cursor drawable. If the drawable is at * the beginning or the end of the text then its drawable edge is aligned with left or right of * the view boundary. * the view boundary. If the drawable is null, horizontal parameter is aligned to left or right * of the view. * * @param drawable Cursor drawable. * @param horizontal Horizontal position for the cursor. * @return The clamped horizontal position for the cursor. * @param drawable Drawable. Can be null. * @param horizontal Horizontal position for the drawable. * @return The clamped horizontal position for the drawable. */ private final int clampCursorHorizontalPosition(final Drawable drawable, float horizontal) { private int clampHorizontalPosition(@Nullable final Drawable drawable, float horizontal) { horizontal = Math.max(0.5f, horizontal - 0.5f); if (mTempRect == null) mTempRect = new Rect(); int drawableWidth = 0; if (drawable != null) { drawable.getPadding(mTempRect); drawableWidth = drawable.getIntrinsicWidth(); } else { mTempRect.setEmpty(); } int scrollX = mTextView.getScrollX(); float horizontalDiff = horizontal - scrollX; int viewClippedWidth = mTextView.getWidth() - mTextView.getCompoundPaddingLeft() Loading @@ -2221,9 +2229,11 @@ public class Editor { final int left; if (horizontalDiff >= (viewClippedWidth - 1f)) { // at the rightmost position final int cursorWidth = drawable.getIntrinsicWidth(); left = viewClippedWidth + scrollX - (cursorWidth - mTempRect.right); } else if (Math.abs(horizontalDiff) <= 1f) { left = viewClippedWidth + scrollX - (drawableWidth - mTempRect.right); } else if (Math.abs(horizontalDiff) <= 1f || (TextUtils.isEmpty(mTextView.getText()) && (TextView.VERY_WIDE - scrollX) <= (viewClippedWidth + 1f) && horizontal <= 1f)) { // at the leftmost position left = scrollX - mTempRect.left; } else { Loading Loading @@ -3772,10 +3782,10 @@ public class Editor { + mHandleHeight); } else { // We have a single cursor. Layout layout = getActiveLayout(); Layout layout = mTextView.getLayout(); int line = layout.getLineForOffset(mTextView.getSelectionStart()); float primaryHorizontal = layout.getPrimaryHorizontal(mTextView.getSelectionStart()); float primaryHorizontal = clampHorizontalPosition(null, layout.getPrimaryHorizontal(mTextView.getSelectionStart())); mSelectionBounds.set( primaryHorizontal, layout.getLineTop(line), Loading Loading @@ -4152,7 +4162,7 @@ public class Editor { prepareCursorControllers(); return; } layout = getActiveLayout(); layout = mTextView.getLayout(); boolean offsetChanged = offset != mPreviousOffset; if (offsetChanged || parentScrolled) { Loading Loading @@ -4322,19 +4332,6 @@ public class Editor { public void onDetached() {} } /** * Returns the active layout (hint or text layout). Note that the text layout can be null. */ private Layout getActiveLayout() { Layout layout = mTextView.getLayout(); Layout hintLayout = mTextView.getHintLayout(); if (TextUtils.isEmpty(layout.getText()) && hintLayout != null && !TextUtils.isEmpty(hintLayout.getText())) { layout = hintLayout; } return layout; } private class InsertionHandleView extends HandleView { private static final int DELAY_BEFORE_HANDLE_FADES_OUT = 4000; private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; // seconds Loading Loading @@ -4431,7 +4428,7 @@ public class Editor { final Drawable drawable = mCursorCount > 0 ? mCursorDrawable[0] : null; if (drawable != null) { final float horizontal = layout.getPrimaryHorizontal(offset); return clampCursorHorizontalPosition(drawable, horizontal) + mTempRect.left; return clampHorizontalPosition(drawable, horizontal) + mTempRect.left; } return super.getCursorHorizontalPosition(layout, offset); } Loading core/java/android/widget/TextView.java +2 −2 Original line number Diff line number Diff line Loading @@ -285,8 +285,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private static final RectF TEMP_RECTF = new RectF(); // XXX should be much larger private static final int VERY_WIDE = 1024*1024; /** @hide */ static final int VERY_WIDE = 1024 * 1024; // XXX should be much larger private static final int ANIMATED_SCROLL_GAP = 250; private static final InputFilter[] NO_FILTERS = new InputFilter[0]; Loading core/tests/coretests/AndroidManifest.xml +1 −1 Original line number Diff line number Diff line Loading @@ -111,7 +111,7 @@ <!-- accessibility test permissions --> <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" /> <application android:theme="@style/Theme"> <application android:theme="@style/Theme" android:supportsRtl="true"> <uses-library android:name="android.test.runner" /> <uses-library android:name="org.apache.http.legacy" android:required="false" /> <meta-data Loading core/tests/coretests/src/android/widget/EditorCursorTest.java +139 −71 Original line number Diff line number Diff line Loading @@ -16,16 +16,34 @@ package android.widget; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.view.Choreographer; import android.view.ViewGroup; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnLeft; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnRight; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.isEmptyString; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; public class EditorCursorTest extends ActivityInstrumentationTestCase2<TextViewActivity> { private final static String LTR_STRING = "aaaaaaaaaaaaaaaaaaaaaa"; private final static String LTR_HINT = "hint"; private final static String RTL_STRING = "مرحبا الروبوت مرحبا الروبوت مرحبا الروبوت"; private final static String RTL_HINT = "الروبوت"; private final static int CURSOR_BLINK_MS = 500; private EditText mEditText; private final String RTL_STRING = "مرحبا الروبوت مرحبا الروبوت مرحبا الروبوت"; public EditorCursorTest() { super(TextViewActivity.class); Loading Loading @@ -55,110 +73,160 @@ public class EditorCursorTest extends ActivityInstrumentationTestCase2<TextViewA @Override public void run() { getActivity().setContentView(layout); mEditText.requestFocus(); } }); getInstrumentation().waitForIdleSync(); onView(sameInstance(mEditText)).perform(click()); } @SmallTest public void testCursorIsInViewBoundariesWhenOnRightForLtr() throws Exception { public void testCursorIsInViewBoundariesWhenOnRightForLtr() { // Asserts that when an EditText has LTR text, and cursor is at the end (right), // cursor is drawn to the right edge of the view getActivity().runOnUiThread(new Runnable() { @Override public void run() { mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa"); int length = mEditText.getText().length(); mEditText.setSelection(length, length); setEditTextText(LTR_STRING, LTR_STRING.length()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } }); getInstrumentation().waitForIdleSync(); Editor editor = mEditText.getEditorForTesting(); Drawable drawable = editor.getCursorDrawable()[0]; Rect drawableBounds = drawable.getBounds(); Rect drawablePadding = new Rect(); drawable.getPadding(drawablePadding); @SmallTest public void testCursorIsInViewBoundariesWhenOnLeftForLtr() { // Asserts that when an EditText has LTR text, and cursor is at the beginning, // cursor is drawn to the left edge of the view setEditTextText(LTR_STRING, 0); // right edge of the view including the scroll int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight() - mEditText.getCompoundPaddingLeft() + +mEditText.getScrollX(); int diff = drawableBounds.right - drawablePadding.right - maxRight; assertTrue(diff >= 0 && diff <= 1); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } @SmallTest public void testCursorIsInViewBoundariesWhenOnLeftForLtr() throws Exception { // Asserts that when an EditText has LTR text, and cursor is at the beginning, public void testCursorIsInViewBoundariesWhenOnRightForRtl() { // Asserts that when an EditText has RTL text, and cursor is at the end, // cursor is drawn to the left edge of the view setEditTextText(RTL_STRING, 0); getActivity().runOnUiThread(new Runnable() { @Override public void run() { mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa"); mEditText.setSelection(0, 0); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } }); getInstrumentation().waitForIdleSync(); Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0]; Rect drawableBounds = drawable.getBounds(); Rect drawablePadding = new Rect(); drawable.getPadding(drawablePadding); @SmallTest public void testCursorIsInViewBoundariesWhenOnLeftForRtl() { // Asserts that when an EditText has RTL text, and cursor is at the beginning, // cursor is drawn to the right edge of the view setEditTextText(RTL_STRING, RTL_STRING.length()); int diff = drawableBounds.left + drawablePadding.left; assertTrue(diff >= 0 && diff <= 1); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } /* Tests for cursor positioning with hint */ @SmallTest public void testCursorIsInViewBoundariesWhenOnRightForRtl() throws Exception { // Asserts that when an EditText has RTL text, and cursor is at the end, // cursor is drawn to the left edge of the view public void testCursorIsOnLeft_withFirstStrongLtrAlgorithm() { setEditTextHint(null, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); assertThat(mEditText.getHint(), nullValue()); getActivity().runOnUiThread(new Runnable() { @Override public void run() { mEditText.setText(RTL_STRING); mEditText.setSelection(0, 0); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } }); getInstrumentation().waitForIdleSync(); Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0]; Rect drawableBounds = drawable.getBounds(); Rect drawablePadding = new Rect(); drawable.getPadding(drawablePadding); @SmallTest public void testCursorIsOnRight_withFirstStrongRtlAlgorithm() { setEditTextHint(null, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); assertThat(mEditText.getHint(), nullValue()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight() - mEditText.getCompoundPaddingLeft() + mEditText.getScrollX(); setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); int diff = drawableBounds.right - drawablePadding.right - maxRight; assertTrue(diff >= 0 && diff <= 1); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } @SmallTest public void testCursorIsInViewBoundariesWhenOnLeftForRtl() throws Exception { // Asserts that when an EditText has RTL text, and cursor is at the beginning, // cursor is drawn to the right edge of the view public void testCursorIsOnLeft_withLtrAlgorithm() { setEditTextHint(null, TextView.TEXT_DIRECTION_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); assertThat(mEditText.getHint(), nullValue()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } @SmallTest public void testCursorIsOnRight_withRtlAlgorithm() { setEditTextHint(null, TextView.TEXT_DIRECTION_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); assertThat(mEditText.getHint(), nullValue()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } private void setEditTextProperties(final String text, final String hint, final Integer textDirection, final Integer selection) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { mEditText.setText(RTL_STRING); int length = mEditText.getText().length(); mEditText.setSelection(length, length); if (textDirection != null) mEditText.setTextDirection(textDirection); if (text != null) mEditText.setText(text); if (hint != null) mEditText.setHint(hint); if (selection != null) mEditText.setSelection(selection); } }); getInstrumentation().waitForIdleSync(); Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0]; Rect drawableBounds = drawable.getBounds(); Rect drawablePadding = new Rect(); drawable.getPadding(drawablePadding); // wait for cursor to be drawn. updateCursorPositions function is called during draw() and // only when cursor is visible during blink. final CountDownLatch latch = new CountDownLatch(1); mEditText.postOnAnimationDelayed(new Runnable() { @Override public void run() { latch.countDown(); } }, CURSOR_BLINK_MS); try { assertThat("Problem while waiting for the cursor to blink", latch.await(10, TimeUnit.SECONDS), equalTo(true)); } catch (Exception e) { fail("Problem while waiting for the cursor to blink"); } } int diff = drawableBounds.left - mEditText.getScrollX() + drawablePadding.left; assertTrue(diff >= 0 && diff <= 1); private void setEditTextHint(final String hint, final int textDirection, final int selection) { setEditTextProperties(null, hint, textDirection, selection); } private void setEditTextText(final String text, final Integer selection) { setEditTextProperties(text, null, null, selection); } } core/tests/coretests/src/android/widget/TextViewActivityTest.java +30 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package android.widget; import static android.support.test.espresso.action.ViewActions.longClick; import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles; import static android.widget.espresso.DragHandleUtils.onHandleView; import static android.widget.espresso.FloatingToolbarEspressoUtils.onFloatingToolBarItem; import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText; import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex; Loading Loading @@ -49,6 +51,7 @@ import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.text.Selection; import android.text.Spannable; import android.text.InputType; import android.view.KeyEvent; import static org.hamcrest.Matchers.anyOf; Loading Loading @@ -233,6 +236,33 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV assertFloatingToolbarIsNotDisplayed(); } @SmallTest public void testToolbarAppearsAfterSelection_withFirstStringLtrAlgorithmAndRtlHint() throws Exception { // after the hint layout change, the floating toolbar was not visible in the case below // this test tests that the floating toolbar is displayed on the screen and is visible to // user. final TextView textView = (TextView) getActivity().findViewById(R.id.textview); textView.post(new Runnable() { @Override public void run() { textView.setTextDirection(TextView.TEXT_DIRECTION_FIRST_STRONG_LTR); textView.setInputType(InputType.TYPE_CLASS_TEXT); textView.setSingleLine(true); textView.setHint("الروبوت"); } }); getInstrumentation().waitForIdleSync(); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView("test")); onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(1)); onFloatingToolBarItem(withText(com.android.internal.R.string.cut)).perform(click()); onView(withId(R.id.textview)).perform(longClick()); sleepForFloatingToolbarPopup(); assertFloatingToolbarIsDisplayed(); } @SmallTest public void testToolbarAndInsertionHandle() throws Exception { final String text = "text"; Loading Loading
core/java/android/widget/Editor.java +29 −32 Original line number Diff line number Diff line Loading @@ -1827,7 +1827,7 @@ public class Editor { return; } Layout layout = getActiveLayout(); Layout layout = mTextView.getLayout(); final int offset = mTextView.getSelectionStart(); final int line = layout.getLineForOffset(offset); final int top = layout.getLineTop(line); Loading Loading @@ -2192,27 +2192,35 @@ public class Editor { mCursorDrawable[cursorIndex] = mTextView.getContext().getDrawable( mTextView.mCursorDrawableRes); final Drawable drawable = mCursorDrawable[cursorIndex]; final int left = clampCursorHorizontalPosition(drawable, horizontal); final int left = clampHorizontalPosition(drawable, horizontal); final int width = drawable.getIntrinsicWidth(); drawable.setBounds(left, top - mTempRect.top, left + width, bottom + mTempRect.bottom); } /** * Return clamped position for the cursor. If the cursor is within the boundaries of the view, * then it is offset with the left padding of the cursor drawable. If the cursor is at * Return clamped position for the drawable. If the drawable is within the boundaries of the * view, then it is offset with the left padding of the cursor drawable. If the drawable is at * the beginning or the end of the text then its drawable edge is aligned with left or right of * the view boundary. * the view boundary. If the drawable is null, horizontal parameter is aligned to left or right * of the view. * * @param drawable Cursor drawable. * @param horizontal Horizontal position for the cursor. * @return The clamped horizontal position for the cursor. * @param drawable Drawable. Can be null. * @param horizontal Horizontal position for the drawable. * @return The clamped horizontal position for the drawable. */ private final int clampCursorHorizontalPosition(final Drawable drawable, float horizontal) { private int clampHorizontalPosition(@Nullable final Drawable drawable, float horizontal) { horizontal = Math.max(0.5f, horizontal - 0.5f); if (mTempRect == null) mTempRect = new Rect(); int drawableWidth = 0; if (drawable != null) { drawable.getPadding(mTempRect); drawableWidth = drawable.getIntrinsicWidth(); } else { mTempRect.setEmpty(); } int scrollX = mTextView.getScrollX(); float horizontalDiff = horizontal - scrollX; int viewClippedWidth = mTextView.getWidth() - mTextView.getCompoundPaddingLeft() Loading @@ -2221,9 +2229,11 @@ public class Editor { final int left; if (horizontalDiff >= (viewClippedWidth - 1f)) { // at the rightmost position final int cursorWidth = drawable.getIntrinsicWidth(); left = viewClippedWidth + scrollX - (cursorWidth - mTempRect.right); } else if (Math.abs(horizontalDiff) <= 1f) { left = viewClippedWidth + scrollX - (drawableWidth - mTempRect.right); } else if (Math.abs(horizontalDiff) <= 1f || (TextUtils.isEmpty(mTextView.getText()) && (TextView.VERY_WIDE - scrollX) <= (viewClippedWidth + 1f) && horizontal <= 1f)) { // at the leftmost position left = scrollX - mTempRect.left; } else { Loading Loading @@ -3772,10 +3782,10 @@ public class Editor { + mHandleHeight); } else { // We have a single cursor. Layout layout = getActiveLayout(); Layout layout = mTextView.getLayout(); int line = layout.getLineForOffset(mTextView.getSelectionStart()); float primaryHorizontal = layout.getPrimaryHorizontal(mTextView.getSelectionStart()); float primaryHorizontal = clampHorizontalPosition(null, layout.getPrimaryHorizontal(mTextView.getSelectionStart())); mSelectionBounds.set( primaryHorizontal, layout.getLineTop(line), Loading Loading @@ -4152,7 +4162,7 @@ public class Editor { prepareCursorControllers(); return; } layout = getActiveLayout(); layout = mTextView.getLayout(); boolean offsetChanged = offset != mPreviousOffset; if (offsetChanged || parentScrolled) { Loading Loading @@ -4322,19 +4332,6 @@ public class Editor { public void onDetached() {} } /** * Returns the active layout (hint or text layout). Note that the text layout can be null. */ private Layout getActiveLayout() { Layout layout = mTextView.getLayout(); Layout hintLayout = mTextView.getHintLayout(); if (TextUtils.isEmpty(layout.getText()) && hintLayout != null && !TextUtils.isEmpty(hintLayout.getText())) { layout = hintLayout; } return layout; } private class InsertionHandleView extends HandleView { private static final int DELAY_BEFORE_HANDLE_FADES_OUT = 4000; private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; // seconds Loading Loading @@ -4431,7 +4428,7 @@ public class Editor { final Drawable drawable = mCursorCount > 0 ? mCursorDrawable[0] : null; if (drawable != null) { final float horizontal = layout.getPrimaryHorizontal(offset); return clampCursorHorizontalPosition(drawable, horizontal) + mTempRect.left; return clampHorizontalPosition(drawable, horizontal) + mTempRect.left; } return super.getCursorHorizontalPosition(layout, offset); } Loading
core/java/android/widget/TextView.java +2 −2 Original line number Diff line number Diff line Loading @@ -285,8 +285,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private static final RectF TEMP_RECTF = new RectF(); // XXX should be much larger private static final int VERY_WIDE = 1024*1024; /** @hide */ static final int VERY_WIDE = 1024 * 1024; // XXX should be much larger private static final int ANIMATED_SCROLL_GAP = 250; private static final InputFilter[] NO_FILTERS = new InputFilter[0]; Loading
core/tests/coretests/AndroidManifest.xml +1 −1 Original line number Diff line number Diff line Loading @@ -111,7 +111,7 @@ <!-- accessibility test permissions --> <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" /> <application android:theme="@style/Theme"> <application android:theme="@style/Theme" android:supportsRtl="true"> <uses-library android:name="android.test.runner" /> <uses-library android:name="org.apache.http.legacy" android:required="false" /> <meta-data Loading
core/tests/coretests/src/android/widget/EditorCursorTest.java +139 −71 Original line number Diff line number Diff line Loading @@ -16,16 +16,34 @@ package android.widget; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.view.Choreographer; import android.view.ViewGroup; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnLeft; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnRight; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.isEmptyString; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; public class EditorCursorTest extends ActivityInstrumentationTestCase2<TextViewActivity> { private final static String LTR_STRING = "aaaaaaaaaaaaaaaaaaaaaa"; private final static String LTR_HINT = "hint"; private final static String RTL_STRING = "مرحبا الروبوت مرحبا الروبوت مرحبا الروبوت"; private final static String RTL_HINT = "الروبوت"; private final static int CURSOR_BLINK_MS = 500; private EditText mEditText; private final String RTL_STRING = "مرحبا الروبوت مرحبا الروبوت مرحبا الروبوت"; public EditorCursorTest() { super(TextViewActivity.class); Loading Loading @@ -55,110 +73,160 @@ public class EditorCursorTest extends ActivityInstrumentationTestCase2<TextViewA @Override public void run() { getActivity().setContentView(layout); mEditText.requestFocus(); } }); getInstrumentation().waitForIdleSync(); onView(sameInstance(mEditText)).perform(click()); } @SmallTest public void testCursorIsInViewBoundariesWhenOnRightForLtr() throws Exception { public void testCursorIsInViewBoundariesWhenOnRightForLtr() { // Asserts that when an EditText has LTR text, and cursor is at the end (right), // cursor is drawn to the right edge of the view getActivity().runOnUiThread(new Runnable() { @Override public void run() { mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa"); int length = mEditText.getText().length(); mEditText.setSelection(length, length); setEditTextText(LTR_STRING, LTR_STRING.length()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } }); getInstrumentation().waitForIdleSync(); Editor editor = mEditText.getEditorForTesting(); Drawable drawable = editor.getCursorDrawable()[0]; Rect drawableBounds = drawable.getBounds(); Rect drawablePadding = new Rect(); drawable.getPadding(drawablePadding); @SmallTest public void testCursorIsInViewBoundariesWhenOnLeftForLtr() { // Asserts that when an EditText has LTR text, and cursor is at the beginning, // cursor is drawn to the left edge of the view setEditTextText(LTR_STRING, 0); // right edge of the view including the scroll int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight() - mEditText.getCompoundPaddingLeft() + +mEditText.getScrollX(); int diff = drawableBounds.right - drawablePadding.right - maxRight; assertTrue(diff >= 0 && diff <= 1); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } @SmallTest public void testCursorIsInViewBoundariesWhenOnLeftForLtr() throws Exception { // Asserts that when an EditText has LTR text, and cursor is at the beginning, public void testCursorIsInViewBoundariesWhenOnRightForRtl() { // Asserts that when an EditText has RTL text, and cursor is at the end, // cursor is drawn to the left edge of the view setEditTextText(RTL_STRING, 0); getActivity().runOnUiThread(new Runnable() { @Override public void run() { mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa"); mEditText.setSelection(0, 0); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } }); getInstrumentation().waitForIdleSync(); Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0]; Rect drawableBounds = drawable.getBounds(); Rect drawablePadding = new Rect(); drawable.getPadding(drawablePadding); @SmallTest public void testCursorIsInViewBoundariesWhenOnLeftForRtl() { // Asserts that when an EditText has RTL text, and cursor is at the beginning, // cursor is drawn to the right edge of the view setEditTextText(RTL_STRING, RTL_STRING.length()); int diff = drawableBounds.left + drawablePadding.left; assertTrue(diff >= 0 && diff <= 1); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } /* Tests for cursor positioning with hint */ @SmallTest public void testCursorIsInViewBoundariesWhenOnRightForRtl() throws Exception { // Asserts that when an EditText has RTL text, and cursor is at the end, // cursor is drawn to the left edge of the view public void testCursorIsOnLeft_withFirstStrongLtrAlgorithm() { setEditTextHint(null, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); assertThat(mEditText.getHint(), nullValue()); getActivity().runOnUiThread(new Runnable() { @Override public void run() { mEditText.setText(RTL_STRING); mEditText.setSelection(0, 0); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } }); getInstrumentation().waitForIdleSync(); Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0]; Rect drawableBounds = drawable.getBounds(); Rect drawablePadding = new Rect(); drawable.getPadding(drawablePadding); @SmallTest public void testCursorIsOnRight_withFirstStrongRtlAlgorithm() { setEditTextHint(null, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); assertThat(mEditText.getHint(), nullValue()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight() - mEditText.getCompoundPaddingLeft() + mEditText.getScrollX(); setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); int diff = drawableBounds.right - drawablePadding.right - maxRight; assertTrue(diff >= 0 && diff <= 1); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } @SmallTest public void testCursorIsInViewBoundariesWhenOnLeftForRtl() throws Exception { // Asserts that when an EditText has RTL text, and cursor is at the beginning, // cursor is drawn to the right edge of the view public void testCursorIsOnLeft_withLtrAlgorithm() { setEditTextHint(null, TextView.TEXT_DIRECTION_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); assertThat(mEditText.getHint(), nullValue()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } @SmallTest public void testCursorIsOnRight_withRtlAlgorithm() { setEditTextHint(null, TextView.TEXT_DIRECTION_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); assertThat(mEditText.getHint(), nullValue()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } private void setEditTextProperties(final String text, final String hint, final Integer textDirection, final Integer selection) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { mEditText.setText(RTL_STRING); int length = mEditText.getText().length(); mEditText.setSelection(length, length); if (textDirection != null) mEditText.setTextDirection(textDirection); if (text != null) mEditText.setText(text); if (hint != null) mEditText.setHint(hint); if (selection != null) mEditText.setSelection(selection); } }); getInstrumentation().waitForIdleSync(); Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0]; Rect drawableBounds = drawable.getBounds(); Rect drawablePadding = new Rect(); drawable.getPadding(drawablePadding); // wait for cursor to be drawn. updateCursorPositions function is called during draw() and // only when cursor is visible during blink. final CountDownLatch latch = new CountDownLatch(1); mEditText.postOnAnimationDelayed(new Runnable() { @Override public void run() { latch.countDown(); } }, CURSOR_BLINK_MS); try { assertThat("Problem while waiting for the cursor to blink", latch.await(10, TimeUnit.SECONDS), equalTo(true)); } catch (Exception e) { fail("Problem while waiting for the cursor to blink"); } } int diff = drawableBounds.left - mEditText.getScrollX() + drawablePadding.left; assertTrue(diff >= 0 && diff <= 1); private void setEditTextHint(final String hint, final int textDirection, final int selection) { setEditTextProperties(null, hint, textDirection, selection); } private void setEditTextText(final String text, final Integer selection) { setEditTextProperties(text, null, null, selection); } }
core/tests/coretests/src/android/widget/TextViewActivityTest.java +30 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package android.widget; import static android.support.test.espresso.action.ViewActions.longClick; import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles; import static android.widget.espresso.DragHandleUtils.onHandleView; import static android.widget.espresso.FloatingToolbarEspressoUtils.onFloatingToolBarItem; import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText; import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex; Loading Loading @@ -49,6 +51,7 @@ import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.text.Selection; import android.text.Spannable; import android.text.InputType; import android.view.KeyEvent; import static org.hamcrest.Matchers.anyOf; Loading Loading @@ -233,6 +236,33 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV assertFloatingToolbarIsNotDisplayed(); } @SmallTest public void testToolbarAppearsAfterSelection_withFirstStringLtrAlgorithmAndRtlHint() throws Exception { // after the hint layout change, the floating toolbar was not visible in the case below // this test tests that the floating toolbar is displayed on the screen and is visible to // user. final TextView textView = (TextView) getActivity().findViewById(R.id.textview); textView.post(new Runnable() { @Override public void run() { textView.setTextDirection(TextView.TEXT_DIRECTION_FIRST_STRONG_LTR); textView.setInputType(InputType.TYPE_CLASS_TEXT); textView.setSingleLine(true); textView.setHint("الروبوت"); } }); getInstrumentation().waitForIdleSync(); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView("test")); onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(1)); onFloatingToolBarItem(withText(com.android.internal.R.string.cut)).perform(click()); onView(withId(R.id.textview)).perform(longClick()); sleepForFloatingToolbarPopup(); assertFloatingToolbarIsDisplayed(); } @SmallTest public void testToolbarAndInsertionHandle() throws Exception { final String text = "text"; Loading