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 static android.text.Layout.Alignment.ALIGN_NORMAL; 20 import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE; 21 22 import static com.google.common.truth.Truth.assertThat; 23 import static com.google.common.truth.Truth.assertWithMessage; 24 25 import static org.junit.Assert.assertEquals; 26 import static org.junit.Assert.assertFalse; 27 import static org.junit.Assert.assertNotNull; 28 import static org.junit.Assert.assertTrue; 29 import static org.junit.Assert.fail; 30 import static org.mockito.Mockito.mock; 31 32 import android.graphics.Paint.FontMetricsInt; 33 import android.graphics.text.LineBreakConfig; 34 import android.text.DynamicLayout; 35 import android.text.Layout; 36 import android.text.SpannableStringBuilder; 37 import android.text.StaticLayout; 38 import android.text.TextPaint; 39 import android.text.TextUtils; 40 import android.text.style.TypefaceSpan; 41 42 import androidx.test.ext.junit.runners.AndroidJUnit4; 43 import androidx.test.filters.SmallTest; 44 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 49 @SmallTest 50 @RunWith(AndroidJUnit4.class) 51 public class DynamicLayoutTest { 52 53 private static final float SPACING_MULT_NO_SCALE = 1.0f; 54 private static final float SPACING_ADD_NO_SCALE = 0.0f; 55 private static final int DEFAULT_OUTER_WIDTH = 150; 56 private static final int ELLIPSIZE_WIDTH = 8; 57 private static final CharSequence SINGLELINE_CHAR_SEQUENCE = "......"; 58 private static final String[] TEXT = {"CharSequence\n", "Char\tSequence\n", "CharSequence"}; 59 private static final CharSequence MULTLINE_CHAR_SEQUENCE = TEXT[0] + TEXT[1] + TEXT[2]; 60 private static final Layout.Alignment DEFAULT_ALIGN = Layout.Alignment.ALIGN_CENTER; 61 private TextPaint mDefaultPaint; 62 63 private static final int LINE0 = 0; 64 private static final int LINE0_TOP = 0; 65 private static final int LINE1 = 1; 66 private static final int LINE2 = 2; 67 private static final int LINE3 = 3; 68 69 private static final int ELLIPSIS_UNDEFINED = 0x80000000; 70 71 private DynamicLayout mDynamicLayout; 72 73 @Before setup()74 public void setup() { 75 mDefaultPaint = new TextPaint(); 76 mDynamicLayout = createBuilderWithDefaults(MULTLINE_CHAR_SEQUENCE).build(); 77 } 78 79 @Test testConstructors()80 public void testConstructors() { 81 new DynamicLayout(SINGLELINE_CHAR_SEQUENCE, 82 MULTLINE_CHAR_SEQUENCE, 83 mDefaultPaint, 84 DEFAULT_OUTER_WIDTH, 85 DEFAULT_ALIGN, 86 SPACING_MULT_NO_SCALE, 87 SPACING_ADD_NO_SCALE, 88 true); 89 new DynamicLayout(SINGLELINE_CHAR_SEQUENCE, 90 MULTLINE_CHAR_SEQUENCE, 91 mDefaultPaint, 92 DEFAULT_OUTER_WIDTH, 93 DEFAULT_ALIGN, 94 SPACING_MULT_NO_SCALE, 95 SPACING_ADD_NO_SCALE, 96 true, 97 TextUtils.TruncateAt.START, 98 DEFAULT_OUTER_WIDTH); 99 new DynamicLayout(MULTLINE_CHAR_SEQUENCE, 100 mDefaultPaint, 101 DEFAULT_OUTER_WIDTH, 102 DEFAULT_ALIGN, 103 SPACING_MULT_NO_SCALE, 104 SPACING_ADD_NO_SCALE, 105 true); 106 } 107 108 /* 109 * Test the ellipsis result when no ellipsis is needed, for a singleline text. 110 */ 111 @Test testEllipsis_singlelineNotEllipsized()112 public void testEllipsis_singlelineNotEllipsized() { 113 final DynamicLayout dynamicLayout = new DynamicLayout(SINGLELINE_CHAR_SEQUENCE, 114 SINGLELINE_CHAR_SEQUENCE, 115 mDefaultPaint, 116 DEFAULT_OUTER_WIDTH, 117 DEFAULT_ALIGN, 118 SPACING_MULT_NO_SCALE, 119 SPACING_ADD_NO_SCALE, 120 true, 121 TextUtils.TruncateAt.START, 122 DEFAULT_OUTER_WIDTH); 123 assertThat(dynamicLayout.getEllipsisCount(LINE0)).isEqualTo(0); 124 assertThat(dynamicLayout.getEllipsisStart(LINE0)).isEqualTo(0); 125 assertThat(dynamicLayout.getEllipsisCount(LINE1)).isEqualTo(0); 126 assertThat(dynamicLayout.getEllipsisStart(LINE1)).isEqualTo(ELLIPSIS_UNDEFINED); 127 assertThat(dynamicLayout.getEllipsizedWidth()).isEqualTo(DEFAULT_OUTER_WIDTH); 128 } 129 130 /* 131 * Test the ellipsis result when no ellipsis is needed, for a multiline text. 132 */ 133 @Test testEllipsis_multilineNotEllipsized()134 public void testEllipsis_multilineNotEllipsized() { 135 final DynamicLayout dynamicLayout = new DynamicLayout(MULTLINE_CHAR_SEQUENCE, 136 MULTLINE_CHAR_SEQUENCE, 137 mDefaultPaint, 138 DEFAULT_OUTER_WIDTH, 139 DEFAULT_ALIGN, 140 SPACING_MULT_NO_SCALE, 141 SPACING_ADD_NO_SCALE, 142 true, 143 TextUtils.TruncateAt.START, 144 DEFAULT_OUTER_WIDTH); 145 assertThat(dynamicLayout.getLineCount()).isEqualTo(3); 146 for (int i = 0; i < LINE3; i++) { 147 assertWithMessage("Ellipsis count for line " + i) 148 .that(dynamicLayout.getEllipsisCount(i)).isEqualTo(0); 149 assertWithMessage("Ellipsis start for line " + i) 150 .that(dynamicLayout.getEllipsisStart(i)).isEqualTo(0); 151 } 152 assertThat(dynamicLayout.getEllipsisCount(LINE3)).isEqualTo(0); 153 assertThat(dynamicLayout.getEllipsisStart(LINE3)).isEqualTo(ELLIPSIS_UNDEFINED); 154 assertThat(dynamicLayout.getEllipsizedWidth()).isEqualTo(DEFAULT_OUTER_WIDTH); 155 } 156 157 /* 158 * Test the ellipsis result when no ellipsis is needed, when the display text is different from 159 * the base. 160 */ 161 @Test testEllipsis_transformedNotEllipsized()162 public void testEllipsis_transformedNotEllipsized() { 163 final DynamicLayout dynamicLayout = new DynamicLayout(SINGLELINE_CHAR_SEQUENCE, 164 MULTLINE_CHAR_SEQUENCE, 165 mDefaultPaint, 166 DEFAULT_OUTER_WIDTH, 167 DEFAULT_ALIGN, 168 SPACING_MULT_NO_SCALE, 169 SPACING_ADD_NO_SCALE, 170 true, 171 TextUtils.TruncateAt.START, 172 DEFAULT_OUTER_WIDTH); 173 assertThat(dynamicLayout.getLineCount()).isEqualTo(3); 174 for (int i = 0; i < LINE3; i++) { 175 assertWithMessage("Ellipsis count for line " + i) 176 .that(dynamicLayout.getEllipsisCount(i)).isEqualTo(0); 177 assertWithMessage("Ellipsis start for line " + i) 178 .that(dynamicLayout.getEllipsisStart(i)).isEqualTo(0); 179 } 180 assertThat(dynamicLayout.getEllipsisCount(LINE3)).isEqualTo(0); 181 assertThat(dynamicLayout.getEllipsisStart(LINE3)).isEqualTo(ELLIPSIS_UNDEFINED); 182 assertThat(dynamicLayout.getEllipsizedWidth()).isEqualTo(DEFAULT_OUTER_WIDTH); 183 } 184 185 /* 186 * Test whether include the padding to calculate the layout. 187 * 1. Include padding while calculate the layout. 188 * 2. Don't include padding while calculate the layout. 189 */ 190 @Test testIncludePadding()191 public void testIncludePadding() { 192 final FontMetricsInt fontMetricsInt = mDefaultPaint.getFontMetricsInt(); 193 194 DynamicLayout dynamicLayout = new DynamicLayout(SINGLELINE_CHAR_SEQUENCE, 195 mDefaultPaint, 196 DEFAULT_OUTER_WIDTH, 197 DEFAULT_ALIGN, 198 SPACING_MULT_NO_SCALE, 199 SPACING_ADD_NO_SCALE, 200 true); 201 assertEquals(fontMetricsInt.top - fontMetricsInt.ascent, dynamicLayout.getTopPadding()); 202 assertEquals(fontMetricsInt.bottom - fontMetricsInt.descent, 203 dynamicLayout.getBottomPadding()); 204 205 dynamicLayout = new DynamicLayout(SINGLELINE_CHAR_SEQUENCE, 206 mDefaultPaint, 207 DEFAULT_OUTER_WIDTH, 208 DEFAULT_ALIGN, 209 SPACING_MULT_NO_SCALE, 210 SPACING_ADD_NO_SCALE, 211 false); 212 assertEquals(0, dynamicLayout.getTopPadding()); 213 assertEquals(0, dynamicLayout.getBottomPadding()); 214 } 215 216 /* 217 * Test the line count and whether include the Tab the layout. 218 * 1. Include Tab. 2. Don't include Tab Use the Y-coordinate to calculate the line number 219 * Test the line top 220 * 1. the Y-coordinate of line top.2. the Y-coordinate of baseline. 221 */ 222 @Test testLineLayout()223 public void testLineLayout() { 224 assertEquals(TEXT.length, mDynamicLayout.getLineCount()); 225 assertFalse(mDynamicLayout.getLineContainsTab(LINE0)); 226 assertTrue(mDynamicLayout.getLineContainsTab(LINE1)); 227 228 assertEquals(LINE0_TOP, mDynamicLayout.getLineTop(LINE0)); 229 230 assertEquals(mDynamicLayout.getLineBottom(LINE0), mDynamicLayout.getLineTop(LINE1)); 231 assertEquals(mDynamicLayout.getLineBottom(LINE1), mDynamicLayout.getLineTop(LINE2)); 232 assertEquals(mDynamicLayout.getLineBottom(LINE2), mDynamicLayout.getLineTop(LINE3)); 233 234 try { 235 assertEquals(mDynamicLayout.getLineBottom(mDynamicLayout.getLineCount()), 236 mDynamicLayout.getLineTop(mDynamicLayout.getLineCount() + 1)); 237 fail("Test DynamicLayout fail, should throw IndexOutOfBoundsException."); 238 } catch (IndexOutOfBoundsException e) { 239 // expected 240 } 241 242 assertEquals(mDynamicLayout.getLineDescent(LINE0) - mDynamicLayout.getLineAscent(LINE0), 243 mDynamicLayout.getLineBottom(LINE0)); 244 245 assertEquals(mDynamicLayout.getLineDescent(LINE1) - mDynamicLayout.getLineAscent(LINE1), 246 mDynamicLayout.getLineBottom(LINE1) - mDynamicLayout.getLineBottom(LINE0)); 247 248 assertEquals(mDynamicLayout.getLineDescent(LINE2) - mDynamicLayout.getLineAscent(LINE2), 249 mDynamicLayout.getLineBottom(LINE2) - mDynamicLayout.getLineBottom(LINE1)); 250 251 assertEquals(LINE0, mDynamicLayout.getLineForVertical(mDynamicLayout.getLineTop(LINE0))); 252 253 assertNotNull(mDynamicLayout.getLineDirections(LINE0)); 254 assertEquals(Layout.DIR_LEFT_TO_RIGHT, mDynamicLayout.getParagraphDirection(LINE0)); 255 256 assertEquals(0, mDynamicLayout.getLineStart(LINE0)); 257 assertEquals(TEXT[0].length(), mDynamicLayout.getLineStart(LINE1)); 258 assertEquals(TEXT[0].length() + TEXT[1].length(), mDynamicLayout.getLineStart(LINE2)); 259 } 260 261 @Test testLineSpacing()262 public void testLineSpacing() { 263 SpannableStringBuilder text = new SpannableStringBuilder("a\nb\nc"); 264 final float spacingMultiplier = 2f; 265 final float spacingAdd = 4; 266 final int width = 1000; 267 final TextPaint textPaint = new TextPaint(); 268 // create the DynamicLayout 269 final DynamicLayout dynamicLayout = new DynamicLayout(text, 270 textPaint, 271 width, 272 ALIGN_NORMAL, 273 spacingMultiplier, 274 spacingAdd, 275 false /*includepad*/); 276 277 // create a StaticLayout with same text, this will define the expectations 278 Layout expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd, 279 spacingMultiplier); 280 assertLineSpecs(expected, dynamicLayout); 281 282 // add a new line to the end, DynamicLayout will re-calculate 283 text = text.append("\nd"); 284 expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd, 285 spacingMultiplier); 286 assertLineSpecs(expected, dynamicLayout); 287 288 // insert a next line and a char as the new second line 289 text = text.insert(TextUtils.indexOf(text, '\n') + 1, "a1\n"); 290 expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd, 291 spacingMultiplier); 292 assertLineSpecs(expected, dynamicLayout); 293 } 294 295 @Test testLineSpacing_textEndingWithNextLine()296 public void testLineSpacing_textEndingWithNextLine() { 297 final SpannableStringBuilder text = new SpannableStringBuilder("a\n"); 298 final float spacingMultiplier = 2f; 299 final float spacingAdd = 4f; 300 final int width = 1000; 301 final TextPaint textPaint = new TextPaint(); 302 // create the DynamicLayout 303 final DynamicLayout dynamicLayout = new DynamicLayout(text, 304 textPaint, 305 width, 306 ALIGN_NORMAL, 307 spacingMultiplier, 308 spacingAdd, 309 false /*includepad*/); 310 311 // create a StaticLayout with same text, this will define the expectations 312 final Layout expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd, 313 spacingMultiplier); 314 assertLineSpecs(expected, dynamicLayout); 315 } 316 createStaticLayout(CharSequence text, TextPaint textPaint, int width, float spacingAdd, float spacingMultiplier)317 private Layout createStaticLayout(CharSequence text, TextPaint textPaint, int width, 318 float spacingAdd, float spacingMultiplier) { 319 return StaticLayout.Builder.obtain(text, 0, 320 text.length(), textPaint, width) 321 .setAlignment(ALIGN_NORMAL) 322 .setIncludePad(false) 323 .setLineSpacing(spacingAdd, spacingMultiplier) 324 .build(); 325 } 326 assertLineSpecs(Layout expected, DynamicLayout actual)327 private void assertLineSpecs(Layout expected, DynamicLayout actual) { 328 final int lineCount = expected.getLineCount(); 329 assertTrue(lineCount > 1); 330 assertEquals(lineCount, actual.getLineCount()); 331 332 for (int i = 0; i < lineCount; i++) { 333 assertEquals(expected.getLineTop(i), actual.getLineTop(i)); 334 assertEquals(expected.getLineDescent(i), actual.getLineDescent(i)); 335 assertEquals(expected.getLineBaseline(i), actual.getLineBaseline(i)); 336 assertEquals(expected.getLineBottom(i), actual.getLineBottom(i)); 337 } 338 } 339 340 @Test testLineSpacing_notAffectedByPreviousEllipsization()341 public void testLineSpacing_notAffectedByPreviousEllipsization() { 342 // Create an ellipsized DynamicLayout, but throw it away. 343 final String ellipsizedText = "Some arbitrary relatively long text"; 344 final DynamicLayout ellipsizedLayout = new DynamicLayout( 345 ellipsizedText, 346 ellipsizedText, 347 mDefaultPaint, 348 1 << 20 /* width */, 349 DEFAULT_ALIGN, 350 SPACING_MULT_NO_SCALE, 351 SPACING_ADD_NO_SCALE, 352 true /* include pad */, 353 TextUtils.TruncateAt.END, 354 2 * (int) mDefaultPaint.getTextSize() /* ellipsizedWidth */); 355 356 // Now try to measure linespacing in a non-ellipsized DynamicLayout. 357 final String text = "a\nb\nc"; 358 final float spacingMultiplier = 2f; 359 final float spacingAdd = 4f; 360 final int width = 1000; 361 final TextPaint textPaint = new TextPaint(); 362 // create the DynamicLayout 363 final DynamicLayout dynamicLayout = new DynamicLayout(text, 364 textPaint, 365 width, 366 ALIGN_NORMAL, 367 spacingMultiplier, 368 spacingAdd, 369 false /*includepad*/); 370 371 // create a StaticLayout with same text, this will define the expectations 372 Layout expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd, 373 spacingMultiplier); 374 assertLineSpecs(expected, dynamicLayout); 375 } 376 377 /* 378 * Tests that the ellipsis result, for the case of TruncateAt.START and no ellipsization needed, 379 * isn't affected by a previous ellipsization. This tests the fix for a bug where the static 380 * StaticLayout instance reused internally was not properly reinitialized for this specific 381 * case. 382 */ 383 @Test testEllipsis_notAffectedByPreviousEllipsization()384 public void testEllipsis_notAffectedByPreviousEllipsization() { 385 // Create an ellipsized DynamicLayout, but throw it away. 386 final String ellipsizedText = "Some arbitrary relatively long text"; 387 final DynamicLayout ellipsizedLayout = 388 DynamicLayout.Builder.obtain(ellipsizedText, mDefaultPaint, 1 << 20 /* width */) 389 .setEllipsize(TextUtils.TruncateAt.END) 390 .setEllipsizedWidth(2 * (int) mDefaultPaint.getTextSize()) 391 .build(); 392 // Make sure it was actually ellipsized. 393 assertThat(ellipsizedLayout.getEllipsisCount(LINE0)).isGreaterThan(0); 394 395 // Create a DynamicLayout that would trigger the bug. 396 final String text = "a\nb"; 397 final DynamicLayout dynamicLayout = 398 createBuilderWithDefaults(text).setEllipsize(TextUtils.TruncateAt.START).build(); 399 400 assertThat(dynamicLayout.getEllipsisCount(LINE0)).isEqualTo(0); 401 } 402 403 @Test testBuilder_obtain()404 public void testBuilder_obtain() { 405 final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE, 406 mDefaultPaint, DEFAULT_OUTER_WIDTH); 407 final DynamicLayout layout = builder.build(); 408 // Check values passed to obtain(). 409 assertEquals(MULTLINE_CHAR_SEQUENCE, layout.getText()); 410 assertEquals(mDefaultPaint, layout.getPaint()); 411 assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth()); 412 // Check default values. 413 assertEquals(Layout.Alignment.ALIGN_NORMAL, layout.getAlignment()); 414 assertEquals(0.0f, layout.getSpacingAdd(), 0.0f); 415 assertEquals(1.0f, layout.getSpacingMultiplier(), 0.0f); 416 assertEquals(DEFAULT_OUTER_WIDTH, layout.getEllipsizedWidth()); 417 } 418 419 @Test(expected = NullPointerException.class) testBuilder_obtainWithNullText()420 public void testBuilder_obtainWithNullText() { 421 final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(null, mDefaultPaint, 0); 422 final DynamicLayout layout = builder.build(); 423 } 424 425 @Test(expected = NullPointerException.class) testBuilder_obtainWithNullPaint()426 public void testBuilder_obtainWithNullPaint() { 427 final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE, 428 null, 0); 429 final DynamicLayout layout = builder.build(); 430 } 431 432 @Test testBuilder_setDisplayTest()433 public void testBuilder_setDisplayTest() { 434 final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE, 435 mDefaultPaint, DEFAULT_OUTER_WIDTH); 436 builder.setDisplayText(SINGLELINE_CHAR_SEQUENCE); 437 final DynamicLayout layout = builder.build(); 438 assertEquals(SINGLELINE_CHAR_SEQUENCE, layout.getText()); 439 } 440 441 @Test testBuilder_setAlignment()442 public void testBuilder_setAlignment() { 443 final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE, 444 mDefaultPaint, DEFAULT_OUTER_WIDTH); 445 builder.setAlignment(DEFAULT_ALIGN); 446 final DynamicLayout layout = builder.build(); 447 assertEquals(DEFAULT_ALIGN, layout.getAlignment()); 448 } 449 450 @Test testBuilder_setLineSpacing()451 public void testBuilder_setLineSpacing() { 452 final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE, 453 mDefaultPaint, DEFAULT_OUTER_WIDTH); 454 builder.setLineSpacing(1.0f, 2.0f); 455 final DynamicLayout layout = builder.build(); 456 assertEquals(1.0f, layout.getSpacingAdd(), 0.0f); 457 assertEquals(2.0f, layout.getSpacingMultiplier(), 0.0f); 458 } 459 460 @Test testBuilder_setLineBreakConfig()461 public void testBuilder_setLineBreakConfig() { 462 final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE, 463 mDefaultPaint, DEFAULT_OUTER_WIDTH); 464 LineBreakConfig.Builder lbcBuilder = new LineBreakConfig.Builder(); 465 LineBreakConfig lbc = lbcBuilder.setLineBreakStyle(LineBreakConfig.LINE_BREAK_STYLE_STRICT) 466 .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE).build(); 467 builder.setLineBreakConfig(lbc); 468 final DynamicLayout layout = builder.build(); 469 assertEquals(lbc, layout.getLineBreakConfig()); 470 } 471 472 @Test testBuilder_ellipsization()473 public void testBuilder_ellipsization() { 474 final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE, 475 mDefaultPaint, DEFAULT_OUTER_WIDTH); 476 builder.setEllipsize(TextUtils.TruncateAt.END) 477 .setEllipsizedWidth(ELLIPSIZE_WIDTH); 478 final DynamicLayout layout = builder.build(); 479 assertEquals(ELLIPSIZE_WIDTH, layout.getEllipsizedWidth()); 480 assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth()); 481 for (int i = 0; i < TEXT.length; i++) { 482 if (i == TEXT.length - 1) { // last line 483 assertTrue(layout.getEllipsisCount(i) > 0); 484 } else { 485 assertEquals(0, layout.getEllipsisCount(i)); 486 } 487 } 488 } 489 490 @Test testBuilder_otherSetters()491 public void testBuilder_otherSetters() { 492 // Setter methods that cannot be directly tested. 493 // setBreakStrategy, setHyphenationFrequency, setIncludePad, and setJustificationMode. 494 final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE, 495 mDefaultPaint, DEFAULT_OUTER_WIDTH); 496 builder.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY) 497 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL) 498 .setIncludePad(true) 499 .setJustificationMode(Layout.JUSTIFICATION_MODE_INTER_WORD); 500 final DynamicLayout layout = builder.build(); 501 assertNotNull(layout); 502 } 503 504 /* 505 * Tests that DynamicLayout accounts for TransformationMethods that can change text length, such 506 * as AllCapsTransformationMethod ("ß" becomes "SS") and TranslationTransformationMethod 507 * (arbitrary length changes). 508 */ 509 @Test testDisplayTextUsedInsteadOfBase()510 public void testDisplayTextUsedInsteadOfBase() { 511 DynamicLayout layout = 512 createBuilderWithDefaults(SINGLELINE_CHAR_SEQUENCE) 513 .setDisplayText(MULTLINE_CHAR_SEQUENCE) 514 .setEllipsize(TextUtils.TruncateAt.END) 515 .setEllipsizedWidth(ELLIPSIZE_WIDTH) 516 .build(); 517 518 assertThat(layout.getLineCount()).isEqualTo(TEXT.length); 519 520 assertThat(layout.getLineStart(LINE0)).isEqualTo(0); 521 assertThat(layout.getLineStart(LINE1)).isEqualTo(TEXT[0].length()); 522 assertThat(layout.getLineStart(LINE2)).isEqualTo(TEXT[0].length() + TEXT[1].length()); 523 524 assertThat(layout.getEllipsisCount(LINE0)).isEqualTo(0); 525 assertThat(layout.getEllipsisCount(LINE1)).isEqualTo(0); 526 assertThat(layout.getEllipsisCount(LINE2)).isGreaterThan(0); 527 } 528 529 @Test testReflow_afterSpanChangedShouldNotThrowException()530 public void testReflow_afterSpanChangedShouldNotThrowException() { 531 final SpannableStringBuilder builder = new SpannableStringBuilder("crash crash crash!!"); 532 533 final TypefaceSpan span = mock(TypefaceSpan.class); 534 builder.setSpan(span, 1, 4, SPAN_EXCLUSIVE_EXCLUSIVE); 535 536 final DynamicLayout layout = DynamicLayout.Builder.obtain(builder, 537 new TextPaint(), Integer.MAX_VALUE).build(); 538 try { 539 builder.insert(1, "Hello there\n\n"); 540 } catch (Throwable e) { 541 throw new RuntimeException("Inserting text into DynamicLayout should not crash", e); 542 } 543 } 544 createBuilderWithDefaults(CharSequence base)545 private DynamicLayout.Builder createBuilderWithDefaults(CharSequence base) { 546 final DynamicLayout.Builder builder = 547 DynamicLayout.Builder.obtain(base, mDefaultPaint, DEFAULT_OUTER_WIDTH); 548 return builder.setAlignment(DEFAULT_ALIGN) 549 .setLineSpacing(SPACING_ADD_NO_SCALE, SPACING_MULT_NO_SCALE) 550 .setIncludePad(true); 551 } 552 } 553