1 /* 2 * Copyright (C) 2022 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.graphics.text; 18 19 import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN; 20 import static com.android.text.flags.Flags.FLAG_WORD_STYLE_AUTO; 21 22 import android.annotation.FlaggedApi; 23 import android.annotation.IntDef; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.SuppressLint; 27 import android.app.ActivityThread; 28 import android.os.Build; 29 import android.os.LocaleList; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 import java.util.Objects; 36 37 /** 38 * Specifies the line-break strategies for text wrapping. 39 * 40 * <p>See the 41 * <a href="https://www.w3.org/TR/css-text-3/#line-break-property" class="external"> 42 * line-break property</a> for more information. 43 */ 44 public final class LineBreakConfig implements Parcelable { 45 /** 46 * No hyphenation preference is specified. 47 * 48 * <p> 49 * This is a special value of hyphenation preference indicating no hyphenation preference is 50 * specified. When overriding a {@link LineBreakConfig} with another {@link LineBreakConfig} 51 * with {@link Builder#merge(LineBreakConfig)} function, the hyphenation preference of 52 * overridden config will be kept if the hyphenation preference of overriding config is 53 * {@link #HYPHENATION_UNSPECIFIED}. 54 * 55 * <p> 56 * <pre> 57 * val override = LineBreakConfig.Builder() 58 * .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) 59 * .build(); // UNSPECIFIED if no setHyphenation is called. 60 * val config = LineBreakConfig.Builder() 61 * .setHyphenation(LineBreakConfig.HYPHENATION_DISABLED) 62 * .merge(override) 63 * .build() 64 * // Here, config has HYPHENATION_DISABLED for line break config and 65 * // LINE_BREAK_WORD_STYLE_PHRASE for line break word style. 66 * </pre> 67 * 68 * <p> 69 * This value is resolved to {@link #HYPHENATION_ENABLED} if this value is used for text 70 * layout/rendering. 71 */ 72 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) 73 public static final int HYPHENATION_UNSPECIFIED = -1; 74 75 /** 76 * The hyphenation is disabled. 77 */ 78 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) 79 public static final int HYPHENATION_DISABLED = 0; 80 81 /** 82 * The hyphenation is enabled. 83 * 84 * Note: Even if the hyphenation is enabled with a line break strategy 85 * {@link LineBreaker#BREAK_STRATEGY_SIMPLE}, the hyphenation will not be performed unless a 86 * single word cannot meet width constraints. 87 */ 88 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) 89 public static final int HYPHENATION_ENABLED = 1; 90 91 /** @hide */ 92 @IntDef(prefix = { "HYPHENATION_" }, value = { 93 HYPHENATION_UNSPECIFIED, HYPHENATION_ENABLED, HYPHENATION_DISABLED, 94 }) 95 @Retention(RetentionPolicy.SOURCE) 96 public @interface Hyphenation {} 97 98 /** 99 * No line break style is specified. 100 * 101 * <p> 102 * This is a special value of line break style indicating no style value is specified. 103 * When overriding a {@link LineBreakConfig} with another {@link LineBreakConfig} with 104 * {@link Builder#merge(LineBreakConfig)} function, the line break style of overridden config 105 * will be kept if the line break style of overriding config is 106 * {@link #LINE_BREAK_STYLE_UNSPECIFIED}. 107 * 108 * <pre> 109 * val override = LineBreakConfig.Builder() 110 * .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) 111 * .build(); // UNSPECIFIED if no setLineBreakStyle is called. 112 * val config = LineBreakConfig.Builder() 113 * .setLineBreakStyle(LineBreakConfig.LINE_BREAK_STYLE_STRICT) 114 * .merge(override) 115 * .build() 116 * // Here, config has LINE_BREAK_STYLE_STRICT for line break config and 117 * // LINE_BREAK_WORD_STYLE_PHRASE for line break word style. 118 * </pre> 119 * 120 * <p> 121 * This value is resolved to {@link #LINE_BREAK_STYLE_NONE} if the target SDK version is API 122 * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or before and this value is used for text 123 * layout/rendering. This value is resolved to {@link #LINE_BREAK_STYLE_AUTO} if the target SDK 124 * version is API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or after and this value is 125 * used for text layout/rendering. 126 */ 127 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) 128 public static final int LINE_BREAK_STYLE_UNSPECIFIED = -1; 129 130 /** 131 * No line-break rules are used for line breaking. 132 */ 133 public static final int LINE_BREAK_STYLE_NONE = 0; 134 135 /** 136 * The least restrictive line-break rules are used for line breaking. This 137 * setting is typically used for short lines. 138 */ 139 public static final int LINE_BREAK_STYLE_LOOSE = 1; 140 141 /** 142 * The most common line-break rules are used for line breaking. 143 */ 144 public static final int LINE_BREAK_STYLE_NORMAL = 2; 145 146 /** 147 * The most strict line-break rules are used for line breaking. 148 */ 149 public static final int LINE_BREAK_STYLE_STRICT = 3; 150 151 /** 152 * The line break style that used for preventing automatic line breaking. 153 * 154 * This is useful when you want to preserve some words in the same line by using 155 * {@link android.text.style.LineBreakConfigSpan} or 156 * {@link android.text.style.LineBreakConfigSpan#createNoBreakSpan()} as a shorthand. 157 * Note that even if this style is specified, the grapheme based line break is still performed 158 * for preventing clipping text. 159 * 160 * @see android.text.style.LineBreakConfigSpan 161 * @see android.text.style.LineBreakConfigSpan#createNoBreakSpan() 162 */ 163 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) 164 public static final int LINE_BREAK_STYLE_NO_BREAK = 4; 165 166 /** 167 * A special value for the line breaking style option. 168 * 169 * <p> 170 * The auto option for the line break style set the line break style based on the locale of the 171 * text rendering context. You can specify the context locale by 172 * {@link android.widget.TextView#setTextLocales(LocaleList)} or 173 * {@link android.graphics.Paint#setTextLocales(LocaleList)}. 174 * 175 * <p> 176 * In the API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, auto option does followings: 177 * - If at least one locale in the locale list contains Japanese script, this option is 178 * equivalent to {@link #LINE_BREAK_STYLE_STRICT}. 179 * - Otherwise, this option is equivalent to {@link #LINE_BREAK_STYLE_NONE}. 180 * 181 * <p> 182 * Note: future versions may have special line breaking style rules for other locales. 183 */ 184 @FlaggedApi(FLAG_WORD_STYLE_AUTO) 185 public static final int LINE_BREAK_STYLE_AUTO = 5; 186 187 /** @hide */ 188 @IntDef(prefix = { "LINE_BREAK_STYLE_" }, value = { 189 LINE_BREAK_STYLE_NONE, LINE_BREAK_STYLE_LOOSE, LINE_BREAK_STYLE_NORMAL, 190 LINE_BREAK_STYLE_STRICT, LINE_BREAK_STYLE_UNSPECIFIED, LINE_BREAK_STYLE_NO_BREAK, 191 LINE_BREAK_STYLE_AUTO 192 }) 193 @Retention(RetentionPolicy.SOURCE) 194 public @interface LineBreakStyle {} 195 196 /** 197 * No line break word style is specified. 198 * 199 * This is a special value of line break word style indicating no style value is specified. 200 * When overriding a {@link LineBreakConfig} with another {@link LineBreakConfig} with 201 * {@link Builder#merge(LineBreakConfig)} function, the line break word style of overridden 202 * config will be kept if the line break word style of overriding config is 203 * {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}. 204 * 205 * <pre> 206 * val override = LineBreakConfig.Builder() 207 * .setLineBreakStyle(LineBreakConfig.LINE_BREAK_STYLE_STRICT) 208 * .build(); // UNSPECIFIED if no setLineBreakWordStyle is called. 209 * val config = LineBreakConfig.Builder() 210 * .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) 211 * .merge(override) 212 * .build() 213 * // Here, config has LINE_BREAK_STYLE_STRICT for line break config and 214 * // LINE_BREAK_WORD_STYLE_PHRASE for line break word style. 215 * </pre> 216 * 217 * This value is resolved to {@link #LINE_BREAK_WORD_STYLE_NONE} if the target SDK version is 218 * API {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or before and this value is used for text 219 * layout/rendering. This value is resolved to {@link #LINE_BREAK_WORD_STYLE_AUTO} if the target 220 * SDK version is API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or after and this value is 221 * used for text layout/rendering. 222 */ 223 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) 224 public static final int LINE_BREAK_WORD_STYLE_UNSPECIFIED = -1; 225 226 /** 227 * No line-break word style is used for line breaking. 228 */ 229 public static final int LINE_BREAK_WORD_STYLE_NONE = 0; 230 231 /** 232 * Line breaking is based on phrases, which results in text wrapping only on 233 * meaningful words. 234 * 235 * <p>Support for this line-break word style depends on locale. If the 236 * current locale does not support phrase-based text wrapping, this setting 237 * has no effect. 238 */ 239 public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; 240 241 /** 242 * A special value for the line breaking word style option. 243 * 244 * <p> 245 * The auto option for the line break word style does some heuristics based on locales and line 246 * count. 247 * 248 * <p> 249 * In the API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, auto option does followings: 250 * - If at least one locale in the locale list contains Korean script, this option is equivalent 251 * to {@link #LINE_BREAK_WORD_STYLE_PHRASE}. 252 * - If not, then if at least one locale in the locale list contains Japanese script, this 253 * option is equivalent to {@link #LINE_BREAK_WORD_STYLE_PHRASE} if the result of its line 254 * count is less than 5 lines. 255 * - Otherwise, this option is equivalent to {@link #LINE_BREAK_WORD_STYLE_NONE}. 256 * 257 * <p> 258 * Note: future versions may have special line breaking word style rules for other locales. 259 */ 260 @FlaggedApi(FLAG_WORD_STYLE_AUTO) 261 public static final int LINE_BREAK_WORD_STYLE_AUTO = 2; 262 263 /** @hide */ 264 @IntDef(prefix = { "LINE_BREAK_WORD_STYLE_" }, value = { 265 LINE_BREAK_WORD_STYLE_NONE, LINE_BREAK_WORD_STYLE_PHRASE, LINE_BREAK_WORD_STYLE_UNSPECIFIED, 266 LINE_BREAK_WORD_STYLE_AUTO 267 }) 268 @Retention(RetentionPolicy.SOURCE) 269 public @interface LineBreakWordStyle {} 270 271 /** 272 * A builder for creating a {@code LineBreakConfig} instance. 273 */ 274 public static final class Builder { 275 // The line break style for the LineBreakConfig. 276 private @LineBreakStyle int mLineBreakStyle = LineBreakConfig.LINE_BREAK_STYLE_UNSPECIFIED; 277 278 // The line break word style for the LineBreakConfig. 279 private @LineBreakWordStyle int mLineBreakWordStyle = 280 LineBreakConfig.LINE_BREAK_WORD_STYLE_UNSPECIFIED; 281 282 private @Hyphenation int mHyphenation = LineBreakConfig.HYPHENATION_UNSPECIFIED; 283 284 /** 285 * Builder constructor. 286 */ Builder()287 public Builder() { 288 reset(null); 289 } 290 291 /** 292 * Merges line break config with other config 293 * 294 * Update the internal configurations with passed {@code config}. If the config values of 295 * passed {@code config} are unspecified, the original config values are kept. For example, 296 * the following code passes {@code config} that has {@link #LINE_BREAK_STYLE_UNSPECIFIED}. 297 * This code generates {@link LineBreakConfig} that has line break config 298 * {@link #LINE_BREAK_STYLE_STRICT}. 299 * 300 * <pre> 301 * val override = LineBreakConfig.Builder() 302 * .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) 303 * .build(); // UNSPECIFIED if no setLineBreakStyle is called. 304 * val config = LineBreakConfig.Builder() 305 * .setLineBreakStyle(LineBreakConfig.LINE_BREAK_STYLE_STRICT) 306 * .merge(override) 307 * .build() 308 * // Here, config has LINE_BREAK_STYLE_STRICT of line break config and 309 * // LINE_BREAK_WORD_STYLE_PHRASE of line break word style. 310 * </pre> 311 * 312 * @see #LINE_BREAK_STYLE_UNSPECIFIED 313 * @see #LINE_BREAK_WORD_STYLE_UNSPECIFIED 314 * 315 * @param config an override line break config 316 * @return This {@code Builder}. 317 */ 318 @SuppressLint("BuilderSetStyle") 319 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) merge(@onNull LineBreakConfig config)320 public @NonNull Builder merge(@NonNull LineBreakConfig config) { 321 if (config.mLineBreakStyle != LINE_BREAK_STYLE_UNSPECIFIED) { 322 mLineBreakStyle = config.mLineBreakStyle; 323 } 324 if (config.mLineBreakWordStyle != LINE_BREAK_WORD_STYLE_UNSPECIFIED) { 325 mLineBreakWordStyle = config.mLineBreakWordStyle; 326 } 327 if (config.mHyphenation != HYPHENATION_UNSPECIFIED) { 328 mHyphenation = config.mHyphenation; 329 } 330 return this; 331 } 332 333 /** 334 * Resets this builder to the given config state. 335 * 336 * @param config a config value used for resetting. {@code null} is allowed. If {@code null} 337 * is passed, all configs are reset to unspecified. 338 * @return This {@code Builder}. 339 * @hide 340 */ reset(@ullable LineBreakConfig config)341 public @NonNull Builder reset(@Nullable LineBreakConfig config) { 342 if (config == null) { 343 mLineBreakStyle = LINE_BREAK_STYLE_UNSPECIFIED; 344 mLineBreakWordStyle = LINE_BREAK_WORD_STYLE_UNSPECIFIED; 345 mHyphenation = HYPHENATION_UNSPECIFIED; 346 } else { 347 mLineBreakStyle = config.mLineBreakStyle; 348 mLineBreakWordStyle = config.mLineBreakWordStyle; 349 mHyphenation = config.mHyphenation; 350 } 351 return this; 352 } 353 354 // TODO(316208691): Revive following removed API docs. 355 // Note: different from {@link #merge(LineBreakConfig)} if this function is called with 356 // {@link #LINE_BREAK_STYLE_UNSPECIFIED}, the line break style is reset to 357 // {@link #LINE_BREAK_STYLE_UNSPECIFIED}. 358 /** 359 * Sets the line-break style. 360 * 361 * @see <a href="https://unicode.org/reports/tr35/#UnicodeLineBreakStyleIdentifier"> 362 * Unicode Line Break Style Identifier</a> 363 * @see <a href="https://drafts.csswg.org/css-text/#line-break-property"> 364 * CSS Line Break Property</a> 365 * 366 * @param lineBreakStyle The new line-break style. 367 * @return This {@code Builder}. 368 */ setLineBreakStyle(@ineBreakStyle int lineBreakStyle)369 public @NonNull Builder setLineBreakStyle(@LineBreakStyle int lineBreakStyle) { 370 mLineBreakStyle = lineBreakStyle; 371 return this; 372 } 373 374 // TODO(316208691): Revive following removed API docs. 375 // Note: different from {@link #merge(LineBreakConfig)} method, if this function is called 376 // with {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}, the line break style is reset to 377 // {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}. 378 /** 379 * Sets the line-break word style. 380 * 381 * @see <a href="https://unicode.org/reports/tr35/#UnicodeLineBreakWordIdentifier"> 382 * Unicode Line Break Word Identifier</a> 383 * @see <a href="https://drafts.csswg.org/css-text/#word-break-property"> 384 * CSS Word Break Property</a> 385 * 386 * @param lineBreakWordStyle The new line-break word style. 387 * @return This {@code Builder}. 388 */ setLineBreakWordStyle(@ineBreakWordStyle int lineBreakWordStyle)389 public @NonNull Builder setLineBreakWordStyle(@LineBreakWordStyle int lineBreakWordStyle) { 390 mLineBreakWordStyle = lineBreakWordStyle; 391 return this; 392 } 393 394 /** 395 * Sets the hyphenation preference 396 * 397 * Note: Even if the {@link LineBreakConfig#HYPHENATION_ENABLED} is specified, the 398 * hyphenation will not be performed if the {@link android.widget.TextView} or underlying 399 * {@link android.text.StaticLayout}, {@link LineBreaker} are configured with 400 * {@link LineBreaker#HYPHENATION_FREQUENCY_NONE}. 401 * 402 * Note: Even if the hyphenation is enabled with a line break strategy 403 * {@link LineBreaker#BREAK_STRATEGY_SIMPLE}, the hyphenation will not be performed unless a 404 * single word cannot meet width constraints. 405 * 406 * @param hyphenation The hyphenation preference. 407 * @return This {@code Builder}. 408 */ 409 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) setHyphenation(@yphenation int hyphenation)410 public @NonNull Builder setHyphenation(@Hyphenation int hyphenation) { 411 mHyphenation = hyphenation; 412 return this; 413 } 414 415 /** 416 * Builds a {@link LineBreakConfig} instance. 417 * 418 * This method can be called multiple times for generating multiple {@link LineBreakConfig} 419 * instances. 420 * 421 * @return The {@code LineBreakConfig} instance. 422 */ build()423 public @NonNull LineBreakConfig build() { 424 return new LineBreakConfig(mLineBreakStyle, mLineBreakWordStyle, mHyphenation); 425 } 426 } 427 428 /** 429 * Creates a {@code LineBreakConfig} instance with the provided line break 430 * parameters. 431 * 432 * @param lineBreakStyle The line-break style for text wrapping. 433 * @param lineBreakWordStyle The line-break word style for text wrapping. 434 * @return The {@code LineBreakConfig} instance. 435 * @hide 436 */ getLineBreakConfig(@ineBreakStyle int lineBreakStyle, @LineBreakWordStyle int lineBreakWordStyle)437 public static @NonNull LineBreakConfig getLineBreakConfig(@LineBreakStyle int lineBreakStyle, 438 @LineBreakWordStyle int lineBreakWordStyle) { 439 LineBreakConfig.Builder builder = new LineBreakConfig.Builder(); 440 return builder.setLineBreakStyle(lineBreakStyle) 441 .setLineBreakWordStyle(lineBreakWordStyle) 442 .build(); 443 } 444 445 /** @hide */ 446 public static final LineBreakConfig NONE = 447 new Builder().setLineBreakStyle(LINE_BREAK_STYLE_NONE) 448 .setLineBreakWordStyle(LINE_BREAK_WORD_STYLE_NONE).build(); 449 450 private final @LineBreakStyle int mLineBreakStyle; 451 private final @LineBreakWordStyle int mLineBreakWordStyle; 452 private final @Hyphenation int mHyphenation; 453 454 /** 455 * Constructor with line-break parameters. 456 * 457 * <p>Use {@link LineBreakConfig.Builder} to create the 458 * {@code LineBreakConfig} instance. 459 * @hide 460 */ LineBreakConfig(@ineBreakStyle int lineBreakStyle, @LineBreakWordStyle int lineBreakWordStyle, @Hyphenation int hyphenation)461 public LineBreakConfig(@LineBreakStyle int lineBreakStyle, 462 @LineBreakWordStyle int lineBreakWordStyle, 463 @Hyphenation int hyphenation) { 464 mLineBreakStyle = lineBreakStyle; 465 mLineBreakWordStyle = lineBreakWordStyle; 466 mHyphenation = hyphenation; 467 } 468 469 /** 470 * Gets the current line-break style. 471 * 472 * @return The line-break style to be used for text wrapping. 473 */ getLineBreakStyle()474 public @LineBreakStyle int getLineBreakStyle() { 475 return mLineBreakStyle; 476 } 477 478 /** 479 * Gets the resolved line break style. 480 * 481 * This method never returns {@link #LINE_BREAK_STYLE_UNSPECIFIED}. 482 * 483 * @return The line break style. 484 * @hide 485 */ getResolvedLineBreakStyle(@ullable LineBreakConfig config)486 public static @LineBreakStyle int getResolvedLineBreakStyle(@Nullable LineBreakConfig config) { 487 final int targetSdkVersion = ActivityThread.currentApplication().getApplicationInfo() 488 .targetSdkVersion; 489 final int defaultStyle; 490 final int vicVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM; 491 if (targetSdkVersion >= vicVersion) { 492 defaultStyle = LINE_BREAK_STYLE_AUTO; 493 } else { 494 defaultStyle = LINE_BREAK_STYLE_NONE; 495 } 496 if (config == null) { 497 return defaultStyle; 498 } 499 return config.mLineBreakStyle == LINE_BREAK_STYLE_UNSPECIFIED 500 ? defaultStyle : config.mLineBreakStyle; 501 } 502 503 /** 504 * Gets the current line-break word style. 505 * 506 * @return The line-break word style to be used for text wrapping. 507 */ getLineBreakWordStyle()508 public @LineBreakWordStyle int getLineBreakWordStyle() { 509 return mLineBreakWordStyle; 510 } 511 512 /** 513 * Gets the resolved line break style. 514 * 515 * This method never returns {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}. 516 * 517 * @return The line break word style. 518 * @hide 519 */ getResolvedLineBreakWordStyle( @ullable LineBreakConfig config)520 public static @LineBreakWordStyle int getResolvedLineBreakWordStyle( 521 @Nullable LineBreakConfig config) { 522 final int targetSdkVersion = ActivityThread.currentApplication().getApplicationInfo() 523 .targetSdkVersion; 524 final int defaultWordStyle; 525 final int vicVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM; 526 if (targetSdkVersion >= vicVersion) { 527 defaultWordStyle = LINE_BREAK_WORD_STYLE_AUTO; 528 } else { 529 defaultWordStyle = LINE_BREAK_WORD_STYLE_NONE; 530 } 531 if (config == null) { 532 return defaultWordStyle; 533 } 534 return config.mLineBreakWordStyle == LINE_BREAK_WORD_STYLE_UNSPECIFIED 535 ? defaultWordStyle : config.mLineBreakWordStyle; 536 } 537 538 /** 539 * Returns a hyphenation preference. 540 * 541 * @return A hyphenation preference. 542 */ 543 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) getHyphenation()544 public @Hyphenation int getHyphenation() { 545 return mHyphenation; 546 } 547 548 /** 549 * Returns a hyphenation preference. 550 * 551 * This method never returns {@link #HYPHENATION_UNSPECIFIED}. 552 * 553 * @return A hyphenation preference. 554 * @hide 555 */ getResolvedHyphenation( @ullable LineBreakConfig config)556 public static @Hyphenation int getResolvedHyphenation( 557 @Nullable LineBreakConfig config) { 558 if (config == null) { 559 return HYPHENATION_ENABLED; 560 } 561 return config.mHyphenation == HYPHENATION_UNSPECIFIED 562 ? HYPHENATION_ENABLED : config.mHyphenation; 563 } 564 565 566 /** 567 * Generates a new {@link LineBreakConfig} instance merged with given {@code config}. 568 * 569 * If values of passing {@code config} are unspecified, the original values are kept. For 570 * example, the following code shows how line break config is merged. 571 * 572 * <pre> 573 * val override = LineBreakConfig.Builder() 574 * .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) 575 * .build(); // UNSPECIFIED if no setLineBreakStyle is called. 576 * val config = LineBreakConfig.Builder() 577 * .setLineBreakStyle(LineBreakConfig.LINE_BREAK_STYLE_STRICT) 578 * .build(); 579 * 580 * val newConfig = config.merge(override) 581 * // newConfig has LINE_BREAK_STYLE_STRICT of line break style and 582 * LINE_BREAK_WORD_STYLE_PHRASE of line break word style. 583 * </pre> 584 * 585 * @param config an overriding config. 586 * @return newly created instance that is current style merged with passed config. 587 */ 588 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) merge(@onNull LineBreakConfig config)589 public @NonNull LineBreakConfig merge(@NonNull LineBreakConfig config) { 590 return new LineBreakConfig( 591 config.mLineBreakStyle == LINE_BREAK_STYLE_UNSPECIFIED 592 ? mLineBreakStyle : config.mLineBreakStyle, 593 config.mLineBreakWordStyle == LINE_BREAK_WORD_STYLE_UNSPECIFIED 594 ? mLineBreakWordStyle : config.mLineBreakWordStyle, 595 config.mHyphenation == HYPHENATION_UNSPECIFIED 596 ? mHyphenation : config.mHyphenation); 597 } 598 599 @Override equals(Object o)600 public boolean equals(Object o) { 601 if (o == null) return false; 602 if (this == o) return true; 603 if (!(o instanceof LineBreakConfig)) return false; 604 LineBreakConfig that = (LineBreakConfig) o; 605 return (mLineBreakStyle == that.mLineBreakStyle) 606 && (mLineBreakWordStyle == that.mLineBreakWordStyle) 607 && (mHyphenation == that.mHyphenation); 608 } 609 610 @Override hashCode()611 public int hashCode() { 612 return Objects.hash(mLineBreakStyle, mLineBreakWordStyle, mHyphenation); 613 } 614 615 @Override toString()616 public String toString() { 617 return "LineBreakConfig{" 618 + "mLineBreakStyle=" + mLineBreakStyle 619 + ", mLineBreakWordStyle=" + mLineBreakWordStyle 620 + ", mHyphenation= " + mHyphenation 621 + '}'; 622 } 623 624 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) 625 @Override describeContents()626 public int describeContents() { 627 return 0; 628 } 629 630 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) 631 @Override writeToParcel(@onNull Parcel dest, int flags)632 public void writeToParcel(@NonNull Parcel dest, int flags) { 633 dest.writeInt(mLineBreakStyle); 634 dest.writeInt(mLineBreakWordStyle); 635 dest.writeInt(mHyphenation); 636 } 637 638 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) 639 public static final @NonNull Creator<LineBreakConfig> CREATOR = new Creator<>() { 640 641 @Override 642 public LineBreakConfig createFromParcel(Parcel source) { 643 final int lineBreakStyle = source.readInt(); 644 final int lineBreakWordStyle = source.readInt(); 645 final int hyphenation = source.readInt(); 646 return new LineBreakConfig(lineBreakStyle, lineBreakWordStyle, hyphenation); 647 } 648 649 @Override 650 public LineBreakConfig[] newArray(int size) { 651 return new LineBreakConfig[size]; 652 } 653 }; 654 } 655