Loading core/api/current.txt +9 −0 Original line number Original line Diff line number Diff line Loading @@ -17533,17 +17533,21 @@ package android.graphics.text { public final class LineBreakConfig { public final class LineBreakConfig { method public int getLineBreakStyle(); method public int getLineBreakStyle(); method public int getLineBreakWordStyle(); method public int getLineBreakWordStyle(); method @NonNull public android.graphics.text.LineBreakConfig merge(@NonNull android.graphics.text.LineBreakConfig); field public static final int LINE_BREAK_STYLE_LOOSE = 1; // 0x1 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_NONE = 0; // 0x0 field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2 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_STYLE_STRICT = 3; // 0x3 field public static final int LINE_BREAK_STYLE_UNSPECIFIED = -1; // 0xffffffff field public static final int LINE_BREAK_WORD_STYLE_NONE = 0; // 0x0 field public static final int LINE_BREAK_WORD_STYLE_NONE = 0; // 0x0 field public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; // 0x1 field public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; // 0x1 field public static final int LINE_BREAK_WORD_STYLE_UNSPECIFIED = -1; // 0xffffffff } } public static final class LineBreakConfig.Builder { public static final class LineBreakConfig.Builder { ctor public LineBreakConfig.Builder(); ctor public LineBreakConfig.Builder(); method @NonNull public android.graphics.text.LineBreakConfig build(); method @NonNull public android.graphics.text.LineBreakConfig build(); method @NonNull public android.graphics.text.LineBreakConfig.Builder merge(@NonNull android.graphics.text.LineBreakConfig); method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakStyle(int); method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakStyle(int); method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakWordStyle(int); method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakWordStyle(int); } } Loading Loading @@ -48402,6 +48406,11 @@ package android.text.style { method public void writeToParcel(@NonNull android.os.Parcel, int); method public void writeToParcel(@NonNull android.os.Parcel, int); } } public class LineBreakConfigSpan { ctor public LineBreakConfigSpan(@NonNull android.graphics.text.LineBreakConfig); method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig(); } public interface LineHeightSpan extends android.text.style.ParagraphStyle android.text.style.WrapTogetherSpan { 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); method public void chooseHeight(CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt); } } core/api/test-current.txt +9 −0 Original line number Original line Diff line number Diff line Loading @@ -3335,6 +3335,15 @@ package android.text { field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig.NamedFamilyList> CREATOR; field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig.NamedFamilyList> CREATOR; } } public class MeasuredParagraph { method @NonNull public static android.text.MeasuredParagraph buildForStaticLayoutTest(@NonNull android.text.TextPaint, @Nullable android.graphics.text.LineBreakConfig, @NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, int, boolean, @Nullable android.text.MeasuredParagraph.StyleRunCallback); } public static interface MeasuredParagraph.StyleRunCallback { method public void onAppendReplacementRun(@NonNull android.graphics.Paint, @IntRange(from=0) int, @FloatRange(from=0) @Px float); method public void onAppendStyleRun(@NonNull android.graphics.Paint, @Nullable android.graphics.text.LineBreakConfig, @IntRange(from=0) int, boolean); } public static final class Selection.MemoryTextWatcher implements android.text.TextWatcher { public static final class Selection.MemoryTextWatcher implements android.text.TextWatcher { ctor public Selection.MemoryTextWatcher(); ctor public Selection.MemoryTextWatcher(); method public void afterTextChanged(android.text.Editable); method public void afterTextChanged(android.text.Editable); Loading core/java/android/text/MeasuredParagraph.java +149 −17 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,9 @@ import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.Px; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.graphics.Paint; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Rect; import android.graphics.text.LineBreakConfig; import android.graphics.text.LineBreakConfig; Loading @@ -28,6 +31,7 @@ import android.text.AutoGrowArray.ByteArray; import android.text.AutoGrowArray.FloatArray; import android.text.AutoGrowArray.FloatArray; import android.text.AutoGrowArray.IntArray; import android.text.AutoGrowArray.IntArray; import android.text.Layout.Directions; import android.text.Layout.Directions; import android.text.style.LineBreakConfigSpan; import android.text.style.MetricAffectingSpan; import android.text.style.MetricAffectingSpan; import android.text.style.ReplacementSpan; import android.text.style.ReplacementSpan; import android.util.Pools.SynchronizedPool; import android.util.Pools.SynchronizedPool; Loading Loading @@ -57,6 +61,7 @@ import java.util.Arrays; * MeasuredParagraph is NOT a thread safe object. * MeasuredParagraph is NOT a thread safe object. * @hide * @hide */ */ @TestApi public class MeasuredParagraph { public class MeasuredParagraph { private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC'; private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC'; Loading @@ -73,6 +78,7 @@ public class MeasuredParagraph { * Recycle the MeasuredParagraph. * Recycle the MeasuredParagraph. * * * Do not call any methods after you call this method. * Do not call any methods after you call this method. * @hide */ */ public void recycle() { public void recycle() { release(); release(); Loading Loading @@ -126,11 +132,14 @@ public class MeasuredParagraph { private @Nullable MeasuredText mMeasuredText; private @Nullable MeasuredText mMeasuredText; // Following three objects are for avoiding object allocation. // Following three objects are for avoiding object allocation. private @NonNull TextPaint mCachedPaint = new TextPaint(); private final @NonNull TextPaint mCachedPaint = new TextPaint(); private @Nullable Paint.FontMetricsInt mCachedFm; private @Nullable Paint.FontMetricsInt mCachedFm; private final @NonNull LineBreakConfig.Builder mLineBreakConfigBuilder = new LineBreakConfig.Builder(); /** /** * Releases internal buffers. * Releases internal buffers. * @hide */ */ public void release() { public void release() { reset(); reset(); Loading Loading @@ -158,6 +167,7 @@ public class MeasuredParagraph { * Returns the length of the paragraph. * Returns the length of the paragraph. * * * This is always available. * This is always available. * @hide */ */ public int getTextLength() { public int getTextLength() { return mTextLength; return mTextLength; Loading @@ -167,6 +177,7 @@ public class MeasuredParagraph { * Returns the characters to be measured. * Returns the characters to be measured. * * * This is always available. * This is always available. * @hide */ */ public @NonNull char[] getChars() { public @NonNull char[] getChars() { return mCopiedBuffer; return mCopiedBuffer; Loading @@ -176,6 +187,7 @@ public class MeasuredParagraph { * Returns the paragraph direction. * Returns the paragraph direction. * * * This is always available. * This is always available. * @hide */ */ public @Layout.Direction int getParagraphDir() { public @Layout.Direction int getParagraphDir() { return mParaDir; return mParaDir; Loading @@ -185,6 +197,7 @@ public class MeasuredParagraph { * Returns the directions. * Returns the directions. * * * This is always available. * This is always available. * @hide */ */ public Directions getDirections(@IntRange(from = 0) int start, // inclusive public Directions getDirections(@IntRange(from = 0) int start, // inclusive @IntRange(from = 0) int end) { // exclusive @IntRange(from = 0) int end) { // exclusive Loading @@ -202,6 +215,7 @@ public class MeasuredParagraph { * * * This is available only if the MeasuredParagraph is computed with buildForMeasurement. * This is available only if the MeasuredParagraph is computed with buildForMeasurement. * Returns 0 in other cases. * Returns 0 in other cases. * @hide */ */ public @FloatRange(from = 0.0f) float getWholeWidth() { public @FloatRange(from = 0.0f) float getWholeWidth() { return mWholeWidth; return mWholeWidth; Loading @@ -212,6 +226,7 @@ public class MeasuredParagraph { * * * This is available only if the MeasuredParagraph is computed with buildForMeasurement. * This is available only if the MeasuredParagraph is computed with buildForMeasurement. * Returns empty array in other cases. * Returns empty array in other cases. * @hide */ */ public @NonNull FloatArray getWidths() { public @NonNull FloatArray getWidths() { return mWidths; return mWidths; Loading @@ -224,6 +239,7 @@ public class MeasuredParagraph { * * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * Returns empty array in other cases. * Returns empty array in other cases. * @hide */ */ public @NonNull IntArray getSpanEndCache() { public @NonNull IntArray getSpanEndCache() { return mSpanEndCache; return mSpanEndCache; Loading @@ -236,6 +252,7 @@ public class MeasuredParagraph { * * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * Returns empty array in other cases. * Returns empty array in other cases. * @hide */ */ public @NonNull IntArray getFontMetrics() { public @NonNull IntArray getFontMetrics() { return mFontMetrics; return mFontMetrics; Loading @@ -246,6 +263,7 @@ public class MeasuredParagraph { * * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * Returns null in other cases. * Returns null in other cases. * @hide */ */ public MeasuredText getMeasuredText() { public MeasuredText getMeasuredText() { return mMeasuredText; return mMeasuredText; Loading @@ -259,6 +277,7 @@ public class MeasuredParagraph { * * * @param start the inclusive start offset of the target region in the text * @param start the inclusive start offset of the target region in the text * @param end the exclusive end offset of the target region in the text * @param end the exclusive end offset of the target region in the text * @hide */ */ public float getWidth(int start, int end) { public float getWidth(int start, int end) { if (mMeasuredText == null) { if (mMeasuredText == null) { Loading @@ -280,6 +299,7 @@ public class MeasuredParagraph { * at (0, 0). * at (0, 0). * * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * @hide */ */ public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end, public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull Rect bounds) { @NonNull Rect bounds) { Loading @@ -290,6 +310,7 @@ public class MeasuredParagraph { * Retrieves the font metrics for the given range. * Retrieves the font metrics for the given range. * * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * @hide */ */ public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end, public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull Paint.FontMetricsInt fmi) { @NonNull Paint.FontMetricsInt fmi) { Loading @@ -300,6 +321,7 @@ public class MeasuredParagraph { * Returns a width of the character at the offset. * Returns a width of the character at the offset. * * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * @hide */ */ public float getCharWidthAt(@IntRange(from = 0) int offset) { public float getCharWidthAt(@IntRange(from = 0) int offset) { return mMeasuredText.getCharWidthAt(offset); return mMeasuredText.getCharWidthAt(offset); Loading @@ -318,6 +340,7 @@ public class MeasuredParagraph { * @param recycle pass existing MeasuredParagraph if you want to recycle it. * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * * @return measured text * @return measured text * @hide */ */ public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text, public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int start, Loading @@ -343,6 +366,7 @@ public class MeasuredParagraph { * @param recycle pass existing MeasuredParagraph if you want to recycle it. * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * * @return measured text * @return measured text * @hide */ */ public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint, public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint, @NonNull CharSequence text, @NonNull CharSequence text, Loading @@ -361,24 +385,52 @@ public class MeasuredParagraph { if (mt.mSpanned == null) { if (mt.mSpanned == null) { // No style change by MetricsAffectingSpan. Just measure all text. // No style change by MetricsAffectingSpan. Just measure all text. mt.applyMetricsAffectingSpan( mt.applyMetricsAffectingSpan( paint, null /* lineBreakConfig */, null /* spans */, start, end, paint, null /* lineBreakConfig */, null /* spans */, null /* lbcSpans */, null /* native builder ptr */); start, end, null /* native builder ptr */, null); } else { } else { // There may be a MetricsAffectingSpan. Split into span transitions and apply styles. // There may be a MetricsAffectingSpan. Split into span transitions and apply styles. int spanEnd; int spanEnd; for (int spanStart = start; spanStart < end; spanStart = spanEnd) { for (int spanStart = start; spanStart < end; spanStart = spanEnd) { spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class); int maSpanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class); int lbcSpanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, LineBreakConfigSpan.class); spanEnd = Math.min(maSpanEnd, lbcSpanEnd); MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class); MetricAffectingSpan.class); LineBreakConfigSpan[] lbcSpans = mt.mSpanned.getSpans(spanStart, spanEnd, LineBreakConfigSpan.class); spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); lbcSpans = TextUtils.removeEmptySpans(lbcSpans, mt.mSpanned, LineBreakConfigSpan.class); mt.applyMetricsAffectingSpan( mt.applyMetricsAffectingSpan( paint, null /* line break config */, spans, spanStart, spanEnd, paint, null /* line break config */, spans, lbcSpans, spanStart, spanEnd, null /* native builder ptr */); null /* native builder ptr */, null); } } } } return mt; return mt; } } /** * A test interface for observing the style run calculation. * @hide */ @TestApi public interface StyleRunCallback { /** * Called when a single style run is identified. */ void onAppendStyleRun(@NonNull Paint paint, @Nullable LineBreakConfig lineBreakConfig, @IntRange(from = 0) int length, boolean isRtl); /** * Called when a single replacement run is identified. */ void onAppendReplacementRun(@NonNull Paint paint, @IntRange(from = 0) int length, @Px @FloatRange(from = 0) float width); } /** /** * Generates new MeasuredParagraph for StaticLayout. * Generates new MeasuredParagraph for StaticLayout. * * Loading @@ -397,6 +449,7 @@ public class MeasuredParagraph { * @param recycle pass existing MeasuredParagraph if you want to recycle it. * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * * @return measured text * @return measured text * @hide */ */ public static @NonNull MeasuredParagraph buildForStaticLayout( public static @NonNull MeasuredParagraph buildForStaticLayout( @NonNull TextPaint paint, @NonNull TextPaint paint, Loading @@ -409,6 +462,57 @@ public class MeasuredParagraph { boolean computeLayout, boolean computeLayout, @Nullable MeasuredParagraph hint, @Nullable MeasuredParagraph hint, @Nullable MeasuredParagraph recycle) { @Nullable MeasuredParagraph recycle) { return buildForStaticLayoutInternal(paint, lineBreakConfig, text, start, end, textDir, hyphenationMode, computeLayout, hint, recycle, null); } /** * Generates new MeasuredParagraph for StaticLayout. * * If recycle is null, this returns new instance. If recycle is not null, this fills computed * result to recycle and returns recycle. * * @param paint the paint to be used for rendering the text. * @param lineBreakConfig the line break configuration for text wrapping. * @param text the character sequence to be measured * @param start the inclusive start offset of the target region in the text * @param end the exclusive end offset of the target region in the text * @param textDir the text direction * @param hyphenationMode a hyphenation mode * @param computeLayout true if need to compute full layout, otherwise false. * * @return measured text * @hide */ @SuppressLint("ExecutorRegistration") @TestApi @NonNull public static MeasuredParagraph buildForStaticLayoutTest( @NonNull TextPaint paint, @Nullable LineBreakConfig lineBreakConfig, @NonNull CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull TextDirectionHeuristic textDir, int hyphenationMode, boolean computeLayout, @Nullable StyleRunCallback testCallback) { return buildForStaticLayoutInternal(paint, lineBreakConfig, text, start, end, textDir, hyphenationMode, computeLayout, null, null, testCallback); } private static @NonNull MeasuredParagraph buildForStaticLayoutInternal( @NonNull TextPaint paint, @Nullable LineBreakConfig lineBreakConfig, @NonNull CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull TextDirectionHeuristic textDir, int hyphenationMode, boolean computeLayout, @Nullable MeasuredParagraph hint, @Nullable MeasuredParagraph recycle, @Nullable StyleRunCallback testCallback) { final MeasuredParagraph mt = recycle == null ? obtain() : recycle; final MeasuredParagraph mt = recycle == null ? obtain() : recycle; mt.resetAndAnalyzeBidi(text, start, end, textDir); mt.resetAndAnalyzeBidi(text, start, end, textDir); final MeasuredText.Builder builder; final MeasuredText.Builder builder; Loading @@ -426,23 +530,29 @@ public class MeasuredParagraph { } else { } else { if (mt.mSpanned == null) { if (mt.mSpanned == null) { // No style change by MetricsAffectingSpan. Just measure all text. // No style change by MetricsAffectingSpan. Just measure all text. mt.applyMetricsAffectingSpan(paint, lineBreakConfig, null /* spans */, start, end, mt.applyMetricsAffectingSpan(paint, lineBreakConfig, null /* spans */, null, builder); start, end, builder, testCallback); mt.mSpanEndCache.append(end); mt.mSpanEndCache.append(end); } else { } else { // There may be a MetricsAffectingSpan. Split into span transitions and apply // There may be a MetricsAffectingSpan. Split into span transitions and apply // styles. // styles. int spanEnd; int spanEnd; for (int spanStart = start; spanStart < end; spanStart = spanEnd) { for (int spanStart = start; spanStart < end; spanStart = spanEnd) { spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, int maSpanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class); MetricAffectingSpan.class); int lbcSpanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, LineBreakConfigSpan.class); spanEnd = Math.min(maSpanEnd, lbcSpanEnd); MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class); MetricAffectingSpan.class); LineBreakConfigSpan[] lbcSpans = mt.mSpanned.getSpans(spanStart, spanEnd, LineBreakConfigSpan.class); spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); MetricAffectingSpan.class); // TODO: Update line break config with spans. lbcSpans = TextUtils.removeEmptySpans(lbcSpans, mt.mSpanned, mt.applyMetricsAffectingSpan(paint, lineBreakConfig, spans, spanStart, spanEnd, LineBreakConfigSpan.class); builder); mt.applyMetricsAffectingSpan(paint, lineBreakConfig, spans, lbcSpans, spanStart, spanEnd, builder, testCallback); mt.mSpanEndCache.append(spanEnd); mt.mSpanEndCache.append(spanEnd); } } } } Loading Loading @@ -519,7 +629,8 @@ public class MeasuredParagraph { @IntRange(from = 0) int start, // inclusive, in copied buffer @IntRange(from = 0) int start, // inclusive, in copied buffer @IntRange(from = 0) int end, // exclusive, in copied buffer @IntRange(from = 0) int end, // exclusive, in copied buffer @NonNull TextPaint paint, @NonNull TextPaint paint, @Nullable MeasuredText.Builder builder) { @Nullable MeasuredText.Builder builder, @Nullable StyleRunCallback testCallback) { // Use original text. Shouldn't matter. // Use original text. Shouldn't matter. // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for // backward compatibility? or Should we initialize them for getFontMetricsInt? // backward compatibility? or Should we initialize them for getFontMetricsInt? Loading @@ -535,13 +646,17 @@ public class MeasuredParagraph { } else { } else { builder.appendReplacementRun(paint, end - start, width); builder.appendReplacementRun(paint, end - start, width); } } if (testCallback != null) { testCallback.onAppendReplacementRun(paint, end - start, width); } } } private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer @IntRange(from = 0) int end, // exclusive, in copied buffer @IntRange(from = 0) int end, // exclusive, in copied buffer @NonNull TextPaint paint, @NonNull TextPaint paint, @Nullable LineBreakConfig config, @Nullable LineBreakConfig config, @Nullable MeasuredText.Builder builder) { @Nullable MeasuredText.Builder builder, @Nullable StyleRunCallback testCallback) { if (mLtrWithoutBidi) { if (mLtrWithoutBidi) { // If the whole text is LTR direction, just apply whole region. // If the whole text is LTR direction, just apply whole region. Loading @@ -552,6 +667,9 @@ public class MeasuredParagraph { } else { } else { builder.appendStyleRun(paint, config, end - start, false /* isRtl */); builder.appendStyleRun(paint, config, end - start, false /* isRtl */); } } if (testCallback != null) { testCallback.onAppendStyleRun(paint, config, end - start, false); } } else { } else { // If there is multiple bidi levels, split into individual bidi level and apply style. // If there is multiple bidi levels, split into individual bidi level and apply style. byte level = mLevels.get(start); byte level = mLevels.get(start); Loading @@ -568,6 +686,9 @@ public class MeasuredParagraph { } else { } else { builder.appendStyleRun(paint, config, levelEnd - levelStart, isRtl); builder.appendStyleRun(paint, config, levelEnd - levelStart, isRtl); } } if (testCallback != null) { testCallback.onAppendStyleRun(paint, config, levelEnd - levelStart, isRtl); } if (levelEnd == end) { if (levelEnd == end) { break; break; } } Loading @@ -582,9 +703,11 @@ public class MeasuredParagraph { @NonNull TextPaint paint, @NonNull TextPaint paint, @Nullable LineBreakConfig lineBreakConfig, @Nullable LineBreakConfig lineBreakConfig, @Nullable MetricAffectingSpan[] spans, @Nullable MetricAffectingSpan[] spans, @Nullable LineBreakConfigSpan[] lbcSpans, @IntRange(from = 0) int start, // inclusive, in original text buffer @IntRange(from = 0) int start, // inclusive, in original text buffer @IntRange(from = 0) int end, // exclusive, in original text buffer @IntRange(from = 0) int end, // exclusive, in original text buffer @Nullable MeasuredText.Builder builder) { @Nullable MeasuredText.Builder builder, @Nullable StyleRunCallback testCallback) { mCachedPaint.set(paint); mCachedPaint.set(paint); // XXX paint should not have a baseline shift, but... // XXX paint should not have a baseline shift, but... mCachedPaint.baselineShift = 0; mCachedPaint.baselineShift = 0; Loading @@ -609,6 +732,14 @@ public class MeasuredParagraph { } } } } if (lbcSpans != null) { mLineBreakConfigBuilder.reset(lineBreakConfig); for (LineBreakConfigSpan lbcSpan : lbcSpans) { mLineBreakConfigBuilder.merge(lbcSpan.getLineBreakConfig()); } lineBreakConfig = mLineBreakConfigBuilder.build(); } final int startInCopiedBuffer = start - mTextStart; final int startInCopiedBuffer = start - mTextStart; final int endInCopiedBuffer = end - mTextStart; final int endInCopiedBuffer = end - mTextStart; Loading @@ -618,10 +749,10 @@ public class MeasuredParagraph { if (replacement != null) { if (replacement != null) { applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, mCachedPaint, applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, mCachedPaint, builder); builder, testCallback); } else { } else { applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, mCachedPaint, applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, mCachedPaint, lineBreakConfig, builder); lineBreakConfig, builder, testCallback); } } if (needFontMetrics) { if (needFontMetrics) { Loading Loading @@ -690,6 +821,7 @@ public class MeasuredParagraph { /** /** * This only works if the MeasuredParagraph is computed with buildForStaticLayout. * This only works if the MeasuredParagraph is computed with buildForStaticLayout. * @hide */ */ public @IntRange(from = 0) int getMemoryUsage() { public @IntRange(from = 0) int getMemoryUsage() { return mMeasuredText.getMemoryUsage(); return mMeasuredText.getMemoryUsage(); Loading core/java/android/text/PrecomputedText.java +6 −10 Original line number Original line Diff line number Diff line Loading @@ -329,22 +329,17 @@ public class PrecomputedText implements Spannable { @Override @Override public int hashCode() { public int hashCode() { // TODO: implement MinikinPaint::hashCode and use it to keep consistency with equals. // TODO: implement MinikinPaint::hashCode and use it to keep consistency with equals. int lineBreakStyle = (mLineBreakConfig != null) ? mLineBreakConfig.getLineBreakStyle() : LineBreakConfig.LINE_BREAK_STYLE_NONE; return Objects.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), mPaint.getTextSkewX(), return Objects.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), mPaint.getTextSkewX(), mPaint.getLetterSpacing(), mPaint.getWordSpacing(), mPaint.getFlags(), mPaint.getLetterSpacing(), mPaint.getWordSpacing(), mPaint.getFlags(), mPaint.getTextLocales(), mPaint.getTypeface(), mPaint.getTextLocales(), mPaint.getTypeface(), mPaint.getFontVariationSettings(), mPaint.isElegantTextHeight(), mTextDir, mPaint.getFontVariationSettings(), mPaint.isElegantTextHeight(), mTextDir, mBreakStrategy, mHyphenationFrequency, lineBreakStyle); mBreakStrategy, mHyphenationFrequency, LineBreakConfig.getResolvedLineBreakStyle(mLineBreakConfig), LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig)); } } @Override @Override public String toString() { 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 "{" return "{" + "textSize=" + mPaint.getTextSize() + "textSize=" + mPaint.getTextSize() + ", textScaleX=" + mPaint.getTextScaleX() + ", textScaleX=" + mPaint.getTextScaleX() Loading @@ -357,8 +352,9 @@ public class PrecomputedText implements Spannable { + ", textDir=" + mTextDir + ", textDir=" + mTextDir + ", breakStrategy=" + mBreakStrategy + ", breakStrategy=" + mBreakStrategy + ", hyphenationFrequency=" + mHyphenationFrequency + ", hyphenationFrequency=" + mHyphenationFrequency + ", lineBreakStyle=" + lineBreakStyle + ", lineBreakStyle=" + LineBreakConfig.getResolvedLineBreakStyle(mLineBreakConfig) + ", lineBreakWordStyle=" + lineBreakWordStyle + ", lineBreakWordStyle=" + LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig) + "}"; + "}"; } } }; }; Loading core/java/android/text/StaticLayout.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -448,7 +448,7 @@ public class StaticLayout extends Layout { private void reviseLineBreakConfig() { private void reviseLineBreakConfig() { boolean autoPhraseBreaking = mLineBreakConfig.getAutoPhraseBreaking(); boolean autoPhraseBreaking = mLineBreakConfig.getAutoPhraseBreaking(); int wordStyle = mLineBreakConfig.getLineBreakWordStyle(); int wordStyle = LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig); if (autoPhraseBreaking) { if (autoPhraseBreaking) { if (wordStyle != LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) { if (wordStyle != LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) { if (shouldEnablePhraseBreaking()) { if (shouldEnablePhraseBreaking()) { Loading Loading
core/api/current.txt +9 −0 Original line number Original line Diff line number Diff line Loading @@ -17533,17 +17533,21 @@ package android.graphics.text { public final class LineBreakConfig { public final class LineBreakConfig { method public int getLineBreakStyle(); method public int getLineBreakStyle(); method public int getLineBreakWordStyle(); method public int getLineBreakWordStyle(); method @NonNull public android.graphics.text.LineBreakConfig merge(@NonNull android.graphics.text.LineBreakConfig); field public static final int LINE_BREAK_STYLE_LOOSE = 1; // 0x1 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_NONE = 0; // 0x0 field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2 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_STYLE_STRICT = 3; // 0x3 field public static final int LINE_BREAK_STYLE_UNSPECIFIED = -1; // 0xffffffff field public static final int LINE_BREAK_WORD_STYLE_NONE = 0; // 0x0 field public static final int LINE_BREAK_WORD_STYLE_NONE = 0; // 0x0 field public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; // 0x1 field public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; // 0x1 field public static final int LINE_BREAK_WORD_STYLE_UNSPECIFIED = -1; // 0xffffffff } } public static final class LineBreakConfig.Builder { public static final class LineBreakConfig.Builder { ctor public LineBreakConfig.Builder(); ctor public LineBreakConfig.Builder(); method @NonNull public android.graphics.text.LineBreakConfig build(); method @NonNull public android.graphics.text.LineBreakConfig build(); method @NonNull public android.graphics.text.LineBreakConfig.Builder merge(@NonNull android.graphics.text.LineBreakConfig); method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakStyle(int); method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakStyle(int); method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakWordStyle(int); method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakWordStyle(int); } } Loading Loading @@ -48402,6 +48406,11 @@ package android.text.style { method public void writeToParcel(@NonNull android.os.Parcel, int); method public void writeToParcel(@NonNull android.os.Parcel, int); } } public class LineBreakConfigSpan { ctor public LineBreakConfigSpan(@NonNull android.graphics.text.LineBreakConfig); method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig(); } public interface LineHeightSpan extends android.text.style.ParagraphStyle android.text.style.WrapTogetherSpan { 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); method public void chooseHeight(CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt); } }
core/api/test-current.txt +9 −0 Original line number Original line Diff line number Diff line Loading @@ -3335,6 +3335,15 @@ package android.text { field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig.NamedFamilyList> CREATOR; field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig.NamedFamilyList> CREATOR; } } public class MeasuredParagraph { method @NonNull public static android.text.MeasuredParagraph buildForStaticLayoutTest(@NonNull android.text.TextPaint, @Nullable android.graphics.text.LineBreakConfig, @NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, int, boolean, @Nullable android.text.MeasuredParagraph.StyleRunCallback); } public static interface MeasuredParagraph.StyleRunCallback { method public void onAppendReplacementRun(@NonNull android.graphics.Paint, @IntRange(from=0) int, @FloatRange(from=0) @Px float); method public void onAppendStyleRun(@NonNull android.graphics.Paint, @Nullable android.graphics.text.LineBreakConfig, @IntRange(from=0) int, boolean); } public static final class Selection.MemoryTextWatcher implements android.text.TextWatcher { public static final class Selection.MemoryTextWatcher implements android.text.TextWatcher { ctor public Selection.MemoryTextWatcher(); ctor public Selection.MemoryTextWatcher(); method public void afterTextChanged(android.text.Editable); method public void afterTextChanged(android.text.Editable); Loading
core/java/android/text/MeasuredParagraph.java +149 −17 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,9 @@ import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.Px; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.graphics.Paint; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Rect; import android.graphics.text.LineBreakConfig; import android.graphics.text.LineBreakConfig; Loading @@ -28,6 +31,7 @@ import android.text.AutoGrowArray.ByteArray; import android.text.AutoGrowArray.FloatArray; import android.text.AutoGrowArray.FloatArray; import android.text.AutoGrowArray.IntArray; import android.text.AutoGrowArray.IntArray; import android.text.Layout.Directions; import android.text.Layout.Directions; import android.text.style.LineBreakConfigSpan; import android.text.style.MetricAffectingSpan; import android.text.style.MetricAffectingSpan; import android.text.style.ReplacementSpan; import android.text.style.ReplacementSpan; import android.util.Pools.SynchronizedPool; import android.util.Pools.SynchronizedPool; Loading Loading @@ -57,6 +61,7 @@ import java.util.Arrays; * MeasuredParagraph is NOT a thread safe object. * MeasuredParagraph is NOT a thread safe object. * @hide * @hide */ */ @TestApi public class MeasuredParagraph { public class MeasuredParagraph { private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC'; private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC'; Loading @@ -73,6 +78,7 @@ public class MeasuredParagraph { * Recycle the MeasuredParagraph. * Recycle the MeasuredParagraph. * * * Do not call any methods after you call this method. * Do not call any methods after you call this method. * @hide */ */ public void recycle() { public void recycle() { release(); release(); Loading Loading @@ -126,11 +132,14 @@ public class MeasuredParagraph { private @Nullable MeasuredText mMeasuredText; private @Nullable MeasuredText mMeasuredText; // Following three objects are for avoiding object allocation. // Following three objects are for avoiding object allocation. private @NonNull TextPaint mCachedPaint = new TextPaint(); private final @NonNull TextPaint mCachedPaint = new TextPaint(); private @Nullable Paint.FontMetricsInt mCachedFm; private @Nullable Paint.FontMetricsInt mCachedFm; private final @NonNull LineBreakConfig.Builder mLineBreakConfigBuilder = new LineBreakConfig.Builder(); /** /** * Releases internal buffers. * Releases internal buffers. * @hide */ */ public void release() { public void release() { reset(); reset(); Loading Loading @@ -158,6 +167,7 @@ public class MeasuredParagraph { * Returns the length of the paragraph. * Returns the length of the paragraph. * * * This is always available. * This is always available. * @hide */ */ public int getTextLength() { public int getTextLength() { return mTextLength; return mTextLength; Loading @@ -167,6 +177,7 @@ public class MeasuredParagraph { * Returns the characters to be measured. * Returns the characters to be measured. * * * This is always available. * This is always available. * @hide */ */ public @NonNull char[] getChars() { public @NonNull char[] getChars() { return mCopiedBuffer; return mCopiedBuffer; Loading @@ -176,6 +187,7 @@ public class MeasuredParagraph { * Returns the paragraph direction. * Returns the paragraph direction. * * * This is always available. * This is always available. * @hide */ */ public @Layout.Direction int getParagraphDir() { public @Layout.Direction int getParagraphDir() { return mParaDir; return mParaDir; Loading @@ -185,6 +197,7 @@ public class MeasuredParagraph { * Returns the directions. * Returns the directions. * * * This is always available. * This is always available. * @hide */ */ public Directions getDirections(@IntRange(from = 0) int start, // inclusive public Directions getDirections(@IntRange(from = 0) int start, // inclusive @IntRange(from = 0) int end) { // exclusive @IntRange(from = 0) int end) { // exclusive Loading @@ -202,6 +215,7 @@ public class MeasuredParagraph { * * * This is available only if the MeasuredParagraph is computed with buildForMeasurement. * This is available only if the MeasuredParagraph is computed with buildForMeasurement. * Returns 0 in other cases. * Returns 0 in other cases. * @hide */ */ public @FloatRange(from = 0.0f) float getWholeWidth() { public @FloatRange(from = 0.0f) float getWholeWidth() { return mWholeWidth; return mWholeWidth; Loading @@ -212,6 +226,7 @@ public class MeasuredParagraph { * * * This is available only if the MeasuredParagraph is computed with buildForMeasurement. * This is available only if the MeasuredParagraph is computed with buildForMeasurement. * Returns empty array in other cases. * Returns empty array in other cases. * @hide */ */ public @NonNull FloatArray getWidths() { public @NonNull FloatArray getWidths() { return mWidths; return mWidths; Loading @@ -224,6 +239,7 @@ public class MeasuredParagraph { * * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * Returns empty array in other cases. * Returns empty array in other cases. * @hide */ */ public @NonNull IntArray getSpanEndCache() { public @NonNull IntArray getSpanEndCache() { return mSpanEndCache; return mSpanEndCache; Loading @@ -236,6 +252,7 @@ public class MeasuredParagraph { * * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * Returns empty array in other cases. * Returns empty array in other cases. * @hide */ */ public @NonNull IntArray getFontMetrics() { public @NonNull IntArray getFontMetrics() { return mFontMetrics; return mFontMetrics; Loading @@ -246,6 +263,7 @@ public class MeasuredParagraph { * * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * Returns null in other cases. * Returns null in other cases. * @hide */ */ public MeasuredText getMeasuredText() { public MeasuredText getMeasuredText() { return mMeasuredText; return mMeasuredText; Loading @@ -259,6 +277,7 @@ public class MeasuredParagraph { * * * @param start the inclusive start offset of the target region in the text * @param start the inclusive start offset of the target region in the text * @param end the exclusive end offset of the target region in the text * @param end the exclusive end offset of the target region in the text * @hide */ */ public float getWidth(int start, int end) { public float getWidth(int start, int end) { if (mMeasuredText == null) { if (mMeasuredText == null) { Loading @@ -280,6 +299,7 @@ public class MeasuredParagraph { * at (0, 0). * at (0, 0). * * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * @hide */ */ public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end, public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull Rect bounds) { @NonNull Rect bounds) { Loading @@ -290,6 +310,7 @@ public class MeasuredParagraph { * Retrieves the font metrics for the given range. * Retrieves the font metrics for the given range. * * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * @hide */ */ public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end, public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull Paint.FontMetricsInt fmi) { @NonNull Paint.FontMetricsInt fmi) { Loading @@ -300,6 +321,7 @@ public class MeasuredParagraph { * Returns a width of the character at the offset. * Returns a width of the character at the offset. * * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * @hide */ */ public float getCharWidthAt(@IntRange(from = 0) int offset) { public float getCharWidthAt(@IntRange(from = 0) int offset) { return mMeasuredText.getCharWidthAt(offset); return mMeasuredText.getCharWidthAt(offset); Loading @@ -318,6 +340,7 @@ public class MeasuredParagraph { * @param recycle pass existing MeasuredParagraph if you want to recycle it. * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * * @return measured text * @return measured text * @hide */ */ public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text, public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int start, Loading @@ -343,6 +366,7 @@ public class MeasuredParagraph { * @param recycle pass existing MeasuredParagraph if you want to recycle it. * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * * @return measured text * @return measured text * @hide */ */ public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint, public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint, @NonNull CharSequence text, @NonNull CharSequence text, Loading @@ -361,24 +385,52 @@ public class MeasuredParagraph { if (mt.mSpanned == null) { if (mt.mSpanned == null) { // No style change by MetricsAffectingSpan. Just measure all text. // No style change by MetricsAffectingSpan. Just measure all text. mt.applyMetricsAffectingSpan( mt.applyMetricsAffectingSpan( paint, null /* lineBreakConfig */, null /* spans */, start, end, paint, null /* lineBreakConfig */, null /* spans */, null /* lbcSpans */, null /* native builder ptr */); start, end, null /* native builder ptr */, null); } else { } else { // There may be a MetricsAffectingSpan. Split into span transitions and apply styles. // There may be a MetricsAffectingSpan. Split into span transitions and apply styles. int spanEnd; int spanEnd; for (int spanStart = start; spanStart < end; spanStart = spanEnd) { for (int spanStart = start; spanStart < end; spanStart = spanEnd) { spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class); int maSpanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class); int lbcSpanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, LineBreakConfigSpan.class); spanEnd = Math.min(maSpanEnd, lbcSpanEnd); MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class); MetricAffectingSpan.class); LineBreakConfigSpan[] lbcSpans = mt.mSpanned.getSpans(spanStart, spanEnd, LineBreakConfigSpan.class); spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); lbcSpans = TextUtils.removeEmptySpans(lbcSpans, mt.mSpanned, LineBreakConfigSpan.class); mt.applyMetricsAffectingSpan( mt.applyMetricsAffectingSpan( paint, null /* line break config */, spans, spanStart, spanEnd, paint, null /* line break config */, spans, lbcSpans, spanStart, spanEnd, null /* native builder ptr */); null /* native builder ptr */, null); } } } } return mt; return mt; } } /** * A test interface for observing the style run calculation. * @hide */ @TestApi public interface StyleRunCallback { /** * Called when a single style run is identified. */ void onAppendStyleRun(@NonNull Paint paint, @Nullable LineBreakConfig lineBreakConfig, @IntRange(from = 0) int length, boolean isRtl); /** * Called when a single replacement run is identified. */ void onAppendReplacementRun(@NonNull Paint paint, @IntRange(from = 0) int length, @Px @FloatRange(from = 0) float width); } /** /** * Generates new MeasuredParagraph for StaticLayout. * Generates new MeasuredParagraph for StaticLayout. * * Loading @@ -397,6 +449,7 @@ public class MeasuredParagraph { * @param recycle pass existing MeasuredParagraph if you want to recycle it. * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * * @return measured text * @return measured text * @hide */ */ public static @NonNull MeasuredParagraph buildForStaticLayout( public static @NonNull MeasuredParagraph buildForStaticLayout( @NonNull TextPaint paint, @NonNull TextPaint paint, Loading @@ -409,6 +462,57 @@ public class MeasuredParagraph { boolean computeLayout, boolean computeLayout, @Nullable MeasuredParagraph hint, @Nullable MeasuredParagraph hint, @Nullable MeasuredParagraph recycle) { @Nullable MeasuredParagraph recycle) { return buildForStaticLayoutInternal(paint, lineBreakConfig, text, start, end, textDir, hyphenationMode, computeLayout, hint, recycle, null); } /** * Generates new MeasuredParagraph for StaticLayout. * * If recycle is null, this returns new instance. If recycle is not null, this fills computed * result to recycle and returns recycle. * * @param paint the paint to be used for rendering the text. * @param lineBreakConfig the line break configuration for text wrapping. * @param text the character sequence to be measured * @param start the inclusive start offset of the target region in the text * @param end the exclusive end offset of the target region in the text * @param textDir the text direction * @param hyphenationMode a hyphenation mode * @param computeLayout true if need to compute full layout, otherwise false. * * @return measured text * @hide */ @SuppressLint("ExecutorRegistration") @TestApi @NonNull public static MeasuredParagraph buildForStaticLayoutTest( @NonNull TextPaint paint, @Nullable LineBreakConfig lineBreakConfig, @NonNull CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull TextDirectionHeuristic textDir, int hyphenationMode, boolean computeLayout, @Nullable StyleRunCallback testCallback) { return buildForStaticLayoutInternal(paint, lineBreakConfig, text, start, end, textDir, hyphenationMode, computeLayout, null, null, testCallback); } private static @NonNull MeasuredParagraph buildForStaticLayoutInternal( @NonNull TextPaint paint, @Nullable LineBreakConfig lineBreakConfig, @NonNull CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull TextDirectionHeuristic textDir, int hyphenationMode, boolean computeLayout, @Nullable MeasuredParagraph hint, @Nullable MeasuredParagraph recycle, @Nullable StyleRunCallback testCallback) { final MeasuredParagraph mt = recycle == null ? obtain() : recycle; final MeasuredParagraph mt = recycle == null ? obtain() : recycle; mt.resetAndAnalyzeBidi(text, start, end, textDir); mt.resetAndAnalyzeBidi(text, start, end, textDir); final MeasuredText.Builder builder; final MeasuredText.Builder builder; Loading @@ -426,23 +530,29 @@ public class MeasuredParagraph { } else { } else { if (mt.mSpanned == null) { if (mt.mSpanned == null) { // No style change by MetricsAffectingSpan. Just measure all text. // No style change by MetricsAffectingSpan. Just measure all text. mt.applyMetricsAffectingSpan(paint, lineBreakConfig, null /* spans */, start, end, mt.applyMetricsAffectingSpan(paint, lineBreakConfig, null /* spans */, null, builder); start, end, builder, testCallback); mt.mSpanEndCache.append(end); mt.mSpanEndCache.append(end); } else { } else { // There may be a MetricsAffectingSpan. Split into span transitions and apply // There may be a MetricsAffectingSpan. Split into span transitions and apply // styles. // styles. int spanEnd; int spanEnd; for (int spanStart = start; spanStart < end; spanStart = spanEnd) { for (int spanStart = start; spanStart < end; spanStart = spanEnd) { spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, int maSpanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class); MetricAffectingSpan.class); int lbcSpanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, LineBreakConfigSpan.class); spanEnd = Math.min(maSpanEnd, lbcSpanEnd); MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class); MetricAffectingSpan.class); LineBreakConfigSpan[] lbcSpans = mt.mSpanned.getSpans(spanStart, spanEnd, LineBreakConfigSpan.class); spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); MetricAffectingSpan.class); // TODO: Update line break config with spans. lbcSpans = TextUtils.removeEmptySpans(lbcSpans, mt.mSpanned, mt.applyMetricsAffectingSpan(paint, lineBreakConfig, spans, spanStart, spanEnd, LineBreakConfigSpan.class); builder); mt.applyMetricsAffectingSpan(paint, lineBreakConfig, spans, lbcSpans, spanStart, spanEnd, builder, testCallback); mt.mSpanEndCache.append(spanEnd); mt.mSpanEndCache.append(spanEnd); } } } } Loading Loading @@ -519,7 +629,8 @@ public class MeasuredParagraph { @IntRange(from = 0) int start, // inclusive, in copied buffer @IntRange(from = 0) int start, // inclusive, in copied buffer @IntRange(from = 0) int end, // exclusive, in copied buffer @IntRange(from = 0) int end, // exclusive, in copied buffer @NonNull TextPaint paint, @NonNull TextPaint paint, @Nullable MeasuredText.Builder builder) { @Nullable MeasuredText.Builder builder, @Nullable StyleRunCallback testCallback) { // Use original text. Shouldn't matter. // Use original text. Shouldn't matter. // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for // backward compatibility? or Should we initialize them for getFontMetricsInt? // backward compatibility? or Should we initialize them for getFontMetricsInt? Loading @@ -535,13 +646,17 @@ public class MeasuredParagraph { } else { } else { builder.appendReplacementRun(paint, end - start, width); builder.appendReplacementRun(paint, end - start, width); } } if (testCallback != null) { testCallback.onAppendReplacementRun(paint, end - start, width); } } } private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer @IntRange(from = 0) int end, // exclusive, in copied buffer @IntRange(from = 0) int end, // exclusive, in copied buffer @NonNull TextPaint paint, @NonNull TextPaint paint, @Nullable LineBreakConfig config, @Nullable LineBreakConfig config, @Nullable MeasuredText.Builder builder) { @Nullable MeasuredText.Builder builder, @Nullable StyleRunCallback testCallback) { if (mLtrWithoutBidi) { if (mLtrWithoutBidi) { // If the whole text is LTR direction, just apply whole region. // If the whole text is LTR direction, just apply whole region. Loading @@ -552,6 +667,9 @@ public class MeasuredParagraph { } else { } else { builder.appendStyleRun(paint, config, end - start, false /* isRtl */); builder.appendStyleRun(paint, config, end - start, false /* isRtl */); } } if (testCallback != null) { testCallback.onAppendStyleRun(paint, config, end - start, false); } } else { } else { // If there is multiple bidi levels, split into individual bidi level and apply style. // If there is multiple bidi levels, split into individual bidi level and apply style. byte level = mLevels.get(start); byte level = mLevels.get(start); Loading @@ -568,6 +686,9 @@ public class MeasuredParagraph { } else { } else { builder.appendStyleRun(paint, config, levelEnd - levelStart, isRtl); builder.appendStyleRun(paint, config, levelEnd - levelStart, isRtl); } } if (testCallback != null) { testCallback.onAppendStyleRun(paint, config, levelEnd - levelStart, isRtl); } if (levelEnd == end) { if (levelEnd == end) { break; break; } } Loading @@ -582,9 +703,11 @@ public class MeasuredParagraph { @NonNull TextPaint paint, @NonNull TextPaint paint, @Nullable LineBreakConfig lineBreakConfig, @Nullable LineBreakConfig lineBreakConfig, @Nullable MetricAffectingSpan[] spans, @Nullable MetricAffectingSpan[] spans, @Nullable LineBreakConfigSpan[] lbcSpans, @IntRange(from = 0) int start, // inclusive, in original text buffer @IntRange(from = 0) int start, // inclusive, in original text buffer @IntRange(from = 0) int end, // exclusive, in original text buffer @IntRange(from = 0) int end, // exclusive, in original text buffer @Nullable MeasuredText.Builder builder) { @Nullable MeasuredText.Builder builder, @Nullable StyleRunCallback testCallback) { mCachedPaint.set(paint); mCachedPaint.set(paint); // XXX paint should not have a baseline shift, but... // XXX paint should not have a baseline shift, but... mCachedPaint.baselineShift = 0; mCachedPaint.baselineShift = 0; Loading @@ -609,6 +732,14 @@ public class MeasuredParagraph { } } } } if (lbcSpans != null) { mLineBreakConfigBuilder.reset(lineBreakConfig); for (LineBreakConfigSpan lbcSpan : lbcSpans) { mLineBreakConfigBuilder.merge(lbcSpan.getLineBreakConfig()); } lineBreakConfig = mLineBreakConfigBuilder.build(); } final int startInCopiedBuffer = start - mTextStart; final int startInCopiedBuffer = start - mTextStart; final int endInCopiedBuffer = end - mTextStart; final int endInCopiedBuffer = end - mTextStart; Loading @@ -618,10 +749,10 @@ public class MeasuredParagraph { if (replacement != null) { if (replacement != null) { applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, mCachedPaint, applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, mCachedPaint, builder); builder, testCallback); } else { } else { applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, mCachedPaint, applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, mCachedPaint, lineBreakConfig, builder); lineBreakConfig, builder, testCallback); } } if (needFontMetrics) { if (needFontMetrics) { Loading Loading @@ -690,6 +821,7 @@ public class MeasuredParagraph { /** /** * This only works if the MeasuredParagraph is computed with buildForStaticLayout. * This only works if the MeasuredParagraph is computed with buildForStaticLayout. * @hide */ */ public @IntRange(from = 0) int getMemoryUsage() { public @IntRange(from = 0) int getMemoryUsage() { return mMeasuredText.getMemoryUsage(); return mMeasuredText.getMemoryUsage(); Loading
core/java/android/text/PrecomputedText.java +6 −10 Original line number Original line Diff line number Diff line Loading @@ -329,22 +329,17 @@ public class PrecomputedText implements Spannable { @Override @Override public int hashCode() { public int hashCode() { // TODO: implement MinikinPaint::hashCode and use it to keep consistency with equals. // TODO: implement MinikinPaint::hashCode and use it to keep consistency with equals. int lineBreakStyle = (mLineBreakConfig != null) ? mLineBreakConfig.getLineBreakStyle() : LineBreakConfig.LINE_BREAK_STYLE_NONE; return Objects.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), mPaint.getTextSkewX(), return Objects.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), mPaint.getTextSkewX(), mPaint.getLetterSpacing(), mPaint.getWordSpacing(), mPaint.getFlags(), mPaint.getLetterSpacing(), mPaint.getWordSpacing(), mPaint.getFlags(), mPaint.getTextLocales(), mPaint.getTypeface(), mPaint.getTextLocales(), mPaint.getTypeface(), mPaint.getFontVariationSettings(), mPaint.isElegantTextHeight(), mTextDir, mPaint.getFontVariationSettings(), mPaint.isElegantTextHeight(), mTextDir, mBreakStrategy, mHyphenationFrequency, lineBreakStyle); mBreakStrategy, mHyphenationFrequency, LineBreakConfig.getResolvedLineBreakStyle(mLineBreakConfig), LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig)); } } @Override @Override public String toString() { 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 "{" return "{" + "textSize=" + mPaint.getTextSize() + "textSize=" + mPaint.getTextSize() + ", textScaleX=" + mPaint.getTextScaleX() + ", textScaleX=" + mPaint.getTextScaleX() Loading @@ -357,8 +352,9 @@ public class PrecomputedText implements Spannable { + ", textDir=" + mTextDir + ", textDir=" + mTextDir + ", breakStrategy=" + mBreakStrategy + ", breakStrategy=" + mBreakStrategy + ", hyphenationFrequency=" + mHyphenationFrequency + ", hyphenationFrequency=" + mHyphenationFrequency + ", lineBreakStyle=" + lineBreakStyle + ", lineBreakStyle=" + LineBreakConfig.getResolvedLineBreakStyle(mLineBreakConfig) + ", lineBreakWordStyle=" + lineBreakWordStyle + ", lineBreakWordStyle=" + LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig) + "}"; + "}"; } } }; }; Loading
core/java/android/text/StaticLayout.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -448,7 +448,7 @@ public class StaticLayout extends Layout { private void reviseLineBreakConfig() { private void reviseLineBreakConfig() { boolean autoPhraseBreaking = mLineBreakConfig.getAutoPhraseBreaking(); boolean autoPhraseBreaking = mLineBreakConfig.getAutoPhraseBreaking(); int wordStyle = mLineBreakConfig.getLineBreakWordStyle(); int wordStyle = LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig); if (autoPhraseBreaking) { if (autoPhraseBreaking) { if (wordStyle != LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) { if (wordStyle != LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) { if (shouldEnablePhraseBreaking()) { if (shouldEnablePhraseBreaking()) { Loading