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

Commit a1ff74c6 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

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

parents 642c0be3 0c5daa19
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);