1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.text.cts;
18 
19 import android.test.AndroidTestCase;
20 import android.text.Editable;
21 import android.text.GetChars;
22 import android.text.GraphicsOperations;
23 import android.text.Layout.Alignment;
24 import android.text.TextUtils.TruncateAt;
25 import android.text.SpannableString;
26 import android.text.SpannableStringBuilder;
27 import android.text.SpannedString;
28 import android.text.StaticLayout;
29 import android.text.TextDirectionHeuristics;
30 import android.text.TextPaint;
31 import android.text.TextUtils;
32 
33 import java.text.Normalizer;
34 import java.util.ArrayList;
35 import java.util.List;
36 
37 public class StaticLayoutTest extends AndroidTestCase {
38     private static final float SPACE_MULTI = 1.0f;
39     private static final float SPACE_ADD = 0.0f;
40     private static final int DEFAULT_OUTER_WIDTH = 150;
41 
42     private static final int LAST_LINE = 5;
43     private static final int LINE_COUNT = 6;
44     private static final int LARGER_THAN_LINE_COUNT  = 50;
45 
46     /* the first line must have one tab. the others not. totally 6 lines
47      */
48     private static final CharSequence LAYOUT_TEXT = "CharSe\tq\nChar"
49             + "Sequence\nCharSequence\nHelllo\n, world\nLongLongLong";
50 
51     private static final CharSequence LAYOUT_TEXT_SINGLE_LINE = "CharSequence";
52 
53     private static final int VERTICAL_BELOW_TEXT = 1000;
54 
55     private static final Alignment DEFAULT_ALIGN = Alignment.ALIGN_CENTER;
56 
57     private static final int ELLIPSIZE_WIDTH = 8;
58 
59     private StaticLayout mDefaultLayout;
60     private TextPaint mDefaultPaint;
61 
62     @Override
setUp()63     protected void setUp() throws Exception {
64         super.setUp();
65         if (mDefaultPaint == null) {
66             mDefaultPaint = new TextPaint();
67         }
68         if (mDefaultLayout == null) {
69             mDefaultLayout = createDefaultStaticLayout();
70         }
71     }
72 
createDefaultStaticLayout()73     private StaticLayout createDefaultStaticLayout() {
74         return new StaticLayout(LAYOUT_TEXT, mDefaultPaint,
75                 DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
76     }
77 
createEllipsizeStaticLayout()78     private StaticLayout createEllipsizeStaticLayout() {
79         return new StaticLayout(LAYOUT_TEXT, 0, LAYOUT_TEXT.length(), mDefaultPaint,
80                 DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true,
81                 TextUtils.TruncateAt.MIDDLE, ELLIPSIZE_WIDTH);
82     }
83 
createEllipsizeStaticLayout(CharSequence text, TextUtils.TruncateAt ellipsize, int maxLines)84     private StaticLayout createEllipsizeStaticLayout(CharSequence text,
85             TextUtils.TruncateAt ellipsize, int maxLines) {
86         return new StaticLayout(text, 0, text.length(),
87                 mDefaultPaint, DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN,
88                 TextDirectionHeuristics.FIRSTSTRONG_LTR,
89                 SPACE_MULTI, SPACE_ADD, true /* include pad */,
90                 ellipsize,
91                 ELLIPSIZE_WIDTH,
92                 maxLines);
93     }
94 
95 
96 
97     /**
98      * Constructor test
99      */
testConstructor()100     public void testConstructor() {
101         new StaticLayout(LAYOUT_TEXT, mDefaultPaint, DEFAULT_OUTER_WIDTH,
102                 DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
103 
104         new StaticLayout(LAYOUT_TEXT, 0, LAYOUT_TEXT.length(), mDefaultPaint,
105                 DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
106 
107         new StaticLayout(LAYOUT_TEXT, 0, LAYOUT_TEXT.length(), mDefaultPaint,
108                 DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, false, null, 0);
109 
110         try {
111             new StaticLayout(null, null, -1, null, 0, 0, true);
112             fail("should throw NullPointerException here");
113         } catch (NullPointerException e) {
114         }
115     }
116 
testBuilder()117     public void testBuilder() {
118         {
119             // Obtain.
120             StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
121                     LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
122             StaticLayout layout = builder.build();
123             // Check values passed to obtain().
124             assertEquals(LAYOUT_TEXT, layout.getText());
125             assertEquals(mDefaultPaint, layout.getPaint());
126             assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth());
127             // Check default values.
128             assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR,
129                     layout.getTextDirectionHeuristic());
130             assertEquals(Alignment.ALIGN_NORMAL, layout.getAlignment());
131             assertEquals(0.0f, layout.getSpacingAdd());
132             assertEquals(1.0f, layout.getSpacingMultiplier());
133             assertEquals(DEFAULT_OUTER_WIDTH, layout.getEllipsizedWidth());
134         }
135         {
136             // Obtain with null objects.
137             StaticLayout.Builder builder = StaticLayout.Builder.obtain(null, 0, 0, null, 0);
138             try {
139                 StaticLayout layout = builder.build();
140                 fail("should throw NullPointerException here");
141             } catch (NullPointerException e) {
142             }
143         }
144         {
145             // setText.
146             StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
147                     LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
148             builder.setText(LAYOUT_TEXT_SINGLE_LINE);
149             StaticLayout layout = builder.build();
150             assertEquals(LAYOUT_TEXT_SINGLE_LINE, layout.getText());
151         }
152         {
153             // setAlignment.
154             StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
155                     LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
156             builder.setAlignment(DEFAULT_ALIGN);
157             StaticLayout layout = builder.build();
158             assertEquals(DEFAULT_ALIGN, layout.getAlignment());
159         }
160         {
161             // setTextDirection.
162             StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
163                     LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
164             builder.setTextDirection(TextDirectionHeuristics.RTL);
165             StaticLayout layout = builder.build();
166             // Always returns TextDirectionHeuristics.FIRSTSTRONG_LTR.
167             assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR,
168                     layout.getTextDirectionHeuristic());
169         }
170         {
171             // setLineSpacing.
172             StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
173                     LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
174             builder.setLineSpacing(1.0f, 2.0f);
175             StaticLayout layout = builder.build();
176             assertEquals(1.0f, layout.getSpacingAdd());
177             assertEquals(2.0f, layout.getSpacingMultiplier());
178         }
179         {
180             // setEllipsizedWidth and setEllipsize.
181             StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
182                     LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
183             builder.setEllipsize(TruncateAt.END);
184             builder.setEllipsizedWidth(ELLIPSIZE_WIDTH);
185             StaticLayout layout = builder.build();
186             assertEquals(ELLIPSIZE_WIDTH, layout.getEllipsizedWidth());
187             assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth());
188             assertTrue(layout.getEllipsisCount(0) == 0);
189             assertTrue(layout.getEllipsisCount(5) > 0);
190         }
191         {
192             // setMaxLines.
193             StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
194                     LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
195             builder.setMaxLines(1);
196             builder.setEllipsize(TruncateAt.END);
197             StaticLayout layout = builder.build();
198             assertTrue(layout.getEllipsisCount(0) > 0);
199             assertEquals(1, layout.getLineCount());
200         }
201         {
202             // Setter methods that cannot be directly tested.
203             // setBreakStrategy, setHyphenationFrequency, setIncludePad, and setIndents.
204             StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
205                     LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
206             builder.setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY);
207             builder.setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_FULL);
208             builder.setIncludePad(true);
209             builder.setIndents(null, null);
210             StaticLayout layout = builder.build();
211             assertNotNull(layout);
212         }
213     }
214 
215     /*
216      * Get the line number corresponding to the specified vertical position.
217      *  If you ask for a position above 0, you get 0. above 0 means pixel above the fire line
218      *  if you ask for a position in the range of the height, return the pixel in line
219      *  if you ask for a position below the bottom of the text, you get the last line.
220      *  Test 4 values containing -1, 0, normal number and > count
221      */
testGetLineForVertical()222     public void testGetLineForVertical() {
223         assertEquals(0, mDefaultLayout.getLineForVertical(-1));
224         assertEquals(0, mDefaultLayout.getLineForVertical(0));
225         assertTrue(mDefaultLayout.getLineForVertical(50) > 0);
226         assertEquals(LAST_LINE, mDefaultLayout.getLineForVertical(VERTICAL_BELOW_TEXT));
227     }
228 
229     /**
230      * Return the number of lines of text in this layout.
231      */
testGetLineCount()232     public void testGetLineCount() {
233         assertEquals(LINE_COUNT, mDefaultLayout.getLineCount());
234     }
235 
236     /*
237      * Return the vertical position of the top of the specified line.
238      * If the specified line is one beyond the last line, returns the bottom of the last line.
239      * A line of text contains top and bottom in height. this method just get the top of a line
240      * Test 4 values containing -1, 0, normal number and > count
241      */
testGetLineTop()242     public void testGetLineTop() {
243         assertTrue(mDefaultLayout.getLineTop(0) >= 0);
244         assertTrue(mDefaultLayout.getLineTop(1) > mDefaultLayout.getLineTop(0));
245 
246         try {
247             mDefaultLayout.getLineTop(-1);
248             fail("should throw ArrayIndexOutOfBoundsException");
249         } catch (ArrayIndexOutOfBoundsException e) {
250         }
251 
252         try {
253             mDefaultLayout.getLineTop(LARGER_THAN_LINE_COUNT );
254             fail("should throw ArrayIndexOutOfBoundsException");
255         } catch (ArrayIndexOutOfBoundsException e) {
256         }
257     }
258 
259     /**
260      * Return the descent of the specified line.
261      * This method just like getLineTop, descent means the bottom pixel of the line
262      * Test 4 values containing -1, 0, normal number and > count
263      */
testGetLineDescent()264     public void testGetLineDescent() {
265         assertTrue(mDefaultLayout.getLineDescent(0) > 0);
266         assertTrue(mDefaultLayout.getLineDescent(1) > 0);
267 
268         try {
269             mDefaultLayout.getLineDescent(-1);
270             fail("should throw ArrayIndexOutOfBoundsException");
271         } catch (ArrayIndexOutOfBoundsException e) {
272         }
273 
274         try {
275             mDefaultLayout.getLineDescent(LARGER_THAN_LINE_COUNT );
276             fail("should throw ArrayIndexOutOfBoundsException");
277         } catch (ArrayIndexOutOfBoundsException e) {
278         }
279     }
280 
281     /**
282      * Returns the primary directionality of the paragraph containing the specified line.
283      * By default, each line should be same
284      */
testGetParagraphDirection()285     public void testGetParagraphDirection() {
286         assertEquals(mDefaultLayout.getParagraphDirection(0),
287                 mDefaultLayout.getParagraphDirection(1));
288         try {
289             mDefaultLayout.getParagraphDirection(-1);
290             fail("should throw ArrayIndexOutOfBoundsException");
291         } catch (ArrayIndexOutOfBoundsException e) {
292         }
293 
294         try {
295             mDefaultLayout.getParagraphDirection(LARGER_THAN_LINE_COUNT);
296             fail("should throw ArrayIndexOutOfBoundsException");
297         } catch (ArrayIndexOutOfBoundsException e) {
298         }
299     }
300 
301     /**
302      * Return the text offset of the beginning of the specified line.
303      * If the specified line is one beyond the last line, returns the end of the last line.
304      * Test 4 values containing -1, 0, normal number and > count
305      * Each line's offset must >= 0
306      */
testGetLineStart()307     public void testGetLineStart() {
308         assertTrue(mDefaultLayout.getLineStart(0) >= 0);
309         assertTrue(mDefaultLayout.getLineStart(1) >= 0);
310 
311         try {
312             mDefaultLayout.getLineStart(-1);
313             fail("should throw ArrayIndexOutOfBoundsException");
314         } catch (ArrayIndexOutOfBoundsException e) {
315         }
316 
317         try {
318             mDefaultLayout.getLineStart(LARGER_THAN_LINE_COUNT);
319             fail("should throw ArrayIndexOutOfBoundsException");
320         } catch (ArrayIndexOutOfBoundsException e) {
321         }
322     }
323 
324     /*
325      * Returns whether the specified line contains one or more tabs.
326      */
testGetContainsTab()327     public void testGetContainsTab() {
328         assertTrue(mDefaultLayout.getLineContainsTab(0));
329         assertFalse(mDefaultLayout.getLineContainsTab(1));
330 
331         try {
332             mDefaultLayout.getLineContainsTab(-1);
333             fail("should throw ArrayIndexOutOfBoundsException");
334         } catch (ArrayIndexOutOfBoundsException e) {
335         }
336 
337         try {
338             mDefaultLayout.getLineContainsTab(LARGER_THAN_LINE_COUNT );
339             fail("should throw ArrayIndexOutOfBoundsException");
340         } catch (ArrayIndexOutOfBoundsException e) {
341         }
342     }
343 
344     /**
345      * Returns an array of directionalities for the specified line.
346      * The array alternates counts of characters in left-to-right
347      * and right-to-left segments of the line.
348      * We can not check the return value, for Directions's field is package private
349      * So only check it not null
350      */
testGetLineDirections()351     public void testGetLineDirections() {
352         assertNotNull(mDefaultLayout.getLineDirections(0));
353         assertNotNull(mDefaultLayout.getLineDirections(1));
354 
355         try {
356             mDefaultLayout.getLineDirections(-1);
357             fail("should throw ArrayIndexOutOfBoundsException");
358         } catch (ArrayIndexOutOfBoundsException e) {
359         }
360 
361         try {
362             mDefaultLayout.getLineDirections(LARGER_THAN_LINE_COUNT);
363             fail("should throw ArrayIndexOutOfBoundsException");
364         } catch (ArrayIndexOutOfBoundsException e) {
365         }
366     }
367 
368     /**
369      * Returns the (negative) number of extra pixels of ascent padding
370      * in the top line of the Layout.
371      */
testGetTopPadding()372     public void testGetTopPadding() {
373         assertTrue(mDefaultLayout.getTopPadding() < 0);
374     }
375 
376     /**
377      * Returns the number of extra pixels of descent padding in the bottom line of the Layout.
378      */
379     public void testGetBottomPadding() {
380         assertTrue(mDefaultLayout.getBottomPadding() > 0);
381     }
382 
383     /*
384      * Returns the number of characters to be ellipsized away, or 0 if no ellipsis is to take place.
385      * So each line must >= 0
386      */
testGetEllipsisCount()387     public void testGetEllipsisCount() {
388         // Multilines (6 lines) and TruncateAt.START so no ellipsis at all
389         mDefaultLayout = createEllipsizeStaticLayout(LAYOUT_TEXT,
390                 TextUtils.TruncateAt.MIDDLE,
391                 Integer.MAX_VALUE /* maxLines */);
392 
393         assertTrue(mDefaultLayout.getEllipsisCount(0) == 0);
394         assertTrue(mDefaultLayout.getEllipsisCount(1) == 0);
395         assertTrue(mDefaultLayout.getEllipsisCount(2) == 0);
396         assertTrue(mDefaultLayout.getEllipsisCount(3) == 0);
397         assertTrue(mDefaultLayout.getEllipsisCount(4) == 0);
398         assertTrue(mDefaultLayout.getEllipsisCount(5) == 0);
399 
400         try {
401             mDefaultLayout.getEllipsisCount(-1);
402             fail("should throw ArrayIndexOutOfBoundsException");
403         } catch (ArrayIndexOutOfBoundsException e) {
404         }
405 
406         try {
407             mDefaultLayout.getEllipsisCount(LARGER_THAN_LINE_COUNT);
408             fail("should throw ArrayIndexOutOfBoundsException");
409         } catch (ArrayIndexOutOfBoundsException e) {
410         }
411 
412         // Multilines (6 lines) and TruncateAt.MIDDLE so no ellipsis at all
413         mDefaultLayout = createEllipsizeStaticLayout(LAYOUT_TEXT,
414                 TextUtils.TruncateAt.MIDDLE,
415                 Integer.MAX_VALUE /* maxLines */);
416 
417         assertTrue(mDefaultLayout.getEllipsisCount(0) == 0);
418         assertTrue(mDefaultLayout.getEllipsisCount(1) == 0);
419         assertTrue(mDefaultLayout.getEllipsisCount(2) == 0);
420         assertTrue(mDefaultLayout.getEllipsisCount(3) == 0);
421         assertTrue(mDefaultLayout.getEllipsisCount(4) == 0);
422         assertTrue(mDefaultLayout.getEllipsisCount(5) == 0);
423 
424         // Multilines (6 lines) and TruncateAt.END so ellipsis only on the last line
425         mDefaultLayout = createEllipsizeStaticLayout(LAYOUT_TEXT,
426                 TextUtils.TruncateAt.END,
427                 Integer.MAX_VALUE /* maxLines */);
428 
429         assertTrue(mDefaultLayout.getEllipsisCount(0) == 0);
430         assertTrue(mDefaultLayout.getEllipsisCount(1) == 0);
431         assertTrue(mDefaultLayout.getEllipsisCount(2) == 0);
432         assertTrue(mDefaultLayout.getEllipsisCount(3) == 0);
433         assertTrue(mDefaultLayout.getEllipsisCount(4) == 0);
434         assertTrue(mDefaultLayout.getEllipsisCount(5) > 0);
435 
436         // Multilines (6 lines) and TruncateAt.MARQUEE so ellipsis only on the last line
437         mDefaultLayout = createEllipsizeStaticLayout(LAYOUT_TEXT,
438                 TextUtils.TruncateAt.END,
439                 Integer.MAX_VALUE /* maxLines */);
440 
441         assertTrue(mDefaultLayout.getEllipsisCount(0) == 0);
442         assertTrue(mDefaultLayout.getEllipsisCount(1) == 0);
443         assertTrue(mDefaultLayout.getEllipsisCount(2) == 0);
444         assertTrue(mDefaultLayout.getEllipsisCount(3) == 0);
445         assertTrue(mDefaultLayout.getEllipsisCount(4) == 0);
446         assertTrue(mDefaultLayout.getEllipsisCount(5) > 0);
447     }
448 
449     /*
450      * Return the offset of the first character to be ellipsized away
451      * relative to the start of the line.
452      * (So 0 if the beginning of the line is ellipsized, not getLineStart().)
453      */
testGetEllipsisStart()454     public void testGetEllipsisStart() {
455         mDefaultLayout = createEllipsizeStaticLayout();
456         assertTrue(mDefaultLayout.getEllipsisStart(0) >= 0);
457         assertTrue(mDefaultLayout.getEllipsisStart(1) >= 0);
458 
459         try {
460             mDefaultLayout.getEllipsisStart(-1);
461             fail("should throw ArrayIndexOutOfBoundsException");
462         } catch (ArrayIndexOutOfBoundsException e) {
463         }
464 
465         try {
466             mDefaultLayout.getEllipsisStart(LARGER_THAN_LINE_COUNT);
467             fail("should throw ArrayIndexOutOfBoundsException");
468         } catch (ArrayIndexOutOfBoundsException e) {
469         }
470     }
471 
472     /*
473      * Return the width to which this Layout is ellipsizing
474      * or getWidth() if it is not doing anything special.
475      * The constructor's Argument TextUtils.TruncateAt defines which EllipsizedWidth to use
476      * ellipsizedWidth if argument is not null
477      * outerWidth if argument is null
478      */
testGetEllipsizedWidth()479     public void testGetEllipsizedWidth() {
480         int ellipsizedWidth = 60;
481         int outerWidth = 100;
482         StaticLayout layout = new StaticLayout(LAYOUT_TEXT, 0, LAYOUT_TEXT.length(),
483                 mDefaultPaint, outerWidth, DEFAULT_ALIGN, SPACE_MULTI,
484                 SPACE_ADD, false, TextUtils.TruncateAt.END, ellipsizedWidth);
485         assertEquals(ellipsizedWidth, layout.getEllipsizedWidth());
486 
487         layout = new StaticLayout(LAYOUT_TEXT, 0, LAYOUT_TEXT.length(),
488                 mDefaultPaint, outerWidth, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD,
489                 false, null, ellipsizedWidth);
490         assertEquals(outerWidth, layout.getEllipsizedWidth());
491     }
492 
testEllipsis_singleLine()493     public void testEllipsis_singleLine() {
494         {
495             // Single line case and TruncateAt.END so that we have some ellipsis
496             StaticLayout layout = createEllipsizeStaticLayout(LAYOUT_TEXT_SINGLE_LINE,
497                     TextUtils.TruncateAt.END, 1);
498             assertTrue(layout.getEllipsisCount(0) > 0);
499         }
500         {
501             // Single line case and TruncateAt.MIDDLE so that we have some ellipsis
502             StaticLayout layout = createEllipsizeStaticLayout(LAYOUT_TEXT_SINGLE_LINE,
503                     TextUtils.TruncateAt.MIDDLE, 1);
504             assertTrue(layout.getEllipsisCount(0) > 0);
505         }
506         {
507             // Single line case and TruncateAt.END so that we have some ellipsis
508             StaticLayout layout = createEllipsizeStaticLayout(LAYOUT_TEXT_SINGLE_LINE,
509                     TextUtils.TruncateAt.END, 1);
510             assertTrue(layout.getEllipsisCount(0) > 0);
511         }
512         {
513             // Single line case and TruncateAt.MARQUEE so that we have NO ellipsis
514             StaticLayout layout = createEllipsizeStaticLayout(LAYOUT_TEXT_SINGLE_LINE,
515                     TextUtils.TruncateAt.MARQUEE, 1);
516             assertTrue(layout.getEllipsisCount(0) == 0);
517         }
518 
519         final String text = "\u3042" // HIRAGANA LETTER A
520                 + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
521         final float textWidth = mDefaultPaint.measureText(text);
522         final int halfWidth = (int)(textWidth / 2.0f);
523         {
524             StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
525                     halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
526                     SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.END, halfWidth, 1);
527             assertTrue(layout.getEllipsisCount(0) > 0);
528             assertTrue(layout.getEllipsisStart(0) > 0);
529         }
530         {
531             StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
532                     halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
533                     SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.START, halfWidth, 1);
534             assertTrue(layout.getEllipsisCount(0) > 0);
535             assertEquals(0, mDefaultLayout.getEllipsisStart(0));
536         }
537         {
538             StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
539                     halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
540                     SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.MIDDLE, halfWidth, 1);
541             assertTrue(layout.getEllipsisCount(0) > 0);
542             assertTrue(layout.getEllipsisStart(0) > 0);
543         }
544         {
545             StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
546                     halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
547                     SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.MARQUEE, halfWidth, 1);
548             assertEquals(0, layout.getEllipsisCount(0));
549         }
550     }
551 
552     /**
553      * scenario description:
554      * 1. set the text.
555      * 2. change the text
556      * 3. Check the text won't change to the StaticLayout
557     */
testImmutableStaticLayout()558     public void testImmutableStaticLayout() {
559         Editable editable =  Editable.Factory.getInstance().newEditable("123\t\n555");
560         StaticLayout layout = new StaticLayout(editable, mDefaultPaint,
561                 DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
562 
563         assertEquals(2, layout.getLineCount());
564         assertTrue(mDefaultLayout.getLineContainsTab(0));
565 
566         // change the text
567         editable.delete(0, editable.length() - 1);
568 
569         assertEquals(2, layout.getLineCount());
570         assertTrue(layout.getLineContainsTab(0));
571 
572     }
573 
574     // String wrapper for testing not well known implementation of CharSequence.
575     private class FakeCharSequence implements CharSequence {
576         private String mStr;
577 
FakeCharSequence(String str)578         public FakeCharSequence(String str) {
579             mStr = str;
580         }
581 
582         @Override
charAt(int index)583         public char charAt(int index) {
584             return mStr.charAt(index);
585         }
586 
587         @Override
length()588         public int length() {
589             return mStr.length();
590         }
591 
592         @Override
subSequence(int start, int end)593         public CharSequence subSequence(int start, int end) {
594             return mStr.subSequence(start, end);
595         }
596 
597         @Override
toString()598         public String toString() {
599             return mStr;
600         }
601     };
602 
buildTestCharSequences(String testString, Normalizer.Form[] forms)603     private List<CharSequence> buildTestCharSequences(String testString, Normalizer.Form[] forms) {
604         List<CharSequence> result = new ArrayList<CharSequence>();
605 
606         List<String> normalizedStrings = new ArrayList<String>();
607         for (Normalizer.Form form: forms) {
608             normalizedStrings.add(Normalizer.normalize(testString, form));
609         }
610 
611         for (String str: normalizedStrings) {
612             result.add(str);
613             result.add(new SpannedString(str));
614             result.add(new SpannableString(str));
615             result.add(new SpannableStringBuilder(str));  // as a GraphicsOperations implementation.
616             result.add(new FakeCharSequence(str));  // as a not well known implementation.
617         }
618         return result;
619     }
620 
buildTestMessage(CharSequence seq)621     private String buildTestMessage(CharSequence seq) {
622         String normalized;
623         if (Normalizer.isNormalized(seq, Normalizer.Form.NFC)) {
624             normalized = "NFC";
625         } else if (Normalizer.isNormalized(seq, Normalizer.Form.NFD)) {
626             normalized = "NFD";
627         } else if (Normalizer.isNormalized(seq, Normalizer.Form.NFKC)) {
628             normalized = "NFKC";
629         } else if (Normalizer.isNormalized(seq, Normalizer.Form.NFKD)) {
630             normalized = "NFKD";
631         } else {
632             throw new IllegalStateException("Normalized form is not NFC/NFD/NFKC/NFKD");
633         }
634 
635         StringBuilder builder = new StringBuilder();
636         for (int i = 0; i < seq.length(); ++i) {
637             builder.append(String.format("0x%04X ", Integer.valueOf(seq.charAt(i))));
638         }
639 
640         return "testString: \"" + seq.toString() + "\"[" + builder.toString() + "]" +
641                 ", class: " + seq.getClass().getName() +
642                 ", Normalization: " + normalized;
643     }
644 
testGetOffset_ASCII()645     public void testGetOffset_ASCII() {
646         String testStrings[] = { "abcde", "ab\ncd", "ab\tcd", "ab\n\nc", "ab\n\tc" };
647 
648         for (String testString: testStrings) {
649             for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
650                 StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
651                         DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
652 
653                 String testLabel = buildTestMessage(seq);
654 
655                 assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
656                 assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
657                 assertEquals(testLabel, 1, layout.getOffsetToLeftOf(2));
658                 assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
659                 assertEquals(testLabel, 3, layout.getOffsetToLeftOf(4));
660                 assertEquals(testLabel, 4, layout.getOffsetToLeftOf(5));
661 
662                 assertEquals(testLabel, 1, layout.getOffsetToRightOf(0));
663                 assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
664                 assertEquals(testLabel, 3, layout.getOffsetToRightOf(2));
665                 assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
666                 assertEquals(testLabel, 5, layout.getOffsetToRightOf(4));
667                 assertEquals(testLabel, 5, layout.getOffsetToRightOf(5));
668             }
669         }
670 
671         String testString = "ab\r\nde";
672         for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
673             StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
674                     DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
675 
676             String testLabel = buildTestMessage(seq);
677 
678             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
679             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
680             assertEquals(testLabel, 1, layout.getOffsetToLeftOf(2));
681             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
682             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(4));
683             assertEquals(testLabel, 4, layout.getOffsetToLeftOf(5));
684             assertEquals(testLabel, 5, layout.getOffsetToLeftOf(6));
685 
686             assertEquals(testLabel, 1, layout.getOffsetToRightOf(0));
687             assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
688             assertEquals(testLabel, 4, layout.getOffsetToRightOf(2));
689             assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
690             assertEquals(testLabel, 5, layout.getOffsetToRightOf(4));
691             assertEquals(testLabel, 6, layout.getOffsetToRightOf(5));
692             assertEquals(testLabel, 6, layout.getOffsetToRightOf(6));
693         }
694     }
695 
testGetOffset_UNICODE()696     public void testGetOffset_UNICODE() {
697         String testStrings[] = new String[] {
698               // Cyrillic alphabets.
699               "\u0410\u0411\u0412\u0413\u0414",
700               // Japanese Hiragana Characters.
701               "\u3042\u3044\u3046\u3048\u304A",
702         };
703 
704         for (String testString: testStrings) {
705             for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
706                 StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
707                         DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
708 
709                 String testLabel = buildTestMessage(seq);
710 
711                 assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
712                 assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
713                 assertEquals(testLabel, 1, layout.getOffsetToLeftOf(2));
714                 assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
715                 assertEquals(testLabel, 3, layout.getOffsetToLeftOf(4));
716                 assertEquals(testLabel, 4, layout.getOffsetToLeftOf(5));
717 
718                 assertEquals(testLabel, 1, layout.getOffsetToRightOf(0));
719                 assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
720                 assertEquals(testLabel, 3, layout.getOffsetToRightOf(2));
721                 assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
722                 assertEquals(testLabel, 5, layout.getOffsetToRightOf(4));
723                 assertEquals(testLabel, 5, layout.getOffsetToRightOf(5));
724             }
725         }
726     }
727 
testGetOffset_UNICODE_Normalization()728     public void testGetOffset_UNICODE_Normalization() {
729         // "A" with acute, circumflex, tilde, diaeresis, ring above.
730         String testString = "\u00C1\u00C2\u00C3\u00C4\u00C5";
731         Normalizer.Form[] oneUnicodeForms = { Normalizer.Form.NFC, Normalizer.Form.NFKC };
732         for (CharSequence seq: buildTestCharSequences(testString, oneUnicodeForms)) {
733             StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
734                     DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
735 
736             String testLabel = buildTestMessage(seq);
737 
738             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
739             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
740             assertEquals(testLabel, 1, layout.getOffsetToLeftOf(2));
741             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
742             assertEquals(testLabel, 3, layout.getOffsetToLeftOf(4));
743             assertEquals(testLabel, 4, layout.getOffsetToLeftOf(5));
744 
745             assertEquals(testLabel, 1, layout.getOffsetToRightOf(0));
746             assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
747             assertEquals(testLabel, 3, layout.getOffsetToRightOf(2));
748             assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
749             assertEquals(testLabel, 5, layout.getOffsetToRightOf(4));
750             assertEquals(testLabel, 5, layout.getOffsetToRightOf(5));
751         }
752 
753         Normalizer.Form[] twoUnicodeForms = { Normalizer.Form.NFD, Normalizer.Form.NFKD };
754         for (CharSequence seq: buildTestCharSequences(testString, twoUnicodeForms)) {
755             StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
756                     DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
757 
758             String testLabel = buildTestMessage(seq);
759 
760             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
761             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
762             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(2));
763             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
764             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(4));
765             assertEquals(testLabel, 4, layout.getOffsetToLeftOf(5));
766             assertEquals(testLabel, 4, layout.getOffsetToLeftOf(6));
767             assertEquals(testLabel, 6, layout.getOffsetToLeftOf(7));
768             assertEquals(testLabel, 6, layout.getOffsetToLeftOf(8));
769             assertEquals(testLabel, 8, layout.getOffsetToLeftOf(9));
770             assertEquals(testLabel, 8, layout.getOffsetToLeftOf(10));
771 
772             assertEquals(testLabel, 2, layout.getOffsetToRightOf(0));
773             assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
774             assertEquals(testLabel, 4, layout.getOffsetToRightOf(2));
775             assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
776             assertEquals(testLabel, 6, layout.getOffsetToRightOf(4));
777             assertEquals(testLabel, 6, layout.getOffsetToRightOf(5));
778             assertEquals(testLabel, 8, layout.getOffsetToRightOf(6));
779             assertEquals(testLabel, 8, layout.getOffsetToRightOf(7));
780             assertEquals(testLabel, 10, layout.getOffsetToRightOf(8));
781             assertEquals(testLabel, 10, layout.getOffsetToRightOf(9));
782             assertEquals(testLabel, 10, layout.getOffsetToRightOf(10));
783         }
784     }
785 
testGetOffset_UNICODE_SurrogatePairs()786     public void testGetOffset_UNICODE_SurrogatePairs() {
787         // Emoticons for surrogate pairs tests.
788         String testString =
789                 "\uD83D\uDE00\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03\uD83D\uDE04";
790         for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
791             StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
792                     DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
793 
794             String testLabel = buildTestMessage(seq);
795 
796             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
797             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
798             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(2));
799             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
800             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(4));
801             assertEquals(testLabel, 4, layout.getOffsetToLeftOf(5));
802             assertEquals(testLabel, 4, layout.getOffsetToLeftOf(6));
803             assertEquals(testLabel, 6, layout.getOffsetToLeftOf(7));
804             assertEquals(testLabel, 6, layout.getOffsetToLeftOf(8));
805             assertEquals(testLabel, 8, layout.getOffsetToLeftOf(9));
806             assertEquals(testLabel, 8, layout.getOffsetToLeftOf(10));
807 
808             assertEquals(testLabel, 2, layout.getOffsetToRightOf(0));
809             assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
810             assertEquals(testLabel, 4, layout.getOffsetToRightOf(2));
811             assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
812             assertEquals(testLabel, 6, layout.getOffsetToRightOf(4));
813             assertEquals(testLabel, 6, layout.getOffsetToRightOf(5));
814             assertEquals(testLabel, 8, layout.getOffsetToRightOf(6));
815             assertEquals(testLabel, 8, layout.getOffsetToRightOf(7));
816             assertEquals(testLabel, 10, layout.getOffsetToRightOf(8));
817             assertEquals(testLabel, 10, layout.getOffsetToRightOf(9));
818             assertEquals(testLabel, 10, layout.getOffsetToRightOf(10));
819         }
820     }
821 
testGetOffset_UNICODE_Thai()822     public void testGetOffset_UNICODE_Thai() {
823         // Thai Characters. The expected cursorable boundary is
824         // | \u0E02 | \u0E2D | \u0E1A | \u0E04\u0E38 | \u0E13 |
825         String testString = "\u0E02\u0E2D\u0E1A\u0E04\u0E38\u0E13";
826         for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
827             StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
828                     DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
829 
830             String testLabel = buildTestMessage(seq);
831 
832             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
833             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
834             assertEquals(testLabel, 1, layout.getOffsetToLeftOf(2));
835             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
836             assertEquals(testLabel, 3, layout.getOffsetToLeftOf(4));
837             assertEquals(testLabel, 3, layout.getOffsetToLeftOf(5));
838             assertEquals(testLabel, 5, layout.getOffsetToLeftOf(6));
839 
840             assertEquals(testLabel, 1, layout.getOffsetToRightOf(0));
841             assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
842             assertEquals(testLabel, 3, layout.getOffsetToRightOf(2));
843             assertEquals(testLabel, 5, layout.getOffsetToRightOf(3));
844             assertEquals(testLabel, 5, layout.getOffsetToRightOf(4));
845             assertEquals(testLabel, 6, layout.getOffsetToRightOf(5));
846             assertEquals(testLabel, 6, layout.getOffsetToRightOf(6));
847         }
848     }
849 
testGetOffset_UNICODE_Hebrew()850     public void testGetOffset_UNICODE_Hebrew() {
851         String testString = "\u05DE\u05E1\u05E2\u05D3\u05D4"; // Hebrew Characters
852         for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
853             StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
854                     DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN,
855                     TextDirectionHeuristics.RTL, SPACE_MULTI, SPACE_ADD, true);
856 
857             String testLabel = buildTestMessage(seq);
858 
859             assertEquals(testLabel, 1, layout.getOffsetToLeftOf(0));
860             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(1));
861             assertEquals(testLabel, 3, layout.getOffsetToLeftOf(2));
862             assertEquals(testLabel, 4, layout.getOffsetToLeftOf(3));
863             assertEquals(testLabel, 5, layout.getOffsetToLeftOf(4));
864             assertEquals(testLabel, 5, layout.getOffsetToLeftOf(5));
865 
866             assertEquals(testLabel, 0, layout.getOffsetToRightOf(0));
867             assertEquals(testLabel, 0, layout.getOffsetToRightOf(1));
868             assertEquals(testLabel, 1, layout.getOffsetToRightOf(2));
869             assertEquals(testLabel, 2, layout.getOffsetToRightOf(3));
870             assertEquals(testLabel, 3, layout.getOffsetToRightOf(4));
871             assertEquals(testLabel, 4, layout.getOffsetToRightOf(5));
872         }
873     }
874 
testGetOffset_UNICODE_Arabic()875     public void testGetOffset_UNICODE_Arabic() {
876         // Arabic Characters. The expected cursorable boundary is
877         // | \u0623 \u064F | \u0633 \u0652 | \u0631 \u064E | \u0629 \u064C |";
878         String testString = "\u0623\u064F\u0633\u0652\u0631\u064E\u0629\u064C";
879 
880         Normalizer.Form[] oneUnicodeForms = { Normalizer.Form.NFC, Normalizer.Form.NFKC };
881         for (CharSequence seq: buildTestCharSequences(testString, oneUnicodeForms)) {
882             StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
883                     DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
884 
885             String testLabel = buildTestMessage(seq);
886 
887             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(0));
888             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(1));
889             assertEquals(testLabel, 4, layout.getOffsetToLeftOf(2));
890             assertEquals(testLabel, 4, layout.getOffsetToLeftOf(3));
891             assertEquals(testLabel, 6, layout.getOffsetToLeftOf(4));
892             assertEquals(testLabel, 6, layout.getOffsetToLeftOf(5));
893             assertEquals(testLabel, 8, layout.getOffsetToLeftOf(6));
894             assertEquals(testLabel, 8, layout.getOffsetToLeftOf(7));
895             assertEquals(testLabel, 8, layout.getOffsetToLeftOf(8));
896 
897             assertEquals(testLabel, 0, layout.getOffsetToRightOf(0));
898             assertEquals(testLabel, 0, layout.getOffsetToRightOf(1));
899             assertEquals(testLabel, 0, layout.getOffsetToRightOf(2));
900             assertEquals(testLabel, 2, layout.getOffsetToRightOf(3));
901             assertEquals(testLabel, 2, layout.getOffsetToRightOf(4));
902             assertEquals(testLabel, 4, layout.getOffsetToRightOf(5));
903             assertEquals(testLabel, 4, layout.getOffsetToRightOf(6));
904             assertEquals(testLabel, 6, layout.getOffsetToRightOf(7));
905             assertEquals(testLabel, 6, layout.getOffsetToRightOf(8));
906         }
907     }
908 
testGetOffset_UNICODE_Bidi()909     public void testGetOffset_UNICODE_Bidi() {
910         // String having RTL characters and LTR characters
911 
912         // LTR Context
913         // The first and last two characters are LTR characters.
914         String testString = "\u0061\u0062\u05DE\u05E1\u05E2\u0063\u0064";
915         // Logical order: [L1] [L2] [R1] [R2] [R3] [L3] [L4]
916         //               0    1    2    3    4    5    6    7
917         // Display order: [L1] [L2] [R3] [R2] [R1] [L3] [L4]
918         //               0    1    2    4    3    5    6    7
919         // [L?] means ?th LTR character and [R?] means ?th RTL character.
920         for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
921             StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
922                     DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
923 
924             String testLabel = buildTestMessage(seq);
925 
926             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
927             assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
928             assertEquals(testLabel, 1, layout.getOffsetToLeftOf(2));
929             assertEquals(testLabel, 4, layout.getOffsetToLeftOf(3));
930             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(4));
931             assertEquals(testLabel, 3, layout.getOffsetToLeftOf(5));
932             assertEquals(testLabel, 5, layout.getOffsetToLeftOf(6));
933             assertEquals(testLabel, 6, layout.getOffsetToLeftOf(7));
934 
935             assertEquals(testLabel, 1, layout.getOffsetToRightOf(0));
936             assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
937             assertEquals(testLabel, 4, layout.getOffsetToRightOf(2));
938             assertEquals(testLabel, 5, layout.getOffsetToRightOf(3));
939             assertEquals(testLabel, 3, layout.getOffsetToRightOf(4));
940             assertEquals(testLabel, 6, layout.getOffsetToRightOf(5));
941             assertEquals(testLabel, 7, layout.getOffsetToRightOf(6));
942             assertEquals(testLabel, 7, layout.getOffsetToRightOf(7));
943         }
944 
945         // RTL Context
946         // The first and last two characters are RTL characters.
947         String testString2 = "\u05DE\u05E1\u0063\u0064\u0065\u05DE\u05E1";
948         // Logical order: [R1] [R2] [L1] [L2] [L3] [R3] [R4]
949         //               0    1    2    3    4    5    6    7
950         // Display order: [R4] [R3] [L1] [L2] [L3] [R2] [R1]
951         //               7    6    5    3    4    2    1    0
952         // [L?] means ?th LTR character and [R?] means ?th RTL character.
953         for (CharSequence seq: buildTestCharSequences(testString2, Normalizer.Form.values())) {
954             StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
955                     DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
956 
957             String testLabel = buildTestMessage(seq);
958 
959             assertEquals(testLabel, 1, layout.getOffsetToLeftOf(0));
960             assertEquals(testLabel, 2, layout.getOffsetToLeftOf(1));
961             assertEquals(testLabel, 4, layout.getOffsetToLeftOf(2));
962             assertEquals(testLabel, 5, layout.getOffsetToLeftOf(3));
963             assertEquals(testLabel, 3, layout.getOffsetToLeftOf(4));
964             assertEquals(testLabel, 6, layout.getOffsetToLeftOf(5));
965             assertEquals(testLabel, 7, layout.getOffsetToLeftOf(6));
966             assertEquals(testLabel, 7, layout.getOffsetToLeftOf(7));
967 
968             assertEquals(testLabel, 0, layout.getOffsetToRightOf(0));
969             assertEquals(testLabel, 0, layout.getOffsetToRightOf(1));
970             assertEquals(testLabel, 1, layout.getOffsetToRightOf(2));
971             assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
972             assertEquals(testLabel, 2, layout.getOffsetToRightOf(4));
973             assertEquals(testLabel, 3, layout.getOffsetToRightOf(5));
974             assertEquals(testLabel, 5, layout.getOffsetToRightOf(6));
975             assertEquals(testLabel, 6, layout.getOffsetToRightOf(7));
976         }
977     }
978 }
979