Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fe2cff11 authored by Seigo Nonaka's avatar Seigo Nonaka
Browse files

Fix minimum line height for TextView use case

Bug: 303326708
Test: CtsWidgetTestCases
Test: CtsTextTestCases
Test: CtsGraphicsTestCases
Test: FrameworksCoreTests:android.text
Change-Id: Idcaf94d4a3c8df57f14d8768570ba7ca54b5caa7
parent 5713cf3a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1804,6 +1804,7 @@ package android {
    field public static final int useEmbeddedDex = 16844190; // 0x101059e
    field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
    field public static final int useLevel = 16843167; // 0x101019f
    field @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public static final int useLocalePreferredLineHeightForMinimum;
    field public static final int userVisible = 16843409; // 0x1010291
    field public static final int usesCleartextTraffic = 16844012; // 0x10104ec
    field public static final int usesPermissionFlags = 16844356; // 0x1010644
@@ -60506,6 +60507,7 @@ package android.widget {
    method public boolean isFallbackLineSpacing();
    method public final boolean isHorizontallyScrollable();
    method public boolean isInputMethodTarget();
    method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public boolean isLocalePreferredLineHeightForMinimumUsed();
    method public boolean isSingleLine();
    method public boolean isSuggestionsEnabled();
    method public boolean isTextSelectable();
@@ -60588,6 +60590,7 @@ package android.widget {
    method public final void setLinkTextColor(@ColorInt int);
    method public final void setLinkTextColor(android.content.res.ColorStateList);
    method public final void setLinksClickable(boolean);
    method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public void setLocalePreferredLineHeightForMinimumUsed(boolean);
    method public void setMarqueeRepeatLimit(int);
    method public void setMaxEms(int);
    method public void setMaxHeight(int);
+1 −3
Original line number Diff line number Diff line
@@ -585,9 +585,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
        }

        if (ClientFlags.fixLineHeightForLocale()) {
            if (minimumFontMetrics == null) {
                paint.getFontMetricsIntForLocale(fm);
            } else {
            if (minimumFontMetrics != null) {
                fm.set(minimumFontMetrics);
                // Because the font metrics is provided by public APIs, adjust the top/bottom with
                // ascent/descent: top must be smaller than ascent, bottom must be larger than
+12 −20
Original line number Diff line number Diff line
@@ -767,22 +767,14 @@ public class StaticLayout extends Layout {
        }

        int defaultTop;
        int defaultAscent;
        int defaultDescent;
        final int defaultAscent;
        final int defaultDescent;
        int defaultBottom;
        if (ClientFlags.fixLineHeightForLocale()) {
            if (b.mMinimumFontMetrics != null) {
        if (ClientFlags.fixLineHeightForLocale() && b.mMinimumFontMetrics != null) {
            defaultTop = (int) Math.floor(b.mMinimumFontMetrics.top);
            defaultAscent = Math.round(b.mMinimumFontMetrics.ascent);
            defaultDescent = Math.round(b.mMinimumFontMetrics.descent);
            defaultBottom = (int) Math.ceil(b.mMinimumFontMetrics.bottom);
            } else {
                paint.getFontMetricsIntForLocale(fm);
                defaultTop = fm.top;
                defaultAscent = fm.ascent;
                defaultDescent = fm.descent;
                defaultBottom = fm.bottom;
            }

            // Because the font metrics is provided by public APIs, adjust the top/bottom with
            // ascent/descent: top must be smaller than ascent, bottom must be larger than descent.
@@ -1043,10 +1035,10 @@ public class StaticLayout extends Layout {

                    if (endPos < spanEnd) {
                        // preserve metrics for current span
                        fmTop = fm.top;
                        fmBottom = fm.bottom;
                        fmAscent = fm.ascent;
                        fmDescent = fm.descent;
                        fmTop = Math.min(defaultTop, fm.top);
                        fmBottom = Math.max(defaultBottom, fm.bottom);
                        fmAscent = Math.min(defaultAscent, fm.ascent);
                        fmDescent = Math.max(defaultDescent, fm.descent);
                    } else {
                        fmTop = fmBottom = fmAscent = fmDescent = 0;
                    }
@@ -1069,7 +1061,7 @@ public class StaticLayout extends Layout {
                && mLineCount < mMaximumVisibleLineCount) {
            final MeasuredParagraph measuredPara =
                    MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null);
            if (ClientFlags.fixLineHeightForLocale()) {
            if (defaultAscent != 0 && defaultDescent != 0) {
                fm.top = defaultTop;
                fm.ascent = defaultAscent;
                fm.descent = defaultDescent;
+42 −7
Original line number Diff line number Diff line
@@ -16,9 +16,13 @@

package android.widget;

import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
@@ -29,6 +33,8 @@ import android.text.style.SpanUtils;
import android.util.AttributeSet;
import android.view.KeyEvent;

import com.android.internal.R;

/*
 * This is supposed to be a *very* thin veneer over TextView.
 * Do not make any changes here that do anything that a TextView
@@ -85,6 +91,11 @@ public class EditText extends TextView {
    private static final int ID_ITALIC = android.R.id.italic;
    private static final int ID_UNDERLINE = android.R.id.underline;

    /** @hide */
    @ChangeId
    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
    public static final long LINE_HEIGHT_FOR_LOCALE = 303326708L;

    public EditText(Context context) {
        this(context, null);
    }
@@ -104,6 +115,7 @@ public class EditText extends TextView {
        final TypedArray a = theme.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.EditText, defStyleAttr, defStyleRes);

        try {
            final int n = a.getIndexCount();
            for (int i = 0; i < n; ++i) {
                int attr = a.getIndex(i);
@@ -113,6 +125,29 @@ public class EditText extends TextView {
                        break;
                }
            }
        } finally {
            a.recycle();
        }

        boolean hasUseLocalePreferredLineHeightForMinimumInt = false;
        boolean useLocalePreferredLineHeightForMinimumInt = false;
        TypedArray tvArray = theme.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);
        try {
            hasUseLocalePreferredLineHeightForMinimumInt =
                    tvArray.hasValue(R.styleable.TextView_useLocalePreferredLineHeightForMinimum);
            if (hasUseLocalePreferredLineHeightForMinimumInt) {
                useLocalePreferredLineHeightForMinimumInt = tvArray.getBoolean(
                        R.styleable.TextView_useLocalePreferredLineHeightForMinimum, false);
            }
        } finally {
            tvArray.recycle();
        }
        if (!hasUseLocalePreferredLineHeightForMinimumInt) {
            useLocalePreferredLineHeightForMinimumInt =
                    CompatChanges.isChangeEnabled(LINE_HEIGHT_FOR_LOCALE);
        }
        setLocalePreferredLineHeightForMinimumUsed(useLocalePreferredLineHeightForMinimumInt);
    }

    @Override
+73 −12
Original line number Diff line number Diff line
@@ -867,6 +867,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    private boolean mUseBoundsForWidth;
    @Nullable private Paint.FontMetrics mMinimumFontMetrics;
    @Nullable private Paint.FontMetrics mLocalePreferredFontMetrics;
    private boolean mUseLocalePreferredLineHeightForMinimum;
    @ViewDebug.ExportedProperty(category = "text")
    @UnsupportedAppUsage
@@ -1617,6 +1619,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                case com.android.internal.R.styleable.TextView_useBoundsForWidth:
                    mUseBoundsForWidth = a.getBoolean(attr, false);
                    hasUseBoundForWidthValue = true;
                    break;
                case com.android.internal.R.styleable
                        .TextView_useLocalePreferredLineHeightForMinimum:
                    mUseLocalePreferredLineHeightForMinimum = a.getBoolean(attr, false);
                    break;
            }
        }
@@ -4991,6 +4998,41 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        return mMinimumFontMetrics;
    }
    /**
     * Returns true if the locale preferred line height is used for the minimum line height.
     *
     * @return true if using locale preferred line height for the minimum line height. Otherwise
     *         false.
     *
     * @see #setLocalePreferredLineHeightForMinimumUsed(boolean)
     * @see #setMinimumFontMetrics(Paint.FontMetrics)
     * @see #getMinimumFontMetrics()
     */
    @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE)
    public boolean isLocalePreferredLineHeightForMinimumUsed() {
        return mUseLocalePreferredLineHeightForMinimum;
    }
    /**
     * Set true if the locale preferred line height is used for the minimum line height.
     *
     * By setting this flag to true is equivalenet to call
     * {@link #setMinimumFontMetrics(Paint.FontMetrics)} with the one obtained by
     * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)}.
     *
     * If custom minimum line height was specified by
     * {@link #setMinimumFontMetrics(Paint.FontMetrics)}, this flag will be ignored.
     *
     * @param flag true for using locale preferred line height for the minimum line height.
     * @see #isLocalePreferredLineHeightForMinimumUsed()
     * @see #setMinimumFontMetrics(Paint.FontMetrics)
     * @see #getMinimumFontMetrics()
     */
    @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE)
    public void setLocalePreferredLineHeightForMinimumUsed(boolean flag) {
        mUseLocalePreferredLineHeightForMinimum = flag;
    }
    /**
     * @return whether fallback line spacing is enabled, {@code true} by default
     *
@@ -10728,6 +10770,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        return alignment;
    }
    private Paint.FontMetrics getResolvedMinimumFontMetrics() {
        if (mMinimumFontMetrics != null) {
            return mMinimumFontMetrics;
        }
        if (!mUseLocalePreferredLineHeightForMinimum) {
            return null;
        }
        if (mLocalePreferredFontMetrics == null) {
            mLocalePreferredFontMetrics = new Paint.FontMetrics();
        }
        mTextPaint.getFontMetricsForLocale(mLocalePreferredFontMetrics);
        return mLocalePreferredFontMetrics;
    }
    /**
     * The width passed in is now the desired layout width,
     * not the full view width with padding.
@@ -10792,7 +10849,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            if (hintBoring == UNKNOWN_BORING) {
                hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
                        isFallbackLineSpacingForBoringLayout(),
                        mMinimumFontMetrics, mHintBoring);
                        getResolvedMinimumFontMetrics(), mHintBoring);
                if (hintBoring != null) {
                    mHintBoring = hintBoring;
                }
@@ -10842,7 +10900,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                        .setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
                                mLineBreakStyle, mLineBreakWordStyle))
                        .setUseBoundsForWidth(mUseBoundsForWidth)
                        .setMinimumFontMetrics(mMinimumFontMetrics);
                        .setMinimumFontMetrics(getResolvedMinimumFontMetrics());
                if (shouldEllipsize) {
                    builder.setEllipsize(mEllipsize)
                            .setEllipsizedWidth(ellipsisWidth);
@@ -10907,12 +10966,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                    .setUseBoundsForWidth(mUseBoundsForWidth)
                    .setEllipsize(getKeyListener() == null ? effectiveEllipsize : null)
                    .setEllipsizedWidth(ellipsisWidth)
                    .setMinimumFontMetrics(mMinimumFontMetrics);
                    .setMinimumFontMetrics(getResolvedMinimumFontMetrics());
            result = builder.build();
        } else {
            if (boring == UNKNOWN_BORING) {
                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
                        isFallbackLineSpacingForBoringLayout(), mMinimumFontMetrics, mBoring);
                        isFallbackLineSpacingForBoringLayout(), getResolvedMinimumFontMetrics(),
                        mBoring);
                if (boring != null) {
                    mBoring = boring;
                }
@@ -10926,7 +10986,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                                wantWidth, alignment, mSpacingMult, mSpacingAdd,
                                boring, mIncludePad, null, wantWidth,
                                isFallbackLineSpacingForBoringLayout(),
                                mUseBoundsForWidth, mMinimumFontMetrics);
                                mUseBoundsForWidth, getResolvedMinimumFontMetrics());
                    } else {
                        result = new BoringLayout(
                                mTransformed,
@@ -10941,7 +11001,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                                null,
                                boring,
                                mUseBoundsForWidth,
                                mMinimumFontMetrics);
                                getResolvedMinimumFontMetrics());
                    }
                    if (useSaved) {
@@ -10953,7 +11013,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                                wantWidth, alignment, mSpacingMult, mSpacingAdd,
                                boring, mIncludePad, effectiveEllipsize,
                                ellipsisWidth, isFallbackLineSpacingForBoringLayout(),
                                mUseBoundsForWidth, mMinimumFontMetrics);
                                mUseBoundsForWidth, getResolvedMinimumFontMetrics());
                    } else {
                        result = new BoringLayout(
                                mTransformed,
@@ -10968,7 +11028,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                                effectiveEllipsize,
                                boring,
                                mUseBoundsForWidth,
                                mMinimumFontMetrics);
                                getResolvedMinimumFontMetrics());
                    }
                }
            }
@@ -10988,7 +11048,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                    .setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
                            mLineBreakStyle, mLineBreakWordStyle))
                    .setUseBoundsForWidth(mUseBoundsForWidth)
                    .setMinimumFontMetrics(mMinimumFontMetrics);
                    .setMinimumFontMetrics(getResolvedMinimumFontMetrics());
            if (shouldEllipsize) {
                builder.setEllipsize(effectiveEllipsize)
                        .setEllipsizedWidth(ellipsisWidth);
@@ -11116,7 +11176,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            if (des < 0) {
                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
                        isFallbackLineSpacingForBoringLayout(), mMinimumFontMetrics, mBoring);
                        isFallbackLineSpacingForBoringLayout(), getResolvedMinimumFontMetrics(),
                        mBoring);
                if (boring != null) {
                    mBoring = boring;
                }
@@ -11156,7 +11217,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                if (hintDes < 0) {
                    hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
                            isFallbackLineSpacingForBoringLayout(), mMinimumFontMetrics,
                            isFallbackLineSpacingForBoringLayout(), getResolvedMinimumFontMetrics(),
                            mHintBoring);
                    if (hintBoring != null) {
                        mHintBoring = hintBoring;
@@ -11370,7 +11431,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                .setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
                        mLineBreakStyle, mLineBreakWordStyle))
                .setUseBoundsForWidth(mUseBoundsForWidth)
                .setMinimumFontMetrics(mMinimumFontMetrics);
                .setMinimumFontMetrics(getResolvedMinimumFontMetrics());
        final StaticLayout layout = layoutBuilder.build();
Loading