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