Merge changes Ib039b1bd,Ibbe63244 into androidx-main

* changes:
  Implement AutoSize for Text in ProtoLayout
  Extend FontStyle#size to accept a list of sizes
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/layout.proto b/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
index f248a31..ab3c6c2 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
@@ -83,9 +83,29 @@
 
 // The styling of a font (e.g. font size, and metrics).
 message FontStyle {
-  // The size of the font, in scaled pixels (sp). If not specified, defaults to
-  // the size of the system's "body" font.
-  SpProp size = 1;
+  // The available sizes of the font, in scaled pixels (sp). If not specified, defaults to the size
+  // of the system's "body" font.
+  //
+  // If more than one size is specified and this FontStyle is applied to a Text element with static
+  // text, the text size will be automatically picked from the provided sizes to try to perfectly
+  // fit within its parent bounds. In other words, the largest size from the specified preset sizes
+  // that can fit the most text within the parent bounds will be used.
+  //
+  // The specified sizes don't have to be sorted. The maximum number of sizes used is limited to 10.
+  //
+  // Note that, if multiple sizes are set, the parent of the Text element this corresponds to
+  // shouldn't have its width and height set to wrapped, as it can lead to unexpected results.
+  //
+  // If this FontStyle is set to any other element besides Text, or that Text element has dynamic
+  // field, only the last added size will be use.
+  //
+  // Any previously added values with this method or #setSize will be cleared.
+  //
+  // While this field is accessible from 1.0 as singular, it only accepts multiple values since
+  // version 1.3 and renderers supporting version 1.3 will use the multiple values to automatically
+  // scale text. Renderers who don't support this version will use the first size among multiple
+  // values.
+  repeated SpProp size = 1;
 
   // Whether the text should be rendered in a italic typeface. If not specified,
   // defaults to "false".
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
index 1c4789b..e03b58a 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
@@ -16,6 +16,8 @@
 
 package androidx.wear.protolayout.renderer.inflater;
 
+import static android.util.TypedValue.COMPLEX_UNIT_SP;
+
 import static androidx.core.util.Preconditions.checkNotNull;
 import static androidx.wear.protolayout.renderer.common.ProtoLayoutDiffer.FIRST_CHILD_INDEX;
 import static androidx.wear.protolayout.renderer.common.ProtoLayoutDiffer.ROOT_NODE_ID;
@@ -249,6 +251,8 @@
 
     private static final int TEXT_COLOR_DEFAULT = 0xFFFFFFFF;
     private static final int TEXT_MAX_LINES_DEFAULT = 1;
+    @VisibleForTesting
+    static final int TEXT_AUTOSIZES_LIMIT = 10;
     private static final int TEXT_MIN_LINES = 1;
 
     private static final ContainerDimension CONTAINER_DIMENSION_DEFAULT =
@@ -1137,7 +1141,7 @@
 
     private float toPx(SpProp spField) {
         return TypedValue.applyDimension(
-                TypedValue.COMPLEX_UNIT_SP,
+                COMPLEX_UNIT_SP,
                 spField.getValue(),
                 mUiContext.getResources().getDisplayMetrics());
     }
@@ -1146,7 +1150,8 @@
             FontStyle style,
             TextView textView,
             String posId,
-            Optional<ProtoLayoutDynamicDataPipeline.PipelineMaker> pipelineMaker) {
+            Optional<ProtoLayoutDynamicDataPipeline.PipelineMaker> pipelineMaker,
+            boolean isAutoSizeAllowed) {
         // Note: Underline must be applied as a Span to work correctly (as opposed to using
         // TextPaint#setTextUnderline). This is applied in the caller instead.
 
@@ -1155,8 +1160,43 @@
         // flags in Paint if they're not supported by the given typeface).
         textView.setTypeface(createTypeface(style), fontStyleToTypefaceStyle(style));
 
-        if (style.hasSize()) {
-            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, style.getSize().getValue());
+        if (fontStyleHasSize(style)) {
+            List<SpProp> sizes = style.getSizeList();
+            int sizesCnt = sizes.size();
+
+            if (sizesCnt == 1) {
+                // No autosizing needed.
+                textView.setTextSize(COMPLEX_UNIT_SP, sizes.get(0).getValue());
+            } else if (isAutoSizeAllowed && sizesCnt <= TEXT_AUTOSIZES_LIMIT) {
+                // Max size is needed so that TextView leaves enough space for it. Otherwise,
+                // the text won't be able to grow.
+                int maxSize =
+                        sizes.stream().mapToInt(sp -> (int) sp.getValue()).max().getAsInt();
+                textView.setTextSize(COMPLEX_UNIT_SP, maxSize);
+
+                // No need for sorting, TextView does that.
+                textView.setAutoSizeTextTypeUniformWithPresetSizes(
+                        sizes.stream().mapToInt(spProp -> (int) spProp.getValue()).toArray(),
+                        COMPLEX_UNIT_SP);
+            } else {
+                // Fallback where multiple values can't be used and the last value would be used.
+                // This can happen in two cases.
+                if (!isAutoSizeAllowed) {
+                    // There is more than 1 size specified, but autosizing is not allowed.
+                    Log.w(
+                            TAG,
+                            "Trying to autosize text with multiple font sizes where it's not "
+                                    + "allowed. Ignoring all other sizes and using the last one.");
+                } else {
+                    Log.w(
+                            TAG,
+                            "More than " + TEXT_AUTOSIZES_LIMIT + " sizes has been added for the "
+                                    + "text autosizing. Ignoring values after the "
+                                    + TEXT_AUTOSIZES_LIMIT + " one.");
+                }
+
+                textView.setTextSize(COMPLEX_UNIT_SP, sizes.get(sizesCnt - 1).getValue());
+            }
         }
 
         if (style.hasLetterSpacing()) {
@@ -1176,10 +1216,16 @@
         // flags in Paint if they're not supported by the given typeface).
         textView.setTypeface(createTypeface(style), fontStyleToTypefaceStyle(style));
 
-        // underline. We can implement this later by drawing a line under the text ourselves though.
-
-        if (style.hasSize()) {
-            textView.setTextSize(toPx(style.getSize()));
+        if (fontStyleHasSize(style)) {
+            // We are using the first added size in the FontStyle because ArcText doesn't support
+            // autosizing. This is the same behaviour as it was before size has made repeated.
+            if (style.getSizeList().size() > 1) {
+                Log.w(
+                        TAG,
+                        "Font size with multiple values has been used on Arc Text. Ignoring "
+                                + "all size except the first one.");
+            }
+            textView.setTextSize(toPx(style.getSize(style.getSizeCount() - 1)));
         }
     }
 
@@ -2225,12 +2271,24 @@
         }
         applyTextOverflow(textView, text.getOverflow(), text.getMarqueeParameters());
 
+        // Text auto size is not supported for dynamic text.
+        boolean isAutoSizeAllowed = !(text.hasText() && text.getText().hasDynamicValue());
         // Setting colours **must** go after setting the Text Appearance, otherwise it will get
         // immediately overridden.
         if (text.hasFontStyle()) {
-            applyFontStyle(text.getFontStyle(), textView, posId, pipelineMaker);
+            applyFontStyle(
+                    text.getFontStyle(),
+                    textView,
+                    posId,
+                    pipelineMaker,
+                    isAutoSizeAllowed);
         } else {
-            applyFontStyle(FontStyle.getDefaultInstance(), textView, posId, pipelineMaker);
+            applyFontStyle(
+                    FontStyle.getDefaultInstance(),
+                    textView,
+                    posId,
+                    pipelineMaker,
+                    isAutoSizeAllowed);
         }
 
         boolean excludeFontPadding = false;
@@ -2828,8 +2886,17 @@
 
     private void applyStylesToSpan(
             SpannableStringBuilder builder, int start, int end, FontStyle fontStyle) {
-        if (fontStyle.hasSize()) {
-            AbsoluteSizeSpan span = new AbsoluteSizeSpan(round(toPx(fontStyle.getSize())));
+        if (fontStyleHasSize(fontStyle)) {
+            // We are using the first added size in the FontStyle because ArcText doesn't support
+            // autosizing. This is the same behaviour as it was before size has made repeated.
+            if (fontStyle.getSizeList().size() > 1) {
+                Log.w(
+                        TAG,
+                        "Font size with multiple values has been used on Span Text. Ignoring "
+                        + "all size except the first one.");
+            }
+            AbsoluteSizeSpan span = new AbsoluteSizeSpan(round(toPx(
+                    fontStyle.getSize(fontStyle.getSizeCount() - 1))));
             builder.setSpan(span, start, end, Spanned.SPAN_MARK_MARK);
         }
 
@@ -2858,6 +2925,10 @@
         builder.setSpan(colorSpan, start, end, Spanned.SPAN_MARK_MARK);
     }
 
+    private static boolean fontStyleHasSize(FontStyle fontStyle) {
+        return !fontStyle.getSizeList().isEmpty();
+    }
+
     private void applyModifiersToSpan(
             SpannableStringBuilder builder, int start, int end, SpanModifiers modifiers) {
         if (modifiers.hasClickable()) {
@@ -3002,7 +3073,12 @@
         // Setting colours **must** go after setting the Text Appearance, otherwise it will get
         // immediately overridden.
         if (mApplyFontVariantBodyAsDefault) {
-            applyFontStyle(FontStyle.getDefaultInstance(), tv, posId, pipelineMaker);
+            applyFontStyle(
+                    FontStyle.getDefaultInstance(),
+                    tv,
+                    posId,
+                    pipelineMaker,
+                    /* isAutoSizeAllowed= */ false);
         }
 
         LayoutParams layoutParams = generateDefaultLayoutParams();
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestDsl.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestDsl.java
index a291354..520b0b4 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestDsl.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestDsl.java
@@ -124,7 +124,7 @@
 
         private LayoutElementProto.FontStyle toProto() {
             return LayoutElementProto.FontStyle.newBuilder()
-                    .setSize(sp(sizeSp))
+                    .addSize(sp(sizeSp))
                     .setItalic(bool(italic))
                     .setColor(color(colorArgb))
                     .build();
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
index fc19485..6c32f06 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
@@ -17,6 +17,7 @@
 package androidx.wear.protolayout.renderer.inflater;
 
 import static android.os.Looper.getMainLooper;
+import static android.util.TypedValue.COMPLEX_UNIT_SP;
 
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 import static androidx.wear.protolayout.proto.ModifiersProto.SlideParentSnapOption.SLIDE_PARENT_SNAP_TO_INSIDE;
@@ -31,6 +32,7 @@
 import static androidx.wear.protolayout.renderer.helper.TestDsl.layout;
 import static androidx.wear.protolayout.renderer.helper.TestDsl.row;
 import static androidx.wear.protolayout.renderer.helper.TestDsl.text;
+import static androidx.wear.protolayout.renderer.inflater.ProtoLayoutInflater.TEXT_AUTOSIZES_LIMIT;
 import static androidx.wear.protolayout.renderer.inflater.ProtoLayoutInflater.getFrameLayoutGravity;
 import static androidx.wear.protolayout.renderer.inflater.ProtoLayoutInflater.getRenderedMetadata;
 import static androidx.wear.protolayout.renderer.test.R.drawable.android_animated_24dp;
@@ -57,6 +59,7 @@
 import android.os.SystemClock;
 import android.text.TextUtils.TruncateAt;
 import android.util.Pair;
+import android.util.TypedValue;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.MeasureSpec;
@@ -112,6 +115,7 @@
 import androidx.wear.protolayout.proto.AlignmentProto.VerticalAlignment;
 import androidx.wear.protolayout.proto.AlignmentProto.VerticalAlignmentProp;
 import androidx.wear.protolayout.proto.ColorProto.ColorProp;
+import androidx.wear.protolayout.proto.DimensionProto;
 import androidx.wear.protolayout.proto.DimensionProto.ArcLineLength;
 import androidx.wear.protolayout.proto.DimensionProto.ArcSpacerLength;
 import androidx.wear.protolayout.proto.DimensionProto.ContainerDimension;
@@ -1124,6 +1128,31 @@
     }
 
     @Test
+    public void inflate_arc_withText_autoSize_notSet() {
+        int lastSize = 12;
+        FontStyle.Builder style = FontStyle.newBuilder()
+                .addAllSize(buildSizesList(new int[]{10, 20, lastSize}));
+        LayoutElement root =
+                LayoutElement.newBuilder()
+                        .setArc(
+                                Arc.newBuilder()
+                                        .setAnchorAngle(degrees(0).build())
+                                        .addContents(
+                                                ArcLayoutElement.newBuilder()
+                                                        .setText(
+                                                                ArcText.newBuilder()
+                                                                        .setText(string("text1"))
+                                                                        .setFontStyle(style))))
+                        .build();
+
+        FrameLayout rootLayout = renderer(fingerprintedLayout(root)).inflate();
+        ArcLayout arcLayout = (ArcLayout) rootLayout.getChildAt(0);
+        CurvedTextView tv = (CurvedTextView) arcLayout.getChildAt(0);
+        assertThat(tv.getText()).isEqualTo("text1");
+        expect.that(tv.getTextSize()).isEqualTo(lastSize);
+    }
+
+    @Test
     public void inflate_arc_withSpacer() {
         LayoutElement root =
                 LayoutElement.newBuilder()
@@ -1934,6 +1963,137 @@
     }
 
     @Test
+    public void inflate_textView_autosize_set() {
+        String text = "Test text";
+        int[] presetSizes = new int[]{12, 20, 10};
+        List<DimensionProto.SpProp> sizes = buildSizesList(presetSizes);
+
+        LayoutElement textElement =
+                LayoutElement.newBuilder()
+                        .setText(
+                                Text.newBuilder()
+                                        .setText(string(text))
+                                        .setFontStyle(
+                                                FontStyle.newBuilder()
+                                                        .addAllSize(sizes)))
+                        .build();
+        LayoutElement root =
+                LayoutElement.newBuilder().setBox(
+                        Box.newBuilder()
+                                .setWidth(expand())
+                                .setHeight(expand())
+                                .addContents(textElement)).build();
+
+        FrameLayout rootLayout = renderer(fingerprintedLayout(root)).inflate();
+        ViewGroup firstChild = (ViewGroup) rootLayout.getChildAt(0);
+        TextView tv = (TextView) firstChild.getChildAt(0);
+
+        // TextView sorts preset sizes.
+        Arrays.sort(presetSizes);
+        expect.that(tv.getAutoSizeTextType()).isEqualTo(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+        expect.that(tv.getAutoSizeTextAvailableSizes()).isEqualTo(presetSizes);
+        expect.that(tv.getTextSize()).isEqualTo(20);
+    }
+
+    @Test
+    public void inflate_textView_autosize_setLimit_usesSingleSize() {
+        String text = "Test text";
+        int sizesLength = TEXT_AUTOSIZES_LIMIT + 5;
+        int[] presetSizes = new int[sizesLength];
+        int expectedLastSize = 120;
+        for (int i = 0; i < sizesLength - 1; i++) {
+            presetSizes[i] = i + 1;
+        }
+        presetSizes[sizesLength - 1] = expectedLastSize;
+        List<DimensionProto.SpProp> sizes = buildSizesList(presetSizes);
+
+        LayoutElement textElement =
+                LayoutElement.newBuilder()
+                        .setText(
+                                Text.newBuilder()
+                                        .setText(string(text))
+                                        .setMaxLines(Int32Prop.newBuilder().setValue(4))
+                                        .setFontStyle(
+                                                FontStyle.newBuilder()
+                                                        .addAllSize(sizes)))
+                        .build();
+        LayoutElement root =
+                LayoutElement.newBuilder().setBox(
+                        Box.newBuilder()
+                                .setWidth(expand())
+                                .setHeight(expand())
+                                .addContents(textElement)).build();
+
+        FrameLayout rootLayout = renderer(fingerprintedLayout(root)).inflate();
+        ViewGroup firstChild = (ViewGroup) rootLayout.getChildAt(0);
+        TextView tv = (TextView) firstChild.getChildAt(0);
+        expect.that(tv.getAutoSizeTextType()).isEqualTo(TextView.AUTO_SIZE_TEXT_TYPE_NONE);
+        expect.that(tv.getAutoSizeTextAvailableSizes()).isEmpty();
+        expect.that(tv.getTextSize()).isEqualTo(expectedLastSize);
+    }
+
+    @Test
+    public void inflate_textView_autosize_notSet() {
+        String text = "Test text";
+        int size = 24;
+        List<DimensionProto.SpProp> sizes = buildSizesList(new int[]{size});
+
+        LayoutElement textElement =
+                LayoutElement.newBuilder()
+                        .setText(
+                                Text.newBuilder()
+                                        .setText(string(text))
+                                        .setFontStyle(
+                                                FontStyle.newBuilder()
+                                                        .addAllSize(sizes)))
+                        .build();
+        LayoutElement root =
+                LayoutElement.newBuilder().setBox(
+                        Box.newBuilder()
+                                .setWidth(expand())
+                                .setHeight(expand())
+                                .addContents(textElement)).build();
+
+        FrameLayout rootLayout = renderer(fingerprintedLayout(root)).inflate();
+        ViewGroup firstChild = (ViewGroup) rootLayout.getChildAt(0);
+        TextView tv = (TextView) firstChild.getChildAt(0);
+        expect.that(tv.getAutoSizeTextType()).isEqualTo(TextView.AUTO_SIZE_TEXT_TYPE_NONE);
+        expect.that(tv.getAutoSizeTextAvailableSizes()).isEmpty();
+        expect.that(tv.getTextSize()).isEqualTo(size);
+    }
+
+    @Test
+    public void inflate_textView_autosize_setDynamic_noop() {
+        String text = "Test text";
+        int lastSize = 24;
+        List<DimensionProto.SpProp> sizes = buildSizesList(new int[]{10, 30, lastSize});
+
+        LayoutElement textElement =
+                LayoutElement.newBuilder()
+                        .setText(
+                                Text.newBuilder()
+                                        .setText(dynamicString(text))
+                                        .setFontStyle(
+                                                FontStyle.newBuilder()
+                                                        .addAllSize(sizes)))
+                        .build();
+        LayoutElement root =
+                LayoutElement.newBuilder().setBox(
+                        Box.newBuilder()
+                                .setWidth(expand())
+                                .setHeight(expand())
+                                .addContents(textElement)).build();
+
+        FrameLayout rootLayout = renderer(fingerprintedLayout(root)).inflate();
+        ArrayList<View> textChildren = new ArrayList<>();
+        rootLayout.findViewsWithText(textChildren, text, View.FIND_VIEWS_WITH_TEXT);
+        TextView tv = (TextView) textChildren.get(0);
+        expect.that(tv.getAutoSizeTextType()).isEqualTo(TextView.AUTO_SIZE_TEXT_TYPE_NONE);
+        expect.that(tv.getAutoSizeTextAvailableSizes()).isEmpty();
+        expect.that(tv.getTextSize()).isEqualTo(lastSize);
+    }
+
+    @Test
     public void inflate_spannable_marqueeAnimation() {
         String text = "Marquee Animation";
         LayoutElement root =
@@ -1964,6 +2124,29 @@
     }
 
     @Test
+    public void inflate_spantext_ignoresMultipleSizes() {
+        String text = "Test text";
+        int firstSize = 12;
+        FontStyle.Builder style = FontStyle.newBuilder()
+                .addAllSize(buildSizesList(new int[]{firstSize, 10, 20}));
+        LayoutElement root =
+                LayoutElement.newBuilder()
+                        .setSpannable(
+                                Spannable.newBuilder()
+                                        .addSpans(
+                                                Span.newBuilder()
+                                                        .setText(
+                                                                SpanText.newBuilder()
+                                                                        .setText(string(text))
+                                                                        .setFontStyle(style))))
+                        .build();
+
+        FrameLayout rootLayout = renderer(fingerprintedLayout(root)).inflate();
+        TextView tv = (TextView) rootLayout.getChildAt(0);
+        expect.that(tv.getAutoSizeTextType()).isEqualTo(TextView.AUTO_SIZE_TEXT_TYPE_NONE);
+    }
+
+    @Test
     public void inflate_spannable_marqueeAnimation_repeatLimit() {
         String text = "Marquee Animation";
         int marqueeIterations = 5;
@@ -4452,6 +4635,11 @@
     }
 
     @NonNull
+    private static DimensionProto.SpProp sp(float value) {
+        return DimensionProto.SpProp.newBuilder().setValue(value).build();
+    }
+
+    @NonNull
     private static ContainerDimension.Builder expand() {
         return ContainerDimension.newBuilder()
                 .setExpandedDimension(ExpandedDimensionProp.getDefaultInstance());
@@ -4480,8 +4668,33 @@
     }
 
     @NonNull
+    private static StringProp.Builder dynamicString(String value) {
+        return StringProp.newBuilder()
+                .setValue(value)
+                .setDynamicValue(
+                        DynamicString.newBuilder()
+                                .setFixed(FixedString.newBuilder().setValue(value)));
+    }
+
+    @NonNull
     private static ImageDimension.Builder expandImage() {
         return ImageDimension.newBuilder()
                 .setExpandedDimension(ExpandedDimensionProp.getDefaultInstance());
     }
+
+    @NonNull
+    private static List<DimensionProto.SpProp> buildSizesList(int[] presetSizes) {
+        List<DimensionProto.SpProp> sizes = new ArrayList<>(3);
+        for (int s: presetSizes) {
+            sizes.add(sp(s));
+        }
+        return sizes;
+    }
+
+    private static float toPx(int spValue) {
+        return TypedValue.applyDimension(
+                COMPLEX_UNIT_SP,
+                spValue,
+                getApplicationContext().getResources().getDisplayMetrics());
+    }
 }
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index 180def5..72af032 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -553,6 +553,7 @@
     method public androidx.wear.protolayout.TypeBuilders.BoolProp? getItalic();
     method public androidx.wear.protolayout.DimensionBuilders.EmProp? getLetterSpacing();
     method public androidx.wear.protolayout.DimensionBuilders.SpProp? getSize();
+    method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public java.util.List<androidx.wear.protolayout.DimensionBuilders.SpProp!> getSizes();
     method public androidx.wear.protolayout.TypeBuilders.BoolProp? getUnderline();
     method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp? getVariant();
     method public androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp? getWeight();
@@ -566,6 +567,7 @@
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setItalic(boolean);
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setLetterSpacing(androidx.wear.protolayout.DimensionBuilders.EmProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setSize(androidx.wear.protolayout.DimensionBuilders.SpProp);
+    method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setSizes(androidx.wear.protolayout.DimensionBuilders.SpProp!...);
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setUnderline(androidx.wear.protolayout.TypeBuilders.BoolProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setUnderline(boolean);
     method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setVariant(androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp);
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index 180def5..72af032 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -553,6 +553,7 @@
     method public androidx.wear.protolayout.TypeBuilders.BoolProp? getItalic();
     method public androidx.wear.protolayout.DimensionBuilders.EmProp? getLetterSpacing();
     method public androidx.wear.protolayout.DimensionBuilders.SpProp? getSize();
+    method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public java.util.List<androidx.wear.protolayout.DimensionBuilders.SpProp!> getSizes();
     method public androidx.wear.protolayout.TypeBuilders.BoolProp? getUnderline();
     method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp? getVariant();
     method public androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp? getWeight();
@@ -566,6 +567,7 @@
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setItalic(boolean);
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setLetterSpacing(androidx.wear.protolayout.DimensionBuilders.EmProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setSize(androidx.wear.protolayout.DimensionBuilders.SpProp);
+    method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setSizes(androidx.wear.protolayout.DimensionBuilders.SpProp!...);
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setUnderline(androidx.wear.protolayout.TypeBuilders.BoolProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setUnderline(boolean);
     method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setVariant(androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp);
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
index 80c73df..1cf6bce 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
@@ -27,6 +27,7 @@
 import androidx.annotation.OptIn;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.annotation.VisibleForTesting;
 import androidx.wear.protolayout.ColorBuilders.Brush;
 import androidx.wear.protolayout.ColorBuilders.ColorProp;
 import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
@@ -608,21 +609,6 @@
         }
 
         /**
-         * Gets the size of the font, in scaled pixels (sp). If not specified, defaults to the size
-         * of the system's "body" font.
-         *
-         * @since 1.0
-         */
-        @Nullable
-        public SpProp getSize() {
-            if (mImpl.hasSize()) {
-                return SpProp.fromProto(mImpl.getSize());
-            } else {
-                return null;
-            }
-        }
-
-        /**
          * Gets whether the text should be rendered in a italic typeface. If not specified, defaults
          * to "false".
          *
@@ -713,6 +699,35 @@
             }
         }
 
+        /**
+         * Gets the size of the font, in scaled pixels (sp). If not specified, defaults to the size
+         * of the system's "body" font. If more than one size was originally added, it will
+         * return the last one.
+         *
+         * @since 1.0
+         */
+        @Nullable
+        public SpProp getSize() {
+            List<DimensionProto.SpProp> sizes = mImpl.getSizeList();
+            return !sizes.isEmpty() ? SpProp.fromProto(sizes.get(sizes.size() - 1)) : null;
+        }
+
+        /**
+         * Gets the available sizes of the font, in scaled pixels (sp). If not specified, defaults
+         * to the size of the system's "body" font.
+         *
+         * @since 1.3
+         */
+        @NonNull
+        @ProtoLayoutExperimental
+        public List<SpProp> getSizes() {
+            List<SpProp> list = new ArrayList<>();
+            for (DimensionProto.SpProp item : mImpl.getSizeList()) {
+                list.add(SpProp.fromProto(item));
+            }
+            return Collections.unmodifiableList(list);
+        }
+
         /** Get the fingerprint for this object, or null if unknown. */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Nullable
@@ -764,6 +779,7 @@
 
         /** Builder for {@link FontStyle} */
         public static final class Builder {
+            @VisibleForTesting static final int TEXT_SIZES_LIMIT = 10;
             private final LayoutElementProto.FontStyle.Builder mImpl =
                     LayoutElementProto.FontStyle.newBuilder();
             private final Fingerprint mFingerprint = new Fingerprint(-374492482);
@@ -771,16 +787,48 @@
             public Builder() {}
 
             /**
-             * Sets the size of the font, in scaled pixels (sp). If not specified, defaults to the
-             * size of the system's "body" font.
+             * Sets the available sizes of the font, in scaled  pixels (sp). If not specified,
+             * defaults to the size of the system's "body" font.
              *
-             * @since 1.0
+             * <p>If more than one size is specified and this {@link FontStyle} is applied to a
+             * {@link Text} element with static text, the text size will be automatically picked
+             * from the provided sizes to try to perfectly fit within its parent bounds. In other
+             * words, the largest size from the specified preset sizes that can fit the most text
+             * within the parent bounds will be used.
+             *
+             * <p>The specified sizes don't have to be sorted. The maximum number of sizes used is
+             * limited to 10.
+             *
+             * <p>Note that, if multiple sizes are set, the parent of the {@link Text} element this
+             * corresponds to shouldn't have its width and height set to wrapped, as it can lead to
+             * unexpected results.
+             *
+             * <p>If this {@link FontStyle} is set to any other element besides {@link Text} or
+             * that {@link Text} element has dynamic field, only the last added size will be use.
+             *
+             * <p>Any previously added values with this method or {@link #setSize} will be cleared.
+             *
+             * <p>While this field is accessible from 1.0 as singular, it only accepts multiple
+             * values since version 1.3 and renderers supporting version 1.3 will use the multiple
+             * values to automatically scale text. Renderers who don't support this version will
+             * use the last size among multiple values.
+             *
+             * @throws IllegalArgumentException if the number of available sizes is larger than 10.
+             * @since 1.3
              */
             @NonNull
-            public Builder setSize(@NonNull SpProp size) {
-                mImpl.setSize(size.toProto());
-                mFingerprint.recordPropertyUpdate(
-                        1, checkNotNull(size.getFingerprint()).aggregateValueAsInt());
+            @ProtoLayoutExperimental
+            public Builder setSizes(@NonNull SpProp... sizes) {
+                if (sizes.length > TEXT_SIZES_LIMIT) {
+                    throw new IllegalArgumentException(
+                            "Number of available sizes can't be larger than 10.");
+                }
+                mImpl.clearSize();
+                for (SpProp size: sizes) {
+                    mImpl.addSize(size.toProto());
+                    mFingerprint.recordPropertyUpdate(
+                            1, checkNotNull(size.getFingerprint()).aggregateValueAsInt());
+                }
                 return this;
             }
 
@@ -830,6 +878,23 @@
             }
 
             /**
+             * Sets the size of the font, in scaled pixels (sp). If not specified, defaults to the
+             * size of the system's "body" font.
+             *
+             * <p>Any previously added values with this method or {@link #setSizes} will be cleared.
+             *
+             * @since 1.0
+             */
+            @NonNull
+            public Builder setSize(@NonNull SpProp size) {
+                mImpl.clearSize();
+                mImpl.addSize(size.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        1, checkNotNull(size.getFingerprint()).aggregateValueAsInt());
+                return this;
+            }
+
+            /**
              * Sets the text color. If not defined, defaults to white.
              *
              * <p>While this field is statically accessible from 1.0, it's only bindable since
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
index a30c860..6f10e5a 100644
--- a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
@@ -16,6 +16,8 @@
 
 package androidx.wear.protolayout;
 
+import static androidx.wear.protolayout.DimensionBuilders.sp;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
@@ -203,6 +205,84 @@
     }
 
     @Test
+    public void testFontStyleSetMultipleSizes() {
+        int size1 = 12;
+        int size2 = 30;
+        int lastSize = 20;
+        int[] expectedSizes = {size1, size2, lastSize};
+        LayoutElementBuilders.FontStyle fontStyle =
+                new LayoutElementBuilders.FontStyle.Builder()
+                        .setSizes(sp(size1), sp(size2), sp(lastSize))
+                        .build();
+
+        LayoutElementProto.FontStyle fontStyleProto = fontStyle.toProto();
+
+        assertThat(
+                fontStyleProto.getSizeList().stream().mapToInt(sp -> (int) sp.getValue()).toArray())
+                .isEqualTo(expectedSizes);
+        // Make sure that if 1 size is used than it's the last one.
+        assertThat(fontStyle.getSize().getValue()).isEqualTo(lastSize);
+        assertThat(
+                fontStyle.getSizes().stream().mapToInt(sp -> (int) sp.getValue()).toArray())
+                .isEqualTo(expectedSizes);
+    }
+
+    @Test
+    public void testFontStyleSetSize_moreTimes_usesLastOne() {
+        int lastSize = 20;
+        LayoutElementBuilders.FontStyle fontStyle =
+                new LayoutElementBuilders.FontStyle.Builder()
+                        .setSize(sp(12))
+                        .setSize(sp(30))
+                        .setSize(sp(lastSize))
+                        .build();
+
+        LayoutElementProto.FontStyle fontStyleProto = fontStyle.toProto();
+
+        assertThat(fontStyleProto.getSizeList().size()).isEqualTo(1);
+        assertThat(fontStyleProto.getSizeList().get(0).getValue()).isEqualTo(lastSize);
+        // Make sure that if 1 size is used than it's the last one.
+        assertThat(fontStyle.getSize().getValue()).isEqualTo(lastSize);
+        assertThat(fontStyle.getSizes().size()).isEqualTo(1);
+        assertThat(fontStyle.getSizes().get(0).getValue()).isEqualTo(lastSize);
+    }
+
+    @Test
+    public void testFontStyleSetSize_setSizes_overrides() {
+        int size1 = 12;
+        int size2 = 30;
+        int[] expectedSizes = {size1, size2};
+        LayoutElementBuilders.FontStyle fontStyle =
+                new LayoutElementBuilders.FontStyle.Builder()
+                        .setSize(sp(20))
+                        .setSizes(sp(size1), sp(size2))
+                        .build();
+
+        LayoutElementProto.FontStyle fontStyleProto = fontStyle.toProto();
+
+        assertThat(
+                fontStyleProto.getSizeList().stream().mapToInt(sp -> (int) sp.getValue()).toArray())
+                .isEqualTo(expectedSizes);
+        // Make sure that if 1 size is used than it's the last one.
+        assertThat(fontStyle.getSize().getValue()).isEqualTo(size2);
+        assertThat(
+                fontStyle.getSizes().stream().mapToInt(sp -> (int) sp.getValue()).toArray())
+                .isEqualTo(expectedSizes);
+    }
+
+    @Test
+    public void testFontStyleSetSize_tooManySizes_throws() {
+        DimensionBuilders.SpProp[] sizes =
+                new DimensionBuilders.SpProp[
+                        LayoutElementBuilders.FontStyle.Builder.TEXT_SIZES_LIMIT + 1];
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> new LayoutElementBuilders.FontStyle.Builder()
+                        .setSizes(sizes)
+                        .build());
+    }
+
+    @Test
     public void textSetText_withoutLayoutConstraint_throws() {
         assertThrows(
                 IllegalStateException.class,
diff --git a/wear/tiles/tiles/lint-baseline.xml b/wear/tiles/tiles/lint-baseline.xml
index eb4c7b5..faa4a6b 100644
--- a/wear/tiles/tiles/lint-baseline.xml
+++ b/wear/tiles/tiles/lint-baseline.xml
@@ -1803,18 +1803,9 @@
 
     <issue
         id="RestrictedApiAndroidX"
-        message="FontStyle.hasSize can only be called from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
-        errorLine1="            if (mImpl.hasSize()) {"
-        errorLine2="                      ~~~~~~~">
-        <location
-            file="src/main/java/androidx/wear/tiles/LayoutElementBuilders.java"/>
-    </issue>
-
-    <issue
-        id="RestrictedApiAndroidX"
-        message="FontStyle.getSize can only be called from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
-        errorLine1="                return DimensionBuilders.SpProp.fromProto(mImpl.getSize());"
-        errorLine2="                                                                ~~~~~~~">
+        message="FontStyle.getSizeList can only be called from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
+        errorLine1="            List&lt;DimensionProto.SpProp> sizes = mImpl.getSizeList();"
+        errorLine2="                                                      ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/wear/tiles/LayoutElementBuilders.java"/>
     </issue>
@@ -1947,8 +1938,17 @@
 
     <issue
         id="RestrictedApiAndroidX"
-        message="Builder.setSize can only be called from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
-        errorLine1="                mImpl.setSize(size.toProto());"
+        message="Builder.clearSize can only be called from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
+        errorLine1="                mImpl.clearSize();"
+        errorLine2="                      ~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/wear/tiles/LayoutElementBuilders.java"/>
+    </issue>
+
+    <issue
+        id="RestrictedApiAndroidX"
+        message="Builder.addSize can only be called from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
+        errorLine1="                mImpl.addSize(size.toProto());"
         errorLine2="                      ~~~~~~~">
         <location
             file="src/main/java/androidx/wear/tiles/LayoutElementBuilders.java"/>
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/LayoutElementBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/LayoutElementBuilders.java
index a089888..259bddf 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/LayoutElementBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/LayoutElementBuilders.java
@@ -31,6 +31,7 @@
 import androidx.annotation.RestrictTo.Scope;
 import androidx.wear.protolayout.expression.Fingerprint;
 import androidx.wear.protolayout.proto.AlignmentProto;
+import androidx.wear.protolayout.proto.DimensionProto;
 import androidx.wear.protolayout.proto.FingerprintProto;
 import androidx.wear.protolayout.proto.FingerprintProto.TreeFingerprint;
 import androidx.wear.protolayout.proto.LayoutElementProto;
@@ -622,8 +623,9 @@
          */
         @Nullable
         public DimensionBuilders.SpProp getSize() {
-            if (mImpl.hasSize()) {
-                return DimensionBuilders.SpProp.fromProto(mImpl.getSize());
+            List<DimensionProto.SpProp> sizes = mImpl.getSizeList();
+            if (!sizes.isEmpty()) {
+                return DimensionBuilders.SpProp.fromProto(sizes.get(0));
             } else {
                 return null;
             }
@@ -744,7 +746,8 @@
              */
             @NonNull
             public Builder setSize(@NonNull DimensionBuilders.SpProp size) {
-                mImpl.setSize(size.toProto());
+                mImpl.clearSize();
+                mImpl.addSize(size.toProto());
                 mFingerprint.recordPropertyUpdate(
                         1, checkNotNull(size.getFingerprint()).aggregateValueAsInt());
                 return this;