diff --git a/core/api/current.txt b/core/api/current.txt index 78d4b4de5cc74..a646537bfe329 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -953,6 +953,7 @@ package android { field public static final int letterSpacing = 16843958; // 0x10104b6 field public static final int level = 16844032; // 0x1010500 field public static final int lineBreakStyle = 16844365; // 0x101064d + field public static final int lineBreakWordStyle = 16844366; // 0x101064e field public static final int lineHeight = 16844159; // 0x101057f field public static final int lineSpacingExtra = 16843287; // 0x1010217 field public static final int lineSpacingMultiplier = 16843288; // 0x1010218 @@ -17618,12 +17619,16 @@ package android.graphics.text { public final class LineBreakConfig { ctor public LineBreakConfig(); method public int getLineBreakStyle(); - method public void set(@Nullable android.graphics.text.LineBreakConfig); + method public int getLineBreakWordStyle(); + method public void set(@NonNull android.graphics.text.LineBreakConfig); method public void setLineBreakStyle(int); + method public void setLineBreakWordStyle(int); field public static final int LINE_BREAK_STYLE_LOOSE = 1; // 0x1 field public static final int LINE_BREAK_STYLE_NONE = 0; // 0x0 field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2 field public static final int LINE_BREAK_STYLE_STRICT = 3; // 0x3 + field public static final int LINE_BREAK_WORD_STYLE_NONE = 0; // 0x0 + field public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; // 0x1 } public class LineBreaker { diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java index ce63376a6d631..be66db2a4c055 100644 --- a/core/java/android/text/PrecomputedText.java +++ b/core/java/android/text/PrecomputedText.java @@ -363,6 +363,9 @@ public class PrecomputedText implements Spannable { public String toString() { int lineBreakStyle = (mLineBreakConfig != null) ? mLineBreakConfig.getLineBreakStyle() : LineBreakConfig.LINE_BREAK_STYLE_NONE; + int lineBreakWordStyle = (mLineBreakConfig != null) + ? mLineBreakConfig.getLineBreakWordStyle() + : LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE; return "{" + "textSize=" + mPaint.getTextSize() + ", textScaleX=" + mPaint.getTextScaleX() @@ -376,6 +379,7 @@ public class PrecomputedText implements Spannable { + ", breakStrategy=" + mBreakStrategy + ", hyphenationFrequency=" + mHyphenationFrequency + ", lineBreakStyle=" + lineBreakStyle + + ", lineBreakWordStyle=" + lineBreakWordStyle + "}"; } }; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 7161730d61a19..41c5401169289 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -350,6 +350,7 @@ import java.util.function.Supplier; * @attr ref android.R.styleable#TextView_breakStrategy * @attr ref android.R.styleable#TextView_hyphenationFrequency * @attr ref android.R.styleable#TextView_lineBreakStyle + * @attr ref android.R.styleable#TextView_lineBreakWordStyle * @attr ref android.R.styleable#TextView_autoSizeTextType * @attr ref android.R.styleable#TextView_autoSizeMinTextSize * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize @@ -461,6 +462,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private static final int FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY = 500; + // The default value of the line break style. + private static final int DEFAULT_LINE_BREAK_STYLE = LineBreakConfig.LINE_BREAK_STYLE_NONE; + + // The default value of the line break word style. + private static final int DEFAULT_LINE_BREAK_WORD_STYLE = + LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE; + /** * This change ID enables the fallback text line spacing (line height) for BoringLayout. * @hide @@ -1453,6 +1461,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener a.getInt(attr, LineBreakConfig.LINE_BREAK_STYLE_NONE)); break; + case com.android.internal.R.styleable.TextView_lineBreakWordStyle: + mLineBreakConfig.setLineBreakWordStyle( + a.getInt(attr, LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)); + break; + case com.android.internal.R.styleable.TextView_autoSizeTextType: mAutoSizeTextType = a.getInt(attr, AUTO_SIZE_TEXT_TYPE_NONE); break; @@ -3985,6 +3998,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener float mLetterSpacing = 0; String mFontFeatureSettings = null; String mFontVariationSettings = null; + boolean mHasLineBreakStyle = false; + boolean mHasLineBreakWordStyle = false; + int mLineBreakStyle = DEFAULT_LINE_BREAK_STYLE; + int mLineBreakWordStyle = DEFAULT_LINE_BREAK_WORD_STYLE; @Override public String toString() { @@ -4015,6 +4032,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener + " mLetterSpacing:" + mLetterSpacing + "\n" + " mFontFeatureSettings:" + mFontFeatureSettings + "\n" + " mFontVariationSettings:" + mFontVariationSettings + "\n" + + " mHasLineBreakStyle:" + mHasLineBreakStyle + "\n" + + " mHasLineBreakWordStyle:" + mHasLineBreakWordStyle + "\n" + + " mLineBreakStyle:" + mLineBreakStyle + "\n" + + " mLineBreakWordStyle:" + mLineBreakWordStyle + "\n" + "}"; } } @@ -4062,6 +4083,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener com.android.internal.R.styleable.TextAppearance_fontFeatureSettings); sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontVariationSettings, com.android.internal.R.styleable.TextAppearance_fontVariationSettings); + sAppearanceValues.put(com.android.internal.R.styleable.TextView_lineBreakStyle, + com.android.internal.R.styleable.TextAppearance_lineBreakStyle); + sAppearanceValues.put(com.android.internal.R.styleable.TextView_lineBreakWordStyle, + com.android.internal.R.styleable.TextAppearance_lineBreakWordStyle); } /** @@ -4177,6 +4202,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextAppearance_fontVariationSettings: attributes.mFontVariationSettings = appearance.getString(attr); break; + case com.android.internal.R.styleable.TextAppearance_lineBreakStyle: + attributes.mHasLineBreakStyle = true; + attributes.mLineBreakStyle = + appearance.getInt(attr, attributes.mLineBreakStyle); + break; + case com.android.internal.R.styleable.TextAppearance_lineBreakWordStyle: + attributes.mHasLineBreakWordStyle = true; + attributes.mLineBreakWordStyle = + appearance.getInt(attr, attributes.mLineBreakWordStyle); + break; default: } } @@ -4242,8 +4277,45 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (attributes.mFontVariationSettings != null) { setFontVariationSettings(attributes.mFontVariationSettings); } + + if (attributes.mHasLineBreakStyle || attributes.mHasLineBreakWordStyle) { + updateLineBreakConfigFromTextAppearance(attributes.mHasLineBreakStyle, + attributes.mHasLineBreakWordStyle, attributes.mLineBreakStyle, + attributes.mLineBreakWordStyle); + } } + /** + * Updates the LineBreakConfig from the TextAppearance. + * + * This method updates the given line configuration from the TextAppearance. This method will + * request new layout if line break config has been changed. + * + * @param isLineBreakStyleSpecified true if the line break style is specified. + * @param isLineBreakWordStyleSpecified true if the line break word style is specified. + * @param lineBreakStyle the value of the line break style in the TextAppearance. + * @param lineBreakWordStyle the value of the line break word style in the TextAppearance. + */ + private void updateLineBreakConfigFromTextAppearance(boolean isLineBreakStyleSpecified, + boolean isLineBreakWordStyleSpecified, + @LineBreakConfig.LineBreakStyle int lineBreakStyle, + @LineBreakConfig.LineBreakWordStyle int lineBreakWordStyle) { + boolean updated = false; + if (isLineBreakStyleSpecified && mLineBreakConfig.getLineBreakStyle() != lineBreakStyle) { + mLineBreakConfig.setLineBreakStyle(lineBreakStyle); + updated = true; + } + if (isLineBreakWordStyleSpecified + && mLineBreakConfig.getLineBreakWordStyle() != lineBreakWordStyle) { + mLineBreakConfig.setLineBreakWordStyle(lineBreakWordStyle); + updated = true; + } + if (updated && mLayout != null) { + nullLayouts(); + requestLayout(); + invalidate(); + } + } /** * Get the default primary {@link Locale} of the text in this TextView. This will always be * the first member of {@link #getTextLocales()}. @@ -4800,18 +4872,29 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Sets line break configuration indicates which strategy needs to be used when calculating the - * text wrapping. There are thee strategies for the line break style(lb): + * text wrapping. + *

+ * There are two types of line break rules that can be configured at the same time. One is + * line break style(lb) and the other is line break word style(lw). The line break style + * affects rule-based breaking. The line break word style affects dictionary-based breaking + * and provide phrase-based breaking opportunities. There are several types for the + * line break style: * {@link LineBreakConfig#LINE_BREAK_STYLE_LOOSE}, * {@link LineBreakConfig#LINE_BREAK_STYLE_NORMAL} and * {@link LineBreakConfig#LINE_BREAK_STYLE_STRICT}. - * The default value of the line break style is {@link LineBreakConfig#LINE_BREAK_STYLE_NONE}, - * which means no line break style is specified. + * The type for the line break word style is + * {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_PHRASE}. + * The default values of the line break style and the line break word style are + * {@link LineBreakConfig#LINE_BREAK_STYLE_NONE} and + * {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_NONE} respectively, indicating that no line + * breaking rules are specified. * See * the line-break property * * @param lineBreakConfig the line break config for text wrapping. */ public void setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) { + Objects.requireNonNull(lineBreakConfig); if (mLineBreakConfig.equals(lineBreakConfig)) { return; } @@ -4858,7 +4941,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mTextDir = params.getTextDirection(); mBreakStrategy = params.getBreakStrategy(); mHyphenationFrequency = params.getHyphenationFrequency(); - mLineBreakConfig.set(params.getLineBreakConfig()); + if (params.getLineBreakConfig() != null) { + mLineBreakConfig.set(params.getLineBreakConfig()); + } else { + // Set default value if the line break config in the PrecomputedText.Params is null. + mLineBreakConfig.setLineBreakStyle(DEFAULT_LINE_BREAK_STYLE); + mLineBreakConfig.setLineBreakWordStyle(DEFAULT_LINE_BREAK_WORD_STYLE); + } if (mLayout != null) { nullLayouts(); requestLayout(); diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index a5da1ba39cfa9..8696f5a94750a 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -5038,6 +5038,10 @@ + + + + + + + + + + + diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 42386fcb76dbd..505fe59cfefd0 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3212,6 +3212,7 @@ + diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java index 4d818583fd236..cffdf28dbc278 100644 --- a/graphics/java/android/graphics/text/LineBreakConfig.java +++ b/graphics/java/android/graphics/text/LineBreakConfig.java @@ -17,7 +17,7 @@ package android.graphics.text; import android.annotation.IntDef; -import android.annotation.Nullable; +import android.annotation.NonNull; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -58,7 +58,28 @@ public final class LineBreakConfig { @Retention(RetentionPolicy.SOURCE) public @interface LineBreakStyle {} + /** + * No line break word style specified. + */ + public static final int LINE_BREAK_WORD_STYLE_NONE = 0; + + /** + * Indicates the line breaking is based on the phrased. This makes text wrapping only on + * meaningful words. The support of the text wrapping word style varies depending on the + * locales. If the locale does not support the phrase based text wrapping, + * there will be no effect. + */ + public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; + + /** @hide */ + @IntDef(prefix = { "LINE_BREAK_WORD_STYLE_" }, value = { + LINE_BREAK_WORD_STYLE_NONE, LINE_BREAK_WORD_STYLE_PHRASE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LineBreakWordStyle {} + private @LineBreakStyle int mLineBreakStyle = LINE_BREAK_STYLE_NONE; + private @LineBreakWordStyle int mLineBreakWordStyle = LINE_BREAK_WORD_STYLE_NONE; public LineBreakConfig() { } @@ -66,14 +87,12 @@ public final class LineBreakConfig { /** * Set the line break configuration. * - * @param config the new line break configuration. + * @param lineBreakConfig the new line break configuration. */ - public void set(@Nullable LineBreakConfig config) { - if (config != null) { - mLineBreakStyle = config.getLineBreakStyle(); - } else { - mLineBreakStyle = LineBreakConfig.LINE_BREAK_STYLE_NONE; - } + public void set(@NonNull LineBreakConfig lineBreakConfig) { + Objects.requireNonNull(lineBreakConfig); + mLineBreakStyle = lineBreakConfig.getLineBreakStyle(); + mLineBreakWordStyle = lineBreakConfig.getLineBreakWordStyle(); } /** @@ -94,17 +113,36 @@ public final class LineBreakConfig { mLineBreakStyle = lineBreakStyle; } + /** + * Get the line break word style. + * + * @return The current line break word style to be used for the text wrapping. + */ + public @LineBreakWordStyle int getLineBreakWordStyle() { + return mLineBreakWordStyle; + } + + /** + * Set the line break word style. + * + * @param lineBreakWordStyle the new line break word style. + */ + public void setLineBreakWordStyle(@LineBreakWordStyle int lineBreakWordStyle) { + mLineBreakWordStyle = lineBreakWordStyle; + } + @Override public boolean equals(Object o) { if (o == null) return false; if (this == o) return true; if (!(o instanceof LineBreakConfig)) return false; LineBreakConfig that = (LineBreakConfig) o; - return mLineBreakStyle == that.mLineBreakStyle; + return (mLineBreakStyle == that.mLineBreakStyle) + && (mLineBreakWordStyle == that.mLineBreakWordStyle); } @Override public int hashCode() { - return Objects.hash(mLineBreakStyle); + return Objects.hash(mLineBreakStyle, mLineBreakWordStyle); } } diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java index 5f4afb7b98880..6d691c122576b 100644 --- a/graphics/java/android/graphics/text/MeasuredText.java +++ b/graphics/java/android/graphics/text/MeasuredText.java @@ -264,8 +264,10 @@ public class MeasuredText { Preconditions.checkArgument(end <= mText.length, "Style exceeds the text length"); int lbStyle = (lineBreakConfig != null) ? lineBreakConfig.getLineBreakStyle() : LineBreakConfig.LINE_BREAK_STYLE_NONE; - nAddStyleRun(mNativePtr, paint.getNativeInstance(), lbStyle, mCurrentOffset, end, - isRtl); + int lbWordStyle = (lineBreakConfig != null) ? lineBreakConfig.getLineBreakWordStyle() : + LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE; + nAddStyleRun(mNativePtr, paint.getNativeInstance(), lbStyle, lbWordStyle, + mCurrentOffset, end, isRtl); mCurrentOffset = end; return this; } @@ -445,7 +447,8 @@ public class MeasuredText { * * @param nativeBuilderPtr The native MeasuredParagraph builder pointer. * @param paintPtr The native paint pointer to be applied. - * @param lineBreakStyle The line break style of the text. + * @param lineBreakStyle The line break style(lb) of the text. + * @param lineBreakWordStyle The line break word style(lw) of the text. * @param start The start offset in the copied buffer. * @param end The end offset in the copied buffer. * @param isRtl True if the text is RTL. @@ -453,6 +456,7 @@ public class MeasuredText { private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr, /* Non Zero */ long paintPtr, int lineBreakStyle, + int lineBreakWordStyle, @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean isRtl); diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp index 09539ecc34b0e..76ea2d5c9ff35 100644 --- a/libs/hwui/jni/text/MeasuredText.cpp +++ b/libs/hwui/jni/text/MeasuredText.cpp @@ -65,11 +65,13 @@ static jlong nInitBuilder(CRITICAL_JNI_PARAMS) { // Regular JNI static void nAddStyleRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr, - jlong paintPtr, jint lbStyle, jint start, jint end, jboolean isRtl) { + jlong paintPtr, jint lbStyle, jint lbWordStyle, jint start, jint end, + jboolean isRtl) { Paint* paint = toPaint(paintPtr); const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface()); minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface); - toBuilder(builderPtr)->addStyleRun(start, end, std::move(minikinPaint), lbStyle, isRtl); + toBuilder(builderPtr) + ->addStyleRun(start, end, std::move(minikinPaint), lbStyle, lbWordStyle, isRtl); } // Regular JNI @@ -144,7 +146,7 @@ static jint nGetMemoryUsage(CRITICAL_JNI_PARAMS_COMMA jlong ptr) { static const JNINativeMethod gMTBuilderMethods[] = { // MeasuredParagraphBuilder native functions. {"nInitBuilder", "()J", (void*)nInitBuilder}, - {"nAddStyleRun", "(JJIIIZ)V", (void*)nAddStyleRun}, + {"nAddStyleRun", "(JJIIIIZ)V", (void*)nAddStyleRun}, {"nAddReplacementRun", "(JJIIF)V", (void*)nAddReplacementRun}, {"nBuildMeasuredText", "(JJ[CZZZ)J", (void*)nBuildMeasuredText}, {"nFreeBuilder", "(J)V", (void*)nFreeBuilder},