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

Commit 9ea756fa authored by Roozbeh Pournader's avatar Roozbeh Pournader
Browse files

Refactor ellipsis attributes and methods

Mostly changed to avoid repetition of code and remove the assumptions
about ellipsis being one code unit. The code for multi-code unit
ellipsis is not triggered yet, but is done in preparation for
potential future locale-dependent cases.

Test: bit CtsTextTestCases:*
Test: bit CtsWidgetTestCases:android.widget.cts.TextViewTest
Test: bit CtsWidgetTestCases:android.widget.cts.EditTextTest
Test: bit CtsWidgetTestCases:android.widget.cts.CheckedTextViewTest
Test: bit CtsWidgetTestCases:android.widget.cts.AutoCompleteTextViewTest
Test: bit CtsWidgetTestCases:android.widget.cts.MultiAutoCompleteTextViewTest
Test: adb shell am instrument -w -e package android.text com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner

Change-Id: Id1dfdc503f87fabed2447d55ab2107eee0eccd08
parent 34dc4c1f
Loading
Loading
Loading
Loading
+12 −20
Original line number Diff line number Diff line
@@ -2036,35 +2036,27 @@ public abstract class Layout {
        }
    }

    private char getEllipsisChar(TextUtils.TruncateAt method) {
        return (method == TextUtils.TruncateAt.END_SMALL) ?
                TextUtils.ELLIPSIS_TWO_DOTS[0] :
                TextUtils.ELLIPSIS_NORMAL[0];
    }

    private void ellipsize(int start, int end, int line,
                           char[] dest, int destoff, TextUtils.TruncateAt method) {
        int ellipsisCount = getEllipsisCount(line);

        final int ellipsisCount = getEllipsisCount(line);
        if (ellipsisCount == 0) {
            return;
        }
        final int ellipsisStart = getEllipsisStart(line);
        final int lineStart = getLineStart(line);

        int ellipsisStart = getEllipsisStart(line);
        int linestart = getLineStart(line);

        for (int i = ellipsisStart; i < ellipsisStart + ellipsisCount; i++) {
            char c;

            if (i == ellipsisStart) {
                c = getEllipsisChar(method); // ellipsis
        final String ellipsisString = TextUtils.getEllipsisString(method);
        final int ellipsisStringLen = ellipsisString.length();
        for (int i = 0; i < ellipsisCount; i++) {
            final char c;
            if (i < ellipsisStringLen && ellipsisCount <= ellipsisStringLen) {
                c = ellipsisString.charAt(i);
            } else {
                c = '\uFEFF'; // 0-width space
                c = TextUtils.ELLIPSIS_FILLER;
            }

            int a = i + linestart;

            if (a >= start && a < end) {
            final int a = i + ellipsisStart + lineStart;
            if (start <= a && a < end) {
                dest[destoff + a - start] = c;
            }
        }
+3 −5
Original line number Diff line number Diff line
@@ -781,8 +781,8 @@ public class StaticLayout extends Layout {
                    && (ellipsize == TextUtils.TruncateAt.END
                        || (mMaximumVisibleLineCount == 1
                                && ellipsize != TextUtils.TruncateAt.MARQUEE));
            if (remainingLineCount > 0 && remainingLineCount < breakCount &&
                    ellipsisMayBeApplied) {
            if (0 < remainingLineCount && remainingLineCount < breakCount
                    && ellipsisMayBeApplied) {
                // Calculate width and flag.
                float width = 0;
                int flag = 0;
@@ -1053,9 +1053,7 @@ public class StaticLayout extends Layout {
            return;
        }

        float ellipsisWidth = paint.measureText(
                (where == TextUtils.TruncateAt.END_SMALL) ?
                        TextUtils.ELLIPSIS_TWO_DOTS : TextUtils.ELLIPSIS_NORMAL, 0, 1);
        float ellipsisWidth = paint.measureText(TextUtils.getEllipsisString(where));
        int ellipsisStart = 0;
        int ellipsisCount = 0;
        int len = lineEnd - lineStart;
+24 −15
Original line number Diff line number Diff line
@@ -77,12 +77,21 @@ import java.util.regex.Pattern;
public class TextUtils {
    private static final String TAG = "TextUtils";

    /* package */ static final char[] ELLIPSIS_NORMAL = { '\u2026' }; // this is "..."
    // Zero-width character used to fill ellipsized strings when codepoint lenght must be preserved.
    /* package */ static final char ELLIPSIS_FILLER = '\uFEFF'; // ZERO WIDTH NO-BREAK SPACE

    // TODO: Based on CLDR data, these need to be localized for Dzongkha (dz) and perhaps
    // Hong Kong Traditional Chinese (zh-Hant-HK), but that may need to depend on the actual word
    // being ellipsized and not the locale.
    private static final String ELLIPSIS_NORMAL = "\u2026"; // HORIZONTAL ELLIPSIS (…)
    private static final String ELLIPSIS_TWO_DOTS = "\u2025"; // TWO DOT LEADER (‥)

    /** {@hide} */
    public static final String ELLIPSIS_STRING = new String(ELLIPSIS_NORMAL);
    @NonNull
    public static String getEllipsisString(@NonNull TextUtils.TruncateAt method) {
        return (method == TextUtils.TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL;
    }

    /* package */ static final char[] ELLIPSIS_TWO_DOTS = { '\u2025' }; // this is ".."
    private static final String ELLIPSIS_TWO_DOTS_STRING = new String(ELLIPSIS_TWO_DOTS);

    private TextUtils() { /* cannot be instantiated */ }

@@ -1190,10 +1199,10 @@ public class TextUtils {
                                         TextPaint paint,
                                         float avail, TruncateAt where,
                                         boolean preserveLength,
                                         EllipsizeCallback callback) {
                                         @Nullable EllipsizeCallback callback) {
        return ellipsize(text, paint, avail, where, preserveLength, callback,
                TextDirectionHeuristics.FIRSTSTRONG_LTR,
                (where == TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS_STRING : ELLIPSIS_STRING);
                getEllipsisString(where));
    }

    /**
@@ -1213,7 +1222,7 @@ public class TextUtils {
            TextPaint paint,
            float avail, TruncateAt where,
            boolean preserveLength,
            EllipsizeCallback callback,
            @Nullable EllipsizeCallback callback,
            TextDirectionHeuristic textDir, String ellipsis) {

        int len = text.length();
@@ -1256,13 +1265,15 @@ public class TextUtils {
            char[] buf = mt.mChars;
            Spanned sp = text instanceof Spanned ? (Spanned) text : null;

            int remaining = len - (right - left);
            final int removed = right - left;
            final int remaining = len - removed;
            if (preserveLength) {
                if (remaining > 0) { // else eliminate the ellipsis too
                    buf[left++] = ellipsis.charAt(0);
                }
                if (remaining > 0 && removed >= ellipsis.length()) {
                    ellipsis.getChars(0, ellipsis.length(), buf, left);
                    left += ellipsis.length();
                } // else skip the ellipsis
                for (int i = left; i < right; i++) {
                    buf[i] = ZWNBS_CHAR;
                    buf[i] = ELLIPSIS_FILLER;
                }
                String s = new String(buf, 0, len);
                if (sp == null) {
@@ -1362,7 +1373,7 @@ public class TextUtils {
            final int remainingElements = totalLen - i - 1;
            if (remainingElements > 0) {
                CharSequence morePiece = (res == null) ?
                        ELLIPSIS_STRING :
                        ELLIPSIS_NORMAL :
                        res.getQuantityString(moreId, remainingElements, remainingElements);
                morePiece = bidiFormatter.unicodeWrap(morePiece);
                output.append(morePiece);
@@ -2029,6 +2040,4 @@ public class TextUtils {
    private static char[] sTemp = null;

    private static String[] EMPTY_STRING_ARRAY = new String[]{};

    private static final char ZWNBS_CHAR = '\uFEFF';
}
+1 −1
Original line number Diff line number Diff line
@@ -181,7 +181,7 @@ public class LocaleHelper {
            // Hong Kong Traditional Chinese (zh_Hant_HK) and Dzongkha (dz). But that has two
            // problems: it's expensive to extract it, and in case the output string becomes
            // automatically ellipsized, it can result in weird output.
            localeNames[maxLocales] = TextUtils.ELLIPSIS_STRING;
            localeNames[maxLocales] = TextUtils.getEllipsisString(TextUtils.TruncateAt.END);
        }

        ListFormatter lfn = ListFormatter.getInstance(dispLocale);
+60 −0
Original line number Diff line number Diff line
@@ -340,6 +340,66 @@ public class TextUtilsTest {
        }
    }

    @Test
    public void testEllipsize_multiCodepoint() {
        final TextPaint paint = new TextPaint();
        final float wordWidth = paint.measureText("MMMM");

        // Establish the ground rules first, for single-codepoint cases.
        final String ellipsis = "."; // one full stop character
        assertEquals(
                "MM.\uFEFF",
                TextUtils.ellipsize("MMMM", paint, 0.7f * wordWidth,
                        TextUtils.TruncateAt.END, true /* preserve length */,
                        null /* no callback */, TextDirectionHeuristics.LTR,
                        ellipsis));
        assertEquals(
                "MM.",
                TextUtils.ellipsize("MMMM", paint, 0.7f * wordWidth,
                        TextUtils.TruncateAt.END, false /* preserve length */,
                        null /* no callback */, TextDirectionHeuristics.LTR,
                        ellipsis));
        assertEquals(
                "M.",
                TextUtils.ellipsize("MM", paint, 0.45f * wordWidth,
                        TextUtils.TruncateAt.END, true /* preserve length */,
                        null /* no callback */, TextDirectionHeuristics.LTR,
                        ellipsis));
        assertEquals(
                "M.",
                TextUtils.ellipsize("MM", paint, 0.45f * wordWidth,
                        TextUtils.TruncateAt.END, false /* preserve length */,
                        null /* no callback */, TextDirectionHeuristics.LTR,
                        ellipsis));

        // Now check the differences for multi-codepoint ellipsis.
        final String longEllipsis = ".."; // two full stop characters
        assertEquals(
                "MM..",
                TextUtils.ellipsize("MMMM", paint, 0.7f * wordWidth,
                        TextUtils.TruncateAt.END, true /* preserve length */,
                        null /* no callback */, TextDirectionHeuristics.LTR,
                        longEllipsis));
        assertEquals(
                "MM..",
                TextUtils.ellipsize("MMMM", paint, 0.7f * wordWidth,
                        TextUtils.TruncateAt.END, false /* preserve length */,
                        null /* no callback */, TextDirectionHeuristics.LTR,
                        longEllipsis));
        assertEquals(
                "M\uFEFF",
                TextUtils.ellipsize("MM", paint, 0.45f * wordWidth,
                        TextUtils.TruncateAt.END, true /* preserve length */,
                        null /* no callback */, TextDirectionHeuristics.LTR,
                        longEllipsis));
        assertEquals(
                "M..",
                TextUtils.ellipsize("MM", paint, 0.45f * wordWidth,
                        TextUtils.TruncateAt.END, false /* preserve length */,
                        null /* no callback */, TextDirectionHeuristics.LTR,
                        longEllipsis));
    }

    @Test
    public void testDelimitedStringContains() {
        assertFalse(TextUtils.delimitedStringContains("", ',', null));