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