Add NoHyphenationSpan and no hyphenation config into LineBreakConfig
NoHyphenationSpan is a specialized class of LineBreakConfigSpan
that disables hyphenation.
Bug: 283193586
Test: atest LineBreakTest
Change-Id: Ie77d345d83d871a5049ff713211a9dab116656b7
diff --git a/core/api/current.txt b/core/api/current.txt
index d92c693..d2ff81b0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17653,9 +17653,13 @@
package android.graphics.text {
public final class LineBreakConfig {
+ method @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public int getHyphenation();
method public int getLineBreakStyle();
method public int getLineBreakWordStyle();
method @NonNull public android.graphics.text.LineBreakConfig merge(@NonNull android.graphics.text.LineBreakConfig);
+ field @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final int HYPHENATION_DISABLED = 0; // 0x0
+ field @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final int HYPHENATION_ENABLED = 1; // 0x1
+ field @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final int HYPHENATION_UNSPECIFIED = -1; // 0xffffffff
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
@@ -17670,6 +17674,7 @@
ctor public LineBreakConfig.Builder();
method @NonNull public android.graphics.text.LineBreakConfig build();
method @NonNull public android.graphics.text.LineBreakConfig.Builder merge(@NonNull android.graphics.text.LineBreakConfig);
+ method @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) @NonNull public android.graphics.text.LineBreakConfig.Builder setHyphenation(int);
method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakStyle(int);
method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakWordStyle(int);
}
@@ -47875,11 +47880,15 @@
method public void writeToParcel(@NonNull android.os.Parcel, int);
}
- public class LineBreakConfigSpan {
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public class LineBreakConfigSpan {
ctor public LineBreakConfigSpan(@NonNull android.graphics.text.LineBreakConfig);
method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
}
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final class LineBreakConfigSpan.NoHyphenationSpan extends android.text.style.LineBreakConfigSpan {
+ ctor @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public LineBreakConfigSpan.NoHyphenationSpan();
+ }
+
public interface LineHeightSpan extends android.text.style.ParagraphStyle android.text.style.WrapTogetherSpan {
method public void chooseHeight(CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
}
diff --git a/core/java/android/text/flags/no_break_no_hyphenation_span.aconfig b/core/java/android/text/flags/no_break_no_hyphenation_span.aconfig
new file mode 100644
index 0000000..60f1e88
--- /dev/null
+++ b/core/java/android/text/flags/no_break_no_hyphenation_span.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.text.flags"
+
+flag {
+ name: "no_break_no_hyphenation_span"
+ namespace: "text"
+ description: "A feature flag that adding new spans that prevents line breaking and hyphenation."
+ bug: "283193586"
+}
diff --git a/core/java/android/text/style/LineBreakConfigSpan.java b/core/java/android/text/style/LineBreakConfigSpan.java
index 90a79c6..b8033a9 100644
--- a/core/java/android/text/style/LineBreakConfigSpan.java
+++ b/core/java/android/text/style/LineBreakConfigSpan.java
@@ -16,6 +16,9 @@
package android.text.style;
+import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.graphics.text.LineBreakConfig;
@@ -24,6 +27,7 @@
/**
* LineBreakSpan for changing line break style of the specific region of the text.
*/
+@FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
public class LineBreakConfigSpan {
private final LineBreakConfig mLineBreakConfig;
@@ -60,4 +64,22 @@
public String toString() {
return "LineBreakConfigSpan{mLineBreakConfig=" + mLineBreakConfig + '}';
}
+
+ private static final LineBreakConfig sNoHyphenationConfig = new LineBreakConfig.Builder()
+ .setHyphenation(LineBreakConfig.HYPHENATION_DISABLED)
+ .build();
+
+ /**
+ * A specialized {@link LineBreakConfigSpan} that used for preventing hyphenation.
+ */
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+ public static final class NoHyphenationSpan extends LineBreakConfigSpan {
+ /**
+ * Construct a new {@link NoHyphenationSpan}.
+ */
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+ public NoHyphenationSpan() {
+ super(sNoHyphenationConfig);
+ }
+ }
}
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index 7b204f2..13540e0 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -16,6 +16,9 @@
package android.graphics.text;
+import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN;
+
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,6 +37,56 @@
public final class LineBreakConfig {
/**
+ * No hyphenation preference is specified.
+ *
+ * This is a special value of hyphenation preference indicating no hyphenation preference is
+ * specified. When overriding a {@link LineBreakConfig} with another {@link LineBreakConfig}
+ * with {@link Builder#merge(LineBreakConfig)} function, the hyphenation preference of
+ * overridden config will be kept if the hyphenation preference of overriding config is
+ * {@link #HYPHENATION_UNSPECIFIED}.
+ *
+ * <pre>
+ * val override = LineBreakConfig.Builder()
+ * .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ * .build(); // UNSPECIFIED if no setHyphenation is called.
+ * val config = LineBreakConfig.Builder()
+ * .setHyphenation(LineBreakConfig.HYPHENATION_DISABLED)
+ * .merge(override)
+ * .build()
+ * // Here, config has HYPHENATION_DISABLED for line break config and
+ * // LINE_BREAK_WORD_STYLE_PHRASE for line break word style.
+ * </pre>
+ *
+ * This value is resolved to {@link #HYPHENATION_ENABLED} if this value is used for text
+ * layout/rendering.
+ */
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+ public static final int HYPHENATION_UNSPECIFIED = -1;
+
+ /**
+ * The hyphenation is disabled.
+ */
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+ public static final int HYPHENATION_DISABLED = 0;
+
+ /**
+ * The hyphenation is enabled.
+ *
+ * Note: Even if the hyphenation is enabled with a line break strategy
+ * {@link LineBreaker#BREAK_STRATEGY_SIMPLE}, the hyphenation will not be performed unless a
+ * single word cannot meet width constraints.
+ */
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+ public static final int HYPHENATION_ENABLED = 1;
+
+ /** @hide */
+ @IntDef(prefix = { "HYPHENATION_" }, value = {
+ HYPHENATION_UNSPECIFIED, HYPHENATION_ENABLED, HYPHENATION_DISABLED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Hyphenation {}
+
+ /**
* No line break style is specified.
*
* This is a special value of line break style indicating no style value is specified.
@@ -147,6 +200,8 @@
private @LineBreakWordStyle int mLineBreakWordStyle =
LineBreakConfig.LINE_BREAK_WORD_STYLE_UNSPECIFIED;
+ private @Hyphenation int mHyphenation = LineBreakConfig.HYPHENATION_UNSPECIFIED;
+
/**
* Builder constructor.
*/
@@ -188,6 +243,9 @@
if (config.mLineBreakWordStyle != LINE_BREAK_WORD_STYLE_UNSPECIFIED) {
mLineBreakWordStyle = config.mLineBreakWordStyle;
}
+ if (config.mHyphenation != HYPHENATION_UNSPECIFIED) {
+ mHyphenation = config.mHyphenation;
+ }
return this;
}
@@ -201,9 +259,11 @@
if (config == null) {
mLineBreakStyle = LINE_BREAK_STYLE_UNSPECIFIED;
mLineBreakWordStyle = LINE_BREAK_WORD_STYLE_UNSPECIFIED;
+ mHyphenation = HYPHENATION_UNSPECIFIED;
} else {
mLineBreakStyle = config.mLineBreakStyle;
mLineBreakWordStyle = config.mLineBreakWordStyle;
+ mHyphenation = config.mHyphenation;
}
return this;
}
@@ -215,6 +275,11 @@
* {@link #LINE_BREAK_STYLE_UNSPECIFIED}, the line break style is reset to
* {@link #LINE_BREAK_STYLE_UNSPECIFIED}.
*
+ * @see <a href="https://unicode.org/reports/tr35/#UnicodeLineBreakStyleIdentifier">
+ * Unicode Line Break Style Identifier</a>
+ * @see <a href="https://drafts.csswg.org/css-text/#line-break-property">
+ * CSS Line Break Property</a>
+ *
* @param lineBreakStyle The new line-break style.
* @return This {@code Builder}.
*/
@@ -230,6 +295,11 @@
* with {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}, the line break style is reset to
* {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}.
*
+ * @see <a href="https://unicode.org/reports/tr35/#UnicodeLineBreakWordIdentifier">
+ * Unicode Line Break Word Identifier</a>
+ * @see <a href="https://drafts.csswg.org/css-text/#word-break-property">
+ * CSS Word Break Property</a>
+ *
* @param lineBreakWordStyle The new line-break word style.
* @return This {@code Builder}.
*/
@@ -239,6 +309,27 @@
}
/**
+ * Sets the hyphenation preference
+ *
+ * Note: Even if the {@link LineBreakConfig#HYPHENATION_ENABLED} is specified, the
+ * hyphenation will not be performed if the {@link android.widget.TextView} or underlying
+ * {@link android.text.StaticLayout}, {@link LineBreaker} are configured with
+ * {@link LineBreaker#HYPHENATION_FREQUENCY_NONE}.
+ *
+ * Note: Even if the hyphenation is enabled with a line break strategy
+ * {@link LineBreaker#BREAK_STRATEGY_SIMPLE}, the hyphenation will not be performed unless a
+ * single word cannot meet width constraints.
+ *
+ * @param hyphenation The hyphenation preference.
+ * @return This {@code Builder}.
+ */
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+ public @NonNull Builder setHyphenation(@Hyphenation int hyphenation) {
+ mHyphenation = hyphenation;
+ return this;
+ }
+
+ /**
* Builds a {@link LineBreakConfig} instance.
*
* This method can be called multiple times for generating multiple {@link LineBreakConfig}
@@ -247,7 +338,7 @@
* @return The {@code LineBreakConfig} instance.
*/
public @NonNull LineBreakConfig build() {
- return new LineBreakConfig(mLineBreakStyle, mLineBreakWordStyle);
+ return new LineBreakConfig(mLineBreakStyle, mLineBreakWordStyle, mHyphenation);
}
}
@@ -275,6 +366,7 @@
private final @LineBreakStyle int mLineBreakStyle;
private final @LineBreakWordStyle int mLineBreakWordStyle;
+ private final @Hyphenation int mHyphenation;
/**
* Constructor with line-break parameters.
@@ -283,9 +375,11 @@
* {@code LineBreakConfig} instance.
*/
private LineBreakConfig(@LineBreakStyle int lineBreakStyle,
- @LineBreakWordStyle int lineBreakWordStyle) {
+ @LineBreakWordStyle int lineBreakWordStyle,
+ @Hyphenation int hyphenation) {
mLineBreakStyle = lineBreakStyle;
mLineBreakWordStyle = lineBreakWordStyle;
+ mHyphenation = hyphenation;
}
/**
@@ -340,6 +434,34 @@
}
/**
+ * Returns a hyphenation preference.
+ *
+ * @return A hyphenation preference.
+ */
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+ public @Hyphenation int getHyphenation() {
+ return mHyphenation;
+ }
+
+ /**
+ * Returns a hyphenation preference.
+ *
+ * This method never returns {@link #HYPHENATION_UNSPECIFIED}.
+ *
+ * @return A hyphenation preference.
+ * @hide
+ */
+ public static @Hyphenation int getResolvedHyphenation(
+ @Nullable LineBreakConfig config) {
+ if (config == null) {
+ return HYPHENATION_ENABLED;
+ }
+ return config.mHyphenation == HYPHENATION_UNSPECIFIED
+ ? HYPHENATION_ENABLED : config.mHyphenation;
+ }
+
+
+ /**
* Generates a new {@link LineBreakConfig} instance merged with given {@code config}.
*
* If values of passing {@code config} are unspecified, the original values are kept. For
@@ -366,7 +488,9 @@
config.mLineBreakStyle == LINE_BREAK_STYLE_UNSPECIFIED
? mLineBreakStyle : config.mLineBreakStyle,
config.mLineBreakWordStyle == LINE_BREAK_WORD_STYLE_UNSPECIFIED
- ? mLineBreakWordStyle : config.mLineBreakWordStyle);
+ ? mLineBreakWordStyle : config.mLineBreakWordStyle,
+ config.mHyphenation == HYPHENATION_UNSPECIFIED
+ ? mHyphenation : config.mHyphenation);
}
@Override
@@ -376,12 +500,13 @@
if (!(o instanceof LineBreakConfig)) return false;
LineBreakConfig that = (LineBreakConfig) o;
return (mLineBreakStyle == that.mLineBreakStyle)
- && (mLineBreakWordStyle == that.mLineBreakWordStyle);
+ && (mLineBreakWordStyle == that.mLineBreakWordStyle)
+ && (mHyphenation == that.mHyphenation);
}
@Override
public int hashCode() {
- return Objects.hash(mLineBreakStyle, mLineBreakWordStyle);
+ return Objects.hash(mLineBreakStyle, mLineBreakWordStyle, mHyphenation);
}
@Override
@@ -389,6 +514,7 @@
return "LineBreakConfig{"
+ "mLineBreakStyle=" + mLineBreakStyle
+ ", mLineBreakWordStyle=" + mLineBreakWordStyle
+ + ", mHyphenation= " + mHyphenation
+ '}';
}
}
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 8317985..2d33e8d 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -301,7 +301,9 @@
Preconditions.checkArgument(end <= mText.length, "Style exceeds the text length");
int lbStyle = LineBreakConfig.getResolvedLineBreakStyle(lineBreakConfig);
int lbWordStyle = LineBreakConfig.getResolvedLineBreakWordStyle(lineBreakConfig);
- nAddStyleRun(mNativePtr, paint.getNativeInstance(), lbStyle, lbWordStyle,
+ boolean hyphenation = LineBreakConfig.getResolvedHyphenation(lineBreakConfig)
+ == LineBreakConfig.HYPHENATION_ENABLED;
+ nAddStyleRun(mNativePtr, paint.getNativeInstance(), lbStyle, lbWordStyle, hyphenation,
mCurrentOffset, end, isRtl);
mCurrentOffset = end;
@@ -510,6 +512,7 @@
/* Non Zero */ long paintPtr,
int lineBreakStyle,
int lineBreakWordStyle,
+ boolean hyphenation,
@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 f6ae169..746745a 100644
--- a/libs/hwui/jni/text/MeasuredText.cpp
+++ b/libs/hwui/jni/text/MeasuredText.cpp
@@ -62,13 +62,14 @@
// Regular JNI
static void nAddStyleRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr,
- jlong paintPtr, jint lbStyle, jint lbWordStyle, jint start, jint end,
- jboolean isRtl) {
+ jlong paintPtr, jint lbStyle, jint lbWordStyle, jboolean hyphenation,
+ 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, lbWordStyle, isRtl);
+ ->addStyleRun(start, end, std::move(minikinPaint), lbStyle, lbWordStyle, hyphenation,
+ isRtl);
}
// Regular JNI
@@ -159,7 +160,7 @@
static const JNINativeMethod gMTBuilderMethods[] = {
// MeasuredParagraphBuilder native functions.
{"nInitBuilder", "()J", (void*)nInitBuilder},
- {"nAddStyleRun", "(JJIIIIZ)V", (void*)nAddStyleRun},
+ {"nAddStyleRun", "(JJIIZIIZ)V", (void*)nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*)nAddReplacementRun},
{"nBuildMeasuredText", "(JJ[CZZZZ)J", (void*)nBuildMeasuredText},
{"nFreeBuilder", "(J)V", (void*)nFreeBuilder},