blob: 97937a45825e880aa0c13676c0dc2e7763d17707 [file] [log] [blame]
Roozbeh Pournader02f167c2017-06-06 11:31:33 -07001/*
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
17package android.text;
18
Seigo Nonaka19e75a62018-05-16 16:57:33 -070019import static org.junit.Assert.assertArrayEquals;
Roozbeh Pournader02f167c2017-06-06 11:31:33 -070020import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertFalse;
22import static org.junit.Assert.assertNull;
23import static org.junit.Assert.assertSame;
24import static org.junit.Assert.assertTrue;
25import static org.junit.Assert.fail;
26
27import android.graphics.Bitmap;
28import android.graphics.Canvas;
29import android.graphics.Paint;
30import android.graphics.Path;
31import android.graphics.Rect;
32import android.graphics.RectF;
Siyamed Sinira6b12de2017-12-11 15:29:18 -080033import android.platform.test.annotations.Presubmit;
Roozbeh Pournader02f167c2017-06-06 11:31:33 -070034import android.support.test.filters.SmallTest;
35import android.support.test.runner.AndroidJUnit4;
36import android.text.Layout.Alignment;
37import android.text.style.StrikethroughSpan;
38
39import org.junit.Before;
40import org.junit.Test;
41import org.junit.runner.RunWith;
42
43import java.util.ArrayList;
44import java.util.List;
Petar Ĺ eginac7f89452017-08-14 16:11:54 +010045import java.util.Locale;
Roozbeh Pournader02f167c2017-06-06 11:31:33 -070046
Siyamed Sinira6b12de2017-12-11 15:29:18 -080047@Presubmit
Roozbeh Pournader02f167c2017-06-06 11:31:33 -070048@SmallTest
49@RunWith(AndroidJUnit4.class)
50public class LayoutTest {
51 private static final int LINE_COUNT = 5;
52 private static final int LINE_HEIGHT = 12;
53 private static final int LINE_DESCENT = 4;
54 private static final CharSequence LAYOUT_TEXT = "alwei\t;sdfs\ndf @";
55
56 private SpannableString mSpannedText;
57
58 private int mWidth;
59 private Layout.Alignment mAlign;
60 private float mSpacingMult;
61 private float mSpacingAdd;
62 private TextPaint mTextPaint;
63
64 @Before
65 public void setup() {
66 mTextPaint = new TextPaint();
67 mSpannedText = new SpannableString(LAYOUT_TEXT);
68 mSpannedText.setSpan(new StrikethroughSpan(), 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
69 mWidth = 11;
70 mAlign = Alignment.ALIGN_CENTER;
71 mSpacingMult = 1;
72 mSpacingAdd = 2;
73 }
74
75 @Test
76 public void testConstructor() {
77 new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, mSpacingMult, mSpacingAdd);
78 }
79
80 @Test(expected = IllegalArgumentException.class)
81 public void testConstructorNull() {
82 new MockLayout(null, null, -1, null, 0, 0);
83 }
84
85 @Test
86 public void testGetText() {
87 CharSequence text = "test case 1";
88 Layout layout = new MockLayout(text, mTextPaint, mWidth,
89 mAlign, mSpacingMult, mSpacingAdd);
90 assertEquals(text, layout.getText());
91
92 layout = new MockLayout(null, mTextPaint, mWidth, mAlign, mSpacingMult, mSpacingAdd);
93 assertNull(layout.getText());
94 }
95
96 @Test
97 public void testGetPaint() {
98 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
99 mAlign, mSpacingMult, mSpacingAdd);
100
101 assertSame(mTextPaint, layout.getPaint());
102
103 layout = new MockLayout(LAYOUT_TEXT, null, mWidth, mAlign, mSpacingMult, mSpacingAdd);
104 assertNull(layout.getPaint());
105 }
106
107 @Test
108 public void testGetWidth() {
109 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, 10,
110 mAlign, mSpacingMult, mSpacingAdd);
111 assertEquals(10, layout.getWidth());
112
113 layout = new MockLayout(LAYOUT_TEXT, mTextPaint, 0, mAlign, mSpacingMult, mSpacingAdd);
114 assertEquals(0, layout.getWidth());
115 }
116
117 @Test
118 public void testGetEllipsizedWidth() {
119 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, 15,
120 mAlign, mSpacingMult, mSpacingAdd);
121 assertEquals(15, layout.getEllipsizedWidth());
122
123 layout = new MockLayout(LAYOUT_TEXT, mTextPaint, 0, mAlign, mSpacingMult, mSpacingAdd);
124 assertEquals(0, layout.getEllipsizedWidth());
125 }
126
127 @Test
128 public void testIncreaseWidthTo() {
129 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
130 mAlign, mSpacingMult, mSpacingAdd);
131 int oldWidth = layout.getWidth();
132
133 layout.increaseWidthTo(oldWidth);
134 assertEquals(oldWidth, layout.getWidth());
135
136 try {
137 layout.increaseWidthTo(oldWidth - 1);
138 fail("should throw runtime exception attempted to reduce Layout width");
139 } catch (RuntimeException e) {
140 }
141
142 layout.increaseWidthTo(oldWidth + 1);
143 assertEquals(oldWidth + 1, layout.getWidth());
144 }
145
146 @Test
147 public void testGetHeight() {
148 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
149 mAlign, mSpacingMult, mSpacingAdd);
150 assertEquals(60, layout.getHeight());
151 }
152
153 @Test
154 public void testGetAlignment() {
155 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
156 mAlign, mSpacingMult, mSpacingAdd);
157 assertSame(mAlign, layout.getAlignment());
158
159 layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, null, mSpacingMult, mSpacingAdd);
160 assertNull(layout.getAlignment());
161 }
162
163 @Test
164 public void testGetSpacingMultiplier() {
165 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, -1, mSpacingAdd);
166 assertEquals(-1.0f, layout.getSpacingMultiplier(), 0.0f);
167
168 layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, 5, mSpacingAdd);
169 assertEquals(5.0f, layout.getSpacingMultiplier(), 0.0f);
170 }
171
172 @Test
173 public void testGetSpacingAdd() {
174 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, mSpacingMult, -1);
175 assertEquals(-1.0f, layout.getSpacingAdd(), 0.0f);
176
177 layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, mSpacingMult, 20);
178 assertEquals(20.0f, layout.getSpacingAdd(), 0.0f);
179 }
180
181 @Test
182 public void testGetLineBounds() {
183 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
184 mAlign, mSpacingMult, mSpacingAdd);
185 Rect bounds = new Rect();
186
187 assertEquals(32, layout.getLineBounds(2, bounds));
188 assertEquals(0, bounds.left);
189 assertEquals(mWidth, bounds.right);
190 assertEquals(24, bounds.top);
191 assertEquals(36, bounds.bottom);
192 }
193
194 @Test
195 public void testGetLineForVertical() {
196 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
197 mAlign, mSpacingMult, mSpacingAdd);
198 assertEquals(0, layout.getLineForVertical(-1));
199 assertEquals(0, layout.getLineForVertical(0));
200 assertEquals(0, layout.getLineForVertical(LINE_COUNT));
201 assertEquals(LINE_COUNT - 1, layout.getLineForVertical(1000));
202 }
203
204 @Test
205 public void testGetLineForOffset() {
206 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
207 mAlign, mSpacingMult, mSpacingAdd);
208 assertEquals(0, layout.getLineForOffset(-1));
209 assertEquals(1, layout.getLineForOffset(1));
210 assertEquals(LINE_COUNT - 1, layout.getLineForOffset(LINE_COUNT - 1));
211 assertEquals(LINE_COUNT - 1, layout.getLineForOffset(1000));
212 }
213
214 @Test
215 public void testGetLineEnd() {
216 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
217 mAlign, mSpacingMult, mSpacingAdd);
218 assertEquals(2, layout.getLineEnd(1));
219 }
220
221 @Test
Siyamed Sinir0fa89d62017-07-24 20:46:41 -0700222 public void testGetLineExtra_returnsZeroByDefault() {
223 final String text = "a\nb\nc\n";
224 final Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
225 mAlign, 100 /* spacingMult*/, 100 /*spacingAdd*/);
226 final int lineCount = text.split("\n").length;
227 for (int i = 0; i < lineCount; i++) {
228 assertEquals(0, layout.getLineExtra(i));
229 }
230 }
231
232 @Test
Roozbeh Pournader02f167c2017-06-06 11:31:33 -0700233 public void testGetLineVisibleEnd() {
234 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
235 mAlign, mSpacingMult, mSpacingAdd);
236
237 assertEquals(2, layout.getLineVisibleEnd(1));
238 assertEquals(LINE_COUNT, layout.getLineVisibleEnd(LINE_COUNT - 1));
239 assertEquals(LAYOUT_TEXT.length(), layout.getLineVisibleEnd(LAYOUT_TEXT.length() - 1));
240 try {
241 layout.getLineVisibleEnd(LAYOUT_TEXT.length());
242 fail("should throw .StringIndexOutOfBoundsException here");
243 } catch (StringIndexOutOfBoundsException e) {
244 }
245 }
246
247 @Test
248 public void testGetLineBottom() {
249 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
250 mAlign, mSpacingMult, mSpacingAdd);
251 assertEquals(LINE_HEIGHT, layout.getLineBottom(0));
252 }
253
254 @Test
255 public void testGetLineBaseline() {
256 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
257 mAlign, mSpacingMult, mSpacingAdd);
258 assertEquals(8, layout.getLineBaseline(0));
259 }
260
261 @Test
262 public void testGetLineAscent() {
263 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
264 mAlign, mSpacingMult, mSpacingAdd);
265 assertEquals(-8, layout.getLineAscent(0));
266 }
267
268 @Test
269 public void testGetParagraphAlignment() {
270 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
271 mAlign, mSpacingMult, mSpacingAdd);
272 assertSame(mAlign, layout.getParagraphAlignment(0));
273
274 layout = new MockLayout(mSpannedText, mTextPaint, mWidth,
275 mAlign, mSpacingMult, mSpacingAdd);
276 assertSame(mAlign, layout.getParagraphAlignment(0));
277 assertSame(mAlign, layout.getParagraphAlignment(1));
278 }
279
280 @Test
281 public void testGetParagraphLeft() {
282 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
283 mAlign, mSpacingMult, mSpacingAdd);
284 assertEquals(0, layout.getParagraphLeft(0));
285 }
286
287 @Test
288 public void testGetParagraphRight() {
289 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
290 mAlign, mSpacingMult, mSpacingAdd);
291 assertEquals(mWidth, layout.getParagraphRight(0));
292 }
293
294 @Test
Petar Ĺ eginac7f89452017-08-14 16:11:54 +0100295 public void testGetSelectionWithEmptySelection() {
296 final Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
297 mAlign, mSpacingMult, mSpacingAdd);
298
299 /*
300 * When the selection is empty (i.e. the start and the end index are the same), we do not
301 * expect any rectangles to be generated.
302 */
303
304 layout.getSelection(5 /* startIndex */, 5 /* endIndex */,
Petar Ĺ eginab92c5392017-09-06 15:25:05 +0100305 (left, top, right, bottom, textSelectionLayout) -> fail(
306 String.format(Locale.getDefault(),
Petar Ĺ eginac7f89452017-08-14 16:11:54 +0100307 "Did not expect any rectangles, got a rectangle with (left: %f,"
308 + " top: %f), (right: %f, bottom: %f)",
309 left, top, right, bottom)));
310 }
311
312 @Test
313 public void testGetSelectionWithASingleLineSelection() {
314 final Layout layout = new StaticLayout("abc", mTextPaint, Integer.MAX_VALUE,
315 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
316
317 final List<RectF> rectangles = new ArrayList<>();
318
319 layout.getSelection(0 /* startIndex */, 1 /* endIndex */,
Petar Ĺ eginab92c5392017-09-06 15:25:05 +0100320 (left, top, right, bottom, textSelectionLayout) -> rectangles.add(
321 new RectF(left, top, right, bottom)));
Petar Ĺ eginac7f89452017-08-14 16:11:54 +0100322
323 /*
324 * The selection we expect will only cover the letter "a". Hence, we expect one rectangle
325 * to be generated and this rectangle should start at the top left of the canvas and should
326 * end somewhere to the right and down.
327 *
328 * | a | b c
329 *
330 */
331
332 assertEquals(1, rectangles.size());
333
334 final RectF rectangle = rectangles.get(0);
335
336 assertEquals(0, rectangle.left, 0.0f);
337 assertEquals(0, rectangle.top, 0.0f);
338 assertTrue(rectangle.right > 0);
339 assertTrue(rectangle.bottom > 0);
340 }
341
342 @Test
343 public void
344 testGetSelectionWithMultilineSelection_secondLineSelectionEndsBeforeFirstCharacter() {
345 final Layout layout = new StaticLayout("a\nb\nc", mTextPaint, Integer.MAX_VALUE,
346 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
347
348 final List<RectF> rectangles = new ArrayList<>();
349
350 layout.getSelection(0 /* startIndex */, 2 /* endIndex */,
Petar Ĺ eginab92c5392017-09-06 15:25:05 +0100351 (left, top, right, bottom, textSelectionLayout) -> rectangles.add(
352 new RectF(left, top, right, bottom)));
Petar Ĺ eginac7f89452017-08-14 16:11:54 +0100353
354 /*
355 * The selection that will be selected is "a\n" - the selection starts at the beginning
356 * of the first line and ends at the start of the second line. This means the selection
357 * highlight will span from the beginning of the first line to the end of the first line
358 * and will appear as a zero width line at the beginning of the second line.
359 *
360 * Hence, we expect three rectangles - one that will select the "a" on the first line,
361 * one that will extend the selection from the "a" to the end of the first line and one
362 * that will prepare the selection for the second line.
363 *
364 * | a | *topToEndOfLineRectangle* |
365 * | b
366 * c
367 */
368
369 assertEquals(3, rectangles.size());
370
371 final RectF topRectangle = rectangles.get(0);
372 final RectF topToEndOfLineRectangle = rectangles.get(1);
373 final RectF bottomLineStartRectangle = rectangles.get(2);
374
375 assertFalse(topRectangle.intersect(bottomLineStartRectangle));
376 assertTrue(topRectangle.top < bottomLineStartRectangle.top);
377 assertTrue(topRectangle.left == bottomLineStartRectangle.left);
378
379 assertFalse(topRectangle.intersect(topToEndOfLineRectangle));
380 assertEquals(Integer.MAX_VALUE, topToEndOfLineRectangle.right, 1);
381 assertTrue(topRectangle.top == topToEndOfLineRectangle.top);
382 assertTrue(topRectangle.right == topToEndOfLineRectangle.left);
383 assertTrue(topRectangle.bottom == topToEndOfLineRectangle.bottom);
384
385 assertEquals(0, bottomLineStartRectangle.left, 0.0f);
386 assertEquals(0, bottomLineStartRectangle.right, 0.0f);
387 }
388
389 @Test
390 public void testGetSelectionWithMultilineSelection_secondLineSelectionEndsAfterACharacter() {
391 final Layout layout = new StaticLayout("a\nb\nc", mTextPaint, Integer.MAX_VALUE,
392 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
393
394 final List<RectF> rectangles = new ArrayList<>();
395
396 layout.getSelection(0 /* startIndex */, 3 /* endIndex */,
Petar Ĺ eginab92c5392017-09-06 15:25:05 +0100397 (left, top, right, bottom, textSelectionLayout) -> rectangles.add(
398 new RectF(left, top, right, bottom)));
Petar Ĺ eginac7f89452017-08-14 16:11:54 +0100399
400 /*
401 * The selection that will be selected is "a\nb" - the selection starts at the beginning
402 * of the first line and ends at the end of the letter "b". This means the selection
403 * highlight will span from the beginning of the first line to the end of the first line
404 * and will also cover the letter "b" on the second line.
405 *
406 * We expect four rectangles - one that will select the "a" on the first line,
407 * one that will extend the selection from the "a" to the end of the first line the one
408 * from the previous case that will prepare the selection for the second line and finally
409 * one that will select the letter b.
410 *
411 * | a | *topToEndOfLineRectangle* |
412 * || b |
413 * c
414 */
415
416 assertEquals(4, rectangles.size());
417
418 final RectF topRectangle = rectangles.get(0);
419 final RectF topToEndOfLineRectangle = rectangles.get(1);
420 final RectF bottomRectangle = rectangles.get(2);
421 final RectF bottomLineStartRectangle = rectangles.get(3);
422
423 assertTrue(topRectangle.top == topToEndOfLineRectangle.top);
424 assertTrue(bottomLineStartRectangle.top == bottomRectangle.top);
425 assertTrue(bottomLineStartRectangle.bottom == bottomRectangle.bottom);
426 assertEquals(0, bottomLineStartRectangle.left, 0.0f);
427 assertEquals(0, bottomLineStartRectangle.right, 0.0f);
428 assertEquals(0, bottomRectangle.left, 0.0f);
429 assertTrue(bottomRectangle.right > 0);
430 }
431
432 @Test
433 public void testGetSelectionPathWithASingleLineSelection() {
434 final Layout layout = new StaticLayout("abc", mTextPaint, Integer.MAX_VALUE,
435 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
436
437 final List<RectF> rectangles = new ArrayList<>();
438
439 layout.getSelection(0 /* startIndex */, 1 /* endIndex */,
Petar Ĺ eginab92c5392017-09-06 15:25:05 +0100440 (left, top, right, bottom, textSelectionLayout) -> rectangles.add(
441 new RectF(left, top, right, bottom)));
Petar Ĺ eginac7f89452017-08-14 16:11:54 +0100442
443 /*
444 * In the single line selection case, we expect that only one rectangle covering the letter
445 * "a" will be generated. Hence, we expect that the generated path will only consist of
446 * that rectangle as well.
447 *
448 * | a | b c
449 *
450 */
451
452 assertEquals(1, rectangles.size());
453
454 final RectF rectangle = rectangles.get(0);
455
456 final Path generatedPath = new Path();
457 layout.getSelectionPath(0 /* startIndex */, 1 /* endIndex */, generatedPath);
458
459 final RectF pathRectangle = new RectF();
460
461 assertTrue(generatedPath.isRect(pathRectangle));
462 assertEquals(rectangle, pathRectangle);
463 }
464
465 @Test
Petar Ĺ eginab92c5392017-09-06 15:25:05 +0100466 public void testGetSelection_latinTextDirection() {
467 final Layout layout = new StaticLayout("abc", mTextPaint, Integer.MAX_VALUE,
468 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
469
470 layout.getSelection(0 /* startIndex */, 2 /* endIndex */,
471 (left, top, right, bottom, textSelectionLayout) ->
Petar Ĺ egina3a92fb62017-09-07 21:03:24 +0100472 assertEquals(Layout.TEXT_SELECTION_LAYOUT_LEFT_TO_RIGHT,
Petar Ĺ eginab92c5392017-09-06 15:25:05 +0100473 textSelectionLayout));
474 }
475
476 @Test
477 public void testGetSelection_arabicTextDirection() {
478 final Layout layout = new StaticLayout("غينيا", mTextPaint, Integer.MAX_VALUE,
479 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
480
481 layout.getSelection(0 /* startIndex */, 2 /* endIndex */,
482 (left, top, right, bottom, textSelectionLayout) ->
Petar Ĺ egina3a92fb62017-09-07 21:03:24 +0100483 assertEquals(Layout.TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT,
Petar Ĺ eginab92c5392017-09-06 15:25:05 +0100484 textSelectionLayout));
485 }
486
487 @Test
488 public void testGetSelection_mixedLatinAndArabicTextDirection() {
489 final Layout layout = new StaticLayout("abcغينيا", mTextPaint, Integer.MAX_VALUE,
490 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
491
492 final List<Integer> layouts = new ArrayList<>(2);
493
494 layout.getSelection(0 /* startIndex */, 6 /* endIndex */,
495 (left, top, right, bottom, textSelectionLayout) -> layouts.add(
496 textSelectionLayout));
497
498 assertEquals(2, layouts.size());
Petar Ĺ egina3a92fb62017-09-07 21:03:24 +0100499 assertEquals(Layout.TEXT_SELECTION_LAYOUT_LEFT_TO_RIGHT, (long) layouts.get(0));
500 assertEquals(Layout.TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT, (long) layouts.get(1));
Petar Ĺ eginab92c5392017-09-06 15:25:05 +0100501 }
502
503 @Test
Roozbeh Pournader02f167c2017-06-06 11:31:33 -0700504 public void testIsSpanned() {
505 MockLayout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
506 mAlign, mSpacingMult, mSpacingAdd);
507 // default is not spanned text
508 assertFalse(layout.mockIsSpanned());
509
510 // try to create a spanned text
511 layout = new MockLayout(mSpannedText, mTextPaint, mWidth,
512 mAlign, mSpacingMult, mSpacingAdd);
513 assertTrue(layout.mockIsSpanned());
514 }
515
516 private static final class MockLayout extends Layout {
517 MockLayout(CharSequence text, TextPaint paint, int width,
518 Alignment align, float spacingmult, float spacingadd) {
519 super(text, paint, width, align, spacingmult, spacingadd);
520 }
521
522 protected boolean mockIsSpanned() {
523 return super.isSpanned();
524 }
525
526 @Override
527 public int getBottomPadding() {
528 return 0;
529 }
530
531 @Override
532 public int getEllipsisCount(int line) {
533 return 0;
534 }
535
536 @Override
537 public int getEllipsisStart(int line) {
538 return 0;
539 }
540
541 @Override
542 public boolean getLineContainsTab(int line) {
543 return false;
544 }
545
546 @Override
547 public int getLineCount() {
548 return LINE_COUNT;
549 }
550
551 @Override
552 public int getLineDescent(int line) {
553 return LINE_DESCENT;
554 }
555
556 @Override
557 public Directions getLineDirections(int line) {
558 return Layout.DIRS_ALL_LEFT_TO_RIGHT;
559 }
560
561 @Override
562 public int getLineStart(int line) {
563 if (line < 0) {
564 return 0;
565 }
566 return line;
567 }
568
569 @Override
570 public int getLineTop(int line) {
571 if (line < 0) {
572 return 0;
573 }
574 return LINE_HEIGHT * (line);
575 }
576
577 @Override
578 public int getParagraphDirection(int line) {
579 return 0;
580 }
581
582 @Override
583 public int getTopPadding() {
584 return 0;
585 }
586 }
587
588 @Test
589 public void testGetLineWidth() {
590 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
591 mAlign, mSpacingMult, mSpacingAdd);
592 for (int i = 0; i < LINE_COUNT; i++) {
593 int start = layout.getLineStart(i);
594 int end = layout.getLineEnd(i);
595 String text = LAYOUT_TEXT.toString().substring(start, end);
596 assertEquals(mTextPaint.measureText(text), layout.getLineWidth(i), 1.0f);
597 }
598 }
599
600 @Test
601 public void testGetCursorPath() {
602 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
603 mAlign, mSpacingMult, mSpacingAdd);
604 Path path = new Path();
605 final float epsilon = 1.0f;
606 for (int i = 0; i < LINE_COUNT; i++) {
607 layout.getCursorPath(i, path, LAYOUT_TEXT);
608 RectF bounds = new RectF();
609 path.computeBounds(bounds, false);
610 assertTrue(bounds.top >= layout.getLineTop(i) - epsilon);
611 assertTrue(bounds.bottom <= layout.getLineBottom(i) + epsilon);
612 }
613 }
614
615 @Test
616 public void testDraw() {
617 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
618 mAlign, mSpacingMult, mSpacingAdd);
619 final int width = 256;
620 final int height = 256;
621 MockCanvas c = new MockCanvas(width, height);
622 layout.draw(c);
623 List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
624 assertEquals(LINE_COUNT, drawCommands.size());
625 for (int i = 0; i < LINE_COUNT; i++) {
626 MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
627 int start = layout.getLineStart(i);
628 int end = layout.getLineEnd(i);
629 assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
630 float expected_y = (i + 1) * LINE_HEIGHT - LINE_DESCENT;
631 assertEquals(expected_y, drawCommand.y, 0.0f);
632 }
633 }
634
635 private final class MockCanvas extends Canvas {
636
637 class DrawCommand {
638 public final String text;
639 public final float x;
640 public final float y;
641
642 DrawCommand(String text, float x, float y) {
643 this.text = text;
644 this.x = x;
645 this.y = y;
646 }
647 }
648
649 List<DrawCommand> mDrawCommands;
650
651 MockCanvas(int width, int height) {
652 super();
653 mDrawCommands = new ArrayList<>();
654 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
655 setBitmap(bitmap);
656 }
657
658 // Drawing text with either drawText or drawTextRun is valid; we don't care which.
659 // We also don't care which of the string representations is used.
660
661 @Override
662 public void drawText(String text, int start, int end, float x, float y, Paint p) {
663 mDrawCommands.add(new DrawCommand(text.substring(start, end), x, y));
664 }
665
666 @Override
667 public void drawText(CharSequence text, int start, int end, float x, float y, Paint p) {
668 drawText(text.toString(), start, end, x, y, p);
669 }
670
671 @Override
672 public void drawText(char[] text, int index, int count, float x, float y, Paint p) {
673 mDrawCommands.add(new DrawCommand(new String(text, index, count), x, y));
674 }
675
676 @Override
677 public void drawTextRun(CharSequence text, int start, int end, int contextStart,
678 int contextEnd, float x, float y, boolean isRtl, Paint paint) {
679 drawText(text, start, end, x, y, paint);
680 }
681
682 @Override
683 public void drawTextRun(char[] text, int index, int count, int contextIndex,
684 int contextCount, float x, float y, boolean isRtl, Paint paint) {
685 drawText(text, index, count, x, y, paint);
686 }
687
688 List<DrawCommand> getDrawCommands() {
689 return mDrawCommands;
690 }
691 }
Seigo Nonaka19e75a62018-05-16 16:57:33 -0700692
693 private static final String LTR = "a";
694 private static final String RTL = "\u05D0"; // HEBREW LETTER ALEF
695 private static final String LTR_SP = "\uD801\uDCB0"; // OSAGE CAPITAL LETTER A
696 private static final String RTL_SP = "\uD83A\uDD00"; // ADLAM CAPITAL LETTER ALIF
697
698 private static final String LRI = "\u2066"; // LEFT-TO-RIGHT ISOLATE
699 private static final String RLI = "\u2067"; // RIGHT-TO-LEFT ISOLATE
700 private static final String PDI = "\u2069"; // POP DIRECTIONAL ISOLATE
701
702 private static void assertPrimaryIsTrailingPrevious(String input, boolean[] expected) {
703 assertEquals(input.length() + 1, expected.length);
704
705 boolean[] actual = new boolean[expected.length];
706 TextPaint paint = new TextPaint();
707 paint.setTextSize(16.0f);
708 Layout layout = StaticLayout.Builder.obtain(
709 input, 0, input.length(), paint, Integer.MAX_VALUE).build();
710 for (int i = 0; i <= input.length(); ++i) {
711 actual[i] = layout.primaryIsTrailingPrevious(i);
712 }
713 assertArrayEquals(expected, actual);
Mihai Popa7626c862018-05-09 17:31:48 +0100714 assertArrayEquals(actual, layout.primaryIsTrailingPreviousAllLineOffsets(0));
Seigo Nonaka19e75a62018-05-16 16:57:33 -0700715 }
716
717 @Test
718 public void testPrimaryIsTrailingPrevious() {
719 assertPrimaryIsTrailingPrevious(
720 LTR + " " + LTR + LTR + " " + LTR + LTR + LTR,
721 new boolean[]{false, false, false, false, false, false, false, false, false});
722 assertPrimaryIsTrailingPrevious(
723 RTL + " " + RTL + RTL + " " + RTL + RTL + RTL,
724 new boolean[]{false, false, false, false, false, false, false, false, false});
725 assertPrimaryIsTrailingPrevious(
726 LTR + RTL + LTR + RTL + LTR,
727 new boolean[]{false, true, false, true, false, false});
728 assertPrimaryIsTrailingPrevious(
729 RTL + LTR + RTL + LTR + RTL,
730 new boolean[]{false, true, false, true, false, false});
731 assertPrimaryIsTrailingPrevious(
732 RTL_SP + LTR_SP + RTL_SP + LTR_SP + RTL_SP,
733 new boolean[]{
734 false, false, true, false, false, false, true, false, false, false, false});
735 assertPrimaryIsTrailingPrevious(
736 LTR_SP + RTL_SP + LTR_SP + RTL_SP + LTR_SP,
737 new boolean[]{
738 false, false, true, false, false, false, true, false, false, false, false});
739 assertPrimaryIsTrailingPrevious(
740 LTR + RLI + LTR + RTL + PDI + LTR,
741 new boolean[]{false, false, true, false, false, false, false});
742 assertPrimaryIsTrailingPrevious(
743 RTL + LRI + RTL + LTR + PDI + RTL,
744 new boolean[]{false, false, true, false, false, false, false});
Seigo Nonaka19e75a62018-05-16 16:57:33 -0700745 }
Roozbeh Pournader02f167c2017-06-06 11:31:33 -0700746}
747