Merge "Use fallback-based line spacing in TextView etc"
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 7fa8066..f9bdb0fe 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -755,14 +755,18 @@
         }
     }
 
-    private void chooseSize(PopupWindow pop, CharSequence text, TextView tv) {
-        int wid = tv.getPaddingLeft() + tv.getPaddingRight();
-        int ht = tv.getPaddingTop() + tv.getPaddingBottom();
+    private void chooseSize(@NonNull PopupWindow pop, @NonNull CharSequence text,
+            @NonNull TextView tv) {
+        final int wid = tv.getPaddingLeft() + tv.getPaddingRight();
+        final int ht = tv.getPaddingTop() + tv.getPaddingBottom();
 
-        int defaultWidthInPixels = mTextView.getResources().getDimensionPixelSize(
+        final int defaultWidthInPixels = mTextView.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.textview_error_popup_default_width);
-        Layout l = new StaticLayout(text, tv.getPaint(), defaultWidthInPixels,
-                                    Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
+        final StaticLayout l = StaticLayout.Builder.obtain(text, 0, text.length(), tv.getPaint(),
+                defaultWidthInPixels)
+                .setUseLineSpacingFromFallbacks(tv.mUseFallbackLineSpacing)
+                .build();
+
         float max = 0;
         for (int i = 0; i < l.getLineCount(); i++) {
             max = Math.max(max, l.getLineWidth(i));
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 40253a1..2e1e963 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -33,6 +33,7 @@
 import android.graphics.Region.Op;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
+import android.os.Build.VERSION_CODES;
 import android.text.Layout;
 import android.text.StaticLayout;
 import android.text.TextPaint;
@@ -111,6 +112,7 @@
     private CharSequence mTextOn;
     private CharSequence mTextOff;
     private boolean mShowText;
+    private boolean mUseFallbackLineSpacing;
 
     private int mTouchMode;
     private int mTouchSlop;
@@ -246,6 +248,11 @@
                 com.android.internal.R.styleable.Switch_switchPadding, 0);
         mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false);
 
+        // TODO: replace CUR_DEVELOPMENT with P once P is added to android.os.Build.VERSION_CODES.
+        // STOPSHIP if the above TODO is not done.
+        mUseFallbackLineSpacing =
+                context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.CUR_DEVELOPMENT;
+
         ColorStateList thumbTintList = a.getColorStateList(
                 com.android.internal.R.styleable.Switch_thumbTint);
         if (thumbTintList != null) {
@@ -894,8 +901,9 @@
 
         int width = (int) Math.ceil(Layout.getDesiredWidth(transformed, 0,
                 transformed.length(), mTextPaint, getTextDirectionHeuristic()));
-        return new StaticLayout(transformed, mTextPaint, width,
-                Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
+        return StaticLayout.Builder.obtain(transformed, 0, transformed.length(), mTextPaint, width)
+                .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
+                .build();
     }
 
     /**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2417b0f..140ecc1 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -650,6 +650,8 @@
     private boolean mListenerChanged = false;
     // True if internationalized input should be used for numbers and date and time.
     private final boolean mUseInternationalizedInput;
+    // True if fallback fonts that end up getting used should be allowed to affect line spacing.
+    /* package */ final boolean mUseFallbackLineSpacing;
 
     @ViewDebug.ExportedProperty(category = "text")
     private int mGravity = Gravity.TOP | Gravity.START;
@@ -1252,8 +1254,11 @@
         final boolean numberPasswordInputType = variation
                 == (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD);
 
-        mUseInternationalizedInput =
-                context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.O;
+        final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+        mUseInternationalizedInput = targetSdkVersion >= VERSION_CODES.O;
+        // TODO: replace CUR_DEVELOPMENT with P once P is added to android.os.Build.VERSION_CODES.
+        // STOPSHIP if the above TODO is not done.
+        mUseFallbackLineSpacing = targetSdkVersion >= VERSION_CODES.CUR_DEVELOPMENT;
 
         if (inputMethod != null) {
             Class<?> c;
@@ -7914,6 +7919,7 @@
                         .setTextDirection(mTextDir)
                         .setLineSpacing(mSpacingAdd, mSpacingMult)
                         .setIncludePad(mIncludePad)
+                        .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
                         .setBreakStrategy(mBreakStrategy)
                         .setHyphenationFrequency(mHyphenationFrequency)
                         .setJustificationMode(mJustificationMode)
@@ -7963,6 +7969,7 @@
                     .setTextDirection(mTextDir)
                     .setLineSpacing(mSpacingAdd, mSpacingMult)
                     .setIncludePad(mIncludePad)
+                    .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
                     .setBreakStrategy(mBreakStrategy)
                     .setHyphenationFrequency(mHyphenationFrequency)
                     .setJustificationMode(mJustificationMode)
@@ -8015,6 +8022,7 @@
                     .setTextDirection(mTextDir)
                     .setLineSpacing(mSpacingAdd, mSpacingMult)
                     .setIncludePad(mIncludePad)
+                    .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
                     .setBreakStrategy(mBreakStrategy)
                     .setHyphenationFrequency(mHyphenationFrequency)
                     .setJustificationMode(mJustificationMode)
@@ -8374,6 +8382,7 @@
         layoutBuilder.setAlignment(getLayoutAlignment())
                 .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
                 .setIncludePad(getIncludeFontPadding())
+                .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
                 .setBreakStrategy(getBreakStrategy())
                 .setHyphenationFrequency(getHyphenationFrequency())
                 .setJustificationMode(getJustificationMode())
diff --git a/core/java/com/android/internal/widget/ImageFloatingTextView.java b/core/java/com/android/internal/widget/ImageFloatingTextView.java
index 31b167d..7870333 100644
--- a/core/java/com/android/internal/widget/ImageFloatingTextView.java
+++ b/core/java/com/android/internal/widget/ImageFloatingTextView.java
@@ -81,6 +81,7 @@
                 .setTextDirection(getTextDirectionHeuristic())
                 .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
                 .setIncludePad(getIncludeFontPadding())
+                .setUseLineSpacingFromFallbacks(true)
                 .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
                 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
         int maxLines;
diff --git a/core/java/com/android/internal/widget/SubtitleView.java b/core/java/com/android/internal/widget/SubtitleView.java
index 3230185..1107828 100644
--- a/core/java/com/android/internal/widget/SubtitleView.java
+++ b/core/java/com/android/internal/widget/SubtitleView.java
@@ -256,8 +256,11 @@
         // StaticLayout.getWidth(), so this is non-trivial.
         mHasMeasurements = true;
         mLastMeasuredWidth = maxWidth;
-        mLayout = new StaticLayout(
-                mText, mTextPaint, maxWidth, mAlignment, mSpacingMult, mSpacingAdd, true);
+        mLayout = StaticLayout.Builder.obtain(mText, 0, mText.length(), mTextPaint, maxWidth)
+                .setAlignment(mAlignment)
+                .setLineSpacing(mSpacingAdd, mSpacingMult)
+                .setUseLineSpacingFromFallbacks(true)
+                .build();
 
         return true;
     }
diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java
index bcf2514..ced74ee 100644
--- a/core/tests/coretests/src/android/text/FontFallbackSetup.java
+++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java
@@ -77,10 +77,14 @@
     }
 
     @NonNull
+    public Typeface getTypefaceFor(@NonNull String fontName) {
+        return mFontMap.get(fontName);
+    }
+
+    @NonNull
     public TextPaint getPaintFor(@NonNull String fontName) {
-        final Typeface testTypeface = mFontMap.get(fontName);
         final TextPaint paint = new TextPaint();
-        paint.setTypeface(testTypeface);
+        paint.setTypeface(getTypefaceFor(fontName));
         return paint;
     }
 
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index 5806bf1..d31da71 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -30,6 +30,7 @@
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
+import android.text.FontFallbackSetup;
 import android.text.GetChars;
 import android.text.Layout;
 import android.text.Selection;
@@ -249,4 +250,56 @@
         }
         return builder.toString();
     }
+
+    @Test
+    public void testFallbackLineSpacing() {
+        // All glyphs in the fonts are 1em wide.
+        final String[] testFontFiles = {
+            // ascent == 1em, descent == 2em, only supports 'a' and space
+            "ascent1em-descent2em.ttf",
+            // ascent == 3em, descent == 4em, only supports 'b'
+            "ascent3em-descent4em.ttf"
+        };
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='sans-serif'>"
+                + "    <font weight='400' style='normal'>ascent1em-descent2em.ttf</font>"
+                + "  </family>"
+                + "  <family>"
+                + "    <font weight='400' style='normal'>ascent3em-descent4em.ttf</font>"
+                + "  </family>"
+                + "</familyset>";
+
+        try (FontFallbackSetup setup =
+                new FontFallbackSetup("DynamicLayout", testFontFiles, xml)) {
+            mTextView = new TextView(mActivity);
+            mTextView.setTypeface(setup.getTypefaceFor("sans-serif"));
+            mTextView.setTextSize(100);
+            mTextView.setText("aaaaa aabaa aaaaa"); // This should result in three lines.
+            mTextView.setPadding(0, 0, 0, 0);
+            mTextView.setIncludeFontPadding(false);
+
+            final int em = (int) Math.ceil(mTextView.getPaint().measureText("a"));
+            final int width = 5 * em;
+            final int height = 30 * em; // tall enough to not affect our other measurements
+            mTextView.measure(
+                    View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
+                    View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
+            mTextView.layout(0, 0, width, height);
+
+            final Layout layout = mTextView.getLayout();
+            assertNotNull(layout);
+            assertEquals(3, layout.getLineCount());
+
+            assertEquals(-em, layout.getLineAscent(0));
+            assertEquals(2 * em, layout.getLineDescent(0));
+
+            // The second line has a 'b', so it needs more ascent and descent.
+            assertEquals(-3 * em, layout.getLineAscent(1));
+            assertEquals(4 * em, layout.getLineDescent(1));
+
+            assertEquals(-em, layout.getLineAscent(2));
+            assertEquals(2 * em, layout.getLineDescent(2));
+        }
+    }
 }