Loading core/api/current.txt +9 −0 Original line number Diff line number Diff line Loading @@ -17533,17 +17533,21 @@ package android.graphics.text { public final class LineBreakConfig { method public int getLineBreakStyle(); 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_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_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_PHRASE = 1; // 0x1 field public static final int LINE_BREAK_WORD_STYLE_UNSPECIFIED = -1; // 0xffffffff } public static final class LineBreakConfig.Builder { 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 @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakStyle(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); } 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 { method public void chooseHeight(CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt); } core/api/test-current.txt +9 −0 Original line number 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; } 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 { ctor public Selection.MemoryTextWatcher(); method public void afterTextChanged(android.text.Editable); Loading core/java/android/text/MeasuredParagraph.java +149 −17 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.text.LineBreakConfig; Loading @@ -28,6 +31,7 @@ import android.text.AutoGrowArray.ByteArray; import android.text.AutoGrowArray.FloatArray; import android.text.AutoGrowArray.IntArray; import android.text.Layout.Directions; import android.text.style.LineBreakConfigSpan; import android.text.style.MetricAffectingSpan; import android.text.style.ReplacementSpan; import android.util.Pools.SynchronizedPool; Loading Loading @@ -57,6 +61,7 @@ import java.util.Arrays; * MeasuredParagraph is NOT a thread safe object. * @hide */ @TestApi public class MeasuredParagraph { private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC'; Loading @@ -73,6 +78,7 @@ public class MeasuredParagraph { * Recycle the MeasuredParagraph. * * Do not call any methods after you call this method. * @hide */ public void recycle() { release(); Loading Loading @@ -126,11 +132,14 @@ public class MeasuredParagraph { private @Nullable MeasuredText mMeasuredText; // 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 final @NonNull LineBreakConfig.Builder mLineBreakConfigBuilder = new LineBreakConfig.Builder(); /** * Releases internal buffers. * @hide */ public void release() { reset(); Loading Loading @@ -158,6 +167,7 @@ public class MeasuredParagraph { * Returns the length of the paragraph. * * This is always available. * @hide */ public int getTextLength() { return mTextLength; Loading @@ -167,6 +177,7 @@ public class MeasuredParagraph { * Returns the characters to be measured. * * This is always available. * @hide */ public @NonNull char[] getChars() { return mCopiedBuffer; Loading @@ -176,6 +187,7 @@ public class MeasuredParagraph { * Returns the paragraph direction. * * This is always available. * @hide */ public @Layout.Direction int getParagraphDir() { return mParaDir; Loading @@ -185,6 +197,7 @@ public class MeasuredParagraph { * Returns the directions. * * This is always available. * @hide */ public Directions getDirections(@IntRange(from = 0) int start, // inclusive @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. * Returns 0 in other cases. * @hide */ public @FloatRange(from = 0.0f) float getWholeWidth() { return mWholeWidth; Loading @@ -212,6 +226,7 @@ public class MeasuredParagraph { * * This is available only if the MeasuredParagraph is computed with buildForMeasurement. * Returns empty array in other cases. * @hide */ public @NonNull FloatArray getWidths() { return mWidths; Loading @@ -224,6 +239,7 @@ public class MeasuredParagraph { * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * Returns empty array in other cases. * @hide */ public @NonNull IntArray getSpanEndCache() { return mSpanEndCache; Loading @@ -236,6 +252,7 @@ public class MeasuredParagraph { * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * Returns empty array in other cases. * @hide */ public @NonNull IntArray getFontMetrics() { return mFontMetrics; Loading @@ -246,6 +263,7 @@ public class MeasuredParagraph { * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * Returns null in other cases. * @hide */ public MeasuredText getMeasuredText() { return mMeasuredText; Loading @@ -259,6 +277,7 @@ public class MeasuredParagraph { * * @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 * @hide */ public float getWidth(int start, int end) { if (mMeasuredText == null) { Loading @@ -280,6 +299,7 @@ public class MeasuredParagraph { * at (0, 0). * * 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, @NonNull Rect bounds) { Loading @@ -290,6 +310,7 @@ public class MeasuredParagraph { * Retrieves the font metrics for the given range. * * 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, @NonNull Paint.FontMetricsInt fmi) { Loading @@ -300,6 +321,7 @@ public class MeasuredParagraph { * Returns a width of the character at the offset. * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * @hide */ public float getCharWidthAt(@IntRange(from = 0) int offset) { return mMeasuredText.getCharWidthAt(offset); Loading @@ -318,6 +340,7 @@ public class MeasuredParagraph { * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * @return measured text * @hide */ public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text, @IntRange(from = 0) int start, Loading @@ -343,6 +366,7 @@ public class MeasuredParagraph { * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * @return measured text * @hide */ public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint, @NonNull CharSequence text, Loading @@ -361,24 +385,52 @@ public class MeasuredParagraph { if (mt.mSpanned == null) { // No style change by MetricsAffectingSpan. Just measure all text. mt.applyMetricsAffectingSpan( paint, null /* lineBreakConfig */, null /* spans */, start, end, null /* native builder ptr */); paint, null /* lineBreakConfig */, null /* spans */, null /* lbcSpans */, start, end, null /* native builder ptr */, null); } else { // There may be a MetricsAffectingSpan. Split into span transitions and apply styles. int 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.class); LineBreakConfigSpan[] lbcSpans = mt.mSpanned.getSpans(spanStart, spanEnd, LineBreakConfigSpan.class); spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); lbcSpans = TextUtils.removeEmptySpans(lbcSpans, mt.mSpanned, LineBreakConfigSpan.class); mt.applyMetricsAffectingSpan( paint, null /* line break config */, spans, spanStart, spanEnd, null /* native builder ptr */); paint, null /* line break config */, spans, lbcSpans, spanStart, spanEnd, null /* native builder ptr */, null); } } 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. * Loading @@ -397,6 +449,7 @@ public class MeasuredParagraph { * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * @return measured text * @hide */ public static @NonNull MeasuredParagraph buildForStaticLayout( @NonNull TextPaint paint, Loading @@ -409,6 +462,57 @@ public class MeasuredParagraph { boolean computeLayout, @Nullable MeasuredParagraph hint, @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; mt.resetAndAnalyzeBidi(text, start, end, textDir); final MeasuredText.Builder builder; Loading @@ -426,23 +530,29 @@ public class MeasuredParagraph { } else { if (mt.mSpanned == null) { // No style change by MetricsAffectingSpan. Just measure all text. mt.applyMetricsAffectingSpan(paint, lineBreakConfig, null /* spans */, start, end, builder); mt.applyMetricsAffectingSpan(paint, lineBreakConfig, null /* spans */, null, start, end, builder, testCallback); mt.mSpanEndCache.append(end); } else { // There may be a MetricsAffectingSpan. Split into span transitions and apply // styles. int spanEnd; for (int spanStart = start; spanStart < end; spanStart = spanEnd) { spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, 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.class); LineBreakConfigSpan[] lbcSpans = mt.mSpanned.getSpans(spanStart, spanEnd, LineBreakConfigSpan.class); spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); // TODO: Update line break config with spans. mt.applyMetricsAffectingSpan(paint, lineBreakConfig, spans, spanStart, spanEnd, builder); lbcSpans = TextUtils.removeEmptySpans(lbcSpans, mt.mSpanned, LineBreakConfigSpan.class); mt.applyMetricsAffectingSpan(paint, lineBreakConfig, spans, lbcSpans, spanStart, spanEnd, builder, testCallback); 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 end, // exclusive, in copied buffer @NonNull TextPaint paint, @Nullable MeasuredText.Builder builder) { @Nullable MeasuredText.Builder builder, @Nullable StyleRunCallback testCallback) { // Use original text. Shouldn't matter. // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for // backward compatibility? or Should we initialize them for getFontMetricsInt? Loading @@ -535,13 +646,17 @@ public class MeasuredParagraph { } else { 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 @IntRange(from = 0) int end, // exclusive, in copied buffer @NonNull TextPaint paint, @Nullable LineBreakConfig config, @Nullable MeasuredText.Builder builder) { @Nullable MeasuredText.Builder builder, @Nullable StyleRunCallback testCallback) { if (mLtrWithoutBidi) { // If the whole text is LTR direction, just apply whole region. Loading @@ -552,6 +667,9 @@ public class MeasuredParagraph { } else { builder.appendStyleRun(paint, config, end - start, false /* isRtl */); } if (testCallback != null) { testCallback.onAppendStyleRun(paint, config, end - start, false); } } else { // If there is multiple bidi levels, split into individual bidi level and apply style. byte level = mLevels.get(start); Loading @@ -568,6 +686,9 @@ public class MeasuredParagraph { } else { builder.appendStyleRun(paint, config, levelEnd - levelStart, isRtl); } if (testCallback != null) { testCallback.onAppendStyleRun(paint, config, levelEnd - levelStart, isRtl); } if (levelEnd == end) { break; } Loading @@ -582,9 +703,11 @@ public class MeasuredParagraph { @NonNull TextPaint paint, @Nullable LineBreakConfig lineBreakConfig, @Nullable MetricAffectingSpan[] spans, @Nullable LineBreakConfigSpan[] lbcSpans, @IntRange(from = 0) int start, // inclusive, 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); // XXX paint should not have a baseline shift, but... 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 endInCopiedBuffer = end - mTextStart; Loading @@ -618,10 +749,10 @@ public class MeasuredParagraph { if (replacement != null) { applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, mCachedPaint, builder); builder, testCallback); } else { applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, mCachedPaint, lineBreakConfig, builder); lineBreakConfig, builder, testCallback); } if (needFontMetrics) { Loading Loading @@ -690,6 +821,7 @@ public class MeasuredParagraph { /** * This only works if the MeasuredParagraph is computed with buildForStaticLayout. * @hide */ public @IntRange(from = 0) int getMemoryUsage() { return mMeasuredText.getMemoryUsage(); Loading core/java/android/text/PrecomputedText.java +6 −10 Original line number Diff line number Diff line Loading @@ -329,22 +329,17 @@ public class PrecomputedText implements Spannable { @Override public int hashCode() { // 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(), mPaint.getLetterSpacing(), mPaint.getWordSpacing(), mPaint.getFlags(), mPaint.getTextLocales(), mPaint.getTypeface(), mPaint.getFontVariationSettings(), mPaint.isElegantTextHeight(), mTextDir, mBreakStrategy, mHyphenationFrequency, lineBreakStyle); mBreakStrategy, mHyphenationFrequency, LineBreakConfig.getResolvedLineBreakStyle(mLineBreakConfig), LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig)); } @Override 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() Loading @@ -357,8 +352,9 @@ public class PrecomputedText implements Spannable { + ", textDir=" + mTextDir + ", breakStrategy=" + mBreakStrategy + ", hyphenationFrequency=" + mHyphenationFrequency + ", lineBreakStyle=" + lineBreakStyle + ", lineBreakWordStyle=" + lineBreakWordStyle + ", lineBreakStyle=" + LineBreakConfig.getResolvedLineBreakStyle(mLineBreakConfig) + ", lineBreakWordStyle=" + LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig) + "}"; } }; Loading core/java/android/text/StaticLayout.java +1 −1 Original line number Diff line number Diff line Loading @@ -448,7 +448,7 @@ public class StaticLayout extends Layout { private void reviseLineBreakConfig() { boolean autoPhraseBreaking = mLineBreakConfig.getAutoPhraseBreaking(); int wordStyle = mLineBreakConfig.getLineBreakWordStyle(); int wordStyle = LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig); if (autoPhraseBreaking) { if (wordStyle != LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) { if (shouldEnablePhraseBreaking()) { Loading Loading
core/api/current.txt +9 −0 Original line number Diff line number Diff line Loading @@ -17533,17 +17533,21 @@ package android.graphics.text { public final class LineBreakConfig { method public int getLineBreakStyle(); 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_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_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_PHRASE = 1; // 0x1 field public static final int LINE_BREAK_WORD_STYLE_UNSPECIFIED = -1; // 0xffffffff } public static final class LineBreakConfig.Builder { 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 @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakStyle(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); } 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 { method public void chooseHeight(CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt); }
core/api/test-current.txt +9 −0 Original line number 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; } 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 { ctor public Selection.MemoryTextWatcher(); method public void afterTextChanged(android.text.Editable); Loading
core/java/android/text/MeasuredParagraph.java +149 −17 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.text.LineBreakConfig; Loading @@ -28,6 +31,7 @@ import android.text.AutoGrowArray.ByteArray; import android.text.AutoGrowArray.FloatArray; import android.text.AutoGrowArray.IntArray; import android.text.Layout.Directions; import android.text.style.LineBreakConfigSpan; import android.text.style.MetricAffectingSpan; import android.text.style.ReplacementSpan; import android.util.Pools.SynchronizedPool; Loading Loading @@ -57,6 +61,7 @@ import java.util.Arrays; * MeasuredParagraph is NOT a thread safe object. * @hide */ @TestApi public class MeasuredParagraph { private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC'; Loading @@ -73,6 +78,7 @@ public class MeasuredParagraph { * Recycle the MeasuredParagraph. * * Do not call any methods after you call this method. * @hide */ public void recycle() { release(); Loading Loading @@ -126,11 +132,14 @@ public class MeasuredParagraph { private @Nullable MeasuredText mMeasuredText; // 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 final @NonNull LineBreakConfig.Builder mLineBreakConfigBuilder = new LineBreakConfig.Builder(); /** * Releases internal buffers. * @hide */ public void release() { reset(); Loading Loading @@ -158,6 +167,7 @@ public class MeasuredParagraph { * Returns the length of the paragraph. * * This is always available. * @hide */ public int getTextLength() { return mTextLength; Loading @@ -167,6 +177,7 @@ public class MeasuredParagraph { * Returns the characters to be measured. * * This is always available. * @hide */ public @NonNull char[] getChars() { return mCopiedBuffer; Loading @@ -176,6 +187,7 @@ public class MeasuredParagraph { * Returns the paragraph direction. * * This is always available. * @hide */ public @Layout.Direction int getParagraphDir() { return mParaDir; Loading @@ -185,6 +197,7 @@ public class MeasuredParagraph { * Returns the directions. * * This is always available. * @hide */ public Directions getDirections(@IntRange(from = 0) int start, // inclusive @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. * Returns 0 in other cases. * @hide */ public @FloatRange(from = 0.0f) float getWholeWidth() { return mWholeWidth; Loading @@ -212,6 +226,7 @@ public class MeasuredParagraph { * * This is available only if the MeasuredParagraph is computed with buildForMeasurement. * Returns empty array in other cases. * @hide */ public @NonNull FloatArray getWidths() { return mWidths; Loading @@ -224,6 +239,7 @@ public class MeasuredParagraph { * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * Returns empty array in other cases. * @hide */ public @NonNull IntArray getSpanEndCache() { return mSpanEndCache; Loading @@ -236,6 +252,7 @@ public class MeasuredParagraph { * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * Returns empty array in other cases. * @hide */ public @NonNull IntArray getFontMetrics() { return mFontMetrics; Loading @@ -246,6 +263,7 @@ public class MeasuredParagraph { * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * Returns null in other cases. * @hide */ public MeasuredText getMeasuredText() { return mMeasuredText; Loading @@ -259,6 +277,7 @@ public class MeasuredParagraph { * * @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 * @hide */ public float getWidth(int start, int end) { if (mMeasuredText == null) { Loading @@ -280,6 +299,7 @@ public class MeasuredParagraph { * at (0, 0). * * 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, @NonNull Rect bounds) { Loading @@ -290,6 +310,7 @@ public class MeasuredParagraph { * Retrieves the font metrics for the given range. * * 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, @NonNull Paint.FontMetricsInt fmi) { Loading @@ -300,6 +321,7 @@ public class MeasuredParagraph { * Returns a width of the character at the offset. * * This is available only if the MeasuredParagraph is computed with buildForStaticLayout. * @hide */ public float getCharWidthAt(@IntRange(from = 0) int offset) { return mMeasuredText.getCharWidthAt(offset); Loading @@ -318,6 +340,7 @@ public class MeasuredParagraph { * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * @return measured text * @hide */ public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text, @IntRange(from = 0) int start, Loading @@ -343,6 +366,7 @@ public class MeasuredParagraph { * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * @return measured text * @hide */ public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint, @NonNull CharSequence text, Loading @@ -361,24 +385,52 @@ public class MeasuredParagraph { if (mt.mSpanned == null) { // No style change by MetricsAffectingSpan. Just measure all text. mt.applyMetricsAffectingSpan( paint, null /* lineBreakConfig */, null /* spans */, start, end, null /* native builder ptr */); paint, null /* lineBreakConfig */, null /* spans */, null /* lbcSpans */, start, end, null /* native builder ptr */, null); } else { // There may be a MetricsAffectingSpan. Split into span transitions and apply styles. int 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.class); LineBreakConfigSpan[] lbcSpans = mt.mSpanned.getSpans(spanStart, spanEnd, LineBreakConfigSpan.class); spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); lbcSpans = TextUtils.removeEmptySpans(lbcSpans, mt.mSpanned, LineBreakConfigSpan.class); mt.applyMetricsAffectingSpan( paint, null /* line break config */, spans, spanStart, spanEnd, null /* native builder ptr */); paint, null /* line break config */, spans, lbcSpans, spanStart, spanEnd, null /* native builder ptr */, null); } } 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. * Loading @@ -397,6 +449,7 @@ public class MeasuredParagraph { * @param recycle pass existing MeasuredParagraph if you want to recycle it. * * @return measured text * @hide */ public static @NonNull MeasuredParagraph buildForStaticLayout( @NonNull TextPaint paint, Loading @@ -409,6 +462,57 @@ public class MeasuredParagraph { boolean computeLayout, @Nullable MeasuredParagraph hint, @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; mt.resetAndAnalyzeBidi(text, start, end, textDir); final MeasuredText.Builder builder; Loading @@ -426,23 +530,29 @@ public class MeasuredParagraph { } else { if (mt.mSpanned == null) { // No style change by MetricsAffectingSpan. Just measure all text. mt.applyMetricsAffectingSpan(paint, lineBreakConfig, null /* spans */, start, end, builder); mt.applyMetricsAffectingSpan(paint, lineBreakConfig, null /* spans */, null, start, end, builder, testCallback); mt.mSpanEndCache.append(end); } else { // There may be a MetricsAffectingSpan. Split into span transitions and apply // styles. int spanEnd; for (int spanStart = start; spanStart < end; spanStart = spanEnd) { spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, 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.class); LineBreakConfigSpan[] lbcSpans = mt.mSpanned.getSpans(spanStart, spanEnd, LineBreakConfigSpan.class); spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); // TODO: Update line break config with spans. mt.applyMetricsAffectingSpan(paint, lineBreakConfig, spans, spanStart, spanEnd, builder); lbcSpans = TextUtils.removeEmptySpans(lbcSpans, mt.mSpanned, LineBreakConfigSpan.class); mt.applyMetricsAffectingSpan(paint, lineBreakConfig, spans, lbcSpans, spanStart, spanEnd, builder, testCallback); 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 end, // exclusive, in copied buffer @NonNull TextPaint paint, @Nullable MeasuredText.Builder builder) { @Nullable MeasuredText.Builder builder, @Nullable StyleRunCallback testCallback) { // Use original text. Shouldn't matter. // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for // backward compatibility? or Should we initialize them for getFontMetricsInt? Loading @@ -535,13 +646,17 @@ public class MeasuredParagraph { } else { 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 @IntRange(from = 0) int end, // exclusive, in copied buffer @NonNull TextPaint paint, @Nullable LineBreakConfig config, @Nullable MeasuredText.Builder builder) { @Nullable MeasuredText.Builder builder, @Nullable StyleRunCallback testCallback) { if (mLtrWithoutBidi) { // If the whole text is LTR direction, just apply whole region. Loading @@ -552,6 +667,9 @@ public class MeasuredParagraph { } else { builder.appendStyleRun(paint, config, end - start, false /* isRtl */); } if (testCallback != null) { testCallback.onAppendStyleRun(paint, config, end - start, false); } } else { // If there is multiple bidi levels, split into individual bidi level and apply style. byte level = mLevels.get(start); Loading @@ -568,6 +686,9 @@ public class MeasuredParagraph { } else { builder.appendStyleRun(paint, config, levelEnd - levelStart, isRtl); } if (testCallback != null) { testCallback.onAppendStyleRun(paint, config, levelEnd - levelStart, isRtl); } if (levelEnd == end) { break; } Loading @@ -582,9 +703,11 @@ public class MeasuredParagraph { @NonNull TextPaint paint, @Nullable LineBreakConfig lineBreakConfig, @Nullable MetricAffectingSpan[] spans, @Nullable LineBreakConfigSpan[] lbcSpans, @IntRange(from = 0) int start, // inclusive, 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); // XXX paint should not have a baseline shift, but... 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 endInCopiedBuffer = end - mTextStart; Loading @@ -618,10 +749,10 @@ public class MeasuredParagraph { if (replacement != null) { applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, mCachedPaint, builder); builder, testCallback); } else { applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, mCachedPaint, lineBreakConfig, builder); lineBreakConfig, builder, testCallback); } if (needFontMetrics) { Loading Loading @@ -690,6 +821,7 @@ public class MeasuredParagraph { /** * This only works if the MeasuredParagraph is computed with buildForStaticLayout. * @hide */ public @IntRange(from = 0) int getMemoryUsage() { return mMeasuredText.getMemoryUsage(); Loading
core/java/android/text/PrecomputedText.java +6 −10 Original line number Diff line number Diff line Loading @@ -329,22 +329,17 @@ public class PrecomputedText implements Spannable { @Override public int hashCode() { // 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(), mPaint.getLetterSpacing(), mPaint.getWordSpacing(), mPaint.getFlags(), mPaint.getTextLocales(), mPaint.getTypeface(), mPaint.getFontVariationSettings(), mPaint.isElegantTextHeight(), mTextDir, mBreakStrategy, mHyphenationFrequency, lineBreakStyle); mBreakStrategy, mHyphenationFrequency, LineBreakConfig.getResolvedLineBreakStyle(mLineBreakConfig), LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig)); } @Override 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() Loading @@ -357,8 +352,9 @@ public class PrecomputedText implements Spannable { + ", textDir=" + mTextDir + ", breakStrategy=" + mBreakStrategy + ", hyphenationFrequency=" + mHyphenationFrequency + ", lineBreakStyle=" + lineBreakStyle + ", lineBreakWordStyle=" + lineBreakWordStyle + ", lineBreakStyle=" + LineBreakConfig.getResolvedLineBreakStyle(mLineBreakConfig) + ", lineBreakWordStyle=" + LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig) + "}"; } }; Loading
core/java/android/text/StaticLayout.java +1 −1 Original line number Diff line number Diff line Loading @@ -448,7 +448,7 @@ public class StaticLayout extends Layout { private void reviseLineBreakConfig() { boolean autoPhraseBreaking = mLineBreakConfig.getAutoPhraseBreaking(); int wordStyle = mLineBreakConfig.getLineBreakWordStyle(); int wordStyle = LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig); if (autoPhraseBreaking) { if (wordStyle != LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) { if (shouldEnablePhraseBreaking()) { Loading