Merge "Support rotate method in ExifInterface" into oc-support-26.0-dev
diff --git a/api/26.0.0-SNAPSHOT.txt b/api/26.0.0-SNAPSHOT.txt
index dd7e68b..8182291 100644
--- a/api/26.0.0-SNAPSHOT.txt
+++ b/api/26.0.0-SNAPSHOT.txt
@@ -7544,6 +7544,7 @@
     method public abstract void sendCustomAction(java.lang.String, android.os.Bundle);
     method public abstract void setCaptioningEnabled(boolean);
     method public abstract void setRating(android.support.v4.media.RatingCompat);
+    method public abstract void setRating(android.support.v4.media.RatingCompat, android.os.Bundle);
     method public abstract void setRepeatMode(int);
     method public abstract void setShuffleMode(int);
     method public abstract deprecated void setShuffleModeEnabled(boolean);
@@ -7585,12 +7586,12 @@
     method public void setSessionActivity(android.app.PendingIntent);
     method public void setShuffleMode(int);
     method public deprecated void setShuffleModeEnabled(boolean);
-    field public static final java.lang.String ACTION_ARGUMENT_MEDIA_ATTRIBUTE = "android.support.v4.media.session.action.ARGUMENT_MEDIA_ATTRIBUTE";
-    field public static final java.lang.String ACTION_ARGUMENT_MEDIA_ATTRIBUTE_VALUE = "android.support.v4.media.session.action.ARGUMENT_MEDIA_ATTRIBUTE_VALUE";
     field public static final java.lang.String ACTION_FLAG_AS_INAPPROPRIATE = "android.support.v4.media.session.action.FLAG_AS_INAPPROPRIATE";
     field public static final java.lang.String ACTION_FOLLOW = "android.support.v4.media.session.action.FOLLOW";
     field public static final java.lang.String ACTION_SKIP_AD = "android.support.v4.media.session.action.SKIP_AD";
     field public static final java.lang.String ACTION_UNFOLLOW = "android.support.v4.media.session.action.UNFOLLOW";
+    field public static final java.lang.String ARGUMENT_MEDIA_ATTRIBUTE = "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE";
+    field public static final java.lang.String ARGUMENT_MEDIA_ATTRIBUTE_VALUE = "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE_VALUE";
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
@@ -7622,6 +7623,7 @@
     method public void onSeekTo(long);
     method public void onSetCaptioningEnabled(boolean);
     method public void onSetRating(android.support.v4.media.RatingCompat);
+    method public void onSetRating(android.support.v4.media.RatingCompat, android.os.Bundle);
     method public void onSetRepeatMode(int);
     method public void onSetShuffleMode(int);
     method public deprecated void onSetShuffleModeEnabled(boolean);
diff --git a/buildSrc/dependencies.gradle b/buildSrc/dependencies.gradle
index a58b1d0..a00c139 100644
--- a/buildSrc/dependencies.gradle
+++ b/buildSrc/dependencies.gradle
@@ -33,7 +33,7 @@
     libs.gradle = 'com.android.tools.build:gradle:' + androidPluginVersionOverride
 } else {
     // Keep gradle plugin version in sync with ub_supportlib-master manifest.
-    libs.gradle = 'com.android.tools.build:gradle:3.0.0-alpha4'
+    libs.gradle = 'com.android.tools.build:gradle:3.0.0-alpha5'
 }
 
 // Other dependencies
diff --git a/compat/java/android/support/v4/app/JobIntentService.java b/compat/java/android/support/v4/app/JobIntentService.java
index b08c654..4c83eb9 100644
--- a/compat/java/android/support/v4/app/JobIntentService.java
+++ b/compat/java/android/support/v4/app/JobIntentService.java
@@ -309,6 +309,7 @@
                 work = mParams.dequeueWork();
             }
             if (work != null) {
+                work.getIntent().setExtrasClassLoader(mService.getClassLoader());
                 return new WrapperWorkItem(work);
             } else {
                 return null;
@@ -393,13 +394,13 @@
         }
 
         @Override
+        protected void onCancelled(Void aVoid) {
+            processorFinished();
+        }
+
+        @Override
         protected void onPostExecute(Void aVoid) {
-            if (mCompatQueue != null) {
-                synchronized (mCompatQueue) {
-                    mCurProcessor = null;
-                    checkForMoreCompatWorkLocked();
-                }
-            }
+            processorFinished();
         }
     }
 
@@ -602,11 +603,22 @@
         }
     }
 
-    void checkForMoreCompatWorkLocked() {
-        // The async task has finished, but we may have gotten more work scheduled in the
-        // meantime.  If so,
-        if (mCompatQueue != null && mCompatQueue.size() > 0) {
-            ensureProcessorRunningLocked();
+    void processorFinished() {
+        if (mCompatQueue != null) {
+            synchronized (mCompatQueue) {
+                mCurProcessor = null;
+                // The async task has finished, but we may have gotten more work scheduled in the
+                // meantime.  If so, we need to restart the new processor to execute it.  If there
+                // is no more work at this point, either the service is in the process of being
+                // destroyed (because we called stopSelf on the last intent started for it), or
+                // someone has already called startService with a new Intent that will be
+                // arriving shortly.  In either case, we want to just leave the service
+                // waiting -- either to get destroyed, or get a new onStartCommand() callback
+                // which will then kick off a new processor.
+                if (mCompatQueue != null && mCompatQueue.size() > 0) {
+                    ensureProcessorRunningLocked();
+                }
+            }
         }
     }
 
diff --git a/gradle.properties b/gradle.properties
index b8e17ca..e5750c9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,7 @@
 org.gradle.jvmargs=-Xmx4g
 org.gradle.daemon=true
-org.gradle.configureondemand=true
+# Disabled due to b/63329112
+# org.gradle.configureondemand=true
 org.gradle.parallel=true
 org.gradle.caching=true
 android.useDexArchive=false
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 15afcfd..2a386c4 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=../../../../tools/external/gradle/gradle-4.0-bin.zip
+distributionUrl=../../../../tools/external/gradle/gradle-4.1-milestone-1-bin.zip
diff --git a/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java b/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
index ad9727d..698e37d 100644
--- a/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
@@ -167,6 +167,7 @@
         void onStop();
         void onSeekTo(long position);
         void onSetRating(Object ratingObject);
+        void onSetRating(Object ratingObject, Bundle extras);
         void onCustomAction(String action, Bundle extras);
     }
 
diff --git a/media-compat/java/android/support/v4/media/session/IMediaSession.aidl b/media-compat/java/android/support/v4/media/session/IMediaSession.aidl
index 2ea696b..3926ac2 100644
--- a/media-compat/java/android/support/v4/media/session/IMediaSession.aidl
+++ b/media-compat/java/android/support/v4/media/session/IMediaSession.aidl
@@ -80,6 +80,7 @@
     void rewind() = 22;
     void seekTo(long pos) = 23;
     void rate(in RatingCompat rating) = 24;
+    void rateWithExtras(in RatingCompat rating, in Bundle extras) = 50;
     void setCaptioningEnabled(boolean enabled) = 45;
     void setRepeatMode(int repeatMode) = 38;
     void setShuffleModeEnabledDeprecated(boolean shuffleMode) = 39;
diff --git a/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java b/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
index cfe13b3..9024a87 100644
--- a/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -181,9 +181,9 @@
             case MediaSessionCompat.ACTION_FOLLOW:
             case MediaSessionCompat.ACTION_UNFOLLOW:
                 if (args == null
-                        || !args.containsKey(MediaSessionCompat.ACTION_ARGUMENT_MEDIA_ATTRIBUTE)) {
+                        || !args.containsKey(MediaSessionCompat.ARGUMENT_MEDIA_ATTRIBUTE)) {
                     throw new IllegalArgumentException("An extra field "
-                            + MediaSessionCompat.ACTION_ARGUMENT_MEDIA_ATTRIBUTE + " is required "
+                            + MediaSessionCompat.ARGUMENT_MEDIA_ATTRIBUTE + " is required "
                             + "for this action " + action + ".");
                 }
                 break;
@@ -1088,14 +1088,28 @@
 
         /**
          * Rates the current content. This will cause the rating to be set for
-         * the current user. The Rating type must match the type returned by
-         * {@link #getRatingType()}.
+         * the current user. The rating type of the given {@link RatingCompat} must match the type
+         * returned by {@link #getRatingType()}.
          *
          * @param rating The rating to set for the current content
          */
         public abstract void setRating(RatingCompat rating);
 
         /**
+         * Rates a media item. This will cause the rating to be set for
+         * the specific media item. The rating type of the given {@link RatingCompat} must match
+         * the type returned by {@link #getRatingType()}.
+         *
+         * @param rating The rating to set for the media item.
+         * @param extras Optional arguments that can include information about the media item
+         *               to be rated.
+         *
+         * @see MediaSessionCompat#ARGUMENT_MEDIA_ATTRIBUTE
+         * @see MediaSessionCompat#ARGUMENT_MEDIA_ATTRIBUTE_VALUE
+         */
+        public abstract void setRating(RatingCompat rating, Bundle extras);
+
+        /**
          * Enables/disables captioning for this session.
          *
          * @param enabled {@code true} to enable captioning, {@code false} to disable.
@@ -1721,6 +1735,15 @@
         }
 
         @Override
+        public void setRating(RatingCompat rating, Bundle extras) {
+            try {
+                mBinder.rateWithExtras(rating, extras);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in setRating.", e);
+            }
+        }
+
+        @Override
         public void setCaptioningEnabled(boolean enabled) {
             try {
                 mBinder.setCaptioningEnabled(enabled);
@@ -2269,6 +2292,14 @@
         }
 
         @Override
+        public void setRating(RatingCompat rating, Bundle extras) {
+            Bundle bundle = new Bundle();
+            bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_RATING, rating);
+            bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras);
+            sendCustomAction(MediaSessionCompat.ACTION_SET_RATING, bundle);
+        }
+
+        @Override
         public void setCaptioningEnabled(boolean enabled) {
             Bundle bundle = new Bundle();
             bundle.putBoolean(MediaSessionCompat.ACTION_ARGUMENT_CAPTIONING_ENABLED, enabled);
diff --git a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
index 5df4b72..654e7bc 100644
--- a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -149,83 +149,71 @@
 
     /**
      * Predefined custom action to follow an artist, album, or playlist. The extra bundle must have
-     * {@link #ACTION_ARGUMENT_MEDIA_ATTRIBUTE} to indicate the type of the follow action. The
+     * {@link #ARGUMENT_MEDIA_ATTRIBUTE} to indicate the type of the follow action. The
      * bundle can also have an optional string argument,
-     * {@link #ACTION_ARGUMENT_MEDIA_ATTRIBUTE_VALUE}, to specify the target to follow (e.g., the
+     * {@link #ARGUMENT_MEDIA_ATTRIBUTE_VALUE}, to specify the target to follow (e.g., the
      * name of the artist to follow). If this argument is omitted, the currently playing media will
      * be the target of the action. Thus, the session must perform the follow action with the
      * current metadata. If there's no specified attribute in the current metadata, the controller
      * must not omit this argument.
      *
-     * @see #ACTION_ARGUMENT_MEDIA_ATTRIBUTE
-     * @see #ACTION_ARGUMENT_MEDIA_ATTRIBUTE_VALUE
+     * @see #ARGUMENT_MEDIA_ATTRIBUTE
+     * @see #ARGUMENT_MEDIA_ATTRIBUTE_VALUE
      * @see Callback#onCustomAction
      */
     public static final String ACTION_FOLLOW = "android.support.v4.media.session.action.FOLLOW";
 
     /**
      * Predefined custom action to unfollow an artist, album, or playlist. The extra bundle must
-     * have {@link #ACTION_ARGUMENT_MEDIA_ATTRIBUTE} to indicate the type of the unfollow action.
+     * have {@link #ARGUMENT_MEDIA_ATTRIBUTE} to indicate the type of the unfollow action.
      * The bundle can also have an optional string argument,
-     * {@link #ACTION_ARGUMENT_MEDIA_ATTRIBUTE_VALUE}, to specify the target to unfollow (e.g., the
+     * {@link #ARGUMENT_MEDIA_ATTRIBUTE_VALUE}, to specify the target to unfollow (e.g., the
      * name of the artist to unfollow). If this argument is omitted, the currently playing media
      * will be the target of the action. Thus, the session must perform the unfollow action with the
      * current metadata. If there's no specified attribute in the current metadata, the controller
      * must not omit this argument.
      *
-     * @see #ACTION_ARGUMENT_MEDIA_ATTRIBUTE
-     * @see #ACTION_ARGUMENT_MEDIA_ATTRIBUTE_VALUE
+     * @see #ARGUMENT_MEDIA_ATTRIBUTE
+     * @see #ARGUMENT_MEDIA_ATTRIBUTE_VALUE
      * @see Callback#onCustomAction
      */
     public static final String ACTION_UNFOLLOW = "android.support.v4.media.session.action.UNFOLLOW";
 
     /**
-     * Argument for use with {@link #ACTION_FOLLOW} and {@link #ACTION_UNFOLLOW} indicating the
-     * media attribute of the follow/unfollow action. It should be one of the following:
+     * Argument to indicate the media attribute. It should be one of the following:
      * <ul>
      * <li>{@link #MEDIA_ATTRIBUTE_ARTIST}</li>
      * <li>{@link #MEDIA_ATTRIBUTE_PLAYLIST}</li>
      * <li>{@link #MEDIA_ATTRIBUTE_ALBUM}</li>
      * </ul>
-     *
-     * @see #ACTION_FOLLOW
-     * @see #ACTION_UNFOLLOW
      */
-    public static final String ACTION_ARGUMENT_MEDIA_ATTRIBUTE =
-            "android.support.v4.media.session.action.ARGUMENT_MEDIA_ATTRIBUTE";
+    public static final String ARGUMENT_MEDIA_ATTRIBUTE =
+            "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE";
 
     /**
-     * String argument for use with {@link #ACTION_FOLLOW} and {@link #ACTION_UNFOLLOW} indicating
-     * the value of the media attribute of the follow/unfollow action (e.g., the name of the artist
-     * to follow).
-     *
-     * @see #ACTION_FOLLOW
-     * @see #ACTION_UNFOLLOW
+     * String argument to indicate the value of the media attribute (e.g., the name of the artist).
      */
-    public static final String ACTION_ARGUMENT_MEDIA_ATTRIBUTE_VALUE =
-            "android.support.v4.media.session.action.ARGUMENT_MEDIA_ATTRIBUTE_VALUE";
+    public static final String ARGUMENT_MEDIA_ATTRIBUTE_VALUE =
+            "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE_VALUE";
 
     /**
-     * The media attribute of the follow action which indicates that the target of the action is an
-     * artist.
+     * The value of {@link #ARGUMENT_MEDIA_ATTRIBUTE} indicating the artist.
      *
-     * @see ACTION_ARGUMENT_MEDIA_ATTRIBUTE
+     * @see ARGUMENT_MEDIA_ATTRIBUTE
      */
     public static final int MEDIA_ATTRIBUTE_ARTIST = 0;
 
     /**
-     * The media attribute of the follow action which indicates that the target of the action is an
-     * album.
+     * The value of {@link #ARGUMENT_MEDIA_ATTRIBUTE} indicating the album.
      *
-     * @see ACTION_ARGUMENT_MEDIA_ATTRIBUTE
+     * @see ARGUMENT_MEDIA_ATTRIBUTE
      */
     public static final int MEDIA_ATTRIBUTE_ALBUM = 1;
 
     /**
-     * The media attribute of the follow action which indicates that the target of the action is a
-     * playlist.
+     * The value of {@link #ARGUMENT_MEDIA_ATTRIBUTE} indicating the playlist.
      *
-     * @see ACTION_ARGUMENT_MEDIA_ATTRIBUTE
+     * @see ARGUMENT_MEDIA_ATTRIBUTE
      */
     public static final int MEDIA_ATTRIBUTE_PLAYLIST = 2;
 
@@ -283,6 +271,12 @@
             "android.support.v4.media.session.action.SET_SHUFFLE_MODE";
 
     /**
+     * Custom action to invoke setRating() with extra fields.
+     */
+    static final String ACTION_SET_RATING =
+            "android.support.v4.media.session.action.SET_RATING";
+
+    /**
      * Argument for use with {@link #ACTION_PREPARE_FROM_MEDIA_ID} indicating media id to play.
      */
     static final String ACTION_ARGUMENT_MEDIA_ID =
@@ -302,6 +296,12 @@
             "android.support.v4.media.session.action.ARGUMENT_URI";
 
     /**
+     * Argument for use with {@link #ACTION_SET_RATING} indicating the rate to be set.
+     */
+    static final String ACTION_ARGUMENT_RATING =
+            "android.support.v4.media.session.action.ARGUMENT_RATING";
+
+    /**
      * Argument for use with various actions indicating extra bundle.
      */
     static final String ACTION_ARGUMENT_EXTRAS =
@@ -1105,12 +1105,21 @@
         /**
          * Override to handle the item being rated.
          *
-         * @param rating
+         * @param rating The rating being set.
          */
         public void onSetRating(RatingCompat rating) {
         }
 
         /**
+         * Override to handle the item being rated.
+         *
+         * @param rating The rating being set.
+         * @param extras The extras can include information about the media item being rated.
+         */
+        public void onSetRating(RatingCompat rating, Bundle extras) {
+        }
+
+        /**
          * Override to handle requests to enable/disable captioning.
          *
          * @param enabled {@code true} to enable captioning, {@code false} to disable.
@@ -1359,6 +1368,11 @@
             }
 
             @Override
+            public void onSetRating(Object ratingObj, Bundle extras) {
+                Callback.this.onSetRating(RatingCompat.fromRating(ratingObj), extras);
+            }
+
+            @Override
             public void onCustomAction(String action, Bundle extras) {
                 if (action.equals(ACTION_PLAY_FROM_URI)) {
                     Uri uri = extras.getParcelable(ACTION_ARGUMENT_URI);
@@ -1390,6 +1404,11 @@
                 } else if (action.equals(ACTION_SET_SHUFFLE_MODE)) {
                     int shuffleMode = extras.getInt(ACTION_ARGUMENT_SHUFFLE_MODE);
                     Callback.this.onSetShuffleMode(shuffleMode);
+                } else if (action.equals(ACTION_SET_RATING)) {
+                    extras.setClassLoader(RatingCompat.class.getClassLoader());
+                    RatingCompat rating = extras.getParcelable(ACTION_ARGUMENT_RATING);
+                    Bundle bundle = extras.getBundle(ACTION_ARGUMENT_EXTRAS);
+                    Callback.this.onSetRating(rating, bundle);
                 } else {
                     Callback.this.onCustomAction(action, extras);
                 }
@@ -2673,6 +2692,11 @@
             }
 
             @Override
+            public void rateWithExtras(RatingCompat rating, Bundle extras) throws RemoteException {
+                postToHandler(MessageHandler.MSG_RATE_EXTRA, rating, extras);
+            }
+
+            @Override
             public void setCaptioningEnabled(boolean enabled) throws RemoteException {
                 postToHandler(MessageHandler.MSG_SET_CAPTIONING_ENABLED, enabled);
             }
@@ -2820,6 +2844,7 @@
             private static final int MSG_REWIND = 17;
             private static final int MSG_SEEK_TO = 18;
             private static final int MSG_RATE = 19;
+            private static final int MSG_RATE_EXTRA = 31;
             private static final int MSG_CUSTOM_ACTION = 20;
             private static final int MSG_MEDIA_BUTTON = 21;
             private static final int MSG_SET_VOLUME = 22;
@@ -2929,6 +2954,9 @@
                     case MSG_RATE:
                         cb.onSetRating((RatingCompat) msg.obj);
                         break;
+                    case MSG_RATE_EXTRA:
+                        cb.onSetRating((RatingCompat) msg.obj, msg.getData());
+                        break;
                     case MSG_CUSTOM_ACTION:
                         cb.onCustomAction((String) msg.obj, msg.getData());
                         break;
@@ -3591,6 +3619,12 @@
             }
 
             @Override
+            public void rateWithExtras(RatingCompat rating, Bundle extras) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
             public void setCaptioningEnabled(boolean enabled) throws RemoteException {
                 // Will not be called.
                 throw new AssertionError();
diff --git a/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java b/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
index a1a59e3..6103121 100644
--- a/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
@@ -288,9 +288,17 @@
             assertEquals(rating.getStarRating(), mCallback.mRating.getStarRating(), DELTA);
 
             mCallback.reset();
-            final String mediaId = "test-media-id";
             final Bundle extras = new Bundle();
             extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+            controls.setRating(rating, extras);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSetRatingCalled);
+            assertEquals(rating.getRatingStyle(), mCallback.mRating.getRatingStyle());
+            assertEquals(rating.getStarRating(), mCallback.mRating.getStarRating(), DELTA);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+            mCallback.reset();
+            final String mediaId = "test-media-id";
             controls.playFromMediaId(mediaId, extras);
             mWaitLock.wait(TIME_OUT_MS);
             assertTrue(mCallback.mOnPlayFromMediaIdCalled);
@@ -613,6 +621,16 @@
         }
 
         @Override
+        public void onSetRating(RatingCompat rating, Bundle extras) {
+            synchronized (mWaitLock) {
+                mOnSetRatingCalled = true;
+                mRating = rating;
+                mExtras = extras;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
         public void onPlayFromMediaId(String mediaId, Bundle extras) {
             synchronized (mWaitLock) {
                 mOnPlayFromMediaIdCalled = true;
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsPresenterSelectionActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsPresenterSelectionActivity.java
index 4f8546e..e6cf32f 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsPresenterSelectionActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsPresenterSelectionActivity.java
@@ -69,7 +69,10 @@
                 .build());
     }
 
-    private static class SetupFragment extends GuidedStepFragment {
+    /**
+     * Fragment hosted in DetailsPresenterSelectionActivity.
+     */
+    public static class SetupFragment extends GuidedStepFragment {
 
         @Override
         public Guidance onCreateGuidance(Bundle savedInstanceState) {
diff --git a/v17/leanback/build.gradle b/v17/leanback/build.gradle
index 8c02cc4..a2e7342 100644
--- a/v17/leanback/build.gradle
+++ b/v17/leanback/build.gradle
@@ -36,11 +36,6 @@
         ]
         main.res.srcDir 'res'
     }
-
-    lintOptions {
-        // Remove this once all NewApi breakages have been fixed.
-        disable "NewApi"
-    }
 }
 
 supportLibrary {
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
index 872e836..bb30b00 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
@@ -35,7 +35,7 @@
         SlideKitkat slide = new SlideKitkat();
         slide.setSlideEdge(Gravity.TOP);
         slide.setInterpolator(AnimationUtils.loadInterpolator(context,
-                R.animator.lb_decelerator_4));
+                R.anim.lb_decelerator_4));
         slide.addTarget(R.id.browse_title_group);
         return slide;
     }
diff --git a/v17/leanback/lint-baseline.xml b/v17/leanback/lint-baseline.xml
index 24cb762..b373b5e 100644
--- a/v17/leanback/lint-baseline.xml
+++ b/v17/leanback/lint-baseline.xml
@@ -1,346 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="4" by="lint 2.4.0-alpha6">
-
-    <issue
-        id="MissingSuperCall"
-        message="Overriding method should call `super.onAnimationEnd`"
-        errorLine1="                public void onAnimationEnd(Animation animation) {"
-        errorLine2="                            ~~~~~~~~~~~~~~">
-        <location
-            file="java/android/support/v4/app/FragmentManager.java"
-            line="1604"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="MissingSuperCall"
-        message="Overriding method should call `super.onAnimationEnd`"
-        errorLine1="        public void onAnimationEnd(Animation animation) {"
-        errorLine2="                    ~~~~~~~~~~~~~~">
-        <location
-            file="java/android/support/v4/app/FragmentManager.java"
-            line="3935"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ResourceType"
-        message="Expected resource of type string"
-        errorLine1="                        ? Float.parseFloat(res.getString(R.dimen.lb_browse_header_select_scale))"
-        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/android/support/v17/leanback/widget/FocusHighlightHelper.java"
-            line="276"
-            column="58"/>
-    </issue>
-
-    <issue
-        id="ResourceType"
-        message="Expected resource of type string"
-        errorLine1="                        Integer.parseInt(res.getString(R.dimen.lb_browse_header_select_duration));"
-        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/android/support/v17/leanback/widget/FocusHighlightHelper.java"
-            line="279"
-            column="56"/>
-    </issue>
-
-    <issue
-        id="ResourceType"
-        message="Expected resource of type anim or interpolator"
-        errorLine1="                R.animator.lb_decelerator_4));"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java"
-            line="38"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 27 (DetailsOverviewRowPresenter)"
-        errorLine1="                if (DEBUG) Log.v(TAG, &quot;onLayoutChange &quot; + v);"
-        errorLine2="                                 ~~~">
-        <location
-            file="src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java"
-            line="178"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 27 (DetailsOverviewRowPresenter)"
-        errorLine1="                Log.v(TAG, &quot;checkFirstAndLast fromScroll &quot; + fromScroll"
-        errorLine2="                      ~~~">
-        <location
-            file="src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java"
-            line="237"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 36 (FullWidthDetailsOverviewRowPresenter)"
-        errorLine1="                if (DEBUG) Log.v(TAG, &quot;onLayoutChange &quot; + v);"
-        errorLine2="                                 ~~~">
-        <location
-            file="src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java"
-            line="236"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 36 (FullWidthDetailsOverviewRowPresenter)"
-        errorLine1="                Log.v(TAG, &quot;checkFirstAndLast fromScroll &quot; + fromScroll"
-        errorLine2="                      ~~~">
-        <location
-            file="src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java"
-            line="295"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 25 (GuidedStepSupportFragment)"
-        errorLine1="            Log.w(TAG, &quot;Fragment is already exists, likely calling &quot;"
-        errorLine2="                  ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/GuidedStepSupportFragment.java"
-            line="642"
-            column="19"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 25 (GuidedStepSupportFragment)"
-        errorLine1="        if (DEBUG) Log.v(TAG, &quot;onCreate&quot;);"
-        errorLine2="                         ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/GuidedStepSupportFragment.java"
-            line="1000"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 25 (GuidedStepSupportFragment)"
-        errorLine1="        if (DEBUG) Log.v(TAG, &quot;onCreateView&quot;);"
-        errorLine2="                         ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/GuidedStepSupportFragment.java"
-            line="1039"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 25 (GuidedStepSupportFragment)"
-        errorLine1="        if (DEBUG) Log.v(TAG, &quot;Found guided step theme flag? &quot; + found);"
-        errorLine2="                         ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/GuidedStepSupportFragment.java"
-            line="1239"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 25 (GuidedStepSupportFragment)"
-        errorLine1="            if (DEBUG) Log.v(TAG, &quot;Found guided step theme reference? &quot; + found);"
-        errorLine2="                             ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/GuidedStepSupportFragment.java"
-            line="1352"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 25 (GuidedStepSupportFragment)"
-        errorLine1="                Log.e(TAG, &quot;GuidedStepSupportFragment does not have an appropriate theme set.&quot;);"
-        errorLine2="                      ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/GuidedStepSupportFragment.java"
-            line="1364"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 25 (OnboardingSupportFragment)"
-        errorLine1="            if (DEBUG) Log.v(TAG, &quot;Found onboarding theme reference? &quot; + found);"
-        errorLine2="                             ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/OnboardingSupportFragment.java"
-            line="552"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 30 (PlaybackOverlaySupportFragment)"
-        errorLine1="            if (DEBUG) Log.v(TAG, &quot;onAnimationEnd &quot; + mBgAlpha);"
-        errorLine2="                             ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java"
-            line="148"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 30 (PlaybackOverlaySupportFragment)"
-        errorLine1="        if (DEBUG) Log.v(TAG, &quot;setFadingEnabled &quot; + enabled);"
-        errorLine2="                         ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java"
-            line="236"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 30 (PlaybackOverlaySupportFragment)"
-        errorLine1="        if (DEBUG) Log.v(TAG, &quot;tickle enabled &quot; + mFadingEnabled + &quot; isResumed &quot; + isResumed());"
-        errorLine2="                         ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java"
-            line="313"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 30 (PlaybackOverlaySupportFragment)"
-        errorLine1="        if (DEBUG) Log.v(TAG, &quot;onInterceptInputEvent hidden &quot; + controlsHidden + &quot; &quot; + event);"
-        errorLine2="                         ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java"
-            line="363"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 30 (PlaybackOverlaySupportFragment)"
-        errorLine1="                    if (DEBUG) Log.v(TAG, &quot;fraction &quot; + fraction);"
-        errorLine2="                                     ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java"
-            line="481"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 30 (PlaybackOverlaySupportFragment)"
-        errorLine1="        if (DEBUG) Log.v(TAG, &quot;fade &quot; + fadeIn);"
-        errorLine2="                         ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java"
-            line="577"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 30 (PlaybackOverlaySupportFragment)"
-        errorLine1="            if (DEBUG) Log.v(TAG, &quot;requested fade in progress&quot;);"
-        errorLine2="                             ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java"
-            line="582"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 30 (PlaybackOverlaySupportFragment)"
-        errorLine1="            if (DEBUG) Log.v(TAG, &quot;fade is no-op&quot;);"
-        errorLine2="                             ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java"
-            line="586"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 30 (PlaybackOverlaySupportFragment)"
-        errorLine1="            if (DEBUG) Log.v(TAG, &quot;onAttachedToWindow &quot; + vh.getViewHolder().view);"
-        errorLine2="                             ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java"
-            line="770"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 30 (PlaybackOverlaySupportFragment)"
-        errorLine1="                if (DEBUG) Log.v(TAG, &quot;setting alpha to 0&quot;);"
-        errorLine2="                                 ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java"
-            line="772"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 30 (PlaybackOverlaySupportFragment)"
-        errorLine1="            if (DEBUG) Log.v(TAG, &quot;onDetachedFromWindow &quot; + vh.getViewHolder().view);"
-        errorLine2="                             ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java"
-            line="781"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="LongLogTag"
-        message="The logging tag can be at most 23 characters, was 27 (VerticalGridSupportFragment)"
-        errorLine1="            if (DEBUG) Log.v(TAG, &quot;grid selected position &quot; + position);"
-        errorLine2="                             ~~~">
-        <location
-            file="src/android/support/v17/leanback/app/VerticalGridSupportFragment.java"
-            line="120"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="Orientation"
-        message="No orientation specified, and the default is horizontal. This is a common source of bugs when children are added dynamically."
-        errorLine1="            &lt;LinearLayout"
-        errorLine2="            ^">
-        <location
-            file="res/layout/lb_row_media_item.xml"
-            line="63"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="WrongConstant"
-        message="Must be one of: ViewCompat.LAYOUT_DIRECTION_LTR, ViewCompat.LAYOUT_DIRECTION_RTL"
-        errorLine1="            boolean isRtl = ViewCompat.getLayoutDirection(focused) == View.LAYOUT_DIRECTION_RTL;"
-        errorLine2="                                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/android/support/v17/leanback/app/BrowseFragment.java"
-            line="1029"
-            column="71"/>
-    </issue>
-
-    <issue
-        id="WrongConstant"
-        message="Must be one of: ViewCompat.LAYOUT_DIRECTION_LTR, ViewCompat.LAYOUT_DIRECTION_RTL"
-        errorLine1="            boolean isRtl = ViewCompat.getLayoutDirection(focused) == View.LAYOUT_DIRECTION_RTL;"
-        errorLine2="                                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/android/support/v17/leanback/app/BrowseSupportFragment.java"
-            line="1032"
-            column="71"/>
-    </issue>
+<issues format="4" by="lint 3.0.0-alpha5">
 
     <issue
         id="WrongConstant"
@@ -353,63 +12,4 @@
             column="47"/>
     </issue>
 
-    <issue
-        id="WrongConstant"
-        message="Must be one of: View.VISIBLE, View.INVISIBLE, View.GONE"
-        errorLine1="                    v.setVisibility(mChildVisibility);"
-        errorLine2="                                    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/android/support/v17/leanback/widget/GridLayoutManager.java"
-            line="1566"
-            column="37"/>
-    </issue>
-
-    <issue
-        id="WrongConstant"
-        message="Must be one of: View.VISIBLE, View.INVISIBLE, View.GONE"
-        errorLine1="                getChildAt(i).setVisibility(mChildVisibility);"
-        errorLine2="                                            ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/android/support/v17/leanback/widget/GridLayoutManager.java"
-            line="3476"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="WrongConstant"
-        message="Must be one of: ViewCompat.LAYOUT_DIRECTION_LTR, ViewCompat.LAYOUT_DIRECTION_RTL"
-        errorLine1="        boolean isRtl = ViewCompat.getLayoutDirection(view) == View.LAYOUT_DIRECTION_RTL;"
-        errorLine2="                                                               ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java"
-            line="50"
-            column="64"/>
-    </issue>
-
-    <issue
-        id="WrongConstant"
-        message="Must be one of: ViewCompat.LAYOUT_DIRECTION_LTR, ViewCompat.LAYOUT_DIRECTION_RTL"
-        errorLine1="                    == View.LAYOUT_DIRECTION_RTL;"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/android/support/v17/leanback/widget/TitleHelper.java"
-            line="49"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="RtlCompat"
-        message="Inconsistent alignment specification between `textAlignment` and `gravity` attributes: was `center_vertical|end`, expected `start`"
-        errorLine1="                    android:textAlignment=&quot;viewStart&quot;"
-        errorLine2="                                           ~~~~~~~~~">
-        <location
-            file="res/layout/lb_search_bar.xml"
-            line="70"
-            column="44"/>
-        <location
-            file="res/layout/lb_search_bar.xml"
-            line="57"
-            column="45"/>
-    </issue>
-
 </issues>
diff --git a/v17/leanback/res/animator/lb_decelerator_2.xml b/v17/leanback/res/anim/lb_decelerator_2.xml
similarity index 100%
rename from v17/leanback/res/animator/lb_decelerator_2.xml
rename to v17/leanback/res/anim/lb_decelerator_2.xml
diff --git a/v17/leanback/res/animator/lb_decelerator_4.xml b/v17/leanback/res/anim/lb_decelerator_4.xml
similarity index 100%
rename from v17/leanback/res/animator/lb_decelerator_4.xml
rename to v17/leanback/res/anim/lb_decelerator_4.xml
diff --git a/v17/leanback/res/animator-v21/lb_onboarding_description_enter.xml b/v17/leanback/res/animator-v21/lb_onboarding_description_enter.xml
new file mode 100644
index 0000000..3cb5843
--- /dev/null
+++ b/v17/leanback/res/animator-v21/lb_onboarding_description_enter.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueFrom="0.0"
+        android:valueTo="1.0"
+        android:duration="533"
+        android:startOffset="@integer/lb_onboarding_header_description_delay"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+    <objectAnimator
+        android:propertyName="translationY"
+        android:valueFrom="60dp"
+        android:valueTo="0dp"
+        android:duration="533"
+        android:startOffset="@integer/lb_onboarding_header_description_delay"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/v17/leanback/res/animator-v21/lb_onboarding_logo_enter.xml b/v17/leanback/res/animator-v21/lb_onboarding_logo_enter.xml
new file mode 100644
index 0000000..76a4609
--- /dev/null
+++ b/v17/leanback/res/animator-v21/lb_onboarding_logo_enter.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueFrom="0.0"
+        android:valueTo="1.0"
+        android:duration="333"
+        android:interpolator="@android:interpolator/linear_out_slow_in" />
+</set>
diff --git a/v17/leanback/res/animator-v21/lb_onboarding_logo_exit.xml b/v17/leanback/res/animator-v21/lb_onboarding_logo_exit.xml
new file mode 100644
index 0000000..40b618e
--- /dev/null
+++ b/v17/leanback/res/animator-v21/lb_onboarding_logo_exit.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueFrom="1.0"
+        android:valueTo="0.0"
+        android:duration="666"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/v17/leanback/res/animator-v21/lb_onboarding_page_indicator_enter.xml b/v17/leanback/res/animator-v21/lb_onboarding_page_indicator_enter.xml
new file mode 100644
index 0000000..e9fc46e
--- /dev/null
+++ b/v17/leanback/res/animator-v21/lb_onboarding_page_indicator_enter.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueFrom="0.0"
+        android:valueTo="1.0"
+        android:duration="500"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/v17/leanback/res/animator-v21/lb_onboarding_title_enter.xml b/v17/leanback/res/animator-v21/lb_onboarding_title_enter.xml
new file mode 100644
index 0000000..9b65b48
--- /dev/null
+++ b/v17/leanback/res/animator-v21/lb_onboarding_title_enter.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueFrom="0.0"
+        android:valueTo="1.0"
+        android:duration="533"
+        android:startOffset="@integer/lb_onboarding_header_title_delay"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+    <objectAnimator
+        android:propertyName="translationY"
+        android:valueFrom="60dp"
+        android:valueTo="0dp"
+        android:duration="533"
+        android:startOffset="@integer/lb_onboarding_header_title_delay"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/v17/leanback/res/animator/lb_onboarding_description_enter.xml b/v17/leanback/res/animator/lb_onboarding_description_enter.xml
index 3cb5843..d8393bd 100644
--- a/v17/leanback/res/animator/lb_onboarding_description_enter.xml
+++ b/v17/leanback/res/animator/lb_onboarding_description_enter.xml
@@ -21,13 +21,11 @@
         android:valueFrom="0.0"
         android:valueTo="1.0"
         android:duration="533"
-        android:startOffset="@integer/lb_onboarding_header_description_delay"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
+        android:startOffset="@integer/lb_onboarding_header_description_delay" />
     <objectAnimator
         android:propertyName="translationY"
         android:valueFrom="60dp"
         android:valueTo="0dp"
         android:duration="533"
-        android:startOffset="@integer/lb_onboarding_header_description_delay"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
+        android:startOffset="@integer/lb_onboarding_header_description_delay" />
 </set>
diff --git a/v17/leanback/res/animator/lb_onboarding_logo_enter.xml b/v17/leanback/res/animator/lb_onboarding_logo_enter.xml
index 76a4609..5d8d762 100644
--- a/v17/leanback/res/animator/lb_onboarding_logo_enter.xml
+++ b/v17/leanback/res/animator/lb_onboarding_logo_enter.xml
@@ -20,6 +20,5 @@
         android:propertyName="alpha"
         android:valueFrom="0.0"
         android:valueTo="1.0"
-        android:duration="333"
-        android:interpolator="@android:interpolator/linear_out_slow_in" />
+        android:duration="333" />
 </set>
diff --git a/v17/leanback/res/animator/lb_onboarding_logo_exit.xml b/v17/leanback/res/animator/lb_onboarding_logo_exit.xml
index 40b618e..820ba8e 100644
--- a/v17/leanback/res/animator/lb_onboarding_logo_exit.xml
+++ b/v17/leanback/res/animator/lb_onboarding_logo_exit.xml
@@ -20,6 +20,5 @@
         android:propertyName="alpha"
         android:valueFrom="1.0"
         android:valueTo="0.0"
-        android:duration="666"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
+        android:duration="666" />
 </set>
diff --git a/v17/leanback/res/animator/lb_onboarding_page_indicator_enter.xml b/v17/leanback/res/animator/lb_onboarding_page_indicator_enter.xml
index e9fc46e..b8bd083 100644
--- a/v17/leanback/res/animator/lb_onboarding_page_indicator_enter.xml
+++ b/v17/leanback/res/animator/lb_onboarding_page_indicator_enter.xml
@@ -20,6 +20,5 @@
         android:propertyName="alpha"
         android:valueFrom="0.0"
         android:valueTo="1.0"
-        android:duration="500"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
+        android:duration="500" />
 </set>
diff --git a/v17/leanback/res/animator/lb_onboarding_title_enter.xml b/v17/leanback/res/animator/lb_onboarding_title_enter.xml
index 9b65b48..011f51c 100644
--- a/v17/leanback/res/animator/lb_onboarding_title_enter.xml
+++ b/v17/leanback/res/animator/lb_onboarding_title_enter.xml
@@ -21,13 +21,11 @@
         android:valueFrom="0.0"
         android:valueTo="1.0"
         android:duration="533"
-        android:startOffset="@integer/lb_onboarding_header_title_delay"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
+        android:startOffset="@integer/lb_onboarding_header_title_delay" />
     <objectAnimator
         android:propertyName="translationY"
         android:valueFrom="60dp"
         android:valueTo="0dp"
         android:duration="533"
-        android:startOffset="@integer/lb_onboarding_header_title_delay"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
+        android:startOffset="@integer/lb_onboarding_header_title_delay" />
 </set>
diff --git a/v17/leanback/res/layout/lb_playback_transport_controls_row.xml b/v17/leanback/res/layout/lb_playback_transport_controls_row.xml
index 1b32be6..5825569 100644
--- a/v17/leanback/res/layout/lb_playback_transport_controls_row.xml
+++ b/v17/leanback/res/layout/lb_playback_transport_controls_row.xml
@@ -31,7 +31,8 @@
 
     <FrameLayout
         android:layout_width="match_parent"
-        android:layout_height="match_parent">
+        android:layout_height="match_parent"
+        android:clipChildren="false">
         <LinearLayout
             android:id="@+id/controls_card"
             android:layout_width="match_parent"
@@ -64,7 +65,8 @@
             android:visibility="invisible"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_gravity="bottom" />
+            android:layout_gravity="bottom"
+            android:layout_marginBottom="@dimen/lb_playback_transport_thumbs_bottom_margin" />
     </FrameLayout>
 
     <FrameLayout
diff --git a/v17/leanback/res/layout/lb_row_media_item.xml b/v17/leanback/res/layout/lb_row_media_item.xml
index b25e922..e76e281 100644
--- a/v17/leanback/res/layout/lb_row_media_item.xml
+++ b/v17/leanback/res/layout/lb_row_media_item.xml
@@ -62,6 +62,7 @@
 
             <LinearLayout
                     android:id="@+id/mediaItemActionsContainer"
+                    android:orientation="horizontal"
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
                     android:paddingStart="16dip" />
diff --git a/v17/leanback/res/layout/lb_search_bar.xml b/v17/leanback/res/layout/lb_search_bar.xml
index 37cdfb3..73a5985 100644
--- a/v17/leanback/res/layout/lb_search_bar.xml
+++ b/v17/leanback/res/layout/lb_search_bar.xml
@@ -54,7 +54,7 @@
                     android:id="@+id/lb_search_text_editor"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical|end"
+                    android:layout_gravity="center_vertical|start"
                     android:layout_marginStart="@dimen/lb_search_bar_edit_text_margin_start"
                     android:layout_centerVertical="true"
                     android:cursorVisible="true"
diff --git a/v17/leanback/res/transition-v21/lb_title_out.xml b/v17/leanback/res/transition-v21/lb_title_out.xml
index 69735ab..2402857 100644
--- a/v17/leanback/res/transition-v21/lb_title_out.xml
+++ b/v17/leanback/res/transition-v21/lb_title_out.xml
@@ -18,7 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:lb="http://schemas.android.com/apk/res-auto"
     class="android.support.v17.leanback.transition.SlideKitkat"
-    android:interpolator="@animator/lb_decelerator_2"
+    android:interpolator="@anim/lb_decelerator_2"
     lb:lb_slideEdge="top" >
     <targets>
         <target android:targetId="@id/browse_title_group" />
diff --git a/v17/leanback/res/values-v18/themes.xml b/v17/leanback/res/values-v18/themes.xml
new file mode 100644
index 0000000..9fc7722
--- /dev/null
+++ b/v17/leanback/res/values-v18/themes.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+
+<resources>
+    <style name="Theme.LeanbackBase" parent="android:Theme.Holo.NoActionBar">
+        <item name="playbackProgressPrimaryColor">@color/lb_playback_progress_color_no_theme</item>
+        <item name="playbackControlsIconHighlightColor">@color/lb_playback_icon_highlight_no_theme</item>
+        <item name="defaultBrandColor">@color/lb_default_brand_color</item>
+        <item name="defaultBrandColorDark">@color/lb_default_brand_color_dark</item>
+
+        <item name="android:windowOverscan">true</item>
+        <item name="guidedStepTheme">@style/Theme.Leanback.GuidedStep</item>
+    </style>
+</resources>
diff --git a/v17/leanback/res/values-v21/styles.xml b/v17/leanback/res/values-v21/styles.xml
index 89029c4..cd81934 100644
--- a/v17/leanback/res/values-v21/styles.xml
+++ b/v17/leanback/res/values-v21/styles.xml
@@ -25,4 +25,9 @@
         <item name="android:background">@drawable/lb_action_bg</item>
     </style>
 
+    <style name="Widget.Leanback.OnboardingStartButtonStyleBase">
+        <item name="android:elevation">1.5dp</item>
+        <item name="android:stateListAnimator">@null</item>
+    </style>
+
 </resources>
\ No newline at end of file
diff --git a/v17/leanback/res/values-v21/themes.xml b/v17/leanback/res/values-v21/themes.xml
index 886077a..4e5bd5a 100644
--- a/v17/leanback/res/values-v21/themes.xml
+++ b/v17/leanback/res/values-v21/themes.xml
@@ -26,10 +26,49 @@
 
         <item name="android:windowOverscan">true</item>
         <item name="guidedStepTheme">@style/Theme.Leanback.GuidedStep</item>
+
+        <!-- android:windowSharedElementEnterTransition is kept for backward compatibility for apps still refer
+        to Theme.Leanback, app should use Theme.Leanback.Details instead -->
+        <item name="android:windowSharedElementEnterTransition">@transition/lb_shared_element_enter_transition</item>
+        <!-- android:windowSharedElementReturnTransition is kept for backward compatibility for apps still refer
+        to Theme.Leanback, app should use Theme.Leanback.Details instead -->
+        <item name="android:windowSharedElementReturnTransition">@transition/lb_shared_element_return_transition</item>
+        <item name="android:windowEnterTransition">@transition/lb_enter_transition</item>
+        <item name="android:windowReturnTransition">@transition/lb_return_transition</item>
+        <item name="android:windowTransitionBackgroundFadeDuration">350</item>
+
+    </style>
+
+    <style name="Theme.Leanback.Browse" parent="Theme.Leanback">
+        <item name="android:windowEnterTransition">@transition/lb_browse_enter_transition</item>
+        <item name="android:windowReturnTransition">@transition/lb_browse_return_transition</item>
+    </style>
+
+    <style name="Theme.Leanback.VerticalGrid" parent="Theme.Leanback">
+        <item name="android:windowEnterTransition">@transition/lb_vertical_grid_enter_transition</item>
+        <item name="android:windowReturnTransition">@transition/lb_vertical_grid_return_transition</item>
+    </style>
+
+    <style name="Theme.Leanback.Details" parent="Theme.Leanback">
+        <item name="android:windowEnterTransition">@transition/lb_details_enter_transition</item>
+        <item name="android:windowReturnTransition">@transition/lb_details_return_transition</item>
+        <item name="android:windowSharedElementEnterTransition">@transition/lb_shared_element_enter_transition</item>
+        <item name="android:windowSharedElementReturnTransition">@transition/lb_shared_element_return_transition</item>
+    </style>
+
+    <style name="Theme.Leanback.Details.NoSharedElementTransition">
+        <item name="android:windowSharedElementEnterTransition">@null</item>
+        <item name="android:windowSharedElementReturnTransition">@null</item>
     </style>
 
     <style name="Theme.Leanback.GuidedStepBase" parent="Theme.LeanbackBase">
         <item name="guidedActionsSelectorDrawable">@drawable/lb_selectable_item_rounded_rect</item>
+        <item name="android:windowEnterTransition">@transition/lb_guidedstep_activity_enter</item>
+        <item name="android:windowTransitionBackgroundFadeDuration">@integer/lb_guidedstep_activity_background_fade_duration_ms</item>
+    </style>
+
+    <style name="Theme.Leanback.GuidedStep.HalfBase" parent="Theme.Leanback.GuidedStep">
+        <item name="android:windowEnterTransition">@transition/lb_guidedstep_activity_enter_bottom</item>
     </style>
 
 </resources>
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index 0a2b485..7a39b78 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -176,6 +176,14 @@
     <!-- radius of thumb when focused -->
     <dimen name="lb_playback_transport_progressbar_active_radius">6dp</dimen>
 
+    <!-- Thumbs bar -->
+    <dimen name="lb_playback_transport_thumbs_width">154dp</dimen>
+    <dimen name="lb_playback_transport_thumbs_height">154dp</dimen>
+    <dimen name="lb_playback_transport_hero_thumbs_width">192dp</dimen>
+    <dimen name="lb_playback_transport_hero_thumbs_height">192dp</dimen>
+    <dimen name="lb_playback_transport_thumbs_margin">4dp</dimen>
+    <dimen name="lb_playback_transport_thumbs_bottom_margin">18dp</dimen>
+
     <dimen name="lb_playback_transport_time_margin">8dp</dimen>
     <dimen name="lb_playback_transport_time_margin_top">8dp</dimen>
 
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index d10260d..b7e3da2 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -751,19 +751,20 @@
         <item name="arrowBgColor">@color/lb_page_indicator_arrow_background</item>
     </style>
 
+    <style name="Widget.Leanback.OnboardingStartButtonStyleBase">
+    </style>
+
     <!-- Style for the start button in OnboardingFragment. -->
-    <style name="Widget.Leanback.OnboardingStartButtonStyle">
+    <style name="Widget.Leanback.OnboardingStartButtonStyle" parent="Widget.Leanback.OnboardingStartButtonStyleBase">
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">36dp</item>
         <item name="android:layout_gravity">center_horizontal</item>
         <item name="android:layout_marginBottom">4dp</item>
         <item name="android:background">@drawable/lb_onboarding_start_button_background</item>
-        <item name="android:elevation">1.5dp</item>
         <item name="android:fontFamily">sans-serif</item>
         <item name="android:gravity">center_vertical</item>
         <item name="android:paddingEnd">24dp</item>
         <item name="android:paddingStart">24dp</item>
-        <item name="android:stateListAnimator">@null</item>
         <item name="android:text">@string/lb_onboarding_get_started</item>
         <item name="android:textAllCaps">true</item>
         <item name="android:textColor">#014269</item>
diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml
index e5b98f6..d40734e 100644
--- a/v17/leanback/res/values/themes.xml
+++ b/v17/leanback/res/values/themes.xml
@@ -24,7 +24,6 @@
         <item name="defaultBrandColor">@color/lb_default_brand_color</item>
         <item name="defaultBrandColorDark">@color/lb_default_brand_color_dark</item>
 
-        <item name="android:windowOverscan">true</item>
         <item name="guidedStepTheme">@style/Theme.Leanback.GuidedStep</item>
     </style>
 
@@ -103,16 +102,6 @@
 
         <item name="defaultSectionHeaderColor">?attr/defaultSearchColor</item>
 
-        <!-- android:windowSharedElementEnterTransition is kept for backward compatibility for apps still refer
-        to Theme.Leanback, app should use Theme.Leanback.Details instead -->
-        <item name="android:windowSharedElementEnterTransition">@transition/lb_shared_element_enter_transition</item>
-        <!-- android:windowSharedElementReturnTransition is kept for backward compatibility for apps still refer
-        to Theme.Leanback, app should use Theme.Leanback.Details instead -->
-        <item name="android:windowSharedElementReturnTransition">@transition/lb_shared_element_return_transition</item>
-        <item name="android:windowEnterTransition">@transition/lb_enter_transition</item>
-        <item name="android:windowReturnTransition">@transition/lb_return_transition</item>
-        <item name="android:windowTransitionBackgroundFadeDuration">350</item>
-
         <item name="overlayDimMaskColor">@color/lb_view_dim_mask_color</item>
         <item name="overlayDimActiveLevel">@fraction/lb_view_active_level</item>
         <item name="overlayDimDimmedLevel">@fraction/lb_view_dimmed_level</item>
@@ -120,26 +109,16 @@
     </style>
 
     <style name="Theme.Leanback.Browse" parent="Theme.Leanback">
-        <item name="android:windowEnterTransition">@transition/lb_browse_enter_transition</item>
-        <item name="android:windowReturnTransition">@transition/lb_browse_return_transition</item>
     </style>
 
     <style name="Theme.Leanback.VerticalGrid" parent="Theme.Leanback">
-        <item name="android:windowEnterTransition">@transition/lb_vertical_grid_enter_transition</item>
-        <item name="android:windowReturnTransition">@transition/lb_vertical_grid_return_transition</item>
     </style>
 
     <style name="Theme.Leanback.Details" parent="Theme.Leanback">
-        <item name="android:windowEnterTransition">@transition/lb_details_enter_transition</item>
-        <item name="android:windowReturnTransition">@transition/lb_details_return_transition</item>
-        <item name="android:windowSharedElementEnterTransition">@transition/lb_shared_element_enter_transition</item>
-        <item name="android:windowSharedElementReturnTransition">@transition/lb_shared_element_return_transition</item>
     </style>
 
     <!-- Theme for the details without shared element transition -->
     <style name="Theme.Leanback.Details.NoSharedElementTransition">
-        <item name="android:windowSharedElementEnterTransition">@null</item>
-        <item name="android:windowSharedElementReturnTransition">@null</item>
     </style>
 
     <style name="Theme.Leanback.GuidedStepBase" parent="Theme.LeanbackBase">
@@ -150,7 +129,6 @@
         <item name="guidedStepThemeFlag">true</item>
         <item name="guidedStepHeightWeight">@string/lb_guidedstep_height_weight</item>
 
-        <item name="android:windowEnterTransition">@transition/lb_guidedstep_activity_enter</item>
 
         <!-- background applied to each GuidedStepFragment by default-->
         <item name="guidedStepBackground">?android:attr/colorBackground</item>
@@ -158,7 +136,6 @@
              But We still need a dumb background to keep the temporary translucent state last
              as long as the background view fade-in transition -->
         <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:windowTransitionBackgroundFadeDuration">@integer/lb_guidedstep_activity_background_fade_duration_ms</item>
 
         <item name="guidedStepImeAppearingAnimation">@animator/lb_guidedstep_slide_up</item>
         <item name="guidedStepImeDisappearingAnimation">@animator/lb_guidedstep_slide_down</item>
@@ -198,8 +175,10 @@
         <item name="guidedStepKeyline">@string/lb_guidedstep_keyline</item>
     </style>
 
-    <style name="Theme.Leanback.GuidedStep.Half" parent="Theme.Leanback.GuidedStep">
-      <item name="android:windowEnterTransition">@transition/lb_guidedstep_activity_enter_bottom</item>
+    <style name="Theme.Leanback.GuidedStep.HalfBase" parent="Theme.Leanback.GuidedStep">
+    </style>
+
+    <style name="Theme.Leanback.GuidedStep.Half" parent="Theme.Leanback.GuidedStep.HalfBase">
       <item name="guidedStepHeightWeight">@string/lb_guidedstep_height_weight_translucent</item>
       <item name="android:windowIsTranslucent">true</item>
       <item name="android:windowBackground">@android:color/transparent</item>
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
index 698b5f2..fd930ee 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
@@ -1026,7 +1026,8 @@
                         ? mHeadersFragment.getVerticalGridView() : mMainFragment.getView();
             }
 
-            boolean isRtl = ViewCompat.getLayoutDirection(focused) == View.LAYOUT_DIRECTION_RTL;
+            boolean isRtl = ViewCompat.getLayoutDirection(focused)
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
             int towardStart = isRtl ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
             int towardEnd = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
             if (mCanShowHeaders && direction == towardStart) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
index 34c6775..094fef9 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
@@ -1029,7 +1029,8 @@
                         ? mHeadersSupportFragment.getVerticalGridView() : mMainFragment.getView();
             }
 
-            boolean isRtl = ViewCompat.getLayoutDirection(focused) == View.LAYOUT_DIRECTION_RTL;
+            boolean isRtl = ViewCompat.getLayoutDirection(focused)
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
             int towardStart = isRtl ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
             int towardEnd = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
             if (mCanShowHeaders && direction == towardStart) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
index 33ec805..a01cf26 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
@@ -234,7 +234,7 @@
     @RestrictTo(LIBRARY_GROUP)
     public static final int SLIDE_FROM_BOTTOM = 1;
 
-    private static final String TAG = "GuidedStepFragment";
+    private static final String TAG = "GuidedStepF";
     private static final boolean DEBUG = false;
 
     /**
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
index 9bd9b08..ed34548 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
@@ -237,7 +237,7 @@
     @RestrictTo(LIBRARY_GROUP)
     public static final int SLIDE_FROM_BOTTOM = 1;
 
-    private static final String TAG = "GuidedStepSupportFragment";
+    private static final String TAG = "GuidedStepF";
     private static final boolean DEBUG = false;
 
     /**
diff --git a/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java b/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java
index ab61c4a..5eb2784 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java
@@ -152,7 +152,7 @@
  * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingLogoStyle
  */
 abstract public class OnboardingFragment extends Fragment {
-    private static final String TAG = "OnboardingFragment";
+    private static final String TAG = "OnboardingF";
     private static final boolean DEBUG = false;
 
     private static final long LOGO_SPLASH_PAUSE_DURATION_MS = 1333;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java
index 0c7cc4c..46c2a81 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java
@@ -155,7 +155,7 @@
  * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingLogoStyle
  */
 abstract public class OnboardingSupportFragment extends Fragment {
-    private static final String TAG = "OnboardingSupportFragment";
+    private static final String TAG = "OnboardingF";
     private static final boolean DEBUG = false;
 
     private static final long LOGO_SPLASH_PAUSE_DURATION_MS = 1333;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
index c934f6b..d4b532b 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
@@ -96,7 +96,7 @@
         }
     }
 
-    static final String TAG = "PlaybackOverlayFragment";
+    static final String TAG = "PlaybackOF";
     static final boolean DEBUG = false;
     private static final int ANIMATION_MULTIPLIER = 1;
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
index ecabdaa..d751320 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
@@ -99,7 +99,7 @@
         }
     }
 
-    static final String TAG = "PlaybackOverlaySupportFragment";
+    static final String TAG = "PlaybackOF";
     static final boolean DEBUG = false;
     private static final int ANIMATION_MULTIPLIER = 1;
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
index 9e80dfc..5cf5799 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
@@ -38,7 +38,7 @@
  * an {@link ObjectAdapter}.
  */
 public class VerticalGridFragment extends BaseFragment {
-    static final String TAG = "VerticalGridFragment";
+    static final String TAG = "VerticalGF";
     static boolean DEBUG = false;
 
     private ObjectAdapter mAdapter;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
index 6327790..a38bac5 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
@@ -41,7 +41,7 @@
  * an {@link ObjectAdapter}.
  */
 public class VerticalGridSupportFragment extends BaseSupportFragment {
-    static final String TAG = "VerticalGridSupportFragment";
+    static final String TAG = "VerticalGF";
     static boolean DEBUG = false;
 
     private ObjectAdapter mAdapter;
diff --git a/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java
index 2598969..f97d64e 100644
--- a/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v17.leanback.R;
 
@@ -36,6 +37,7 @@
      * Kitkat does not allow load custom transition from resource, calling
      * LeanbackTransitionHelperKitKat to build custom transition in code.
      */
+    @RequiresApi(19)
     static class LeanbackTransitionHelperKitKatImpl implements LeanbackTransitionHelperVersion {
 
         @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
index 9f92066..de8b374 100644
--- a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.view.Gravity;
 import android.view.View;
@@ -427,6 +428,7 @@
     /**
      * Implementation used on KitKat (and above).
      */
+    @RequiresApi(19)
     static class TransitionHelperKitkatImpl extends TransitionHelperStubImpl {
 
         @Override
@@ -566,6 +568,7 @@
         }
     }
 
+    @RequiresApi(21)
     static final class TransitionHelperApi21Impl extends TransitionHelperKitkatImpl {
 
         @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BackgroundHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/BackgroundHelper.java
index 62d94d2..6bc2462 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BackgroundHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BackgroundHelper.java
@@ -19,6 +19,7 @@
 
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.view.View;
 
@@ -47,6 +48,7 @@
         }
     }
 
+    @RequiresApi(19)
     private static final class BackgroundHelperKitkatImpl implements BackgroundHelperVersionImpl {
         BackgroundHelperKitkatImpl() {
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java b/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java
index 9ca331f..735bb99 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java
@@ -891,7 +891,7 @@
          * @param source The layout params to copy from.
          */
         public LayoutParams(LayoutParams source) {
-            super(source);
+            super((ViewGroup.MarginLayoutParams) source);
 
             this.viewType = source.viewType;
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
index d484bcc..98b9f78 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
@@ -61,7 +61,7 @@
 @Deprecated
 public class DetailsOverviewRowPresenter extends RowPresenter {
 
-    static final String TAG = "DetailsOverviewRowPresenter";
+    static final String TAG = "DetailsOverviewRowP";
     static final boolean DEBUG = false;
 
     private static final int MORE_ACTIONS_FADE_MS = 100;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
index 394142d..49565ab 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
@@ -25,6 +25,7 @@
 import android.support.v17.leanback.app.HeadersFragment;
 import android.support.v17.leanback.graphics.ColorOverlayDimmer;
 import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewParent;
 import android.view.animation.AccelerateDecelerateInterpolator;
@@ -272,11 +273,15 @@
         void lazyInit(View view) {
             if (!mInitialized) {
                 Resources res = view.getResources();
-                mSelectScale = mScaleEnabled
-                        ? Float.parseFloat(res.getString(R.dimen.lb_browse_header_select_scale))
-                        : 1f;
-                mDuration =
-                        Integer.parseInt(res.getString(R.dimen.lb_browse_header_select_duration));
+                TypedValue value = new TypedValue();
+                if (mScaleEnabled) {
+                    res.getValue(R.dimen.lb_browse_header_select_scale, value, true);
+                    mSelectScale = value.getFloat();
+                } else {
+                    mSelectScale = 1f;
+                }
+                res.getValue(R.dimen.lb_browse_header_select_duration, value, true);
+                mDuration = value.data;
                 mInitialized = true;
             }
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java
index 64cb769..4c2a857 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java
@@ -2,6 +2,7 @@
 
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 
 final class ForegroundHelper {
@@ -22,6 +23,7 @@
     /**
      * Implementation used on api 23 (and above).
      */
+    @RequiresApi(23)
     private static final class ForegroundHelperApi23Impl implements ForegroundHelperVersionImpl {
         ForegroundHelperApi23Impl() {
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java
index 189dfe6..dad4414 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java
@@ -62,7 +62,7 @@
  */
 public class FullWidthDetailsOverviewRowPresenter extends RowPresenter {
 
-    static final String TAG = "FullWidthDetailsOverviewRowPresenter";
+    static final String TAG = "FullWidthDetailsRP";
     static final boolean DEBUG = false;
 
     private static Rect sTmpRect = new Rect();
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
index 5ab9ba0..45d69ef 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -504,7 +504,8 @@
     /**
      * override child visibility
      */
-    int mChildVisibility = -1;
+    @Visibility
+    int mChildVisibility;
 
     /**
      * Pixels that scrolled in secondary forward direction. Negative value means backward.
@@ -668,6 +669,7 @@
 
     public GridLayoutManager(BaseGridView baseGridView) {
         mBaseGridView = baseGridView;
+        mChildVisibility = -1;
     }
 
     public void setOrientation(int orientation) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java b/v17/leanback/src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java
index eff822b..8aead19 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java
@@ -47,7 +47,7 @@
         // end edge with row view's end edge, otherwise align start edges.
         view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
-        boolean isRtl = ViewCompat.getLayoutDirection(view) == View.LAYOUT_DIRECTION_RTL;
+        boolean isRtl = ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL;
         if (!isRtl && mCardLeft + view.getMeasuredWidth() > rightLimit) {
             params.leftMargin = rightLimit  - view.getMeasuredWidth();
         } else if (isRtl && mCardLeft < leftLimit) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackTransportRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackTransportRowPresenter.java
index 4c99a3b..e6db33b 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackTransportRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackTransportRowPresenter.java
@@ -380,7 +380,7 @@
             } else {
                 mPositionsLength = 0;
             }
-            mControlsVh.view.setVisibility(View.INVISIBLE);
+            mControlsVh.view.setVisibility(View.GONE);
             mSecondaryControlsVh.view.setVisibility(View.INVISIBLE);
             mDescriptionViewHolder.view.setVisibility(View.INVISIBLE);
             mThumbsBar.setVisibility(View.VISIBLE);
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java
index f9382ff..60fedf2 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java
@@ -13,8 +13,9 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.support.v17.leanback.R;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.support.v17.leanback.R;
 import android.view.View;
 
 /**
@@ -71,6 +72,7 @@
     /**
      * Implementation used on api 21 (and above).
      */
+    @RequiresApi(21)
     private static final class Api21Impl implements Impl {
         Api21Impl() {
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java
index e5fb61d..cbdddbf 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java
@@ -14,6 +14,7 @@
 package android.support.v17.leanback.widget;
 
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 
 
@@ -64,6 +65,7 @@
     /**
      * Implementation used on api 21 (and above).
      */
+    @RequiresApi(21)
     private static final class ShadowHelperApi21Impl implements ShadowHelperVersionImpl {
         ShadowHelperApi21Impl() {
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java
index 4422d62..436668f 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java
@@ -16,6 +16,7 @@
 package android.support.v17.leanback.widget;
 
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.view.ViewGroup;
 
 
@@ -64,6 +65,7 @@
     /**
      * Implementation used on JBMR2 (and above).
      */
+    @RequiresApi(19)
     private static final class ShadowHelperJbmr2Impl implements ShadowHelperVersionImpl {
         ShadowHelperJbmr2Impl() {
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ThumbsBar.java b/v17/leanback/src/android/support/v17/leanback/widget/ThumbsBar.java
index 85dde7f..594393d 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ThumbsBar.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ThumbsBar.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.support.annotation.RestrictTo;
+import android.support.v17.leanback.R;
 import android.util.AttributeSet;
 import android.util.SparseArray;
 import android.view.View;
@@ -33,24 +34,39 @@
 @RestrictTo(LIBRARY_GROUP)
 public class ThumbsBar extends LinearLayout {
 
-    static final int DEFAULT_NUM_OF_THUMBS = 7;
-
-    int mMinimalMargin = 16;
-    int mNumOfThumbs;
-    int mThumbWidth = 160;
-    int mThumbHeight = 160;
-    int mHeroThumbWidth = 240;
-    int mHeroThumbHeight = 240;
-    int mMeasuredMargin;
+    // initial value for Thumb's number before measuring the screen size
+    int mNumOfThumbs = -1;
+    int mThumbWidthInPixel;
+    int mThumbHeightInPixel;
+    int mHeroThumbWidthInPixel;
+    int mHeroThumbHeightInPixel;
+    int mMeasuredMarginInPixel;
     final SparseArray<Bitmap> mBitmaps = new SparseArray<>();
 
+    // flag to determine if the number of thumbs in thumbs bar is set by user through
+    // setNumberofThumbs API or auto-calculated according to android tv design spec.
+    private boolean mIsUserSets = false;
+
     public ThumbsBar(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
     public ThumbsBar(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        setNumberOfThumbs(DEFAULT_NUM_OF_THUMBS);
+        // According to the spec,
+        // the width of non-hero thumb should be 80% of HeroThumb's Width, i.e. 0.8 * 192dp = 154dp
+        mThumbWidthInPixel = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_thumbs_width);
+        mThumbHeightInPixel = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_thumbs_height);
+        // According to the spec, the width of HeroThumb should be 192dp
+        mHeroThumbHeightInPixel = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_hero_thumbs_width);
+        mHeroThumbWidthInPixel = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_hero_thumbs_height);
+        // According to the spec, the margin between thumbs to be 4dp
+        mMeasuredMarginInPixel = context.getResources().getDimensionPixelSize(
+                R.dimen.lb_playback_transport_thumbs_margin);
     }
 
     /**
@@ -64,8 +80,8 @@
      * Set size of thumb view in pixels
      */
     public void setThumbSize(int width, int height) {
-        mThumbHeight = height;
-        mThumbWidth = width;
+        mThumbHeightInPixel = height;
+        mThumbWidthInPixel = width;
         int heroIndex = getHeroIndex();
         for (int i = 0; i < getChildCount(); i++) {
             if (heroIndex != i) {
@@ -91,8 +107,8 @@
      * Set size of hero thumb view in pixels, it is usually larger than other thumbs.
      */
     public void setHeroThumbSize(int width, int height) {
-        mHeroThumbHeight = height;
-        mHeroThumbWidth = width;
+        mHeroThumbHeightInPixel = height;
+        mHeroThumbWidthInPixel = width;
         int heroIndex = getHeroIndex();
         for (int i = 0; i < getChildCount(); i++) {
             if (heroIndex == i) {
@@ -115,23 +131,34 @@
     }
 
     /**
-     * Set number of thumb views. It must be odd or it will be increasing one.
+     * Set the space between thumbs in pixels
+     */
+    public void setThumbSpace(int spaceInPixel) {
+        mMeasuredMarginInPixel = spaceInPixel;
+        requestLayout();
+    }
+
+    /**
+     * Set number of thumb views.
      */
     public void setNumberOfThumbs(int numOfThumbs) {
-        if (numOfThumbs < 0) {
-            throw new IllegalArgumentException();
-        }
-        if ((numOfThumbs & 1) == 0) {
-            // make it odd number
-            numOfThumbs++;
-        }
+        mIsUserSets = true;
         mNumOfThumbs = numOfThumbs;
+        setNumberOfThumbsInternal();
+    }
+
+    /**
+     * Helper function for setNumberOfThumbs.
+     * Will Update the layout settings in ThumbsBar based on mNumOfThumbs
+     */
+    private void setNumberOfThumbsInternal() {
         while (getChildCount() > mNumOfThumbs) {
             removeView(getChildAt(getChildCount() - 1));
         }
         while (getChildCount() < mNumOfThumbs) {
             View view = createThumbView(this);
-            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(mThumbWidth, mThumbHeight);
+            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(mThumbWidthInPixel,
+                    mThumbHeightInPixel);
             addView(view, lp);
         }
         int heroIndex = getHeroIndex();
@@ -139,30 +166,65 @@
             View child = getChildAt(i);
             LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
             if (heroIndex == i) {
-                lp.width = mHeroThumbWidth;
-                lp.height = mHeroThumbHeight;
+                lp.width = mHeroThumbWidthInPixel;
+                lp.height = mHeroThumbHeightInPixel;
             } else {
-                lp.width = mThumbWidth;
-                lp.height = mThumbHeight;
+                lp.width = mThumbWidthInPixel;
+                lp.height = mThumbHeightInPixel;
             }
             child.setLayoutParams(lp);
         }
     }
 
+    /**
+     * Helper function to compute how many thumbs should be put in the screen
+     * Assume we should put x's non-hero thumbs in the screen, the equation should be
+     *   192dp (width of hero thumbs) +
+     *   154dp (width of common thumbs) * x +
+     *   4dp (width of the margin between thumbs) * x
+     *     = width
+     * So the calculated number of non-hero thumbs should be (width - 192dp) / 158dp.
+     * If the calculated number of non-hero thumbs is less than 2, it will be updated to 2
+     * or if the calculated number or non-hero thumbs is not an even number, it will be
+     * decremented by one.
+     * This processing is used to make sure the arrangement of non-hero thumbs
+     * in ThumbsBar is symmetrical.
+     * Also there should be a hero thumb in the middle of the ThumbsBar,
+     * the final result should be non-hero thumbs (after processing) + 1.
+     *
+     * @param  widthInPixel measured width in pixel
+     * @return The number of thumbs
+     */
+    private int calculateNumOfThumbs(int widthInPixel) {
+        int nonHeroThumbNum = (widthInPixel - mHeroThumbWidthInPixel)
+                / (mThumbWidthInPixel + mMeasuredMarginInPixel);
+        if (nonHeroThumbNum < 2) {
+            // If the calculated number of non-hero thumbs is less than 2,
+            // it will be updated to 2
+            nonHeroThumbNum = 2;
+        } else if ((nonHeroThumbNum & 1) != 0) {
+            // If the calculated number or non-hero thumbs is not an even number,
+            // it will be decremented by one.
+            nonHeroThumbNum--;
+        }
+        // Count Hero Thumb to the final result
+        return nonHeroThumbNum + 1;
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         int width = getMeasuredWidth();
-        int spaceForMargin = 0;
-        while (mNumOfThumbs > 1) {
-            spaceForMargin = width - mHeroThumbWidth - mThumbWidth * (mNumOfThumbs - 1);
-            if (spaceForMargin < mMinimalMargin * (mNumOfThumbs - 1)) {
-                setNumberOfThumbs(mNumOfThumbs - 2);
-            } else {
-                break;
+        // If the number of thumbs in ThumbsBar is not set by user explicitly, it will be
+        // recalculated based on Android TV Design Spec
+        if (!mIsUserSets) {
+            int numOfThumbs = calculateNumOfThumbs(width);
+            // Set new number of thumbs when calculation result is different with current number
+            if (mNumOfThumbs != numOfThumbs) {
+                mNumOfThumbs = numOfThumbs;
+                setNumberOfThumbsInternal();
             }
         }
-        mMeasuredMargin = mNumOfThumbs > 0 ? spaceForMargin / (mNumOfThumbs - 1) : 0;
     }
 
     @Override
@@ -177,7 +239,7 @@
         int heroCenter = getPaddingTop() + heroView.getMeasuredHeight() / 2;
 
         for (int i = heroIndex - 1; i >= 0; i--) {
-            heroLeft -= mMeasuredMargin;
+            heroLeft -= mMeasuredMarginInPixel;
             View child = getChildAt(i);
             child.layout(heroLeft - child.getMeasuredWidth(),
                     heroCenter - child.getMeasuredHeight() / 2,
@@ -186,7 +248,7 @@
             heroLeft -= child.getMeasuredWidth();
         }
         for (int i = heroIndex + 1; i < mNumOfThumbs; i++) {
-            heroRight += mMeasuredMargin;
+            heroRight += mMeasuredMarginInPixel;
             View child = getChildAt(i);
             child.layout(heroRight,
                     heroCenter - child.getMeasuredHeight() / 2,
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
index f12d8d0..3aba59d 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
@@ -46,7 +46,7 @@
                 return mTitleView;
             }
             final boolean isRtl = ViewCompat.getLayoutDirection(focused)
-                    == View.LAYOUT_DIRECTION_RTL;
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
             final int forward = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
             if (mTitleView.hasFocus() && (direction == View.FOCUS_DOWN || direction == forward)) {
                 return mSceneRoot;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Visibility.java b/v17/leanback/src/android/support/v17/leanback/widget/Visibility.java
new file mode 100644
index 0000000..b16a2f9
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/Visibility.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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.v17.leanback.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.support.annotation.IntDef;
+import android.support.annotation.RestrictTo;
+import android.view.View;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** @hide */
+@RestrictTo(LIBRARY_GROUP)
+@IntDef({View.VISIBLE, View.INVISIBLE, View.GONE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface Visibility {}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java b/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
index 04698e2..2156dce 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
@@ -22,16 +22,21 @@
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.TintableBackgroundView;
+import android.support.v4.widget.AutoSizeableTextView;
+import android.support.v4.widget.TextViewCompat;
 import android.support.v7.appcompat.R;
 import android.util.AttributeSet;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Button;
+import android.widget.TextView;
 
 /**
  * A {@link Button} which supports compatible features on older versions of the platform,
@@ -48,7 +53,8 @@
  * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
  * You should only need to manually use this class when writing custom views.</p>
  */
-public class AppCompatButton extends Button implements TintableBackgroundView {
+public class AppCompatButton extends Button implements TintableBackgroundView,
+        AutoSizeableTextView {
 
     private final AppCompatBackgroundHelper mBackgroundTintHelper;
     private final AppCompatTextHelper mTextHelper;
@@ -176,6 +182,169 @@
         info.setClassName(Button.class.getName());
     }
 
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (mTextHelper != null) {
+            mTextHelper.onLayout(changed, left, top, right, bottom);
+        }
+    }
+
+    @Override
+    public void setTextSize(int unit, float size) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            super.setTextSize(unit, size);
+        } else {
+            if (mTextHelper != null) {
+                mTextHelper.setTextSize(unit, size);
+            }
+        }
+    }
+
+    @Override
+    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+        super.onTextChanged(text, start, lengthBefore, lengthAfter);
+        if (mTextHelper != null && Build.VERSION.SDK_INT < 26 && mTextHelper.isAutoSizeEnabled()) {
+            mTextHelper.autoSizeText();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public void setAutoSizeTextTypeWithDefaults(
+            @TextViewCompat.AutoSizeTextType int autoSizeTextType) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            super.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
+        } else {
+            if (mTextHelper != null) {
+                mTextHelper.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public void setAutoSizeTextTypeUniformWithConfiguration(
+            int autoSizeMinTextSize,
+            int autoSizeMaxTextSize,
+            int autoSizeStepGranularity,
+            int unit) throws IllegalArgumentException {
+        if (Build.VERSION.SDK_INT >= 26) {
+            super.setAutoSizeTextTypeUniformWithConfiguration(
+                    autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
+        } else {
+            if (mTextHelper != null) {
+                mTextHelper.setAutoSizeTextTypeUniformWithConfiguration(
+                        autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit)
+            throws IllegalArgumentException {
+        if (Build.VERSION.SDK_INT >= 26) {
+            super.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
+        } else {
+            if (mTextHelper != null) {
+                mTextHelper.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    @TextViewCompat.AutoSizeTextType
+    public int getAutoSizeTextType() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeTextType() == TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM
+                    ? TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM
+                    : TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeTextType();
+            }
+        }
+        return TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public int getAutoSizeStepGranularity() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeStepGranularity();
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeStepGranularity();
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public int getAutoSizeMinTextSize() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeMinTextSize();
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeMinTextSize();
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public int getAutoSizeMaxTextSize() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeMaxTextSize();
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeMaxTextSize();
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @Override
+    public int[] getAutoSizeTextAvailableSizes() {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return super.getAutoSizeTextAvailableSizes();
+        } else {
+            if (mTextHelper != null) {
+                return mTextHelper.getAutoSizeTextAvailableSizes();
+            }
+        }
+        return new int[0];
+    }
+
     /**
      * Sets the properties of this field to transform input to ALL CAPS
      * display. This may use a "small caps" formatting if available.
diff --git a/v7/appcompat/tests/AndroidManifest.xml b/v7/appcompat/tests/AndroidManifest.xml
index cb58573..abad7d6 100644
--- a/v7/appcompat/tests/AndroidManifest.xml
+++ b/v7/appcompat/tests/AndroidManifest.xml
@@ -79,6 +79,11 @@
             android:theme="@style/Theme.AppCompat.Light"/>
 
         <activity
+            android:name="android.support.v7.widget.AppCompatButtonAutoSizeActivity"
+            android:label="@string/app_compat_button_auto_size_activity"
+            android:theme="@style/Theme.AppCompat.Light"/>
+
+        <activity
             android:name="android.support.v7.widget.AppCompatImageButtonActivity"
             android:label="@string/app_compat_image_button_activity"
             android:theme="@style/Theme.AppCompat.Light"/>
diff --git a/v7/appcompat/tests/res/layout/appcompat_button_autosize_activity.xml b/v7/appcompat/tests/res/layout/appcompat_button_autosize_activity.xml
new file mode 100644
index 0000000..407154a
--- /dev/null
+++ b/v7/appcompat/tests/res/layout/appcompat_button_autosize_activity.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/view_autosize_uniform"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1"
+        app:autoSizeTextType="uniform"
+        app:autoSizeMinTextSize="2px"
+        app:autoSizeMaxTextSize="50dp"
+        app:autoSizeStepGranularity="1px" />
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/view_autosize_uniform_predef_sizes"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1"
+        app:autoSizeTextType="uniform"
+        app:autoSizePresetSizes="@array/auto_size_predefined_sizes" />
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/view_autosize_uniform_predef_sizes_redundant_values"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1"
+        app:autoSizeTextType="uniform"
+        app:autoSizePresetSizes="@array/auto_size_predefined_sizes_redundant_values" />
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/view_text"
+        android:layout_width="100dp"
+        android:layout_height="200dp"
+        android:text="@string/sample_text1" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/tests/res/layout/appcompat_textview_autosize_activity.xml b/v7/appcompat/tests/res/layout/appcompat_textview_autosize_activity.xml
index b9df4cc..f9aafcc 100644
--- a/v7/appcompat/tests/res/layout/appcompat_textview_autosize_activity.xml
+++ b/v7/appcompat/tests/res/layout/appcompat_textview_autosize_activity.xml
@@ -16,13 +16,13 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/layout_textviewtest"
+    android:id="@+id/container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="horizontal">
+    android:orientation="vertical">
 
     <android.support.v7.widget.AppCompatTextView
-        android:id="@+id/textview_autosize_uniform"
+        android:id="@+id/view_autosize_uniform"
         android:layout_width="100dp"
         android:layout_height="200dp"
         android:text="@string/sample_text1"
@@ -32,7 +32,7 @@
         app:autoSizeStepGranularity="1px" />
 
     <android.support.v7.widget.AppCompatTextView
-        android:id="@+id/textview_autosize_uniform_predef_sizes"
+        android:id="@+id/view_autosize_uniform_predef_sizes"
         android:layout_width="100dp"
         android:layout_height="200dp"
         android:text="@string/sample_text1"
@@ -40,7 +40,7 @@
         app:autoSizePresetSizes="@array/auto_size_predefined_sizes" />
 
     <android.support.v7.widget.AppCompatTextView
-        android:id="@+id/textview_autosize_uniform_predef_sizes_redundant_values"
+        android:id="@+id/view_autosize_uniform_predef_sizes_redundant_values"
         android:layout_width="100dp"
         android:layout_height="200dp"
         android:text="@string/sample_text1"
@@ -48,10 +48,10 @@
         app:autoSizePresetSizes="@array/auto_size_predefined_sizes_redundant_values" />
 
     <android.support.v7.widget.AppCompatTextView
-        android:id="@+id/textview_text"
-        android:text="@string/sample_text1"
+        android:id="@+id/view_text"
         android:layout_width="100dp"
-        android:layout_height="200dp"/>
+        android:layout_height="200dp"
+        android:text="@string/sample_text1" />
 
     <android.support.v7.widget.AppCompatEditText
         android:id="@+id/edittext_autosize_uniform"
diff --git a/v7/appcompat/tests/res/values/strings.xml b/v7/appcompat/tests/res/values/strings.xml
index 86386e2..90be8b3 100644
--- a/v7/appcompat/tests/res/values/strings.xml
+++ b/v7/appcompat/tests/res/values/strings.xml
@@ -56,6 +56,7 @@
     <string name="app_compat_spinner_activity">AppCompat spinner</string>
     <string name="app_compat_text_view_activity">AppCompat text view</string>
     <string name="app_compat_text_view_auto_size_activity">AppCompat text view auto-size</string>
+    <string name="app_compat_button_auto_size_activity">AppCompat button auto-size</string>
     <string name="sample_text1">Sample text 1</string>
     <string name="sample_text2">Sample text 2</string>
     <string name="app_compat_image_button_activity">AppCompat image button</string>
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseAutoSizeTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseAutoSizeTest.java
new file mode 100644
index 0000000..cc6f91b
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseAutoSizeTest.java
@@ -0,0 +1,1210 @@
+/*
+ * Copyright (C) 2017 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.v7.widget;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.IdRes;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v4.content.res.ResourcesCompat;
+import android.support.v4.widget.AutoSizeableTextView;
+import android.support.v4.widget.TextViewCompat;
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.BaseTestActivity;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Base class for testing auto-size-text enabled views in appcompat-v7 that implement the
+ * <code>AutoSizeableTextView</code> interface. Extensions of this class run all tests
+ * from here and can add test cases specific to the functionality they add to the relevant
+ * base view class.
+ */
+public abstract class AppCompatBaseAutoSizeTest<A extends BaseTestActivity,
+        T extends TextView & AutoSizeableTextView> {
+
+    @Rule
+    public final ActivityTestRule<A> mActivityTestRule;
+
+    protected A mActivity;
+    protected Instrumentation mInstrumentation;
+    protected ViewGroup mContainer;
+
+    public AppCompatBaseAutoSizeTest(Class clazz) {
+        mActivityTestRule = new ActivityTestRule<A>(clazz);
+    }
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityTestRule.getActivity();
+        mContainer = mActivity.findViewById(R.id.container);
+    }
+
+    @Test
+    @MediumTest
+    @SdkSuppress(minSdkVersion = 16)
+    // public TextView#getMaxLines only introduced in API 16.
+    public void testAutoSizeCallers_setMaxLines() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        // Configure layout params and auto-size both in pixels to dodge flakiness on different
+        // devices.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                500, 500);
+        final String text = "one two three four five six seven eight nine ten";
+        mActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setLayoutParams(layoutParams);
+                ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                        1 /* autoSizeMinTextSize */,
+                        5000 /* autoSizeMaxTextSize */,
+                        1 /* autoSizeStepGranularity */,
+                        TypedValue.COMPLEX_UNIT_PX);
+                autoSizeView.setText(text);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        float initialSize = 0;
+        for (int i = 1; i < 10; i++) {
+            final int maxLines = i;
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setMaxLines(maxLines);
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            float expectedSmallerSize = autoSizeView.getTextSize();
+            if (i == 1) {
+                initialSize = expectedSmallerSize;
+            }
+
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setMaxLines(maxLines + 1);
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            assertTrue(expectedSmallerSize <= autoSizeView.getTextSize());
+        }
+        assertTrue(initialSize < autoSizeView.getTextSize());
+
+        initialSize = 999999;
+        for (int i = 10; i > 1; i--) {
+            final int maxLines = i;
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setMaxLines(maxLines);
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            float expectedLargerSize = autoSizeView.getTextSize();
+            if (i == 10) {
+                initialSize = expectedLargerSize;
+            }
+
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setMaxLines(maxLines - 1);
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            assertTrue(expectedLargerSize >= autoSizeView.getTextSize());
+        }
+        assertTrue(initialSize > autoSizeView.getTextSize());
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_autoSizeCalledWhenTypeChanged() throws Throwable {
+        final T view = mContainer.findViewById(R.id.view_text);
+
+        // Make sure we pick an already inflated non auto-sized text view.
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) view).getAutoSizeTextType());
+        // Set the text size to a very low value in order to prepare for auto-size.
+        final int customTextSize = 3;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                view.setTextSize(TypedValue.COMPLEX_UNIT_PX, customTextSize);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(customTextSize, view.getTextSize(), 0f);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) view).setAutoSizeTextTypeWithDefaults(
+                        TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        // The size of the text should have changed.
+        assertNotEquals(customTextSize, view.getTextSize(), 0f);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setText() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+
+        // Configure layout params and auto-size both in pixels to dodge flakiness on different
+        // devices.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                500, 500);
+        mActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setLayoutParams(layoutParams);
+                ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                        1, 5000, 1, TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        final String initialText = "13characters ";
+        final StringBuilder textToSet = new StringBuilder().append(initialText);
+        float initialSize = 0;
+
+        // As we add characters the text size shrinks.
+        for (int i = 0; i < 10; i++) {
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setText(textToSet.toString());
+                }
+            });
+
+            mInstrumentation.waitForIdleSync();
+            float expectedLargerSize = autoSizeView.getTextSize();
+            if (i == 0) {
+                initialSize = expectedLargerSize;
+            }
+
+            textToSet.append(initialText);
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setText(textToSet.toString());
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            assertTrue(expectedLargerSize >= autoSizeView.getTextSize());
+        }
+        assertTrue(initialSize > autoSizeView.getTextSize());
+
+        initialSize = 9999999;
+        // As we remove characters the text size expands.
+        for (int i = 9; i >= 0; i--) {
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setText(textToSet.toString());
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            float expectedSmallerSize = autoSizeView.getTextSize();
+            if (i == 0) {
+                initialSize = expectedSmallerSize;
+            }
+
+            textToSet.replace((textToSet.length() - initialText.length()),
+                    textToSet.length(), "");
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    autoSizeView.setText(textToSet.toString());
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+
+            assertTrue(autoSizeView.getTextSize() >= expectedSmallerSize);
+        }
+        assertTrue(autoSizeView.getTextSize() > initialSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_equivalentConfigurations() throws Throwable {
+        final DisplayMetrics dm = mActivity.getResources().getDisplayMetrics();
+        final int minTextSize = 10;
+        final int maxTextSize = 20;
+        final int granularity = 2;
+        final int unit = TypedValue.COMPLEX_UNIT_SP;
+
+        final T granularityView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) granularityView).setAutoSizeTextTypeUniformWithConfiguration(
+                minTextSize, maxTextSize, granularity, unit);
+
+        final T presetView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) presetView).setAutoSizeTextTypeUniformWithPresetSizes(
+                new int[]{minTextSize, 12, 14, 16, 18, maxTextSize}, unit);
+
+        // The TextViews have been configured differently but the end result should be nearly
+        // identical.
+        final int expectedAutoSizeType = AppCompatTextView.AUTO_SIZE_TEXT_TYPE_UNIFORM;
+        assertEquals(expectedAutoSizeType,
+                ((AutoSizeableTextView) granularityView).getAutoSizeTextType());
+        assertEquals(expectedAutoSizeType,
+                ((AutoSizeableTextView) presetView).getAutoSizeTextType());
+
+        final int expectedMinTextSizeInPx = Math.round(
+                TypedValue.applyDimension(unit, minTextSize, dm));
+        assertEquals(expectedMinTextSizeInPx,
+                ((AutoSizeableTextView) granularityView).getAutoSizeMinTextSize());
+        assertEquals(expectedMinTextSizeInPx,
+                ((AutoSizeableTextView) presetView).getAutoSizeMinTextSize());
+
+        final int expectedMaxTextSizeInPx = Math.round(
+                TypedValue.applyDimension(unit, maxTextSize, dm));
+        assertEquals(expectedMaxTextSizeInPx,
+                ((AutoSizeableTextView) granularityView).getAutoSizeMaxTextSize());
+        assertEquals(expectedMaxTextSizeInPx,
+                ((AutoSizeableTextView) presetView).getAutoSizeMaxTextSize());
+
+        // Configured with granularity.
+        assertEquals(Math.round(TypedValue.applyDimension(unit, granularity, dm)),
+                ((AutoSizeableTextView) granularityView).getAutoSizeStepGranularity());
+        // Configured with preset values, there is no granularity.
+        assertEquals(-1,
+                ((AutoSizeableTextView) presetView).getAutoSizeStepGranularity());
+
+        // Both TextViews generate exactly the same sizes in pixels to choose from when auto-sizing.
+        assertArrayEquals(
+                ((AutoSizeableTextView) granularityView).getAutoSizeTextAvailableSizes(),
+                ((AutoSizeableTextView) presetView).getAutoSizeTextAvailableSizes());
+
+        final String someText = "This is a string";
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                500, 500);
+        // Configure identically and attach to layout.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                granularityView.setLayoutParams(layoutParams);
+                presetView.setLayoutParams(layoutParams);
+
+                LinearLayout ll = mActivity.findViewById(R.id.container);
+                ll.removeAllViews();
+                ll.addView(granularityView);
+                ll.addView(presetView);
+
+                granularityView.setText(someText);
+                presetView.setText(someText);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(granularityView.getTextSize(), presetView.getTextSize(), 0f);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setHeight() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(
+                R.id.view_autosize_uniform, true);
+        // Do not force exact height only.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                200,
+                LinearLayout.LayoutParams.WRAP_CONTENT);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setLayoutParams(layoutParams);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setHeight(autoSizeView.getHeight() / 4);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() < initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setCompoundDrawables() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        final float initialTextSize = autoSizeView.getTextSize();
+        final Drawable drawable = ResourcesCompat.getDrawable(mActivity.getResources(),
+                R.drawable.test_drawable_red, null);
+        drawable.setBounds(0, 0, autoSizeView.getWidth() / 3, autoSizeView.getHeight() / 3);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setCompoundDrawables(drawable, drawable, drawable, drawable);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() < initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setCompoundDrawablesRelative() throws Throwable {
+        if (Build.VERSION.SDK_INT >= 17) {
+            final T autoSizeView = prepareAndRetrieveAutoSizeTestData(
+                    R.id.view_autosize_uniform, false);
+            final float initialTextSize = autoSizeView.getTextSize();
+            final Drawable drawable = ResourcesCompat.getDrawable(mActivity.getResources(),
+                    R.drawable.test_drawable_red, null);
+            drawable.setBounds(0, 0, autoSizeView.getWidth() / 3,
+                    autoSizeView.getHeight() / 3);
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (Build.VERSION.SDK_INT >= 17) {
+                        autoSizeView.setCompoundDrawablesRelative(
+                                drawable, drawable, drawable, drawable);
+                    }
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+
+            assertTrue(autoSizeView.getTextSize() < initialTextSize);
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setCompoundDrawablePadding() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        // Prepare a larger layout in order not to hit the min value easily.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setWidth(autoSizeView.getWidth() * 2);
+                autoSizeView.setHeight(autoSizeView.getHeight() * 2);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        // Setup the drawables before setting their padding in order to modify the available
+        // space and trigger a resize.
+        final Drawable drawable = ResourcesCompat.getDrawable(mActivity.getResources(),
+                R.drawable.test_drawable_red, null);
+        drawable.setBounds(0, 0, autoSizeView.getWidth() / 4, autoSizeView.getHeight() / 4);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setCompoundDrawables(
+                        drawable, drawable, drawable, drawable);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setCompoundDrawablePadding(
+                        autoSizeView.getCompoundDrawablePadding() + 10);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() < initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setPadding() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setPadding(
+                        autoSizeView.getWidth() / 3, autoSizeView.getHeight() / 3,
+                        autoSizeView.getWidth() / 3, autoSizeView.getHeight() / 3);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() < initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setPaddingRelative() throws Throwable {
+        if (Build.VERSION.SDK_INT >= 16) {
+            final T autoSizeView = prepareAndRetrieveAutoSizeTestData(
+                    R.id.view_autosize_uniform, false);
+            final float initialTextSize = autoSizeView.getTextSize();
+
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (Build.VERSION.SDK_INT > 16) {
+                        autoSizeView.setPaddingRelative(
+                                autoSizeView.getWidth() / 3, autoSizeView.getHeight() / 3,
+                                autoSizeView.getWidth() / 3, autoSizeView.getHeight() / 3);
+                    }
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+
+            assertTrue(autoSizeView.getTextSize() < initialTextSize);
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setTypeface() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setText("The typeface change needs a bit more text then "
+                        + "the default used for this batch of tests in order to get to resize text."
+                        + " The resize function is always called but even with different typefaces "
+                        + "there may not be a need to resize text because it just fits. The longer "
+                        + "the text, the higher the chance for a resize. And here is yet another "
+                        + "sentence to make sure this test is not flaky. Not flaky at all.");
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float initialTextSize = autoSizeView.getTextSize();
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Typeface differentTypeface = Typeface.MONOSPACE;
+                if (autoSizeView.getTypeface() == Typeface.MONOSPACE) {
+                    differentTypeface = Typeface.SANS_SERIF;
+                }
+                autoSizeView.setTypeface(differentTypeface);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float changedTextSize = autoSizeView.getTextSize();
+
+        // Don't really know if it is larger or smaller (depends on the typeface chosen above),
+        // but it should definitely have changed.
+        assertNotEquals(initialTextSize, changedTextSize, 0f);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setTypeface(autoSizeView.getTypeface());
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(changedTextSize, autoSizeView.getTextSize(), 0f);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setLetterSpacing() throws Throwable {
+        if (Build.VERSION.SDK_INT >= 21) {
+            final T autoSizeView = prepareAndRetrieveAutoSizeTestData(
+                    R.id.view_autosize_uniform, false);
+            final float initialTextSize = autoSizeView.getTextSize();
+
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (Build.VERSION.SDK_INT >= 21) {
+                        autoSizeView.setLetterSpacing(
+                                autoSizeView.getLetterSpacing() * 1.5f + 4.5f);
+                    }
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+            final float changedTextSize = autoSizeView.getTextSize();
+
+            assertTrue(changedTextSize < initialTextSize);
+
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (Build.VERSION.SDK_INT >= 21) {
+                        autoSizeView.setLetterSpacing(autoSizeView.getLetterSpacing());
+                    }
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+
+            assertEquals(changedTextSize, autoSizeView.getTextSize(), 0f);
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setMaxHeight() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                true);
+        // Do not force exact height only.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                200,
+                LinearLayout.LayoutParams.WRAP_CONTENT);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setLayoutParams(layoutParams);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setMaxHeight(autoSizeView.getHeight() / 4);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() < initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setMaxWidth() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                true);
+        // Do not force exact width only.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.WRAP_CONTENT,
+                200);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setLayoutParams(layoutParams);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setMaxWidth(autoSizeView.getWidth() / 4);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() != initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setWidth() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                true);
+        // Do not force exact width only.
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.WRAP_CONTENT,
+                200);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setLayoutParams(layoutParams);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setWidth(autoSizeView.getWidth() / 4);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeView.getTextSize() != initialTextSize);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setTextSizeIsNoOp() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        final float initialTextSize = autoSizeView.getTextSize();
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setTextSize(initialTextSize + 123f);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(initialTextSize, autoSizeView.getTextSize(), 0f);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setHorizontallyScrolling() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        // Horizontal scrolling is expected to be deactivated for this test.
+        final float initialTextSize = autoSizeView.getTextSize();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setHorizontallyScrolling(true);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertTrue(autoSizeView.getTextSize() > initialTextSize);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setHorizontallyScrolling(false);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(initialTextSize, autoSizeView.getTextSize(), 0f);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSize_setEllipsize() throws Throwable {
+        final T autoSizeView = mActivity.findViewById(R.id.view_autosize_uniform_predef_sizes);
+        final int initialAutoSizeType = ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType();
+        final int initialMinTextSize =
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize();
+        final int initialMaxTextSize =
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize();
+        final int initialAutoSizeGranularity =
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity();
+        final int initialSizes =
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextAvailableSizes().length;
+
+        assertEquals(null, autoSizeView.getEllipsize());
+        // Verify styled attributes.
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM, initialAutoSizeType);
+        assertNotEquals(-1, initialMinTextSize);
+        assertNotEquals(-1, initialMaxTextSize);
+        // Because this TextView has been configured to use predefined sizes.
+        assertEquals(-1, initialAutoSizeGranularity);
+        assertNotEquals(0, initialSizes);
+
+        final TextUtils.TruncateAt newEllipsizeValue = TextUtils.TruncateAt.END;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setEllipsize(newEllipsizeValue);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(newEllipsizeValue, autoSizeView.getEllipsize());
+        // Beside the ellipsis no auto-size attribute has changed.
+        assertEquals(initialAutoSizeType,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        assertEquals(initialMinTextSize,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize());
+        assertEquals(initialMaxTextSize,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize());
+        assertEquals(initialAutoSizeGranularity,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+        assertEquals(initialSizes,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextAvailableSizes().length);
+    }
+
+    @Test
+    @MediumTest
+    public void testEllipsize_setAutoSize() throws Throwable {
+        final T view = mActivity.findViewById(R.id.view_text);
+        final TextUtils.TruncateAt newEllipsizeValue = TextUtils.TruncateAt.END;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                view.setEllipsize(newEllipsizeValue);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(newEllipsizeValue, view.getEllipsize());
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) view).getAutoSizeTextType());
+        assertEquals(-1, ((AutoSizeableTextView) view).getAutoSizeMinTextSize());
+        assertEquals(-1, ((AutoSizeableTextView) view).getAutoSizeMaxTextSize());
+        assertEquals(-1, ((AutoSizeableTextView) view).getAutoSizeStepGranularity());
+        assertEquals(0,
+                ((AutoSizeableTextView) view).getAutoSizeTextAvailableSizes().length);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) view).setAutoSizeTextTypeWithDefaults(
+                        TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(newEllipsizeValue, view.getEllipsize());
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
+                ((AutoSizeableTextView) view).getAutoSizeTextType());
+        // The auto-size defaults have been used.
+        assertNotEquals(-1, ((AutoSizeableTextView) view).getAutoSizeMinTextSize());
+        assertNotEquals(-1, ((AutoSizeableTextView) view).getAutoSizeMaxTextSize());
+        assertNotEquals(-1, ((AutoSizeableTextView) view).getAutoSizeStepGranularity());
+        assertNotEquals(0,
+                ((AutoSizeableTextView) view).getAutoSizeTextAvailableSizes().length);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_obtainStyledAttributesUsingPredefinedSizes() {
+        DisplayMetrics m = mActivity.getResources().getDisplayMetrics();
+        final T autoSizeViewUniform = mActivity.findViewById(
+                R.id.view_autosize_uniform_predef_sizes);
+
+        // In arrays.xml predefined the step sizes as: 5px, 11dip, 19sp, 29pt, 43mm and 53in.
+        // TypedValue can not use the math library and instead rounds the value by adding
+        // 0.5f when obtaining styled attributes. Check TypedValue#complexToDimensionPixelSize(...)
+        int[] expectedSizesInPx = new int[] {
+                Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 5f, m)),
+                Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 11f, m)),
+                Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 19f, m)),
+                Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PT, 29f, m)),
+                Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 43f, m)),
+                Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_IN, 53f, m))};
+
+        boolean containsValueFromExpectedSizes = false;
+        final int textSize = (int) autoSizeViewUniform.getTextSize();
+        for (int i = 0; i < expectedSizesInPx.length; i++) {
+            if (expectedSizesInPx[i] == textSize) {
+                containsValueFromExpectedSizes = true;
+                break;
+            }
+        }
+        assertTrue(containsValueFromExpectedSizes);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_obtainStyledAttributesPredefinedSizesFiltering() {
+        T autoSizeViewUniform = mActivity.findViewById(
+                R.id.view_autosize_uniform_predef_sizes_redundant_values);
+
+        // In arrays.xml predefined the step sizes as: 40px, 10px, 10px, 10px, 0dp.
+        final int[] expectedSizes = new int[] {10, 40};
+        assertArrayEquals(expectedSizes,
+                ((AutoSizeableTextView) autoSizeViewUniform).getAutoSizeTextAvailableSizes());
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_predefinedSizesFilteringAndSorting() throws Throwable {
+        final T view = mActivity.findViewById(R.id.view_text);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) view).getAutoSizeTextType());
+
+        final int[] predefinedSizes = new int[] {400, 0, 10, 40, 10, 10, 0, 0};
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) view).setAutoSizeTextTypeUniformWithPresetSizes(
+                        predefinedSizes, TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertArrayEquals(new int[] {10, 40, 400},
+                ((AutoSizeableTextView) view).getAutoSizeTextAvailableSizes());
+    }
+
+    @Test(expected = NullPointerException.class)
+    @SmallTest
+    public void testAutoSizeUniform_predefinedSizesNullArray() throws Throwable {
+        final T view = mActivity.findViewById(R.id.view_text);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) view).getAutoSizeTextType());
+
+        final int[] predefinedSizes = null;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) view).setAutoSizeTextTypeUniformWithPresetSizes(
+                        predefinedSizes, TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_predefinedSizesEmptyArray() throws Throwable {
+        final T view = mActivity.findViewById(R.id.view_text);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) view).getAutoSizeTextType());
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) view).setAutoSizeTextTypeWithDefaults(
+                        TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        final int[] defaultSizes = ((AutoSizeableTextView) view).getAutoSizeTextAvailableSizes();
+        assertNotNull(defaultSizes);
+        assertTrue(defaultSizes.length > 0);
+
+        final int[] predefinedSizes = new int[0];
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) view).setAutoSizeTextTypeUniformWithPresetSizes(
+                        predefinedSizes, TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        final int[] newSizes = ((AutoSizeableTextView) view).getAutoSizeTextAvailableSizes();
+        assertNotNull(defaultSizes);
+        assertArrayEquals(defaultSizes, newSizes);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_buildsSizes() throws Throwable {
+        final T autoSizeViewUniform = mActivity.findViewById(R.id.view_autosize_uniform);
+
+        // Verify that the interval limits are both included.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) autoSizeViewUniform)
+                        .setAutoSizeTextTypeUniformWithConfiguration(10, 20, 2,
+                                TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertArrayEquals(
+                new int[] {10, 12, 14, 16, 18, 20},
+                ((AutoSizeableTextView) autoSizeViewUniform).getAutoSizeTextAvailableSizes());
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) autoSizeViewUniform)
+                        .setAutoSizeTextTypeUniformWithConfiguration(
+                                ((AutoSizeableTextView) autoSizeViewUniform)
+                                        .getAutoSizeMinTextSize(),
+                                19,
+                                ((AutoSizeableTextView) autoSizeViewUniform)
+                                        .getAutoSizeStepGranularity(),
+                                TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertArrayEquals(
+                new int[] {10, 12, 14, 16, 18},
+                ((AutoSizeableTextView) autoSizeViewUniform).getAutoSizeTextAvailableSizes());
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) autoSizeViewUniform)
+                        .setAutoSizeTextTypeUniformWithConfiguration(
+                                ((AutoSizeableTextView) autoSizeViewUniform)
+                                        .getAutoSizeMinTextSize(),
+                                21,
+                                ((AutoSizeableTextView) autoSizeViewUniform)
+                                        .getAutoSizeStepGranularity(),
+                                TypedValue.COMPLEX_UNIT_PX);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertArrayEquals(
+                new int[] {10, 12, 14, 16, 18, 20},
+                ((AutoSizeableTextView) autoSizeViewUniform).getAutoSizeTextAvailableSizes());
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_getSetAutoSizeTextDefaults() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        // Min/Max/Granularity values for auto-sizing are 0 because they are not used.
+        assertEquals(-1, ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize());
+        assertEquals(-1, ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize());
+        assertEquals(-1,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeWithDefaults(
+                TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        // Min/Max default values for auto-sizing XY have been loaded.
+        final int minSize = ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize();
+        final int maxSize = ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize();
+        assertTrue(0 < minSize);
+        assertTrue(minSize < maxSize);
+        assertNotEquals(0,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+
+        ((AutoSizeableTextView) autoSizeView)
+                .setAutoSizeTextTypeWithDefaults(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        // Min/Max values for auto-sizing XY have been cleared.
+        assertEquals(-1, ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize());
+        assertEquals(-1, ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize());
+        assertEquals(-1,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_getSetAutoSizeStepGranularity() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        final int initialValue = -1;
+        assertEquals(initialValue,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeWithDefaults(
+                TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        final int defaultValue = 1; // 1px.
+        // If the auto-size type is AUTO_SIZE_TEXT_TYPE_UNIFORM then it means autoSizeView went
+        // through the auto-size setup and given that 0 is an invalid value it changed it to the
+        // default.
+        assertEquals(defaultValue,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+
+        final int newValue = 33;
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize(),
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize(),
+                newValue,
+                TypedValue.COMPLEX_UNIT_PX);
+        assertEquals(newValue, ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity());
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_getSetAutoSizeMinTextSize() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeWithDefaults(
+                TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        final int minSize = ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize();
+        assertNotEquals(0, minSize);
+        final int maxSize = ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize();
+        assertNotEquals(0, maxSize);
+
+        // This is just a test check to verify the next assertions. If this fails it is a problem
+        // of this test setup (we need at least 2 units).
+        assertTrue((maxSize - minSize) > 1);
+        final int newMinSize = maxSize - 1;
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                newMinSize,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize(),
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity(),
+                TypedValue.COMPLEX_UNIT_PX);
+
+        assertEquals(newMinSize, ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize());
+        // Max size has not changed.
+        assertEquals(maxSize, ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize());
+
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                newMinSize,
+                newMinSize + 10,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity(),
+                TypedValue.COMPLEX_UNIT_SP);
+
+        // It does not matter which unit has been used to set the min size, the getter always
+        // returns it in pixels.
+        assertEquals(Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+                newMinSize, mActivity.getResources().getDisplayMetrics())),
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @SmallTest
+    public void testAutoSizeUniform_throwsException_whenMaxLessThanMin() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                10, 9, 1, TypedValue.COMPLEX_UNIT_SP);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @SmallTest
+    public void testAutoSizeUniform_throwsException_minLessThanZero() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                -1, 9, 1, TypedValue.COMPLEX_UNIT_SP);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @SmallTest
+    public void testAutoSizeUniform_throwsException_maxLessThanZero() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                10, -1, 1, TypedValue.COMPLEX_UNIT_SP);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @SmallTest
+    public void testAutoSizeUniform_throwsException_granularityLessThanZero() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                10, 20, -1, TypedValue.COMPLEX_UNIT_SP);
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeUniform_getSetAutoSizeMaxTextSize() {
+        final T autoSizeView = getNewAutoSizeViewInstance();
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeWithDefaults(
+                TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeTextType());
+        final int minSize = ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize();
+        assertNotEquals(0, minSize);
+        final int maxSize = ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize();
+        assertNotEquals(0, maxSize);
+
+        final int newMaxSize = maxSize + 11;
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize(),
+                newMaxSize,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity(),
+                TypedValue.COMPLEX_UNIT_PX);
+
+        assertEquals(newMaxSize, ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize());
+        // Min size has not changed.
+        assertEquals(minSize, ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize());
+        ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeUniformWithConfiguration(
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMinTextSize(),
+                newMaxSize,
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeStepGranularity(),
+                TypedValue.COMPLEX_UNIT_SP);
+        // It does not matter which unit has been used to set the max size, the getter always
+        // returns it in pixels.
+        assertEquals(Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+                newMaxSize, mActivity.getResources().getDisplayMetrics())),
+                ((AutoSizeableTextView) autoSizeView).getAutoSizeMaxTextSize());
+    }
+
+    @Test
+    @MediumTest
+    public void testAutoSizeCallers_setTextSizeChangesSizeWhenAutoSizeDisabled() throws Throwable {
+        final T autoSizeView = prepareAndRetrieveAutoSizeTestData(R.id.view_autosize_uniform,
+                false);
+        // Disable auto-size.
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AutoSizeableTextView) autoSizeView).setAutoSizeTextTypeWithDefaults(
+                        TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        final float newTextSizeInPx = 123f;
+        assertNotEquals(newTextSizeInPx, autoSizeView.getTextSize(), 0f);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                autoSizeView.setTextSize(TypedValue.COMPLEX_UNIT_PX, newTextSizeInPx);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(newTextSizeInPx, autoSizeView.getTextSize(), 0f);
+    }
+
+    /**
+     * Some View attributes require non-fixed width and/or layout height. This function removes
+     * all other existing views from the layout leaving only one auto-size TextView (for exercising
+     * the auto-size behavior) which has been set up to suit the test needs.
+     *
+     * @param viewId The id of the view to prepare.
+     * @param shouldWrapLayoutContent Specifies if the layout params should wrap content
+     *
+     * @return a View configured for auto size tests.
+     */
+    private T prepareAndRetrieveAutoSizeTestData(final @IdRes int viewId,
+            final boolean shouldWrapLayoutContent) throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                LinearLayout ll = mActivity.findViewById(R.id.container);
+                T targetedView = mActivity.findViewById(viewId);
+                ll.removeAllViews();
+                ll.addView(targetedView);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        final T view = mActivity.findViewById(viewId);
+        if (shouldWrapLayoutContent) {
+            // Do not force exact width or height.
+            final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                    LinearLayout.LayoutParams.WRAP_CONTENT,
+                    LinearLayout.LayoutParams.WRAP_CONTENT);
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    view.setLayoutParams(layoutParams);
+                }
+            });
+            mInstrumentation.waitForIdleSync();
+        }
+
+        return view;
+    }
+
+    // Returns a new instance of the auto-sizable view for mActivity.
+    protected abstract T getNewAutoSizeViewInstance();
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonAutoSizeActivity.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonAutoSizeActivity.java
new file mode 100644
index 0000000..cd5c6a9
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonAutoSizeActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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.v7.widget;
+
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.BaseTestActivity;
+
+/**
+ * This activity is used to test the auto-size feature of the {@link AppCompatButton}
+ * class.
+ */
+public class AppCompatButtonAutoSizeActivity extends BaseTestActivity {
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.appcompat_button_autosize_activity;
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonAutoSizeTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonAutoSizeTest.java
new file mode 100644
index 0000000..cc54463
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonAutoSizeTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.v7.widget;
+
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class AppCompatButtonAutoSizeTest extends
+        AppCompatBaseAutoSizeTest<AppCompatButtonAutoSizeActivity, AppCompatButton> {
+
+    public AppCompatButtonAutoSizeTest() {
+        super(AppCompatButtonAutoSizeActivity.class);
+    }
+
+    @Override
+    protected AppCompatButton getNewAutoSizeViewInstance() {
+        return new AppCompatButton(mActivity);
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeActivity.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeActivity.java
index 17ff6a2..5736dd9 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeActivity.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeActivity.java
@@ -15,6 +15,7 @@
  */
 package android.support.v7.widget;
 
+import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
 
 /**
@@ -24,6 +25,6 @@
 public class AppCompatTextViewAutoSizeActivity extends BaseTestActivity {
     @Override
     protected int getContentViewLayoutResId() {
-        return android.support.v7.appcompat.test.R.layout.appcompat_textview_autosize_activity;
+        return R.layout.appcompat_textview_autosize_activity;
     }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeTest.java
index 95d4af3..45da1e4 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeTest.java
@@ -17,286 +17,39 @@
 
 import static junit.framework.TestCase.assertEquals;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import android.app.Instrumentation;
 import android.content.Context;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.annotation.Nullable;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.v4.content.res.ResourcesCompat;
 import android.support.v4.widget.TextViewCompat;
 import android.support.v7.appcompat.test.R;
-import android.text.TextUtils;
 import android.text.method.SingleLineTransformationMethod;
 import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
-public class AppCompatTextViewAutoSizeTest {
-    @Rule
-    public final ActivityTestRule<AppCompatTextViewAutoSizeActivity> mActivityTestRule;
-
-    private Instrumentation mInstrumentation;
-    private AppCompatTextViewAutoSizeActivity mActivity;
+public class AppCompatTextViewAutoSizeTest extends
+        AppCompatBaseAutoSizeTest<AppCompatTextViewAutoSizeActivity, AppCompatTextView> {
 
     public AppCompatTextViewAutoSizeTest() {
-        mActivityTestRule = new ActivityTestRule<>(AppCompatTextViewAutoSizeActivity.class);
+        super(AppCompatTextViewAutoSizeActivity.class);
     }
 
-    @Before
-    public void setup() {
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        mActivity = mActivityTestRule.getActivity();
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = 16)
-    // public TextView#getMaxLines only introduced in API 16.
-    public void testAutoSizeCallers_setMaxLines() throws Throwable {
-        final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, false);
-        // Configure layout params and auto-size both in pixels to dodge flakiness on different
-        // devices.
-        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
-                500, 500);
-        final String text = "one two three four five six seven eight nine ten";
-        mActivity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setLayoutParams(layoutParams);
-                autoSizeTextView.setAutoSizeTextTypeUniformWithConfiguration(
-                        1 /* autoSizeMinTextSize */,
-                        5000 /* autoSizeMaxTextSize */,
-                        1 /* autoSizeStepGranularity */,
-                        TypedValue.COMPLEX_UNIT_PX);
-                autoSizeTextView.setText(text);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        float initialSize = 0;
-        for (int i = 1; i < 10; i++) {
-            final int maxLines = i;
-            mActivity.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    autoSizeTextView.setMaxLines(maxLines);
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-            float expectedSmallerSize = autoSizeTextView.getTextSize();
-            if (i == 1) {
-                initialSize = expectedSmallerSize;
-            }
-
-            mActivity.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    autoSizeTextView.setMaxLines(maxLines + 1);
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-            assertTrue(expectedSmallerSize <= autoSizeTextView.getTextSize());
-        }
-        assertTrue(initialSize < autoSizeTextView.getTextSize());
-
-        initialSize = 999999;
-        for (int i = 10; i > 1; i--) {
-            final int maxLines = i;
-            mActivity.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    autoSizeTextView.setMaxLines(maxLines);
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-            float expectedLargerSize = autoSizeTextView.getTextSize();
-            if (i == 10) {
-                initialSize = expectedLargerSize;
-            }
-
-            mActivity.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    autoSizeTextView.setMaxLines(maxLines - 1);
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-            assertTrue(expectedLargerSize >= autoSizeTextView.getTextSize());
-        }
-        assertTrue(initialSize > autoSizeTextView.getTextSize());
-    }
-
-    @Test
-    public void testAutoSizeCallers_setText() throws Throwable {
-        final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, false);
-
-        // Configure layout params and auto-size both in pixels to dodge flakiness on different
-        // devices.
-        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
-                500, 500);
-        mActivity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setLayoutParams(layoutParams);
-                autoSizeTextView.setAutoSizeTextTypeUniformWithConfiguration(
-                        1, 5000, 1, TypedValue.COMPLEX_UNIT_PX);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        final String initialText = "13characters ";
-        final StringBuilder textToSet = new StringBuilder().append(initialText);
-        float initialSize = 0;
-
-        // As we add characters the text size shrinks.
-        for (int i = 0; i < 10; i++) {
-            mActivity.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    autoSizeTextView.setText(textToSet.toString());
-                }
-            });
-
-            mInstrumentation.waitForIdleSync();
-            float expectedLargerSize = autoSizeTextView.getTextSize();
-            if (i == 0) {
-                initialSize = expectedLargerSize;
-            }
-
-            textToSet.append(initialText);
-            mActivity.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    autoSizeTextView.setText(textToSet.toString());
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-            assertTrue(expectedLargerSize >= autoSizeTextView.getTextSize());
-        }
-        assertTrue(initialSize > autoSizeTextView.getTextSize());
-
-        initialSize = 9999999;
-        // As we remove characters the text size expands.
-        for (int i = 9; i >= 0; i--) {
-            mActivity.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    autoSizeTextView.setText(textToSet.toString());
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-            float expectedSmallerSize = autoSizeTextView.getTextSize();
-            if (i == 0) {
-                initialSize = expectedSmallerSize;
-            }
-
-            textToSet.replace((textToSet.length() - initialText.length()),
-                    textToSet.length(), "");
-            mActivity.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    autoSizeTextView.setText(textToSet.toString());
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-
-            assertTrue(autoSizeTextView.getTextSize() >= expectedSmallerSize);
-        }
-        assertTrue(autoSizeTextView.getTextSize() > initialSize);
-    }
-
-    @Test
-    public void testAutoSizeUniform_equivalentConfigurations() throws Throwable {
-        final DisplayMetrics dm = mActivity.getResources().getDisplayMetrics();
-        final int minTextSize = 10;
-        final int maxTextSize = 20;
-        final int granularity = 2;
-        final int unit = TypedValue.COMPLEX_UNIT_SP;
-
-        final AppCompatTextView granularityTextView = new AppCompatTextView(mActivity);
-        granularityTextView.setAutoSizeTextTypeUniformWithConfiguration(
-                minTextSize, maxTextSize, granularity, unit);
-
-        final AppCompatTextView presetTextView = new AppCompatTextView(mActivity);
-        presetTextView.setAutoSizeTextTypeUniformWithPresetSizes(
-                new int[]{minTextSize, 12, 14, 16, 18, maxTextSize}, unit);
-
-        // The TextViews have been configured differently but the end result should be nearly
-        // identical.
-        final int expectedAutoSizeType = AppCompatTextView.AUTO_SIZE_TEXT_TYPE_UNIFORM;
-        assertEquals(expectedAutoSizeType, granularityTextView.getAutoSizeTextType());
-        assertEquals(expectedAutoSizeType, presetTextView.getAutoSizeTextType());
-
-        final int expectedMinTextSizeInPx = Math.round(
-                TypedValue.applyDimension(unit, minTextSize, dm));
-        assertEquals(expectedMinTextSizeInPx, granularityTextView.getAutoSizeMinTextSize());
-        assertEquals(expectedMinTextSizeInPx, presetTextView.getAutoSizeMinTextSize());
-
-        final int expectedMaxTextSizeInPx = Math.round(
-                TypedValue.applyDimension(unit, maxTextSize, dm));
-        assertEquals(expectedMaxTextSizeInPx, granularityTextView.getAutoSizeMaxTextSize());
-        assertEquals(expectedMaxTextSizeInPx, presetTextView.getAutoSizeMaxTextSize());
-
-        // Configured with granularity.
-        assertEquals(Math.round(TypedValue.applyDimension(unit, granularity, dm)),
-                granularityTextView.getAutoSizeStepGranularity());
-        // Configured with preset values, there is no granularity.
-        assertEquals(-1, presetTextView.getAutoSizeStepGranularity());
-
-        // Both TextViews generate exactly the same sizes in pixels to choose from when auto-sizing.
-        assertArrayEquals(
-                granularityTextView.getAutoSizeTextAvailableSizes(),
-                presetTextView.getAutoSizeTextAvailableSizes());
-
-        final String someText = "This is a string";
-        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
-                500, 500);
-        // Configure identically and attach to layout.
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                granularityTextView.setLayoutParams(layoutParams);
-                presetTextView.setLayoutParams(layoutParams);
-
-                LinearLayout ll = mActivity.findViewById(R.id.layout_textviewtest);
-                ll.removeAllViews();
-                ll.addView(granularityTextView);
-                ll.addView(presetTextView);
-
-                granularityTextView.setText(someText);
-                presetTextView.setText(someText);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertEquals(granularityTextView.getTextSize(), presetTextView.getTextSize(), 0f);
+    @Override
+    protected AppCompatTextView getNewAutoSizeViewInstance() {
+        return new AppCompatTextView(mActivity);
     }
 
     @Test
     public void testAutoSize_notSupportedByEditText() throws Throwable {
-        final AppCompatEditText autoSizeEditText = (AppCompatEditText) mActivity.findViewById(
+        final AppCompatEditText autoSizeEditText = mActivity.findViewById(
                 R.id.edittext_autosize_uniform);
         // Do not force exact height only.
         final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
@@ -322,854 +75,6 @@
     }
 
     @Test
-    public void testAutoSizeCallers_setHeight() throws Throwable {
-        final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, true);
-        // Do not force exact height only.
-        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
-                200,
-                LinearLayout.LayoutParams.WRAP_CONTENT);
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setLayoutParams(layoutParams);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        final float initialTextSize = autoSizeTextView.getTextSize();
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setHeight(autoSizeTextView.getHeight() / 4);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
-    }
-
-    @Test
-    public void testAutoSizeCallers_setCompoundDrawables() throws Throwable {
-        final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, false);
-        final float initialTextSize = autoSizeTextView.getTextSize();
-        final Drawable drawable = ResourcesCompat.getDrawable(mActivity.getResources(),
-                R.drawable.test_drawable_red, null);
-        drawable.setBounds(0, 0, autoSizeTextView.getWidth() / 3, autoSizeTextView.getHeight() / 3);
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setCompoundDrawables(drawable, drawable, drawable, drawable);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
-    }
-
-    @Test
-    public void testAutoSizeCallers_setCompoundDrawablesRelative() throws Throwable {
-        if (Build.VERSION.SDK_INT >= 17) {
-            final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                    R.id.textview_autosize_uniform, false);
-            final float initialTextSize = autoSizeTextView.getTextSize();
-            final Drawable drawable = ResourcesCompat.getDrawable(mActivity.getResources(),
-                    R.drawable.test_drawable_red, null);
-            drawable.setBounds(0, 0, autoSizeTextView.getWidth() / 3,
-                    autoSizeTextView.getHeight() / 3);
-            mActivityTestRule.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    if (Build.VERSION.SDK_INT >= 17) {
-                        autoSizeTextView.setCompoundDrawablesRelative(
-                                drawable, drawable, drawable, drawable);
-                    }
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-
-            assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
-        }
-    }
-
-    @Test
-    public void testAutoSizeCallers_setCompoundDrawablePadding() throws Throwable {
-        final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, false);
-        // Prepare a larger layout in order not to hit the min value easily.
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setWidth(autoSizeTextView.getWidth() * 2);
-                autoSizeTextView.setHeight(autoSizeTextView.getHeight() * 2);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        // Setup the drawables before setting their padding in order to modify the available
-        // space and trigger a resize.
-        final Drawable drawable = ResourcesCompat.getDrawable(mActivity.getResources(),
-                R.drawable.test_drawable_red, null);
-        drawable.setBounds(0, 0, autoSizeTextView.getWidth() / 4, autoSizeTextView.getHeight() / 4);
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setCompoundDrawables(
-                        drawable, drawable, drawable, drawable);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        final float initialTextSize = autoSizeTextView.getTextSize();
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setCompoundDrawablePadding(
-                        autoSizeTextView.getCompoundDrawablePadding() + 10);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
-    }
-
-    @Test
-    public void testAutoSizeCallers_setPadding() throws Throwable {
-        final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, false);
-        final float initialTextSize = autoSizeTextView.getTextSize();
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setPadding(
-                        autoSizeTextView.getWidth() / 3, autoSizeTextView.getHeight() / 3,
-                        autoSizeTextView.getWidth() / 3, autoSizeTextView.getHeight() / 3);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
-    }
-
-    @Test
-    public void testAutoSizeCallers_setPaddingRelative() throws Throwable {
-        if (Build.VERSION.SDK_INT >= 16) {
-            final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                    R.id.textview_autosize_uniform, false);
-            final float initialTextSize = autoSizeTextView.getTextSize();
-
-            mActivityTestRule.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    if (Build.VERSION.SDK_INT > 16) {
-                        autoSizeTextView.setPaddingRelative(
-                                autoSizeTextView.getWidth() / 3, autoSizeTextView.getHeight() / 3,
-                                autoSizeTextView.getWidth() / 3, autoSizeTextView.getHeight() / 3);
-                    }
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-
-            assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
-        }
-    }
-
-    @Test
-    public void testAutoSizeCallers_setTypeface() throws Throwable {
-        final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, false);
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setText("The typeface change needs a bit more text then "
-                        + "the default used for this batch of tests in order to get to resize text."
-                        + " The resize function is always called but even with different typefaces "
-                        + "there may not be a need to resize text because it just fits. The longer "
-                        + "the text, the higher the chance for a resize. And here is yet another "
-                        + "sentence to make sure this test is not flaky. Not flaky at all.");
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        final float initialTextSize = autoSizeTextView.getTextSize();
-
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                Typeface differentTypeface = Typeface.MONOSPACE;
-                if (autoSizeTextView.getTypeface() == Typeface.MONOSPACE) {
-                    differentTypeface = Typeface.SANS_SERIF;
-                }
-                autoSizeTextView.setTypeface(differentTypeface);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        final float changedTextSize = autoSizeTextView.getTextSize();
-
-        // Don't really know if it is larger or smaller (depends on the typeface chosen above),
-        // but it should definitely have changed.
-        assertNotEquals(initialTextSize, changedTextSize, 0f);
-
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setTypeface(autoSizeTextView.getTypeface());
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertEquals(changedTextSize, autoSizeTextView.getTextSize(), 0f);
-    }
-
-    @Test
-    public void testAutoSizeCallers_setLetterSpacing() throws Throwable {
-        if (Build.VERSION.SDK_INT >= 21) {
-            final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                    R.id.textview_autosize_uniform, false);
-            final float initialTextSize = autoSizeTextView.getTextSize();
-
-            mActivityTestRule.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    if (Build.VERSION.SDK_INT >= 21) {
-                        autoSizeTextView.setLetterSpacing(
-                                autoSizeTextView.getLetterSpacing() * 1.5f + 4.5f);
-                    }
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-            final float changedTextSize = autoSizeTextView.getTextSize();
-
-            assertTrue(changedTextSize < initialTextSize);
-
-            mActivityTestRule.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    if (Build.VERSION.SDK_INT >= 21) {
-                        autoSizeTextView.setLetterSpacing(autoSizeTextView.getLetterSpacing());
-                    }
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-
-            assertEquals(changedTextSize, autoSizeTextView.getTextSize(), 0f);
-        }
-    }
-
-    @Test
-    public void testAutoSizeCallers_setMaxHeight() throws Throwable {
-        final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, true);
-        // Do not force exact height only.
-        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
-                200,
-                LinearLayout.LayoutParams.WRAP_CONTENT);
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setLayoutParams(layoutParams);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        final float initialTextSize = autoSizeTextView.getTextSize();
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setMaxHeight(
-                        autoSizeTextView.getHeight() / 4);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
-    }
-
-    @Test
-    public void testAutoSizeCallers_setMaxWidth() throws Throwable {
-        final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, true);
-        // Do not force exact width only.
-        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
-                LinearLayout.LayoutParams.WRAP_CONTENT,
-                200);
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setLayoutParams(layoutParams);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        final float initialTextSize = autoSizeTextView.getTextSize();
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setMaxWidth(
-                        autoSizeTextView.getWidth() / 4);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertTrue(autoSizeTextView.getTextSize() != initialTextSize);
-    }
-
-    @Test
-    public void testAutoSizeCallers_setWidth() throws Throwable {
-        final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, true);
-        // Do not force exact width only.
-        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
-                LinearLayout.LayoutParams.WRAP_CONTENT,
-                200);
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setLayoutParams(layoutParams);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        final float initialTextSize = autoSizeTextView.getTextSize();
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setWidth(
-                        autoSizeTextView.getWidth() / 4);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertTrue(autoSizeTextView.getTextSize() != initialTextSize);
-    }
-
-    @Test
-    public void testAutoSizeCallers_setTextSizeIsNoOp() throws Throwable {
-        final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, false);
-        final float initialTextSize = autoSizeTextView.getTextSize();
-
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setTextSize(initialTextSize + 123f);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertEquals(initialTextSize, autoSizeTextView.getTextSize(), 0f);
-    }
-
-    @Test
-    public void testAutoSizeCallers_setHorizontallyScrolling() throws Throwable {
-        final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, false);
-        // Horizontal scrolling is expected to be deactivated for this test.
-        final float initialTextSize = autoSizeTextView.getTextSize();
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setHorizontallyScrolling(true);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        assertTrue(autoSizeTextView.getTextSize() > initialTextSize);
-
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextView.setHorizontallyScrolling(false);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        Assert.assertEquals(initialTextSize, autoSizeTextView.getTextSize(), 0f);
-    }
-
-    @Test
-    public void testAutoSize_setEllipsize() throws Throwable {
-        final AppCompatTextView textView = (AppCompatTextView) mActivity.findViewById(
-                R.id.textview_autosize_uniform_predef_sizes);
-        final int initialAutoSizeType = textView.getAutoSizeTextType();
-        final int initialMinTextSize = textView.getAutoSizeMinTextSize();
-        final int initialMaxTextSize = textView.getAutoSizeMaxTextSize();
-        final int initialAutoSizeGranularity = textView.getAutoSizeStepGranularity();
-        final int initialSizes = textView.getAutoSizeTextAvailableSizes().length;
-
-        Assert.assertEquals(null, textView.getEllipsize());
-        // Verify styled attributes.
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM, initialAutoSizeType);
-        assertNotEquals(-1, initialMinTextSize);
-        assertNotEquals(-1, initialMaxTextSize);
-        // Because this TextView has been configured to use predefined sizes.
-        Assert.assertEquals(-1, initialAutoSizeGranularity);
-        assertNotEquals(0, initialSizes);
-
-        final TextUtils.TruncateAt newEllipsizeValue = TextUtils.TruncateAt.END;
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                textView.setEllipsize(newEllipsizeValue);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        Assert.assertEquals(newEllipsizeValue, textView.getEllipsize());
-        // Beside the ellipsis no auto-size attribute has changed.
-        Assert.assertEquals(initialAutoSizeType, textView.getAutoSizeTextType());
-        Assert.assertEquals(initialMinTextSize, textView.getAutoSizeMinTextSize());
-        Assert.assertEquals(initialMaxTextSize, textView.getAutoSizeMaxTextSize());
-        Assert.assertEquals(initialAutoSizeGranularity, textView.getAutoSizeStepGranularity());
-        Assert.assertEquals(initialSizes, textView.getAutoSizeTextAvailableSizes().length);
-    }
-
-    @Test
-    public void testEllipsize_setAutoSize() throws Throwable {
-        final AppCompatTextView textView =
-                (AppCompatTextView) mActivity.findViewById(R.id.textview_text);
-        final TextUtils.TruncateAt newEllipsizeValue = TextUtils.TruncateAt.END;
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                textView.setEllipsize(newEllipsizeValue);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        Assert.assertEquals(newEllipsizeValue, textView.getEllipsize());
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
-                textView.getAutoSizeTextType());
-        Assert.assertEquals(-1, textView.getAutoSizeMinTextSize());
-        Assert.assertEquals(-1, textView.getAutoSizeMaxTextSize());
-        Assert.assertEquals(-1, textView.getAutoSizeStepGranularity());
-        Assert.assertEquals(0, textView.getAutoSizeTextAvailableSizes().length);
-
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                textView.setAutoSizeTextTypeWithDefaults(
-                        TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        Assert.assertEquals(newEllipsizeValue, textView.getEllipsize());
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
-                textView.getAutoSizeTextType());
-        // The auto-size defaults have been used.
-        assertNotEquals(-1, textView.getAutoSizeMinTextSize());
-        assertNotEquals(-1, textView.getAutoSizeMaxTextSize());
-        assertNotEquals(-1, textView.getAutoSizeStepGranularity());
-        assertNotEquals(0, textView.getAutoSizeTextAvailableSizes().length);
-    }
-
-    @Test
-    public void testAutoSizeUniform_obtainStyledAttributesUsingPredefinedSizes() {
-        DisplayMetrics m = mActivity.getResources().getDisplayMetrics();
-        final AppCompatTextView autoSizeTextViewUniform =
-                (AppCompatTextView) mActivity.findViewById(
-                        R.id.textview_autosize_uniform_predef_sizes);
-
-        // In arrays.xml predefined the step sizes as: 5px, 11dip, 19sp, 29pt, 43mm and 53in.
-        // TypedValue can not use the math library and instead rounds the value by adding
-        // 0.5f when obtaining styled attributes. Check TypedValue#complexToDimensionPixelSize(...)
-        int[] expectedSizesInPx = new int[] {
-                (int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 5f, m)),
-                (int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 11f, m)),
-                (int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 19f, m)),
-                (int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PT, 29f, m)),
-                (int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 43f, m)),
-                (int) (0.5f + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_IN, 53f, m))};
-
-        boolean containsValueFromExpectedSizes = false;
-        final int textSize = (int) autoSizeTextViewUniform.getTextSize();
-        for (int i = 0; i < expectedSizesInPx.length; i++) {
-            if (expectedSizesInPx[i] == textSize) {
-                containsValueFromExpectedSizes = true;
-                break;
-            }
-        }
-        assertTrue(containsValueFromExpectedSizes);
-    }
-
-    @Test
-    public void testAutoSizeUniform_obtainStyledAttributesPredefinedSizesFiltering() {
-        AppCompatTextView autoSizeTextViewUniform = (AppCompatTextView) mActivity.findViewById(
-                R.id.textview_autosize_uniform_predef_sizes_redundant_values);
-
-        // In arrays.xml predefined the step sizes as: 40px, 10px, 10px, 10px, 0dp.
-        final int[] expectedSizes = new int[] {10, 40};
-        assertArrayEquals(expectedSizes, autoSizeTextViewUniform.getAutoSizeTextAvailableSizes());
-    }
-
-    @Test
-    public void testAutoSizeUniform_predefinedSizesFilteringAndSorting() throws Throwable {
-        final AppCompatTextView textView = (AppCompatTextView) mActivity.findViewById(
-                R.id.textview_text);
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
-                textView.getAutoSizeTextType());
-
-        final int[] predefinedSizes = new int[] {400, 0, 10, 40, 10, 10, 0, 0};
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                textView.setAutoSizeTextTypeUniformWithPresetSizes(
-                        predefinedSizes, TypedValue.COMPLEX_UNIT_PX);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        assertArrayEquals(new int[] {10, 40, 400}, textView.getAutoSizeTextAvailableSizes());
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void testAutoSizeUniform_predefinedSizesNullArray() throws Throwable {
-        final AppCompatTextView textView = (AppCompatTextView) mActivity.findViewById(
-                R.id.textview_text);
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
-                textView.getAutoSizeTextType());
-
-        final int[] predefinedSizes = null;
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                textView.setAutoSizeTextTypeUniformWithPresetSizes(
-                        predefinedSizes, TypedValue.COMPLEX_UNIT_PX);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-    }
-
-    @Test
-    public void testAutoSizeUniform_predefinedSizesEmptyArray() throws Throwable {
-        final AppCompatTextView textView = (AppCompatTextView) mActivity.findViewById(
-                R.id.textview_text);
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
-                textView.getAutoSizeTextType());
-
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                textView.setAutoSizeTextTypeWithDefaults(
-                        TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        final int[] defaultSizes = textView.getAutoSizeTextAvailableSizes();
-        assertNotNull(defaultSizes);
-        assertTrue(defaultSizes.length > 0);
-
-        final int[] predefinedSizes = new int[0];
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                textView.setAutoSizeTextTypeUniformWithPresetSizes(
-                        predefinedSizes, TypedValue.COMPLEX_UNIT_PX);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        final int[] newSizes = textView.getAutoSizeTextAvailableSizes();
-        assertNotNull(defaultSizes);
-        assertArrayEquals(defaultSizes, newSizes);
-    }
-
-    @Test
-    public void testAutoSizeUniform_buildsSizes() throws Throwable {
-        final AppCompatTextView autoSizeTextViewUniform =
-                (AppCompatTextView) mActivity.findViewById(
-                        R.id.textview_autosize_uniform);
-
-        // Verify that the interval limits are both included.
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextViewUniform
-                        .setAutoSizeTextTypeUniformWithConfiguration(10, 20, 2,
-                                TypedValue.COMPLEX_UNIT_PX);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        assertArrayEquals(
-                new int[] {10, 12, 14, 16, 18, 20},
-                autoSizeTextViewUniform.getAutoSizeTextAvailableSizes());
-
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextViewUniform
-                        .setAutoSizeTextTypeUniformWithConfiguration(
-                                autoSizeTextViewUniform.getAutoSizeMinTextSize(),
-                                19,
-                                autoSizeTextViewUniform.getAutoSizeStepGranularity(),
-                                TypedValue.COMPLEX_UNIT_PX);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        assertArrayEquals(
-                new int[] {10, 12, 14, 16, 18},
-                autoSizeTextViewUniform.getAutoSizeTextAvailableSizes());
-
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                autoSizeTextViewUniform
-                        .setAutoSizeTextTypeUniformWithConfiguration(
-                                autoSizeTextViewUniform.getAutoSizeMinTextSize(),
-                                21,
-                                autoSizeTextViewUniform.getAutoSizeStepGranularity(),
-                                TypedValue.COMPLEX_UNIT_PX);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        assertArrayEquals(
-                new int[] {10, 12, 14, 16, 18, 20},
-                autoSizeTextViewUniform.getAutoSizeTextAvailableSizes());
-    }
-
-    @Test
-    public void testAutoSizeUniform_getSetAutoSizeTextDefaults() {
-        final AppCompatTextView textView = new AppCompatTextView(mActivity);
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
-                textView.getAutoSizeTextType());
-        // Min/Max/Granularity values for auto-sizing are 0 because they are not used.
-        Assert.assertEquals(-1, textView.getAutoSizeMinTextSize());
-        Assert.assertEquals(-1, textView.getAutoSizeMaxTextSize());
-        Assert.assertEquals(-1, textView.getAutoSizeStepGranularity());
-
-        textView.setAutoSizeTextTypeWithDefaults(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
-                textView.getAutoSizeTextType());
-        // Min/Max default values for auto-sizing XY have been loaded.
-        final int minSize = textView.getAutoSizeMinTextSize();
-        final int maxSize = textView.getAutoSizeMaxTextSize();
-        assertTrue(0 < minSize);
-        assertTrue(minSize < maxSize);
-        assertNotEquals(0, textView.getAutoSizeStepGranularity());
-
-        textView.setAutoSizeTextTypeWithDefaults(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
-                textView.getAutoSizeTextType());
-        // Min/Max values for auto-sizing XY have been cleared.
-        Assert.assertEquals(-1, textView.getAutoSizeMinTextSize());
-        Assert.assertEquals(-1, textView.getAutoSizeMaxTextSize());
-        Assert.assertEquals(-1, textView.getAutoSizeStepGranularity());
-    }
-
-    @Test
-    public void testAutoSizeUniform_getSetAutoSizeStepGranularity() {
-        final AppCompatTextView textView = new AppCompatTextView(mActivity);
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
-                textView.getAutoSizeTextType());
-        final int initialValue = -1;
-        Assert.assertEquals(initialValue, textView.getAutoSizeStepGranularity());
-
-        textView.setAutoSizeTextTypeWithDefaults(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
-                textView.getAutoSizeTextType());
-        final int defaultValue = 1; // 1px.
-        // If the auto-size type is AUTO_SIZE_TEXT_TYPE_UNIFORM then it means textView went through
-        // the auto-size setup and given that 0 is an invalid value it changed it to the default.
-        Assert.assertEquals(defaultValue, textView.getAutoSizeStepGranularity());
-
-        final int newValue = 33;
-        textView.setAutoSizeTextTypeUniformWithConfiguration(
-                textView.getAutoSizeMinTextSize(),
-                textView.getAutoSizeMaxTextSize(),
-                newValue,
-                TypedValue.COMPLEX_UNIT_PX);
-        Assert.assertEquals(newValue, textView.getAutoSizeStepGranularity());
-    }
-
-    @Test
-    public void testAutoSizeUniform_getSetAutoSizeMinTextSize() {
-        final AppCompatTextView textView = new AppCompatTextView(mActivity);
-        textView.setAutoSizeTextTypeWithDefaults(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
-                textView.getAutoSizeTextType());
-        final int minSize = textView.getAutoSizeMinTextSize();
-        assertNotEquals(0, minSize);
-        final int maxSize = textView.getAutoSizeMaxTextSize();
-        assertNotEquals(0, maxSize);
-
-        // This is just a test check to verify the next assertions. If this fails it is a problem
-        // of this test setup (we need at least 2 units).
-        assertTrue((maxSize - minSize) > 1);
-        final int newMinSize = maxSize - 1;
-        textView.setAutoSizeTextTypeUniformWithConfiguration(
-                newMinSize,
-                textView.getAutoSizeMaxTextSize(),
-                textView.getAutoSizeStepGranularity(),
-                TypedValue.COMPLEX_UNIT_PX);
-
-        Assert.assertEquals(newMinSize, textView.getAutoSizeMinTextSize());
-        // Max size has not changed.
-        Assert.assertEquals(maxSize, textView.getAutoSizeMaxTextSize());
-
-        textView.setAutoSizeTextTypeUniformWithConfiguration(
-                newMinSize,
-                newMinSize + 10,
-                textView.getAutoSizeStepGranularity(),
-                TypedValue.COMPLEX_UNIT_SP);
-
-        // It does not matter which unit has been used to set the min size, the getter always
-        // returns it in pixels.
-        Assert.assertEquals(Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
-                newMinSize, mActivity.getResources().getDisplayMetrics())),
-                        textView.getAutoSizeMinTextSize());
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testAutoSizeUniform_throwsException_whenMaxLessThanMin() {
-        final AppCompatTextView textView = new AppCompatTextView(mActivity);
-        textView.setAutoSizeTextTypeUniformWithConfiguration(
-                10, 9, 1, TypedValue.COMPLEX_UNIT_SP);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testAutoSizeUniform_throwsException_minLessThanZero() {
-        final AppCompatTextView textView = new AppCompatTextView(mActivity);
-        textView.setAutoSizeTextTypeUniformWithConfiguration(
-                -1, 9, 1, TypedValue.COMPLEX_UNIT_SP);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testAutoSizeUniform_throwsException_maxLessThanZero() {
-        final AppCompatTextView textView = new AppCompatTextView(mActivity);
-        textView.setAutoSizeTextTypeUniformWithConfiguration(
-                10, -1, 1, TypedValue.COMPLEX_UNIT_SP);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testAutoSizeUniform_throwsException_granularityLessThanZero() {
-        final AppCompatTextView textView = new AppCompatTextView(mActivity);
-        textView.setAutoSizeTextTypeUniformWithConfiguration(
-                10, 20, -1, TypedValue.COMPLEX_UNIT_SP);
-    }
-
-    @Test
-    public void testAutoSizeUniform_getSetAutoSizeMaxTextSize() {
-        final AppCompatTextView textView = new AppCompatTextView(mActivity);
-        textView.setAutoSizeTextTypeWithDefaults(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
-                textView.getAutoSizeTextType());
-        final int minSize = textView.getAutoSizeMinTextSize();
-        assertNotEquals(0, minSize);
-        final int maxSize = textView.getAutoSizeMaxTextSize();
-        assertNotEquals(0, maxSize);
-
-        final int newMaxSize = maxSize + 11;
-        textView.setAutoSizeTextTypeUniformWithConfiguration(
-                textView.getAutoSizeMinTextSize(),
-                newMaxSize,
-                textView.getAutoSizeStepGranularity(),
-                TypedValue.COMPLEX_UNIT_PX);
-
-        Assert.assertEquals(newMaxSize, textView.getAutoSizeMaxTextSize());
-        // Min size has not changed.
-        Assert.assertEquals(minSize, textView.getAutoSizeMinTextSize());
-        textView.setAutoSizeTextTypeUniformWithConfiguration(
-                textView.getAutoSizeMinTextSize(),
-                newMaxSize,
-                textView.getAutoSizeStepGranularity(),
-                TypedValue.COMPLEX_UNIT_SP);
-        // It does not matter which unit has been used to set the max size, the getter always
-        // returns it in pixels.
-        Assert.assertEquals(Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
-                newMaxSize, mActivity.getResources().getDisplayMetrics())),
-                        textView.getAutoSizeMaxTextSize());
-    }
-
-    @Test
-    public void testAutoSizeUniform_autoSizeCalledWhenTypeChanged() throws Throwable {
-        final AppCompatTextView textView = (AppCompatTextView) mActivity.findViewById(
-                R.id.textview_text);
-        // Make sure we pick an already inflated non auto-sized text view.
-        Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
-                textView.getAutoSizeTextType());
-        // Set the text size to a very low value in order to prepare for auto-size.
-        final int customTextSize = 3;
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, customTextSize);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        Assert.assertEquals(customTextSize, textView.getTextSize(), 0f);
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                textView.setAutoSizeTextTypeWithDefaults(
-                        TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        // The size of the text should have changed.
-        assertNotEquals(customTextSize, textView.getTextSize(), 0f);
-    }
-
-    @Test
-    public void testAutoSizeCallers_setTextSizeChangesSizeWhenAutoSizeDisabled() throws Throwable {
-        final AppCompatTextView textView = (AppCompatTextView) prepareAndRetrieveAutoSizeTestData(
-                R.id.textview_autosize_uniform, false);
-        // Disable auto-size.
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                textView.setAutoSizeTextTypeWithDefaults(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-        final float newTextSizeInPx = 123f;
-        assertNotEquals(newTextSizeInPx, textView.getTextSize(), 0f);
-
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, newTextSizeInPx);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        assertEquals(newTextSizeInPx, textView.getTextSize(), 0f);
-    }
-
-    /**
-     * Some TextView attributes require non-fixed width and/or layout height. This function removes
-     * all other existing views from the layout leaving only one auto-size TextView (for exercising
-     * the auto-size behavior) which has been set up to suit the test needs.
-     *
-     * @param viewId The id of the view to prepare.
-     * @param shouldWrapLayoutContent Specifies if the layout params should wrap content
-     *
-     * @return a TextView configured for auto size tests.
-     */
-    private AppCompatTextView prepareAndRetrieveAutoSizeTestData(final int viewId,
-            final boolean shouldWrapLayoutContent) throws Throwable {
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                LinearLayout ll = (LinearLayout) mActivity.findViewById(
-                        android.support.v7.appcompat.test.R.id.layout_textviewtest);
-                AppCompatTextView targetedTextView =
-                        (AppCompatTextView) mActivity.findViewById(viewId);
-                ll.removeAllViews();
-                ll.addView(targetedTextView);
-            }
-        });
-        mInstrumentation.waitForIdleSync();
-
-        final AppCompatTextView textView = mActivity.findViewById(viewId);
-        if (shouldWrapLayoutContent) {
-            // Do not force exact width or height.
-            final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
-                    LinearLayout.LayoutParams.WRAP_CONTENT,
-                    LinearLayout.LayoutParams.WRAP_CONTENT);
-            mActivityTestRule.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    textView.setLayoutParams(layoutParams);
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-        }
-
-        return textView;
-    }
-
-    @Test
     public void testAutoSizeWithMaxLines_shouldNotThrowException() throws Throwable {
         // the layout contains an instance of CustomTextViewWithTransformationMethod
         final AppCompatTextView textView = (AppCompatTextView) mActivity
@@ -1179,7 +84,7 @@
         if (Build.VERSION.SDK_INT >= 16) {
             assertEquals(1, textView.getMaxLines());
         }
-        assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, textView.getAutoSizeTextType());
+        assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM, textView.getAutoSizeTextType());
         assertTrue(textView.getTransformationMethod() instanceof SingleLineTransformationMethod);
     }
 
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
index 8b64688..cc5648f 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
@@ -2562,7 +2562,7 @@
 
         private void setSelectedRouteInternal(RouteInfo route, int unselectReason) {
             // TODO: Remove the following logging when no longer needed.
-            if (mBluetoothRoute != null && route != null && route.isDefault()) {
+            if (sGlobal == null || mBluetoothRoute != null && route != null && route.isDefault()) {
                 final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
                 StringBuffer sb = new StringBuffer();
                 // callStack[3] is the caller of this method.
@@ -2571,8 +2571,13 @@
                     sb.append(caller.getClassName() + "." + caller.getMethodName()
                             + ":" + caller.getLineNumber()).append("  ");
                 }
-                Log.w(TAG, "Default route is selected while a BT route is available: pkgName="
-                        + mApplicationContext.getPackageName() + ", callers=" + sb.toString());
+                if (sGlobal == null) {
+                    Log.w(TAG, "setSelectedRouteInternal is called while sGlobal is null: pkgName="
+                            + mApplicationContext.getPackageName() + ", callers=" + sb.toString());
+                } else {
+                    Log.w(TAG, "Default route is selected while a BT route is available: pkgName="
+                            + mApplicationContext.getPackageName() + ", callers=" + sb.toString());
+                }
             }
 
             if (mSelectedRoute != route) {
diff --git a/wear/src/android/support/wear/widget/CircularProgressLayout.java b/wear/src/android/support/wear/widget/CircularProgressLayout.java
index 1bfcc39..c317f28 100644
--- a/wear/src/android/support/wear/widget/CircularProgressLayout.java
+++ b/wear/src/android/support/wear/widget/CircularProgressLayout.java
@@ -20,9 +20,13 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
+import android.graphics.Paint;
+import android.os.Build;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.v4.content.ContextCompat;
 import android.support.v4.widget.CircularProgressDrawable;
 import android.support.wear.R;
 import android.util.AttributeSet;
@@ -44,12 +48,13 @@
  * <p>Alternatively, this layout can be used to show indeterminate progress by calling {@link
  * #setIndeterminate(boolean)} method.
  */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
 public class CircularProgressLayout extends FrameLayout {
 
     /**
-     * Update interval for 30 fps.
+     * Update interval for 60 fps.
      */
-    private static final long DEFAULT_UPDATE_INTERVAL = 1000 / 30;
+    private static final long DEFAULT_UPDATE_INTERVAL = 1000 / 60;
 
     /**
      * Starting rotation for the progress indicator. Geometric clockwise [0..360] degree range
@@ -110,6 +115,7 @@
 
         mProgressDrawable = new CircularProgressDrawable(context);
         mProgressDrawable.setProgressRotation(DEFAULT_ROTATION);
+        mProgressDrawable.setStrokeCap(Paint.Cap.BUTT);
         setBackground(mProgressDrawable);
 
         // If a child view is added, make it center aligned so it fits in the progress drawable.
@@ -149,7 +155,8 @@
                         R.dimen.circular_progress_layout_stroke_width)));
 
         setBackgroundColor(a.getColor(R.styleable.CircularProgressLayout_backgroundColor,
-                r.getColor(R.color.circular_progress_layout_background_color, null)));
+                ContextCompat.getColor(context,
+                        R.color.circular_progress_layout_background_color)));
 
         setIndeterminate(a.getBoolean(R.styleable.CircularProgressLayout_indeterminate, false));