Inspect preset sizes for Autosizing

TextView will throw if there are no specified sizes or all off them
are 0 or negative.

Bug: 302531969
Test: Added
Change-Id: Ie700ca4fc6159697d091d91a8497a09c04d5bd4c
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 cf3b9b5..be6c6ca 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
@@ -1168,16 +1168,32 @@
                 // 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);
+                // We need to check values so that we are certain that there's at least 1 non zero
+                // value.
+                boolean atLeastOneCorrectSize =
+                        sizes.stream()
+                                .mapToInt(sp -> (int) sp.getValue())
+                                .filter(sp -> sp > 0)
+                                .distinct()
+                                .count()
+                                > 0;
 
-                // No need for sorting, TextView does that.
-                textView.setAutoSizeTextTypeUniformWithPresetSizes(
-                        sizes.stream().mapToInt(spProp -> (int) spProp.getValue()).toArray(),
-                        COMPLEX_UNIT_SP);
+                if (atLeastOneCorrectSize) {
+                    // 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 {
+                    Log.w(
+                            TAG,
+                            "Trying to autosize text but no valid font sizes has been specified.");
+                }
             } else {
                 // Fallback where multiple values can't be used and the last value would be used.
                 // This can happen in two cases.
@@ -1191,8 +1207,8 @@
                     Log.w(
                             TAG,
                             "More than " + TEXT_AUTOSIZES_LIMIT + " sizes has been added for the "
-                                    + "text autosizing. Ignoring values after the "
-                                    + TEXT_AUTOSIZES_LIMIT + " one.");
+                                    + "text autosizing. Ignoring all other sizes and using the last"
+                                    + "one.");
                 }
 
                 textView.setTextSize(COMPLEX_UNIT_SP, sizes.get(sizesCnt - 1).getValue());
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 cb9e6d6..c6a3d9c 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
@@ -2092,6 +2092,35 @@
     }
 
     @Test
+    public void inflate_textView_autosize_wrongSizes_noop() {
+        String text = "Test text";
+        List<DimensionProto.SpProp> sizes = buildSizesList(new int[]{0, -2, 0});
+
+        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();
+        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();
+    }
+
+    @Test
     public void inflate_spannable_marqueeAnimation() {
         String text = "Marquee Animation";
         LayoutElement root =
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 1cf6bce..19707de 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
@@ -700,9 +700,8 @@
         }
 
         /**
-         * 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.
+         * Gets the size of the font, in scaled pixels (sp). If more than one size was originally
+         * added, it will return the last one.
          *
          * @since 1.0
          */
@@ -713,8 +712,7 @@
         }
 
         /**
-         * Gets the available sizes of the font, in scaled pixels (sp). If not specified, defaults
-         * to the size of the system's "body" font.
+         * Gets the available sizes of the font, in scaled pixels (sp).
          *
          * @since 1.3
          */
@@ -787,52 +785,6 @@
             public Builder() {}
 
             /**
-             * Sets the available sizes of the font, in scaled  pixels (sp). If not specified,
-             * defaults to the size of the system's "body" font.
-             *
-             * <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
-            @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;
-            }
-
-            /**
              * Sets whether the text should be rendered in a italic typeface. If not specified,
              * defaults to "false".
              *
@@ -878,23 +830,6 @@
             }
 
             /**
-             * 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
@@ -982,6 +917,76 @@
                 return this;
             }
 
+            /**
+             * Sets the available sizes of the font, in scaled  pixels (sp). If not specified,
+             * defaults to the size of the system's "body" font.
+             *
+             * <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, but they need to contain at least
+             * one positive value. 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
+            @ProtoLayoutExperimental
+            public Builder setSizes(@NonNull SpProp... sizes) {
+                if (sizes.length > TEXT_SIZES_LIMIT) {
+                    throw new IllegalArgumentException(
+                            "Number of available sizes of the font style can't be larger than 10.");
+                }
+
+                mImpl.clearSize();
+                for (SpProp size: sizes) {
+                    if (size.getValue() <= 0) {
+                        throw new IllegalArgumentException(
+                                "Available sizes of the font style must contain only positive "
+                                        + "value.");
+                    }
+
+                    mImpl.addSize(size.toProto());
+                    mFingerprint.recordPropertyUpdate(
+                            1, checkNotNull(size.getFingerprint()).aggregateValueAsInt());
+                }
+                return this;
+            }
+
+            /**
+             * 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;
+            }
+
             /** Builds an instance from accumulated values. */
             @NonNull
             public FontStyle build() {
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 6f10e5a..370a8bd 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
@@ -239,11 +239,11 @@
 
         LayoutElementProto.FontStyle fontStyleProto = fontStyle.toProto();
 
-        assertThat(fontStyleProto.getSizeList().size()).isEqualTo(1);
+        assertThat(fontStyleProto.getSizeList()).hasSize(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(fontStyleProto.getSizeList()).hasSize(1);
         assertThat(fontStyle.getSizes().get(0).getValue()).isEqualTo(lastSize);
     }
 
@@ -283,6 +283,15 @@
     }
 
     @Test
+    public void testFontStyleSetSize_allNegativeOrZero_throws() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> new LayoutElementBuilders.FontStyle.Builder()
+                        .setSizes(sp(-1), sp(0))
+                        .build());
+    }
+
+    @Test
     public void textSetText_withoutLayoutConstraint_throws() {
         assertThrows(
                 IllegalStateException.class,