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