16ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt/*
26ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt * Copyright (C) 2010 The Android Open Source Project
36ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt *
46ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt * Licensed under the Apache License, Version 2.0 (the "License"); you may not
56ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt * use this file except in compliance with the License. You may obtain a copy of
66ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt * the License at
76ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt *
86ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt * http://www.apache.org/licenses/LICENSE-2.0
96ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt *
106ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt * Unless required by applicable law or agreed to in writing, software
116ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
126ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
136ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt * License for the specific language governing permissions and limitations under
146ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt * the License.
156ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt */
166ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
176ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Feltpackage android.text;
186ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
19e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanuimport static android.text.Layout.Alignment.ALIGN_NORMAL;
2002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
21e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanuimport static org.junit.Assert.assertEquals;
2202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournaderimport static org.junit.Assert.assertTrue;
23e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanu
246ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Feltimport android.graphics.Paint.FontMetricsInt;
25ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournaderimport android.os.LocaleList;
2668089c81068cc93d3fd42e2f768fc3a2a0d08f3cSiyamed Sinirimport android.support.test.filters.SmallTest;
27e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanuimport android.support.test.runner.AndroidJUnit4;
286ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Feltimport android.text.Layout.Alignment;
29f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonakaimport android.text.method.EditorState;
30ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournaderimport android.text.style.LocaleSpan;
316ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Feltimport android.util.Log;
326ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournaderimport org.junit.Before;
34e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanuimport org.junit.Test;
35e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanuimport org.junit.runner.RunWith;
3668089c81068cc93d3fd42e2f768fc3a2a0d08f3cSiyamed Sinir
3702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournaderimport java.text.Normalizer;
3802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournaderimport java.util.ArrayList;
3902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournaderimport java.util.List;
40ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournaderimport java.util.Locale;
4102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
426ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt/**
436ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt * Tests StaticLayout vertical metrics behavior.
446ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt */
45e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanu@SmallTest
46e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanu@RunWith(AndroidJUnit4.class)
47e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanupublic class StaticLayoutTest {
4802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private static final float SPACE_MULTI = 1.0f;
4902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private static final float SPACE_ADD = 0.0f;
5002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private static final int DEFAULT_OUTER_WIDTH = 150;
5102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
5202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private static final CharSequence LAYOUT_TEXT = "CharSe\tq\nChar"
5302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            + "Sequence\nCharSequence\nHelllo\n, world\nLongLongLong";
5402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private static final CharSequence LAYOUT_TEXT_SINGLE_LINE = "CharSequence";
5502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
5602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private static final Alignment DEFAULT_ALIGN = Alignment.ALIGN_CENTER;
5702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private static final int ELLIPSIZE_WIDTH = 8;
5802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
5902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private StaticLayout mDefaultLayout;
6002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private TextPaint mDefaultPaint;
6102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
6202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    @Before
6302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    public void setup() {
6402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        mDefaultPaint = new TextPaint();
6502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        mDefaultLayout = createDefaultStaticLayout();
6602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    }
6702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
6802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private StaticLayout createDefaultStaticLayout() {
6902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        return new StaticLayout(LAYOUT_TEXT, mDefaultPaint,
7002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
7102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    }
7202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
7302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    @Test
7402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    public void testBuilder() {
7502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        {
7602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            // Obtain.
7702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
7802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
7902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final StaticLayout layout = builder.build();
8002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            // Check default value.
8102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR,
8202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    layout.getTextDirectionHeuristic());
8302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
8402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        {
8502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            // setTextDirection.
8602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
8702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
8802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            builder.setTextDirection(TextDirectionHeuristics.RTL);
8902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final StaticLayout layout = builder.build();
9002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            // Always returns TextDirectionHeuristics.FIRSTSTRONG_LTR.
9102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR,
9202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    layout.getTextDirectionHeuristic());
9302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
9402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    }
9502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
966ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    /**
976ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * Basic test showing expected behavior and relationship between font
986ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * metrics and line metrics.
996ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     */
100e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanu    @Test
1016ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    public void testGetters1() {
1026ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder b = builder();
1036ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        FontMetricsInt fmi = b.paint.getFontMetricsInt();
1046ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
1056ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        // check default paint
1066ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Log.i("TG1:paint", fmi.toString());
1076ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
1086ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Layout l = b.build();
1096ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertVertMetrics(l, 0, 0,
1106ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.ascent, fmi.descent);
1116ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
1126ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        // other quick metrics
1136ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(0, l.getLineStart(0));
1146ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(Layout.DIR_LEFT_TO_RIGHT, l.getParagraphDirection(0));
1156ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(false, l.getLineContainsTab(0));
1166ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(Layout.DIRS_ALL_LEFT_TO_RIGHT, l.getLineDirections(0));
1176ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(0, l.getEllipsisCount(0));
1186ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(0, l.getEllipsisStart(0));
1196ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(b.width, l.getEllipsizedWidth());
1206ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
1216ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
1226ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    /**
1236ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * Basic test showing effect of includePad = true with 1 line.
1246ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * Top and bottom padding are affected, as is the line descent and height.
1256ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     */
126e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanu    @Test
1276ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    public void testGetters2() {
1286ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder b = builder()
1296ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setIncludePad(true);
1306ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        FontMetricsInt fmi = b.paint.getFontMetricsInt();
1316ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
1326ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Layout l = b.build();
1336ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
1346ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.top, fmi.bottom);
1356ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
1366ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
1376ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    /**
1386ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * Basic test showing effect of includePad = true wrapping to 2 lines.
1396ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * Ascent of top line and descent of bottom line are affected.
1406ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     */
141e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanu    @Test
1426ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    public void testGetters3() {
1436ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder b = builder()
1446ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setIncludePad(true)
1456ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setWidth(50);
1466ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        FontMetricsInt fmi = b.paint.getFontMetricsInt();
1476ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
1486ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Layout l =  b.build();
1496ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
1506ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            fmi.top, fmi.descent,
1516ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            fmi.ascent, fmi.bottom);
1526ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
1536ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
1546ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    /**
1556ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * Basic test showing effect of includePad = true wrapping to 3 lines.
1566ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * First line ascent is top, bottom line descent is bottom.
1576ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     */
158e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanu    @Test
1596ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    public void testGetters4() {
1606ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder b = builder()
1616ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setText("This is a longer test")
1626ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setIncludePad(true)
1636ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setWidth(50);
1646ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        FontMetricsInt fmi = b.paint.getFontMetricsInt();
1656ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
1666ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Layout l = b.build();
1676ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
1686ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.top, fmi.descent,
1696ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.ascent, fmi.descent,
1706ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.ascent, fmi.bottom);
1716ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
1726ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
1736ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    /**
1746ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * Basic test showing effect of includePad = true wrapping to 3 lines and
1756ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * large text. See effect of leading. Currently, we don't expect there to
1766ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * even be non-zero leading.
1776ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     */
178e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanu    @Test
1796ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    public void testGetters5() {
1806ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder b = builder()
1816ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setText("This is a longer test")
1826ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setIncludePad(true)
1836ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setWidth(150);
1846ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        b.paint.setTextSize(36);
1856ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        FontMetricsInt fmi = b.paint.getFontMetricsInt();
1866ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
1876ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        if (fmi.leading == 0) { // nothing to test
1886ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            Log.i("TG5", "leading is 0, skipping test");
1896ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            return;
1906ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
1916ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
1926ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        // So far, leading is not used, so this is the same as TG4.  If we start
1936ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        // using leading, this will fail.
1946ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Layout l = b.build();
1956ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
1966ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.top, fmi.descent,
1976ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.ascent, fmi.descent,
1986ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.ascent, fmi.bottom);
1996ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
2006ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
2016ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    /**
2026ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * Basic test showing effect of includePad = true, spacingAdd = 2, wrapping
2036ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * to 3 lines.
2046ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     */
205e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanu    @Test
2066ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    public void testGetters6() {
2076ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        int spacingAdd = 2; // int so expressions return int
2086ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder b = builder()
2096ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setText("This is a longer test")
2106ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setIncludePad(true)
2116ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setWidth(50)
2126ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setSpacingAdd(spacingAdd);
2136ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        FontMetricsInt fmi = b.paint.getFontMetricsInt();
2146ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
2156ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Layout l = b.build();
2166ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
2176ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.top, fmi.descent + spacingAdd,
2186ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.ascent, fmi.descent + spacingAdd,
219ed09ae1fccfb1c02109a0c1428ddff52daa939d5Siyamed Sinir                fmi.ascent, fmi.bottom);
2206ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
2216ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
2226ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    /**
2236ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * Basic test showing effect of includePad = true, spacingAdd = 2,
2246ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * spacingMult = 1.5, wrapping to 3 lines.
2256ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     */
226e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanu    @Test
2276ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    public void testGetters7() {
2286ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder b = builder()
2296ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setText("This is a longer test")
2306ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setIncludePad(true)
2316ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setWidth(50)
2326ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setSpacingAdd(2)
2336ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setSpacingMult(1.5f);
2346ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        FontMetricsInt fmi = b.paint.getFontMetricsInt();
2356ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Scaler s = new Scaler(b.spacingMult, b.spacingAdd);
2366ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
2376ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Layout l = b.build();
2386ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
2396ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top),
2406ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent),
241ed09ae1fccfb1c02109a0c1428ddff52daa939d5Siyamed Sinir                fmi.ascent, fmi.bottom);
2426ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
2436ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
2446ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    /**
2456ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * Basic test showing effect of includePad = true, spacingAdd = 0,
2466ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     * spacingMult = 0.8 when wrapping to 3 lines.
2476ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt     */
248e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanu    @Test
2496ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    public void testGetters8() {
2506ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder b = builder()
2516ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setText("This is a longer test")
2526ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setIncludePad(true)
2536ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setWidth(50)
2546ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setSpacingAdd(2)
2556ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            .setSpacingMult(.8f);
2566ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        FontMetricsInt fmi = b.paint.getFontMetricsInt();
2576ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Scaler s = new Scaler(b.spacingMult, b.spacingAdd);
2586ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
2596ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Layout l = b.build();
2606ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
2616ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top),
2626ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent),
263ed09ae1fccfb1c02109a0c1428ddff52daa939d5Siyamed Sinir                fmi.ascent, fmi.bottom);
2646ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
2656ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
2666ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    // ----- test utility classes and methods -----
2676ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
2686ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    // Models the effect of the scale and add parameters.  I think the current
2696ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    // implementation misbehaves.
2706ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    private static class Scaler {
2716ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        private final float sMult;
2726ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        private final float sAdd;
2736ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
2746ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Scaler(float sMult, float sAdd) {
2756ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            this.sMult = sMult - 1;
2766ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            this.sAdd = sAdd;
2776ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
2786ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
2796ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        public int scale(float height) {
2801065758a0f8966a8597a61492112f7859a7050a4Doug Felt            int altVal = (int)(height * sMult + sAdd + 0.5);
2816ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            int rndVal = Math.round(height * sMult + sAdd);
2826ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            if (altVal != rndVal) {
2836ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                Log.i("Scale", "expected scale: " + rndVal +
2846ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                        " != returned scale: " + altVal);
2856ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            }
2861065758a0f8966a8597a61492112f7859a7050a4Doug Felt            return rndVal;
2876ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
2886ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
2896ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
2909f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    /* package */ static LayoutBuilder builder() {
2916ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        return new LayoutBuilder();
2926ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
2936ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
2949f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    /* package */ static class LayoutBuilder {
2956ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        String text = "This is a test";
2966ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        TextPaint paint = new TextPaint(); // default
2976ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        int width = 100;
2986ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        Alignment align = ALIGN_NORMAL;
2996ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        float spacingMult = 1;
3006ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        float spacingAdd = 0;
3016ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        boolean includePad = false;
3026ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3036ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder setText(String text) {
3046ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            this.text = text;
3056ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            return this;
3066ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
3076ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3086ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder setPaint(TextPaint paint) {
3096ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            this.paint = paint;
3106ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            return this;
3116ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
3126ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3136ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder setWidth(int width) {
3146ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            this.width = width;
3156ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            return this;
3166ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
3176ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3186ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder setAlignment(Alignment align) {
3196ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            this.align = align;
3206ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            return this;
3216ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
3226ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3236ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder setSpacingMult(float spacingMult) {
3246ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            this.spacingMult = spacingMult;
3256ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            return this;
3266ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
3276ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3286ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder setSpacingAdd(float spacingAdd) {
3296ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            this.spacingAdd = spacingAdd;
3306ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            return this;
3316ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
3326ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3336ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        LayoutBuilder setIncludePad(boolean includePad) {
3346ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            this.includePad = includePad;
3356ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            return this;
3366ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
3376ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3386ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt       Layout build() {
3396ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            return  new StaticLayout(text, paint, width, align, spacingMult,
3406ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt                spacingAdd, includePad);
3416ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
3426ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
3436ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3446ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    private void assertVertMetrics(Layout l, int topPad, int botPad, int... values) {
3456ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertTopBotPadding(l, topPad, botPad);
3466ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertLinesMetrics(l, values);
3476ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
3486ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3496ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    private void assertLinesMetrics(Layout l, int... values) {
3506ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        // sanity check
3516ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        if ((values.length & 0x1) != 0) {
3526ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            throw new IllegalArgumentException(String.valueOf(values.length));
3536ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
3546ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3556ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        int lines = values.length >> 1;
3566ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(lines, l.getLineCount());
3576ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3586ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        int t = 0;
3596ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        for (int i = 0, n = 0; i < lines; ++i, n += 2) {
3606ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            int a = values[n];
3616ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            int d = values[n+1];
3626ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            int h = -a + d;
3636ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            assertLineMetrics(l, i, t, a, d, h);
3646ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            t += h;
3656ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        }
3666ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3676ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(t, l.getHeight());
3686ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
3696ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3706ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    private void assertLineMetrics(Layout l, int line,
3716ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt            int top, int ascent, int descent, int height) {
3726ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        String info = "line " + line;
3736ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(info, top, l.getLineTop(line));
3746ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(info, ascent, l.getLineAscent(line));
3756ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(info, descent, l.getLineDescent(line));
3766ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(info, height, l.getLineBottom(line) - top);
3776ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
3786ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt
3796ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    private void assertTopBotPadding(Layout l, int topPad, int botPad) {
3806ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(topPad, l.getTopPadding());
3816ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt        assertEquals(botPad, l.getBottomPadding());
3826ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt    }
383f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka
384f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka    private void moveCursorToRightCursorableOffset(EditorState state, TextPaint paint) {
385f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        assertEquals("The editor has selection", state.mSelectionStart, state.mSelectionEnd);
386f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        final Layout layout = builder().setText(state.mText.toString()).setPaint(paint).build();
387f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        final int newOffset = layout.getOffsetToRightOf(state.mSelectionStart);
388f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.mSelectionStart = state.mSelectionEnd = newOffset;
389f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka    }
390f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka
391f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka    private void moveCursorToLeftCursorableOffset(EditorState state, TextPaint paint) {
392f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        assertEquals("The editor has selection", state.mSelectionStart, state.mSelectionEnd);
393f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        final Layout layout = builder().setText(state.mText.toString()).setPaint(paint).build();
394f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        final int newOffset = layout.getOffsetToLeftOf(state.mSelectionStart);
395f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.mSelectionStart = state.mSelectionEnd = newOffset;
396f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka    }
397f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka
398f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka    /**
399f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka     * Tests for keycap, variation selectors, flags are in CTS.
400f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka     * See {@link android.text.cts.StaticLayoutTest}.
401f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka     */
402e1a7d0d7f7d02edf4e68cd9d3e90154f844bacf9Andrei Stingaceanu    @Test
403f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka    public void testEmojiOffset() {
404f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        EditorState state = new EditorState();
405f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        TextPaint paint = new TextPaint();
406f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka
407f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        // Odd numbered regional indicator symbols.
408f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        // U+1F1E6 is REGIONAL INDICATOR SYMBOL LETTER A, U+1F1E8 is REGIONAL INDICATOR SYMBOL
409f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        // LETTER C.
410f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("| U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 U+1F1E6");
411f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToRightCursorableOffset(state, paint);
412f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("U+1F1E6 U+1F1E8 | U+1F1E6 U+1F1E8 U+1F1E6");
413f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToRightCursorableOffset(state, paint);
414f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 | U+1F1E6");
415f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToRightCursorableOffset(state, paint);
416f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 U+1F1E6 |");
417f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToRightCursorableOffset(state, paint);
418f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 U+1F1E6 |");
419f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
420f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 | U+1F1E6");
421f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
422f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("U+1F1E6 U+1F1E8 | U+1F1E6 U+1F1E8 U+1F1E6");
423f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
424f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("| U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 U+1F1E6");
425f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
426f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("| U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 U+1F1E6");
427f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
428f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka
429f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        // Zero width sequence
430f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        final String zwjSequence = "U+1F468 U+200D U+2764 U+FE0F U+200D U+1F468";
431f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("| " + zwjSequence + " " + zwjSequence + " " + zwjSequence);
432f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToRightCursorableOffset(state, paint);
433f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.assertEquals(zwjSequence + " | " + zwjSequence + " " + zwjSequence);
434f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToRightCursorableOffset(state, paint);
435f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.assertEquals(zwjSequence + " " + zwjSequence + " | " + zwjSequence);
436f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToRightCursorableOffset(state, paint);
437f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.assertEquals(zwjSequence + " " + zwjSequence + " " + zwjSequence + " |");
438f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToRightCursorableOffset(state, paint);
439f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.assertEquals(zwjSequence + " " + zwjSequence + " " + zwjSequence + " |");
440f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
441f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.assertEquals(zwjSequence + " " + zwjSequence + " | " + zwjSequence);
442f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
443f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.assertEquals(zwjSequence + " | " + zwjSequence + " " + zwjSequence);
444f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
445f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.assertEquals("| " + zwjSequence + " " + zwjSequence + " " + zwjSequence);
446f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
447f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.assertEquals("| " + zwjSequence + " " + zwjSequence + " " + zwjSequence);
448f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
449f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka
450f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        // Emoji modifiers
451f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        // U+261D is WHITE UP POINTING INDEX, U+1F3FB is EMOJI MODIFIER FITZPATRICK TYPE-1-2.
452f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("| U+261D U+1F3FB U+261D U+1F3FB U+261D U+1F3FB");
453f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToRightCursorableOffset(state, paint);
454f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("U+261D U+1F3FB | U+261D U+1F3FB U+261D U+1F3FB");
455f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToRightCursorableOffset(state, paint);
456f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("U+261D U+1F3FB U+261D U+1F3FB | U+261D U+1F3FB");
457f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToRightCursorableOffset(state, paint);
458f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("U+261D U+1F3FB U+261D U+1F3FB U+261D U+1F3FB |");
459f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToRightCursorableOffset(state, paint);
460f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("U+261D U+1F3FB U+261D U+1F3FB U+261D U+1F3FB |");
461f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
462f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("U+261D U+1F3FB U+261D U+1F3FB | U+261D U+1F3FB");
463f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
464f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("U+261D U+1F3FB | U+261D U+1F3FB U+261D U+1F3FB");
465f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
466f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("| U+261D U+1F3FB U+261D U+1F3FB U+261D U+1F3FB");
467f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
468f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        state.setByString("| U+261D U+1F3FB U+261D U+1F3FB U+261D U+1F3FB");
469f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka        moveCursorToLeftCursorableOffset(state, paint);
470f4afb095a0073f91c877a79e68b03e7939a9d76dSeigo Nonaka    }
47102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
47202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private StaticLayout createEllipsizeStaticLayout(CharSequence text,
47302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            TextUtils.TruncateAt ellipsize, int maxLines) {
47402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        return new StaticLayout(text, 0, text.length(),
47502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                mDefaultPaint, DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN,
47602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                TextDirectionHeuristics.FIRSTSTRONG_LTR,
47702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                SPACE_MULTI, SPACE_ADD, true /* include pad */,
47802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                ellipsize,
47902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                ELLIPSIZE_WIDTH,
48002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                maxLines);
48102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    }
48202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
48302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    @Test
48402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    public void testEllipsis_singleLine() {
48502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        {
48602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            // Single line case and TruncateAt.END so that we have some ellipsis
48702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            StaticLayout layout = createEllipsizeStaticLayout(LAYOUT_TEXT_SINGLE_LINE,
48802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    TextUtils.TruncateAt.END, 1);
48902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertTrue(layout.getEllipsisCount(0) > 0);
49002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
49102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        {
49202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            // Single line case and TruncateAt.MIDDLE so that we have some ellipsis
49302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            StaticLayout layout = createEllipsizeStaticLayout(LAYOUT_TEXT_SINGLE_LINE,
49402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    TextUtils.TruncateAt.MIDDLE, 1);
49502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertTrue(layout.getEllipsisCount(0) > 0);
49602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
49702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        {
49802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            // Single line case and TruncateAt.END so that we have some ellipsis
49902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            StaticLayout layout = createEllipsizeStaticLayout(LAYOUT_TEXT_SINGLE_LINE,
50002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    TextUtils.TruncateAt.END, 1);
50102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertTrue(layout.getEllipsisCount(0) > 0);
50202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
50302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        {
50402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            // Single line case and TruncateAt.MARQUEE so that we have NO ellipsis
50502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            StaticLayout layout = createEllipsizeStaticLayout(LAYOUT_TEXT_SINGLE_LINE,
50602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    TextUtils.TruncateAt.MARQUEE, 1);
50702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertTrue(layout.getEllipsisCount(0) == 0);
50802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
50902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        {
51002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final String text = "\u3042" // HIRAGANA LETTER A
51102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
51202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final float textWidth = mDefaultPaint.measureText(text);
51302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final int halfWidth = (int) (textWidth / 2.0f);
51402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            {
51502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
51602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                        halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
51702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                        SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.END, halfWidth, 1);
51802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                assertTrue(layout.getEllipsisCount(0) > 0);
51902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                assertTrue(layout.getEllipsisStart(0) > 0);
52002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            }
52102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            {
52202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
52302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                        halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
52402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                        SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.START, halfWidth, 1);
52502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                assertTrue(layout.getEllipsisCount(0) > 0);
52602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                assertEquals(0, mDefaultLayout.getEllipsisStart(0));
52702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            }
52802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            {
52902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
53002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                        halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
53102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                        SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.MIDDLE, halfWidth, 1);
53202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                assertTrue(layout.getEllipsisCount(0) > 0);
53302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                assertTrue(layout.getEllipsisStart(0) > 0);
53402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            }
53502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            {
53602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
53702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                        halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
53802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                        SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.MARQUEE, halfWidth, 1);
53902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                assertEquals(0, layout.getEllipsisCount(0));
54002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            }
54102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
54202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
54302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        {
54402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            // The white spaces in this text will be trailing if maxLines is larger than 1, but
54502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            // width of the trailing white spaces must not be ignored if ellipsis is applied.
54602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final String text = "abc                                             def";
54702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final float textWidth = mDefaultPaint.measureText(text);
54802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final int halfWidth = (int) (textWidth / 2.0f);
54902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            {
55002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
55102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                        halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
55202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                        SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.END, halfWidth, 1);
55302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                assertTrue(layout.getEllipsisCount(0) > 0);
55402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                assertTrue(layout.getEllipsisStart(0) > 0);
55502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            }
55602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
55702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
55802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        {
55902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            // 2 family emojis (11 code units + 11 code units).
56002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final String text = "\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC67\u200D\uD83D\uDC66"
56102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    + "\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC67\u200D\uD83D\uDC66";
56202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final float textWidth = mDefaultPaint.measureText(text);
56302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
56402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            final TextUtils.TruncateAt[] kinds = {TextUtils.TruncateAt.START,
56502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    TextUtils.TruncateAt.MIDDLE, TextUtils.TruncateAt.END};
56602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            for (final TextUtils.TruncateAt kind : kinds) {
56702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                for (int i = 0; i <= 8; i++) {
56802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    int avail = (int) (textWidth * i / 7.0f);
56902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
57002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                            avail, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
57102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                            SPACE_MULTI, SPACE_ADD, false, kind, avail, 1);
57202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
57302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    assertTrue(layout.getEllipsisCount(0) == text.length()
57402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                                    || layout.getEllipsisCount(0) == text.length() / 2
57502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                                    || layout.getEllipsisCount(0) == 0);
57602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                }
57702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            }
57802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
57902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    }
58002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
58102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    // String wrapper for testing not well known implementation of CharSequence.
58202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private class FakeCharSequence implements CharSequence {
58302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        private String mStr;
58402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
58502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        FakeCharSequence(String str) {
58602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            mStr = str;
58702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
58802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
58902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        @Override
59002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        public char charAt(int index) {
59102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            return mStr.charAt(index);
59202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
59302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
59402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        @Override
59502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        public int length() {
59602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            return mStr.length();
59702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
59802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
59902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        @Override
60002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        public CharSequence subSequence(int start, int end) {
60102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            return mStr.subSequence(start, end);
60202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
60302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
60402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        @Override
60502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        public String toString() {
60602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            return mStr;
60702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
60802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    };
60902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
61002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private List<CharSequence> buildTestCharSequences(String testString, Normalizer.Form[] forms) {
61102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        List<CharSequence> result = new ArrayList<>();
61202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
61302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        List<String> normalizedStrings = new ArrayList<>();
61402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        for (Normalizer.Form form: forms) {
61502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            normalizedStrings.add(Normalizer.normalize(testString, form));
61602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
61702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
61802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        for (String str: normalizedStrings) {
61902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            result.add(str);
62002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            result.add(new SpannedString(str));
62102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            result.add(new SpannableString(str));
62202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            result.add(new SpannableStringBuilder(str));  // as a GraphicsOperations implementation.
62302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            result.add(new FakeCharSequence(str));  // as a not well known implementation.
62402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
62502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        return result;
62602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    }
62702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
62802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    private String buildTestMessage(CharSequence seq) {
62902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        String normalized;
63002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        if (Normalizer.isNormalized(seq, Normalizer.Form.NFC)) {
63102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            normalized = "NFC";
63202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        } else if (Normalizer.isNormalized(seq, Normalizer.Form.NFD)) {
63302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            normalized = "NFD";
63402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        } else if (Normalizer.isNormalized(seq, Normalizer.Form.NFKC)) {
63502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            normalized = "NFKC";
63602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        } else if (Normalizer.isNormalized(seq, Normalizer.Form.NFKD)) {
63702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            normalized = "NFKD";
63802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        } else {
63902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            throw new IllegalStateException("Normalized form is not NFC/NFD/NFKC/NFKD");
64002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
64102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
64202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        StringBuilder builder = new StringBuilder();
64302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        for (int i = 0; i < seq.length(); ++i) {
64402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            builder.append(String.format("0x%04X ", Integer.valueOf(seq.charAt(i))));
64502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
64602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
64702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        return "testString: \"" + seq.toString() + "\"[" + builder.toString() + "]"
64802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                + ", class: " + seq.getClass().getName()
64902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                + ", Normalization: " + normalized;
65002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    }
65102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
65202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    @Test
65302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    public void testGetOffset_UNICODE_Hebrew() {
65402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        String testString = "\u05DE\u05E1\u05E2\u05D3\u05D4"; // Hebrew Characters
65502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
65602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
65702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN,
65802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader                    TextDirectionHeuristics.RTL, SPACE_MULTI, SPACE_ADD, true);
65902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
66002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            String testLabel = buildTestMessage(seq);
66102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
66202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(testLabel, 1, layout.getOffsetToLeftOf(0));
66302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(testLabel, 2, layout.getOffsetToLeftOf(1));
66402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(testLabel, 3, layout.getOffsetToLeftOf(2));
66502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(testLabel, 4, layout.getOffsetToLeftOf(3));
66602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(testLabel, 5, layout.getOffsetToLeftOf(4));
66702f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(testLabel, 5, layout.getOffsetToLeftOf(5));
66802f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader
66902f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(testLabel, 0, layout.getOffsetToRightOf(0));
67002f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(testLabel, 0, layout.getOffsetToRightOf(1));
67102f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(testLabel, 1, layout.getOffsetToRightOf(2));
67202f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(testLabel, 2, layout.getOffsetToRightOf(3));
67302f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(testLabel, 3, layout.getOffsetToRightOf(4));
67402f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader            assertEquals(testLabel, 4, layout.getOffsetToRightOf(5));
67502f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader        }
67602f167c35ad81fce82c0ebc14a15892fb6981866Roozbeh Pournader    }
677ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader
678ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader    @Test
679ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader    public void testLocaleSpanAffectsHyphenation() {
680ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        TextPaint paint = new TextPaint();
681ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        paint.setTextLocale(Locale.US);
682ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        // Private use language, with no hyphenation rules.
683ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        final Locale privateLocale = Locale.forLanguageTag("qaa");
684ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader
685ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        final String longWord = "philanthropic";
686ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        final float wordWidth = paint.measureText(longWord);
687ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        // Wide enough that words get hyphenated by default.
688ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        final int paraWidth = Math.round(wordWidth * 1.8f);
689ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        final String sentence = longWord + " " + longWord + " " + longWord + " " + longWord + " "
690ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                + longWord + " " + longWord;
691ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader
692ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        final int numEnglishLines = StaticLayout.Builder
693ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                .obtain(sentence, 0, sentence.length(), paint, paraWidth)
694ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
695ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
696ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                .build()
697ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                .getLineCount();
698ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader
699ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        {
700ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            final SpannableString text = new SpannableString(sentence);
701ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            text.setSpan(new LocaleSpan(privateLocale), 0, text.length(),
702ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    Spanned.SPAN_INCLUSIVE_INCLUSIVE);
703ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            final int numPrivateLocaleLines = StaticLayout.Builder
704ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .obtain(text, 0, text.length(), paint, paraWidth)
705ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
706ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
707ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .build()
708ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .getLineCount();
709ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader
710ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            // Since the paragraph set to English gets hyphenated, the number of lines would be
711ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            // smaller than the number of lines when there is a span setting a language that
712ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            // doesn't get hyphenated.
713ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            assertTrue(numEnglishLines < numPrivateLocaleLines);
714ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        }
715ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        {
716ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            // Same as the above test, except that the locale span now uses a locale list starting
717ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            // with the private non-hyphenating locale.
718ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            final SpannableString text = new SpannableString(sentence);
719ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            final LocaleList locales = new LocaleList(privateLocale, Locale.US);
720ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            text.setSpan(new LocaleSpan(locales), 0, text.length(),
721ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    Spanned.SPAN_INCLUSIVE_INCLUSIVE);
722ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            final int numPrivateLocaleLines = StaticLayout.Builder
723ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .obtain(text, 0, text.length(), paint, paraWidth)
724ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
725ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
726ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .build()
727ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .getLineCount();
728ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader
729ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            assertTrue(numEnglishLines < numPrivateLocaleLines);
730ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        }
731ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        {
732ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            final SpannableString text = new SpannableString(sentence);
733ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            // Apply the private LocaleSpan only to the first word, which is not getting hyphenated
734ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            // anyway.
735ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            text.setSpan(new LocaleSpan(privateLocale), 0, longWord.length(),
736ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    Spanned.SPAN_INCLUSIVE_INCLUSIVE);
737ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            final int numPrivateLocaleLines = StaticLayout.Builder
738ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .obtain(text, 0, text.length(), paint, paraWidth)
739ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
740ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
741ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .build()
742ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader                    .getLineCount();
743ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader
744ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            // Since the first word is not hyphenated anyway (there's enough width), the LocaleSpan
745ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            // should not affect the layout.
746ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader            assertEquals(numEnglishLines, numPrivateLocaleLines);
747ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader        }
748ef7cfa17e78d05b8d931d839d25261c459a0fc90Roozbeh Pournader    }
749261118009def6217cdd70e3b3e76ef4648a223f5Siyamed Sinir
750261118009def6217cdd70e3b3e76ef4648a223f5Siyamed Sinir    @Test
751261118009def6217cdd70e3b3e76ef4648a223f5Siyamed Sinir    public void testGetHeight_zeroMaxLines() {
752261118009def6217cdd70e3b3e76ef4648a223f5Siyamed Sinir        final String text = "a\nb";
753261118009def6217cdd70e3b3e76ef4648a223f5Siyamed Sinir        final TextPaint paint = new TextPaint();
754261118009def6217cdd70e3b3e76ef4648a223f5Siyamed Sinir        final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), paint,
755261118009def6217cdd70e3b3e76ef4648a223f5Siyamed Sinir                Integer.MAX_VALUE).setMaxLines(0).build();
756261118009def6217cdd70e3b3e76ef4648a223f5Siyamed Sinir
757261118009def6217cdd70e3b3e76ef4648a223f5Siyamed Sinir        assertEquals(0, layout.getHeight(true));
758261118009def6217cdd70e3b3e76ef4648a223f5Siyamed Sinir        assertEquals(2, layout.getLineCount());
759261118009def6217cdd70e3b3e76ef4648a223f5Siyamed Sinir    }
7606ad5a7a7c78799ecb306cb97d979bdb98cc52d15Doug Felt}
761