Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 75bf818a authored by Siyamed Sinir's avatar Siyamed Sinir Committed by android-build-merger
Browse files

Merge "Do not use hint text for Editor cursor positioning" into nyc-dev

am: e7a414e1

* commit 'e7a414e1':
  Do not use hint text for Editor cursor positioning
parents d83ae125 e7a414e1
Loading
Loading
Loading
Loading
+29 −32
Original line number Diff line number Diff line
@@ -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);
@@ -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()
@@ -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 {
@@ -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),
@@ -4152,7 +4162,7 @@ public class Editor {
                prepareCursorControllers();
                return;
            }
            layout = getActiveLayout();
            layout = mTextView.getLayout();

            boolean offsetChanged = offset != mPreviousOffset;
            if (offsetChanged || parentScrolled) {
@@ -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
@@ -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);
        }
+2 −2
Original line number Diff line number Diff line
@@ -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];
+1 −1
Original line number Diff line number Diff line
@@ -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
+139 −71
Original line number Diff line number Diff line
@@ -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);
@@ -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);
    }
}
+30 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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