Fix TabLayout crashing due to using getMaxLines()

Added a compatible shim to TextViewCompat

BUG: 24663522
Change-Id: I6ba367338a6c42ecad175c5ebe0163c3dc5a9cd2
diff --git a/design/src/android/support/design/widget/TabLayout.java b/design/src/android/support/design/widget/TabLayout.java
index afcd3b7e..2410a32 100755
--- a/design/src/android/support/design/widget/TabLayout.java
+++ b/design/src/android/support/design/widget/TabLayout.java
@@ -37,6 +37,7 @@
 import android.support.v4.view.PagerAdapter;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewPager;
+import android.support.v4.widget.TextViewCompat;
 import android.support.v7.app.ActionBar;
 import android.support.v7.internal.widget.TintManager;
 import android.text.Layout;
@@ -1246,8 +1247,9 @@
 
                 final float curTextSize = mTextView.getTextSize();
                 final int curLineCount = mTextView.getLineCount();
+                final int curMaxLines = TextViewCompat.getMaxLines(mTextView);
 
-                if (textSize != curTextSize || maxLines != mTextView.getMaxLines()) {
+                if (textSize != curTextSize || (curMaxLines >= 0 && maxLines != curMaxLines)) {
                     // We've got a new text size and/or max lines...
                     boolean updateTextView = true;
 
@@ -1294,7 +1296,7 @@
 
                 mCustomTextView = (TextView) custom.findViewById(android.R.id.text1);
                 if (mCustomTextView != null) {
-                    mDefaultMaxLines = mCustomTextView.getMaxLines();
+                    mDefaultMaxLines = TextViewCompat.getMaxLines(mCustomTextView);
                 }
                 mCustomIconView = (ImageView) custom.findViewById(android.R.id.icon);
             } else {
@@ -1320,7 +1322,7 @@
                             .inflate(R.layout.design_layout_tab_text, this, false);
                     addView(textView);
                     mTextView = textView;
-                    mDefaultMaxLines = mTextView.getMaxLines();
+                    mDefaultMaxLines = TextViewCompat.getMaxLines(mTextView);
                 }
                 mTextView.setTextAppearance(getContext(), mTabTextAppearance);
                 if (mTabTextColors != null) {
diff --git a/v4/api/current.txt b/v4/api/current.txt
index 861e887..ccefe22 100644
--- a/v4/api/current.txt
+++ b/v4/api/current.txt
@@ -3449,6 +3449,8 @@
   }
 
   public class TextViewCompat {
+    method public static int getMaxLines(android.widget.TextView);
+    method public static int getMinLines(android.widget.TextView);
     method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
     method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
     method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, int, int, int, int);
diff --git a/v4/java/android/support/v4/widget/TextViewCompat.java b/v4/java/android/support/v4/widget/TextViewCompat.java
index c31196a..d8c5385 100644
--- a/v4/java/android/support/v4/widget/TextViewCompat.java
+++ b/v4/java/android/support/v4/widget/TextViewCompat.java
@@ -29,26 +29,22 @@
 public class TextViewCompat {
 
     // Hide constructor
-    private TextViewCompat() {
-    }
+    private TextViewCompat() {}
 
     interface TextViewCompatImpl {
-
-        public void setCompoundDrawablesRelative(@NonNull TextView textView,
+        void setCompoundDrawablesRelative(@NonNull TextView textView,
                 @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
                 @Nullable Drawable bottom);
-
-        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+        void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
                 @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
                 @Nullable Drawable bottom);
-
-        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+        void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
                 int start, int top, int end, int bottom);
-
+        int getMaxLines(TextView textView);
+        int getMinLines(TextView textView);
     }
 
     static class BaseTextViewCompatImpl implements TextViewCompatImpl {
-
         @Override
         public void setCompoundDrawablesRelative(@NonNull TextView textView,
                 @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
@@ -69,11 +65,31 @@
             textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
         }
 
+        @Override
+        public int getMaxLines(TextView textView) {
+            return TextViewCompatDonut.getMaxLines(textView);
+        }
+
+        @Override
+        public int getMinLines(TextView textView) {
+            return TextViewCompatDonut.getMinLines(textView);
+        }
     }
 
-    static class JbMr1TextViewCompatImpl extends BaseTextViewCompatImpl {
+    static class JbTextViewCompatImpl extends BaseTextViewCompatImpl {
+        @Override
+        public int getMaxLines(TextView textView) {
+            return TextViewCompatJb.getMaxLines(textView);
+        }
 
         @Override
+        public int getMinLines(TextView textView) {
+            return TextViewCompatJb.getMinLines(textView);
+        }
+    }
+
+    static class JbMr1TextViewCompatImpl extends JbTextViewCompatImpl {
+        @Override
         public void setCompoundDrawablesRelative(@NonNull TextView textView,
                 @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
                 @Nullable Drawable bottom) {
@@ -94,11 +110,9 @@
             TextViewCompatJbMr1.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
                     start, top, end, bottom);
         }
-
     }
 
     static class JbMr2TextViewCompatImpl extends JbMr1TextViewCompatImpl {
-
         @Override
         public void setCompoundDrawablesRelative(@NonNull TextView textView,
                 @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
@@ -131,6 +145,8 @@
             IMPL = new JbMr2TextViewCompatImpl();
         } else if (version >= 17) {
             IMPL = new JbMr1TextViewCompatImpl();
+        } else if (version >= 16) {
+            IMPL = new JbTextViewCompatImpl();
         } else {
             IMPL = new BaseTextViewCompatImpl();
         }
@@ -200,4 +216,19 @@
         IMPL.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, start, top, end, bottom);
     }
 
+    /**
+     * Returns the maximum number of lines displayed in the given TextView, or -1 if the maximum
+     * height was set in pixels instead.
+     */
+    public static int getMaxLines(@NonNull TextView textView) {
+        return IMPL.getMaxLines(textView);
+    }
+
+    /**
+     * Returns the minimum number of lines displayed in the given TextView, or -1 if the minimum
+     * height was set in pixels instead.
+     */
+    public static int getMinLines(@NonNull TextView textView) {
+        return IMPL.getMinLines(textView);
+    }
 }
diff --git a/v4/java/android/support/v4/widget/TextViewCompatDonut.java b/v4/java/android/support/v4/widget/TextViewCompatDonut.java
new file mode 100644
index 0000000..9a36e82
--- /dev/null
+++ b/v4/java/android/support/v4/widget/TextViewCompatDonut.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.widget;
+
+import android.util.Log;
+import android.widget.TextView;
+
+import java.lang.reflect.Field;
+
+class TextViewCompatDonut {
+
+    private static final String LOG_TAG = "TextViewCompatDonut";
+    private static final int LINES = 1;
+
+    private static Field sMaximumField;
+    private static boolean sMaximumFieldFetched;
+    private static Field sMaxModeField;
+    private static boolean sMaxModeFieldFetched;
+
+    private static Field sMinimumField;
+    private static boolean sMinimumFieldFetched;
+    private static Field sMinModeField;
+    private static boolean sMinModeFieldFetched;
+
+    static int getMaxLines(TextView textView) {
+        if (!sMaxModeFieldFetched) {
+            sMaxModeField = retrieveField("mMaxMode");
+            sMaxModeFieldFetched = true;
+        }
+        if (sMaxModeField != null && retrieveIntFromField(sMaxModeField, textView) == LINES) {
+            // If the max mode is using lines, we can grab the maximum value
+            if (!sMaximumFieldFetched) {
+                sMaximumField = retrieveField("mMaximum");
+                sMaximumFieldFetched = true;
+            }
+            if (sMaximumField != null) {
+                return retrieveIntFromField(sMaximumField, textView);
+            }
+        }
+        return -1;
+    }
+
+    static int getMinLines(TextView textView) {
+        if (!sMinModeFieldFetched) {
+            sMinModeField = retrieveField("mMinMode");
+            sMinModeFieldFetched = true;
+        }
+        if (sMinModeField != null && retrieveIntFromField(sMinModeField, textView) == LINES) {
+            // If the min mode is using lines, we can grab the maximum value
+            if (!sMinimumFieldFetched) {
+                sMinimumField = retrieveField("mMinimum");
+                sMinimumFieldFetched = true;
+            }
+            if (sMinimumField != null) {
+                return retrieveIntFromField(sMinimumField, textView);
+            }
+        }
+        return -1;
+    }
+
+    private static Field retrieveField(String fieldName) {
+        Field field = null;
+        try {
+            field = TextView.class.getDeclaredField(fieldName);
+            field.setAccessible(true);
+        } catch (NoSuchFieldException e) {
+            Log.e(LOG_TAG, "Could not retrieve " + fieldName + " field.");
+        }
+        return field;
+    }
+
+    private static int retrieveIntFromField(Field field, TextView textView) {
+        try {
+            return field.getInt(textView);
+        } catch (IllegalAccessException e) {
+            Log.d(LOG_TAG, "Could not retrieve value of " + field.getName() + " field.");
+        }
+        return -1;
+    }
+}
diff --git a/v4/jellybean/android/support/v4/widget/TextViewCompatJb.java b/v4/jellybean/android/support/v4/widget/TextViewCompatJb.java
new file mode 100644
index 0000000..1658874
--- /dev/null
+++ b/v4/jellybean/android/support/v4/widget/TextViewCompatJb.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.widget;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.TextView;
+
+class TextViewCompatJb {
+    static int getMaxLines(TextView textView) {
+        return textView.getMaxLines();
+    }
+
+    static int getMinLines(TextView textView) {
+        return textView.getMinLines();
+    }
+}