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

Commit 48e1f12c authored by Tyler Freeman's avatar Tyler Freeman Committed by Android (Google) Code Review
Browse files

Merge changes from topics "fontscaling-lineheight-after",...

Merge changes from topics "fontscaling-lineheight-after", "fontscaling-lineheight-spfix" into udc-dev

* changes:
  fix(non linear font scaling): recalculate proportional line height if they setTextSize() after setting line height
  fix(non linear font scaling): preserve proportionality of lineHeight when non-linear font scaling is in effect
  feat(non linear font scaling): add FontScaleConverterFactory.isNonLinearFontScalingActive()
parents d55a7a68 fa043bb5
Loading
Loading
Loading
Loading
+22 −4
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ public class FontScaleConverterFactory {
    @VisibleForTesting
    static final SparseArray<FontScaleConverter> LOOKUP_TABLES = new SparseArray<>();

    private static float sMinScaleBeforeCurvesApplied = 1.05f;

    static {
        // These were generated by frameworks/base/tools/fonts/font-scaling-array-generator.js and
        // manually tweaked for optimum readability.
@@ -82,10 +84,29 @@ public class FontScaleConverterFactory {
                        new float[] {  16f,   20f,   24f,   26f,   30f,   34f,   36f,   38f,  100})
        );

        sMinScaleBeforeCurvesApplied = getScaleFromKey(LOOKUP_TABLES.keyAt(0)) - 0.02f;
        if (sMinScaleBeforeCurvesApplied <= 1.0f) {
            throw new IllegalStateException(
                    "You should only apply non-linear scaling to font scales > 1"
            );
        }
    }

    private FontScaleConverterFactory() {}

    /**
     * Returns true if non-linear font scaling curves would be in effect for the given scale, false
     * if the scaling would follow a linear curve or for no scaling.
     *
     * <p>Example usage:
     * <code>isNonLinearFontScalingActive(getResources().getConfiguration().fontScale)</code>
     *
     * @hide
     */
    public static boolean isNonLinearFontScalingActive(float fontScale) {
        return fontScale >= sMinScaleBeforeCurvesApplied;
    }

    /**
     * Finds a matching FontScaleConverter for the given fontScale factor.
     *
@@ -97,10 +118,7 @@ public class FontScaleConverterFactory {
     */
    @Nullable
    public static FontScaleConverter forScale(float fontScale) {
        if (fontScale <= 1) {
            // We don't need non-linear curves for shrinking text or for 100%.
            // Also, fontScale==0 should not have a curve either.
            // And ignore negative font scales; that's just silly.
        if (!isNonLinearFontScalingActive(fontScale)) {
            return null;
        }

+16 −4
Original line number Diff line number Diff line
@@ -385,9 +385,21 @@ public class TypedValue {
     *
     * @return The complex unit type.
     */
     public int getComplexUnit()
     {
         return COMPLEX_UNIT_MASK & (data>>TypedValue.COMPLEX_UNIT_SHIFT);
    public int getComplexUnit() {
        return getUnitFromComplexDimension(data);
    }

    /**
     * Return the complex unit type for the given complex dimension. For example, a dimen type
     * with value 12sp will return {@link #COMPLEX_UNIT_SP}. Use with values created with {@link
     * #createComplexDimension(int, int)} etc.
     *
     * @return The complex unit type.
     *
     * @hide
     */
    public static int getUnitFromComplexDimension(int complexDimension) {
        return COMPLEX_UNIT_MASK & (complexDimension >> TypedValue.COMPLEX_UNIT_SHIFT);
    }

    /**
+75 −5
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.FontScaleConverterFactory;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -867,6 +868,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    @UnsupportedAppUsage
    private float mSpacingAdd = 0.0f;
    /**
     * Remembers what line height was set to originally, before we broke it down into raw pixels.
     *
     * <p>This is stored as a complex dimension with both value and unit packed into one field!
     * {@see TypedValue}
     */
    private int mLineHeightComplexDimen;
    private int mBreakStrategy;
    private int mHyphenationFrequency;
    private int mJustificationMode;
@@ -1233,7 +1242,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                defStyleAttr, defStyleRes);
        int firstBaselineToTopHeight = -1;
        int lastBaselineToBottomHeight = -1;
        int lineHeight = -1;
        float lineHeight = -1f;
        int lineHeightUnit = -1;
        readTextAppearance(context, a, attributes, true /* styleArray */);
@@ -1583,7 +1593,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                    break;
                case com.android.internal.R.styleable.TextView_lineHeight:
                    TypedValue peekValue = a.peekValue(attr);
                    if (peekValue != null && peekValue.type == TypedValue.TYPE_DIMENSION) {
                        lineHeightUnit = peekValue.getComplexUnit();
                        lineHeight = TypedValue.complexToFloat(peekValue.data);
                    } else {
                        lineHeight = a.getDimensionPixelSize(attr, -1);
                    }
                    break;
            }
        }
@@ -1936,7 +1952,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            setLastBaselineToBottomHeight(lastBaselineToBottomHeight);
        }
        if (lineHeight >= 0) {
            setLineHeight(lineHeight);
            if (lineHeightUnit == -1) {
                setLineHeightPx(lineHeight);
            } else {
                setLineHeight(lineHeightUnit, lineHeight);
            }
        }
    }
@@ -4629,6 +4649,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        if (size != mTextPaint.getTextSize()) {
            mTextPaint.setTextSize(size);
            maybeRecalculateLineHeight();
            if (shouldRequestLayout && mLayout != null) {
                // Do not auto-size right after setting the text size.
                mNeedsAutoSizeText = false;
@@ -6214,6 +6235,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        if (lineHeight != fontHeight) {
            // Set lineSpacingExtra by the difference of lineSpacing with lineHeight
            setLineSpacing(lineHeight - fontHeight, 1f);
            mLineHeightComplexDimen =
                        TypedValue.createComplexDimension(lineHeight, TypedValue.COMPLEX_UNIT_PX);
        }
    }
@@ -6236,8 +6260,54 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            @TypedValue.ComplexDimensionUnit int unit,
            @FloatRange(from = 0) float lineHeight
    ) {
        setLineHeightPx(
                TypedValue.applyDimension(unit, lineHeight, getDisplayMetricsOrSystem()));
        var metrics = getDisplayMetricsOrSystem();
        // We can avoid the recalculation if we know non-linear font scaling isn't being used
        // (an optimization for the majority case).
        // We also don't try to do the recalculation unless both textSize and lineHeight are in SP.
        if (!FontScaleConverterFactory.isNonLinearFontScalingActive(
                    getResources().getConfiguration().fontScale)
                || unit != TypedValue.COMPLEX_UNIT_SP
                || mTextSizeUnit != TypedValue.COMPLEX_UNIT_SP
        ) {
            setLineHeightPx(TypedValue.applyDimension(unit, lineHeight, metrics));
            // Do this last so it overwrites what setLineHeightPx() sets it to.
            mLineHeightComplexDimen = TypedValue.createComplexDimension(lineHeight, unit);
            return;
        }
        // Recalculate a proportional line height when non-linear font scaling is in effect.
        // Otherwise, a desired 2x line height at font scale 1.0 will not be 2x at font scale 2.0,
        // due to non-linear font scaling compressing higher SP sizes. See b/273326061 for details.
        // We know they are using SP units for both the text size and the line height
        // at this point, so determine the ratio between them. This is the *intended* line spacing
        // multiplier if font scale == 1.0. We can then determine what the pixel value for the line
        // height would be if we preserved proportions.
        var textSizePx = getTextSize();
        var textSizeSp = TypedValue.convertPixelsToDimension(
                TypedValue.COMPLEX_UNIT_SP,
                textSizePx,
                metrics
        );
        var ratio = lineHeight / textSizeSp;
        setLineHeightPx(textSizePx * ratio);
        // Do this last so it overwrites what setLineHeightPx() sets it to.
        mLineHeightComplexDimen = TypedValue.createComplexDimension(lineHeight, unit);
    }
    private void maybeRecalculateLineHeight() {
        if (mLineHeightComplexDimen == 0) {
            return;
        }
        int unit = TypedValue.getUnitFromComplexDimension(mLineHeightComplexDimen);
        if (unit != TypedValue.COMPLEX_UNIT_SP) {
            // The lineHeight was never supplied in SP, so we didn't do any fancy recalculations
            // in setLineHeight(). We don't need to recalculate.
            return;
        }
        setLineHeight(unit, TypedValue.complexToFloat(mLineHeightComplexDimen));
    }
    /**
+16 −0
Original line number Diff line number Diff line
@@ -122,6 +122,22 @@ class FontScaleConverterFactoryTest {
        }
    }

    @SmallTest
    fun testIsNonLinearFontScalingActive() {
        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1f)).isFalse()
        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(0f)).isFalse()
        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(-1f)).isFalse()
        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(0.85f)).isFalse()
        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.02f)).isFalse()
        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.10f)).isFalse()
        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.15f)).isTrue()
        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.1499999f))
                .isTrue()
        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.5f)).isTrue()
        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(2f)).isTrue()
        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(3f)).isTrue()
    }

    @LargeTest
    @Test
    fun allFeasibleScalesAndConversionsDoNotCrash() {