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

Commit b2d133a8 authored by Seigo Nonaka's avatar Seigo Nonaka Committed by android-build-merger
Browse files

Merge "Stop using DynamicLayout in case of non selectable PrecomputedText" into pi-dev

am: a1ff74c6

Change-Id: Ic003d918ce83224e3daa513a0a8c8ee161f62c42
parents 6527cd70 a1ff74c6
Loading
Loading
Loading
Loading
+60 −42
Original line number Diff line number Diff line
@@ -644,8 +644,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     */
    private Layout mSavedMarqueeModeLayout;

    // Do not update following mText/mSpannable/mPrecomputed except for setTextInternal()
    @ViewDebug.ExportedProperty(category = "text")
    private CharSequence mText;
    private @Nullable CharSequence mText;
    private @Nullable Spannable mSpannable;
    private @Nullable PrecomputedText mPrecomputed;

    private CharSequence mTransformed;
    private BufferType mBufferType = BufferType.NORMAL;

@@ -874,7 +878,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
        }

        mText = "";
        setTextInternal("");

        final Resources res = getResources();
        final CompatibilityInfo compat = res.getCompatibilityInfo();
@@ -1615,6 +1619,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        }
    }

    // Update mText and mPrecomputed
    private void setTextInternal(@Nullable CharSequence text) {
        mText = text;
        mSpannable = (text instanceof Spannable) ? (Spannable) text : null;
        mPrecomputed = (text instanceof PrecomputedText) ? (PrecomputedText) text : null;
    }

    /**
     * Specify whether this widget should automatically scale the text to try to perfectly fit
     * within the layout bounds by using the default auto-size configuration.
@@ -1973,9 +1984,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                        }
                    }
                }
            } else if (mText instanceof Spannable) {
            } else if (mSpannable != null) {
                // Reset the selection.
                Selection.setSelection((Spannable) mText, getSelectionEnd());
                Selection.setSelection(mSpannable, getSelectionEnd());
            }
        }
    }
@@ -2359,7 +2370,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        if (mMovement != movement) {
            mMovement = movement;

            if (movement != null && !(mText instanceof Spannable)) {
            if (movement != null && mSpannable == null) {
                setText(mText);
            }

@@ -2409,8 +2420,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            return;
        }
        if (mTransformation != null) {
            if (mText instanceof Spannable) {
                ((Spannable) mText).removeSpan(mTransformation);
            if (mSpannable != null) {
                mSpannable.removeSpan(mTransformation);
            }
        }

@@ -5254,7 +5265,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        ((Editable) mText).append(text, start, end);

        if (mAutoLinkMask != 0) {
            boolean linksWereAdded = Linkify.addLinks((Spannable) mText, mAutoLinkMask);
            boolean linksWereAdded = Linkify.addLinks(mSpannable, mAutoLinkMask);
            // Do not change the movement method for text that support text selection as it
            // would prevent an arbitrary cursor displacement.
            if (linksWereAdded && mLinksClickable && !textCanBeSelected()) {
@@ -5413,7 +5424,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        }

        if (ss.selStart >= 0 && ss.selEnd >= 0) {
            if (mText instanceof Spannable) {
            if (mSpannable != null) {
                int len = mText.length();

                if (ss.selStart > len || ss.selEnd > len) {
@@ -5426,7 +5437,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                    Log.e(LOG_TAG, "Saved cursor position " + ss.selStart + "/" + ss.selEnd
                            + " out of range for " + restored + "text " + mText);
                } else {
                    Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd);
                    Selection.setSelection(mSpannable, ss.selStart, ss.selEnd);

                    if (ss.frozenWithFocus) {
                        createEditorIfNeeded();
@@ -5688,7 +5699,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 * movement method, because setMovementMethod() may call
                 * setText() again to try to upgrade the buffer type.
                 */
                mText = text;
                setTextInternal(text);

                // Do not change the movement method for text that support text selection as it
                // would prevent an arbitrary cursor displacement.
@@ -5699,7 +5710,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        }

        mBufferType = type;
        mText = text;
        setTextInternal(text);

        if (mTransformation == null) {
            mTransformed = text;
@@ -5825,8 +5836,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        setText(text, type);

        if (start >= 0 || end >= 0) {
            if (mText instanceof Spannable) {
                Selection.setSelection((Spannable) mText,
            if (mSpannable != null) {
                Selection.setSelection(mSpannable,
                                       Math.max(0, Math.min(start, len)),
                                       Math.max(0, Math.min(end, len)));
            }
@@ -6020,7 +6031,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        }

        if (!isSuggestionsEnabled()) {
            mText = removeSuggestionSpans(mText);
            setTextInternal(removeSuggestionSpans(mText));
        }

        InputMethodManager imm = InputMethodManager.peekInstance();
@@ -6948,8 +6959,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    public boolean hasOverlappingRendering() {
        // horizontal fading edge causes SaveLayerAlpha, which doesn't support alpha modulation
        return ((getBackground() != null && getBackground().getCurrent() != null)
                || mText instanceof Spannable || hasSelection()
                || isHorizontalFadingEdgeEnabled());
                || mSpannable != null || hasSelection() || isHorizontalFadingEdgeEnabled());
    }

    /**
@@ -7399,11 +7409,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

    @Override
    public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
        if (mText instanceof Spannable && mLinksClickable) {
        if (mSpannable != null && mLinksClickable) {
            final float x = event.getX(pointerIndex);
            final float y = event.getY(pointerIndex);
            final int offset = getOffsetForPosition(x, y);
            final ClickableSpan[] clickables = ((Spannable) mText).getSpans(offset, offset,
            final ClickableSpan[] clickables = mSpannable.getSpans(offset, offset,
                    ClickableSpan.class);
            if (clickables.length > 0) {
                return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_HAND);
@@ -7496,10 +7506,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

        } else if (which == KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD) {
            // mMovement is not null from doKeyDown
            mMovement.onKeyUp(this, (Spannable) mText, keyCode, up);
            mMovement.onKeyUp(this, mSpannable, keyCode, up);
            while (--repeatCount > 0) {
                mMovement.onKeyDown(this, (Spannable) mText, keyCode, down);
                mMovement.onKeyUp(this, (Spannable) mText, keyCode, up);
                mMovement.onKeyDown(this, mSpannable, keyCode, down);
                mMovement.onKeyUp(this, mSpannable, keyCode, up);
            }
        }

@@ -7694,8 +7704,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            boolean doDown = true;
            if (otherEvent != null) {
                try {
                    boolean handled = mMovement.onKeyOther(this, (Spannable) mText,
                            otherEvent);
                    boolean handled = mMovement.onKeyOther(this, mSpannable, otherEvent);
                    doDown = false;
                    if (handled) {
                        return KEY_EVENT_HANDLED;
@@ -7706,7 +7715,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                }
            }
            if (doDown) {
                if (mMovement.onKeyDown(this, (Spannable) mText, keyCode, event)) {
                if (mMovement.onKeyDown(this, mSpannable, keyCode, event)) {
                    if (event.getRepeatCount() == 0 && !KeyEvent.isModifierKey(keyCode)) {
                        mPreventDefaultMovement = true;
                    }
@@ -7848,7 +7857,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        }

        if (mMovement != null && mLayout != null) {
            if (mMovement.onKeyUp(this, (Spannable) mText, keyCode, event)) {
            if (mMovement.onKeyUp(this, mSpannable, keyCode, event)) {
                return true;
            }
        }
@@ -8313,6 +8322,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        if (mEditor != null) mEditor.prepareCursorControllers();
    }

    /**
     * Returns true if DynamicLayout is required
     *
     * @hide
     */
    @VisibleForTesting
    public boolean useDynamicLayout() {
        return isTextSelectable() || (mSpannable != null && mPrecomputed == null);
    }

    /**
     * @hide
     */
@@ -8320,7 +8339,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize,
            boolean useSaved) {
        Layout result = null;
        if (mText instanceof Spannable) {
        if (useDynamicLayout()) {
            final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(mText, mTextPaint,
                    wantWidth)
                    .setDisplayText(mTransformed)
@@ -9262,7 +9281,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        }

        if (newStart != start) {
            Selection.setSelection((Spannable) mText, newStart);
            Selection.setSelection(mSpannable, newStart);
            return true;
        }

@@ -9999,9 +10018,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        if (mEditor != null) mEditor.onFocusChanged(focused, direction);

        if (focused) {
            if (mText instanceof Spannable) {
                Spannable sp = (Spannable) mText;
                MetaKeyKeyListener.resetMetaState(sp);
            if (mSpannable != null) {
                MetaKeyKeyListener.resetMetaState(mSpannable);
            }
        }

@@ -10039,7 +10057,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     */
    public void clearComposingText() {
        if (mText instanceof Spannable) {
            BaseInputConnection.removeComposingSpans((Spannable) mText);
            BaseInputConnection.removeComposingSpans(mSpannable);
        }
    }

@@ -10095,7 +10113,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            boolean handled = false;

            if (mMovement != null) {
                handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
                handled |= mMovement.onTouchEvent(this, mSpannable, event);
            }

            final boolean textIsSelectable = isTextSelectable();
@@ -10103,7 +10121,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                // The LinkMovementMethod which should handle taps on links has not been installed
                // on non editable text that support text selection.
                // We reproduce its behavior here to open links for these.
                ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(),
                ClickableSpan[] links = mSpannable.getSpans(getSelectionStart(),
                    getSelectionEnd(), ClickableSpan.class);

                if (links.length > 0) {
@@ -10138,7 +10156,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    public boolean onGenericMotionEvent(MotionEvent event) {
        if (mMovement != null && mText instanceof Spannable && mLayout != null) {
            try {
                if (mMovement.onGenericMotionEvent(this, (Spannable) mText, event)) {
                if (mMovement.onGenericMotionEvent(this, mSpannable, event)) {
                    return true;
                }
            } catch (AbstractMethodError ex) {
@@ -10199,8 +10217,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

    @Override
    public boolean onTrackballEvent(MotionEvent event) {
        if (mMovement != null && mText instanceof Spannable && mLayout != null) {
            if (mMovement.onTrackballEvent(this, (Spannable) mText, event)) {
        if (mMovement != null && mSpannable != null && mLayout != null) {
            if (mMovement.onTrackballEvent(this, mSpannable, event)) {
                return true;
            }
        }
@@ -11121,7 +11139,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                if (mText != null) {
                    int updatedTextLength = mText.length();
                    if (updatedTextLength > 0) {
                        Selection.setSelection((Spannable) mText, updatedTextLength);
                        Selection.setSelection(mSpannable, updatedTextLength);
                    }
                }
            } return true;
@@ -11697,7 +11715,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            hideFloatingToolbar(FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY);
        }
        final int length = mText.length();
        Selection.setSelection((Spannable) mText, 0, length);
        Selection.setSelection(mSpannable, 0, length);
        return length > 0;
    }

@@ -11725,7 +11743,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                }
                if (paste != null) {
                    if (!didFirst) {
                        Selection.setSelection((Spannable) mText, max);
                        Selection.setSelection(mSpannable, max);
                        ((Editable) mText).replace(min, max, paste);
                        didFirst = true;
                    } else {
@@ -11747,7 +11765,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            selectedText = TextUtils.trimToParcelableSize(selectedText);
            sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText);
            getContext().startActivity(Intent.createChooser(sharingIntent, null));
            Selection.setSelection((Spannable) mText, getSelectionEnd());
            Selection.setSelection(mSpannable, getSelectionEnd());
        }
    }

@@ -11822,7 +11840,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            case DragEvent.ACTION_DRAG_LOCATION:
                if (mText instanceof Spannable) {
                    final int offset = getOffsetForPosition(event.getX(), event.getY());
                    Selection.setSelection((Spannable) mText, offset);
                    Selection.setSelection(mSpannable, offset);
                }
                return true;

+79 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.widget;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;

@@ -32,9 +33,11 @@ import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.text.GetChars;
import android.text.Layout;
import android.text.PrecomputedText;
import android.text.Selection;
import android.text.Spannable;
import android.view.View;
import android.widget.TextView.BufferType;

import org.junit.Before;
import org.junit.Rule;
@@ -241,6 +244,82 @@ public class TextViewTest {
        mTextView.onTextContextMenuItem(TextView.ID_CUT);
    }

    @Test
    public void testUseDynamicLayout() {
        mTextView = new TextView(mActivity);
        mTextView.setTextIsSelectable(true);
        String text = "HelloWorld";
        PrecomputedText precomputed =
                PrecomputedText.create(text, mTextView.getTextMetricsParams());

        mTextView.setTextIsSelectable(false);
        mTextView.setText(text);
        assertFalse(mTextView.useDynamicLayout());

        mTextView.setTextIsSelectable(true);
        mTextView.setText(text);
        assertTrue(mTextView.useDynamicLayout());

        mTextView.setTextIsSelectable(false);
        mTextView.setText(precomputed);
        assertFalse(mTextView.useDynamicLayout());

        mTextView.setTextIsSelectable(true);
        mTextView.setText(precomputed);
        assertTrue(mTextView.useDynamicLayout());
    }

    @Test
    public void testUseDynamicLayout_SPANNABLE() {
        mTextView = new TextView(mActivity);
        mTextView.setTextIsSelectable(true);
        String text = "HelloWorld";
        PrecomputedText precomputed =
                PrecomputedText.create(text, mTextView.getTextMetricsParams());

        mTextView.setTextIsSelectable(false);
        mTextView.setText(text, BufferType.SPANNABLE);
        android.util.Log.e("TextViewTest", "Text:" + mTextView.getText().getClass().getName());
        assertTrue(mTextView.useDynamicLayout());

        mTextView.setTextIsSelectable(true);
        mTextView.setText(text, BufferType.SPANNABLE);
        assertTrue(mTextView.useDynamicLayout());

        mTextView.setTextIsSelectable(false);
        mTextView.setText(precomputed, BufferType.SPANNABLE);
        assertFalse(mTextView.useDynamicLayout());

        mTextView.setTextIsSelectable(true);
        mTextView.setText(precomputed, BufferType.SPANNABLE);
        assertTrue(mTextView.useDynamicLayout());
    }

    @Test
    public void testUseDynamicLayout_EDITABLE() {
        mTextView = new TextView(mActivity);
        mTextView.setTextIsSelectable(true);
        String text = "HelloWorld";
        PrecomputedText precomputed =
                PrecomputedText.create(text, mTextView.getTextMetricsParams());

        mTextView.setTextIsSelectable(false);
        mTextView.setText(text, BufferType.EDITABLE);
        assertTrue(mTextView.useDynamicLayout());

        mTextView.setTextIsSelectable(true);
        mTextView.setText(text, BufferType.EDITABLE);
        assertTrue(mTextView.useDynamicLayout());

        mTextView.setTextIsSelectable(false);
        mTextView.setText(precomputed, BufferType.EDITABLE);
        assertTrue(mTextView.useDynamicLayout());

        mTextView.setTextIsSelectable(true);
        mTextView.setText(precomputed, BufferType.EDITABLE);
        assertTrue(mTextView.useDynamicLayout());
    }

    private String createLongText() {
        int size = 600 * 1000;
        final StringBuilder builder = new StringBuilder(size);