Merge "Remove use of deprecated ViewCompat.canScrollVertically" into oc-support-26.0-dev
diff --git a/.gitignore b/.gitignore
index e57740b..5e7ac49 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,13 @@
 .classpath
 .gradle
-.idea/
+.idea/*
+.idea/**/*
+!.idea/*/
 !.idea/codeStyleSettings.xml
 !.idea/copyright/AndroidCopyright.xml
 !.idea/copyright/profiles_settings.xml
+!.idea/inspectionProfiles/SupportLib.xml
+!.idea/inspectionProfiles/profiles_settings.xml
 !.idea/vcs.xml
 .project
 .settings/
diff --git a/.idea/inspectionProfiles/SupportLib.xml b/.idea/inspectionProfiles/SupportLib.xml
new file mode 100644
index 0000000..e0d21e6
--- /dev/null
+++ b/.idea/inspectionProfiles/SupportLib.xml
@@ -0,0 +1,53 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="SupportLib" />
+    <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="TOP_LEVEL_CLASS_OPTIONS">
+        <value>
+          <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+          <option name="REQUIRED_TAGS" value="" />
+        </value>
+      </option>
+      <option name="INNER_CLASS_OPTIONS">
+        <value>
+          <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+          <option name="REQUIRED_TAGS" value="" />
+        </value>
+      </option>
+      <option name="METHOD_OPTIONS">
+        <value>
+          <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+          <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
+        </value>
+      </option>
+      <option name="FIELD_OPTIONS">
+        <value>
+          <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+          <option name="REQUIRED_TAGS" value="" />
+        </value>
+      </option>
+      <option name="IGNORE_DEPRECATED" value="false" />
+      <option name="IGNORE_JAVADOC_PERIOD" value="true" />
+      <option name="IGNORE_DUPLICATED_THROWS" value="false" />
+      <option name="IGNORE_POINT_TO_ITSELF" value="false" />
+      <option name="myAdditionalJavadocTags" value="hide,attr" />
+    </inspection_tool>
+    <inspection_tool class="WeakerAccess" enabled="false" level="WARNING" enabled_by_default="false">
+      <option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="true" />
+      <option name="SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES" value="true" />
+      <option name="SUGGEST_PRIVATE_FOR_INNERS" value="false" />
+    </inspection_tool>
+    <inspection_tool class="unused" enabled="true" level="WARNING" enabled_by_default="true" method="protected">
+      <option name="LOCAL_VARIABLE" value="true" />
+      <option name="FIELD" value="true" />
+      <option name="METHOD" value="true" />
+      <option name="CLASS" value="true" />
+      <option name="PARAMETER" value="true" />
+      <option name="REPORT_PARAMETER_FOR_PUBLIC_METHODS" value="true" />
+      <option name="ADD_MAINS_TO_ENTRIES" value="true" />
+      <option name="ADD_APPLET_TO_ENTRIES" value="true" />
+      <option name="ADD_SERVLET_TO_ENTRIES" value="true" />
+      <option name="ADD_NONJAVA_TO_ENTRIES" value="true" />
+    </inspection_tool>
+  </profile>
+</component>
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..83490b5
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="PROJECT_PROFILE" value="SupportLib" />
+    <version value="1.0" />
+  </settings>
+</component>
\ No newline at end of file
diff --git a/api/26.0.0-SNAPSHOT.txt b/api/26.0.0-SNAPSHOT.txt
index 5d7496b..9e9a646 100644
--- a/api/26.0.0-SNAPSHOT.txt
+++ b/api/26.0.0-SNAPSHOT.txt
@@ -1900,16 +1900,19 @@
     method public void unregisterInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
     field public static final java.lang.String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
     field public static final java.lang.String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
-    field public static final int LOAD_STATE_FAILURE = 2; // 0x2
+    field public static final int LOAD_STATE_FAILED = 2; // 0x2
+    field public static final deprecated int LOAD_STATE_FAILURE = 2; // 0x2
     field public static final int LOAD_STATE_LOADING = 0; // 0x0
-    field public static final int LOAD_STATE_SUCCESS = 1; // 0x1
+    field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+    field public static final deprecated int LOAD_STATE_SUCCESS = 1; // 0x1
     field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
     field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
     field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
   }
 
   public static abstract class EmojiCompat.Config {
-    ctor protected EmojiCompat.Config(android.support.text.emoji.EmojiCompat.MetadataLoader);
+    ctor protected EmojiCompat.Config(android.support.text.emoji.EmojiCompat.MetadataRepoLoader);
+    ctor protected deprecated EmojiCompat.Config(android.support.text.emoji.EmojiCompat.MetadataLoader);
     method public android.support.text.emoji.EmojiCompat.Config registerInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
     method public android.support.text.emoji.EmojiCompat.Config setEmojiSpanIndicatorColor(int);
     method public android.support.text.emoji.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
@@ -1923,16 +1926,26 @@
     method public void onInitialized();
   }
 
-  public static abstract class EmojiCompat.LoaderCallback {
+  public static abstract deprecated class EmojiCompat.LoaderCallback {
     ctor public EmojiCompat.LoaderCallback();
     method public abstract void onFailed(java.lang.Throwable);
     method public abstract void onLoaded(android.support.text.emoji.MetadataRepo);
   }
 
-  public static abstract interface EmojiCompat.MetadataLoader {
+  public static abstract deprecated interface EmojiCompat.MetadataLoader {
     method public abstract void load(android.support.text.emoji.EmojiCompat.LoaderCallback);
   }
 
+  public static abstract interface EmojiCompat.MetadataRepoLoader {
+    method public abstract void load(android.support.text.emoji.EmojiCompat.MetadataRepoLoaderCallback);
+  }
+
+  public static abstract class EmojiCompat.MetadataRepoLoaderCallback {
+    ctor public EmojiCompat.MetadataRepoLoaderCallback();
+    method public abstract void onFailed(java.lang.Throwable);
+    method public abstract void onLoaded(android.support.text.emoji.MetadataRepo);
+  }
+
   public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
     method public int getSize(android.graphics.Paint, java.lang.CharSequence, int, int, android.graphics.Paint.FontMetricsInt);
   }
@@ -2211,6 +2224,15 @@
     method public android.support.transition.TransitionManager inflateTransitionManager(int, android.view.ViewGroup);
   }
 
+  public class TransitionListenerAdapter implements android.support.transition.Transition.TransitionListener {
+    ctor public TransitionListenerAdapter();
+    method public void onTransitionCancel(android.support.transition.Transition);
+    method public void onTransitionEnd(android.support.transition.Transition);
+    method public void onTransitionPause(android.support.transition.Transition);
+    method public void onTransitionResume(android.support.transition.Transition);
+    method public void onTransitionStart(android.support.transition.Transition);
+  }
+
   public class TransitionManager {
     ctor public TransitionManager();
     method public static void beginDelayedTransition(android.view.ViewGroup);
diff --git a/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy b/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy
index f598e82..677b63e 100644
--- a/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy
+++ b/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy
@@ -189,6 +189,7 @@
                             // Enforce the following checks.
                             '-Xep:MissingOverride:ERROR',
                             '-Xep:ClassNewInstance:ERROR',
+                            '-Xep:SynchronizeOnNonFinalField:ERROR'
                     ]
                 }
             }
diff --git a/compat/api21/android/support/v4/app/NotificationCompatApi21.java b/compat/api21/android/support/v4/app/NotificationCompatApi21.java
index 44237f3..d56d87e 100644
--- a/compat/api21/android/support/v4/app/NotificationCompatApi21.java
+++ b/compat/api21/android/support/v4/app/NotificationCompatApi21.java
@@ -36,22 +36,6 @@
 
 @RequiresApi(21)
 class NotificationCompatApi21 {
-
-    public static final String CATEGORY_CALL = Notification.CATEGORY_CALL;
-    public static final String CATEGORY_MESSAGE = Notification.CATEGORY_MESSAGE;
-    public static final String CATEGORY_EMAIL = Notification.CATEGORY_EMAIL;
-    public static final String CATEGORY_EVENT = Notification.CATEGORY_EVENT;
-    public static final String CATEGORY_PROMO = Notification.CATEGORY_PROMO;
-    public static final String CATEGORY_ALARM = Notification.CATEGORY_ALARM;
-    public static final String CATEGORY_PROGRESS = Notification.CATEGORY_PROGRESS;
-    public static final String CATEGORY_SOCIAL = Notification.CATEGORY_SOCIAL;
-    public static final String CATEGORY_ERROR = Notification.CATEGORY_ERROR;
-    public static final String CATEGORY_TRANSPORT = Notification.CATEGORY_TRANSPORT;
-    public static final String CATEGORY_SYSTEM = Notification.CATEGORY_SYSTEM;
-    public static final String CATEGORY_SERVICE = Notification.CATEGORY_SERVICE;
-    public static final String CATEGORY_RECOMMENDATION = Notification.CATEGORY_RECOMMENDATION;
-    public static final String CATEGORY_STATUS = Notification.CATEGORY_STATUS;
-
     private static final String KEY_AUTHOR = "author";
     private static final String KEY_TEXT = "text";
     private static final String KEY_MESSAGES = "messages";
diff --git a/compat/api23/android/support/v4/app/NotificationCompatApi23.java b/compat/api23/android/support/v4/app/NotificationCompatApi23.java
deleted file mode 100644
index 2f8216c..0000000
--- a/compat/api23/android/support/v4/app/NotificationCompatApi23.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.app;
-
-import android.app.Notification;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(23)
-class NotificationCompatApi23 {
-
-    public static final String CATEGORY_REMINDER = Notification.CATEGORY_REMINDER;
-}
diff --git a/compat/build.gradle b/compat/build.gradle
index 0274285..fc61134 100644
--- a/compat/build.gradle
+++ b/compat/build.gradle
@@ -13,6 +13,7 @@
     androidTestCompile libs.mockito_core
     androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
+    androidTestCompile project(':support-testutils')
 }
 
 android {
diff --git a/compat/java/android/support/v4/app/JobIntentService.java b/compat/java/android/support/v4/app/JobIntentService.java
index a5417e6..0789039 100644
--- a/compat/java/android/support/v4/app/JobIntentService.java
+++ b/compat/java/android/support/v4/app/JobIntentService.java
@@ -32,7 +32,6 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RequiresApi;
-import android.support.v4.os.BuildCompat;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -481,7 +480,7 @@
     static WorkEnqueuer getWorkEnqueuer(Context context, Class cls, boolean hasJobId, int jobId) {
         WorkEnqueuer we = sClassWorkEnqueuer.get(cls);
         if (we == null) {
-            if (BuildCompat.isAtLeastO()) {
+            if (Build.VERSION.SDK_INT >= 26) {
                 if (!hasJobId) {
                     throw new IllegalArgumentException("Can't be here without a job id");
                 }
diff --git a/compat/java/android/support/v4/app/NotificationCompat.java b/compat/java/android/support/v4/app/NotificationCompat.java
index ec46c55..a93c763 100644
--- a/compat/java/android/support/v4/app/NotificationCompat.java
+++ b/compat/java/android/support/v4/app/NotificationCompat.java
@@ -36,7 +36,6 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.view.GravityCompat;
 import android.view.Gravity;
 import android.widget.RemoteViews;
@@ -451,67 +450,67 @@
     /**
      * Notification category: incoming call (voice or video) or similar synchronous communication request.
      */
-    public static final String CATEGORY_CALL = NotificationCompatApi21.CATEGORY_CALL;
+    public static final String CATEGORY_CALL = Notification.CATEGORY_CALL;
 
     /**
      * Notification category: incoming direct message (SMS, instant message, etc.).
      */
-    public static final String CATEGORY_MESSAGE = NotificationCompatApi21.CATEGORY_MESSAGE;
+    public static final String CATEGORY_MESSAGE = Notification.CATEGORY_MESSAGE;
 
     /**
      * Notification category: asynchronous bulk message (email).
      */
-    public static final String CATEGORY_EMAIL = NotificationCompatApi21.CATEGORY_EMAIL;
+    public static final String CATEGORY_EMAIL = Notification.CATEGORY_EMAIL;
 
     /**
      * Notification category: calendar event.
      */
-    public static final String CATEGORY_EVENT = NotificationCompatApi21.CATEGORY_EVENT;
+    public static final String CATEGORY_EVENT = Notification.CATEGORY_EVENT;
 
     /**
      * Notification category: promotion or advertisement.
      */
-    public static final String CATEGORY_PROMO = NotificationCompatApi21.CATEGORY_PROMO;
+    public static final String CATEGORY_PROMO = Notification.CATEGORY_PROMO;
 
     /**
      * Notification category: alarm or timer.
      */
-    public static final String CATEGORY_ALARM = NotificationCompatApi21.CATEGORY_ALARM;
+    public static final String CATEGORY_ALARM = Notification.CATEGORY_ALARM;
 
     /**
      * Notification category: progress of a long-running background operation.
      */
-    public static final String CATEGORY_PROGRESS = NotificationCompatApi21.CATEGORY_PROGRESS;
+    public static final String CATEGORY_PROGRESS = Notification.CATEGORY_PROGRESS;
 
     /**
      * Notification category: social network or sharing update.
      */
-    public static final String CATEGORY_SOCIAL = NotificationCompatApi21.CATEGORY_SOCIAL;
+    public static final String CATEGORY_SOCIAL = Notification.CATEGORY_SOCIAL;
 
     /**
      * Notification category: error in background operation or authentication status.
      */
-    public static final String CATEGORY_ERROR = NotificationCompatApi21.CATEGORY_ERROR;
+    public static final String CATEGORY_ERROR = Notification.CATEGORY_ERROR;
 
     /**
      * Notification category: media transport control for playback.
      */
-    public static final String CATEGORY_TRANSPORT = NotificationCompatApi21.CATEGORY_TRANSPORT;
+    public static final String CATEGORY_TRANSPORT = Notification.CATEGORY_TRANSPORT;
 
     /**
      * Notification category: system or device status update.  Reserved for system use.
      */
-    public static final String CATEGORY_SYSTEM = NotificationCompatApi21.CATEGORY_SYSTEM;
+    public static final String CATEGORY_SYSTEM = Notification.CATEGORY_SYSTEM;
 
     /**
      * Notification category: indication of running background service.
      */
-    public static final String CATEGORY_SERVICE = NotificationCompatApi21.CATEGORY_SERVICE;
+    public static final String CATEGORY_SERVICE = Notification.CATEGORY_SERVICE;
 
     /**
      * Notification category: user-scheduled reminder.
      */
-    public static final String CATEGORY_REMINDER = NotificationCompatApi23.CATEGORY_REMINDER;
+    public static final String CATEGORY_REMINDER = Notification.CATEGORY_REMINDER;
 
     /**
      * Notification category: a specific, timely recommendation for a single thing.
@@ -519,12 +518,12 @@
      * want to read next.
      */
     public static final String CATEGORY_RECOMMENDATION =
-            NotificationCompatApi21.CATEGORY_RECOMMENDATION;
+            Notification.CATEGORY_RECOMMENDATION;
 
     /**
      * Notification category: ongoing information about device or contextual status.
      */
-    public static final String CATEGORY_STATUS = NotificationCompatApi21.CATEGORY_STATUS;
+    public static final String CATEGORY_STATUS = Notification.CATEGORY_STATUS;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -946,7 +945,7 @@
     }
 
     static {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             IMPL = new NotificationCompatApi26Impl();
         } else if (Build.VERSION.SDK_INT >= 24) {
             IMPL = new NotificationCompatApi24Impl();
@@ -4336,10 +4335,10 @@
         if (Build.VERSION.SDK_INT >= 20) {
             return (notification.flags & Notification.FLAG_LOCAL_ONLY) != 0;
         } else if (Build.VERSION.SDK_INT >= 19) {
-            return notification.extras.getBoolean(NotificationCompatJellybean.EXTRA_LOCAL_ONLY);
+            return notification.extras.getBoolean(NotificationCompatExtras.EXTRA_LOCAL_ONLY);
         } else if (Build.VERSION.SDK_INT >= 16) {
             return NotificationCompatJellybean.getExtras(notification).getBoolean(
-                    NotificationCompatJellybean.EXTRA_LOCAL_ONLY);
+                    NotificationCompatExtras.EXTRA_LOCAL_ONLY);
         } else {
             return false;
         }
@@ -4353,10 +4352,10 @@
         if (Build.VERSION.SDK_INT >= 20) {
             return notification.getGroup();
         } else if (Build.VERSION.SDK_INT >= 19) {
-            return notification.extras.getString(NotificationCompatJellybean.EXTRA_GROUP_KEY);
+            return notification.extras.getString(NotificationCompatExtras.EXTRA_GROUP_KEY);
         } else if (Build.VERSION.SDK_INT >= 16) {
             return NotificationCompatJellybean.getExtras(notification).getString(
-                    NotificationCompatJellybean.EXTRA_GROUP_KEY);
+                    NotificationCompatExtras.EXTRA_GROUP_KEY);
         } else {
             return null;
         }
@@ -4372,10 +4371,10 @@
         if (Build.VERSION.SDK_INT >= 20) {
             return (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
         } else if (Build.VERSION.SDK_INT >= 19) {
-            return notification.extras.getBoolean(NotificationCompatJellybean.EXTRA_GROUP_SUMMARY);
+            return notification.extras.getBoolean(NotificationCompatExtras.EXTRA_GROUP_SUMMARY);
         } else if (Build.VERSION.SDK_INT >= 16) {
             return NotificationCompatJellybean.getExtras(notification).getBoolean(
-                    NotificationCompatJellybean.EXTRA_GROUP_SUMMARY);
+                    NotificationCompatExtras.EXTRA_GROUP_SUMMARY);
         } else {
             return false;
         }
@@ -4397,10 +4396,10 @@
         if (Build.VERSION.SDK_INT >= 20) {
             return notification.getSortKey();
         } else if (Build.VERSION.SDK_INT >= 19) {
-            return notification.extras.getString(NotificationCompatJellybean.EXTRA_SORT_KEY);
+            return notification.extras.getString(NotificationCompatExtras.EXTRA_SORT_KEY);
         } else if (Build.VERSION.SDK_INT >= 16) {
             return NotificationCompatJellybean.getExtras(notification).getString(
-                    NotificationCompatJellybean.EXTRA_SORT_KEY);
+                    NotificationCompatExtras.EXTRA_SORT_KEY);
         } else {
             return null;
         }
@@ -4410,7 +4409,7 @@
      * @return the ID of the channel this notification posts to.
      */
     public static String getChannelId(Notification notification) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return notification.getChannelId();
         } else {
             return null;
@@ -4428,7 +4427,7 @@
      * canceled already.
      */
     public static long getTimeoutAfter(Notification notification) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return notification.getTimeoutAfter();
         } else {
             return 0;
@@ -4447,7 +4446,7 @@
      * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
      */
     public static int getBadgeIconType(Notification notification) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return notification.getBadgeIconType();
         } else {
             return BADGE_ICON_NONE;
@@ -4459,7 +4458,7 @@
      * notification supersedes, if any.
      */
     public static String getShortcutId(Notification notification) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return notification.getShortcutId();
         } else {
             return null;
@@ -4472,7 +4471,7 @@
      * {@link #GROUP_ALERT_SUMMARY}.
      */
     public static int getGroupAlertBehavior(Notification notification) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return notification.getGroupAlertBehavior();
         } else {
             return GROUP_ALERT_ALL;
diff --git a/compat/java/android/support/v4/app/NotificationCompatExtras.java b/compat/java/android/support/v4/app/NotificationCompatExtras.java
index bf9ba0f..33bdd06 100644
--- a/compat/java/android/support/v4/app/NotificationCompatExtras.java
+++ b/compat/java/android/support/v4/app/NotificationCompatExtras.java
@@ -25,37 +25,35 @@
      * the {@link android.app.Notification#FLAG_LOCAL_ONLY} field before it was available.
      * If possible, use {@link NotificationCompat#getLocalOnly} to access this field.
      */
-    public static final String EXTRA_LOCAL_ONLY = NotificationCompatJellybean.EXTRA_LOCAL_ONLY;
+    public static final String EXTRA_LOCAL_ONLY = "android.support.localOnly";
 
     /**
      * Extras key used internally by {@link NotificationCompat} to store the value set
      * by {@link android.app.Notification.Builder#setGroup} before it was available.
      * If possible, use {@link NotificationCompat#getGroup} to access this value.
      */
-    public static final String EXTRA_GROUP_KEY = NotificationCompatJellybean.EXTRA_GROUP_KEY;
+    public static final String EXTRA_GROUP_KEY = "android.support.groupKey";
 
     /**
      * Extras key used internally by {@link NotificationCompat} to store the value set
      * by {@link android.app.Notification.Builder#setGroupSummary} before it was available.
      * If possible, use {@link NotificationCompat#isGroupSummary} to access this value.
      */
-    public static final String EXTRA_GROUP_SUMMARY =
-            NotificationCompatJellybean.EXTRA_GROUP_SUMMARY;
+    public static final String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
 
     /**
      * Extras key used internally by {@link NotificationCompat} to store the value set
      * by {@link android.app.Notification.Builder#setSortKey} before it was available.
      * If possible, use {@link NotificationCompat#getSortKey} to access this value.
      */
-    public static final String EXTRA_SORT_KEY = NotificationCompatJellybean.EXTRA_SORT_KEY;
+    public static final String EXTRA_SORT_KEY = "android.support.sortKey";
 
     /**
      * Extras key used internally by {@link NotificationCompat} to store the value of
      * the {@link android.app.Notification.Action#extras} field before it was available.
      * If possible, use {@link NotificationCompat#getAction} to access this field.
      */
-    public static final String EXTRA_ACTION_EXTRAS =
-            NotificationCompatJellybean.EXTRA_ACTION_EXTRAS;
+    public static final String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
 
     /**
      * Extras key used internally by {@link NotificationCompat} to store the value of
@@ -63,8 +61,7 @@
      * was available.
      * If possible, use {@link NotificationCompat.Action#getRemoteInputs} to access this field.
      */
-    public static final String EXTRA_REMOTE_INPUTS =
-            NotificationCompatJellybean.EXTRA_REMOTE_INPUTS;
+    public static final String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
 
     private NotificationCompatExtras() {}
 }
diff --git a/compat/java/android/support/v4/app/NotificationManagerCompat.java b/compat/java/android/support/v4/app/NotificationManagerCompat.java
index 582c570..8f4c4b4 100644
--- a/compat/java/android/support/v4/app/NotificationManagerCompat.java
+++ b/compat/java/android/support/v4/app/NotificationManagerCompat.java
@@ -66,8 +66,7 @@
      * Notification extras key: if set to true, the posted notification should use
      * the side channel for delivery instead of using notification manager.
      */
-    public static final String EXTRA_USE_SIDE_CHANNEL =
-            NotificationCompatJellybean.EXTRA_USE_SIDE_CHANNEL;
+    public static final String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
 
     /**
      * Intent action to register for on a service to receive side channel
diff --git a/compat/java/android/support/v4/app/RemoteInput.java b/compat/java/android/support/v4/app/RemoteInput.java
index 73eb388..6efa63c 100644
--- a/compat/java/android/support/v4/app/RemoteInput.java
+++ b/compat/java/android/support/v4/app/RemoteInput.java
@@ -37,7 +37,7 @@
     private static final String TAG = "RemoteInput";
 
     /** Label used to denote the clip data type used for remote input transport */
-    public static final String RESULTS_CLIP_LABEL = RemoteInputCompatJellybean.RESULTS_CLIP_LABEL;
+    public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
 
     /** Extra added to a clip data intent object to hold the text results bundle. */
     public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
diff --git a/compat/java/android/support/v4/content/ContextCompat.java b/compat/java/android/support/v4/content/ContextCompat.java
index 2790234..9d8f551 100644
--- a/compat/java/android/support/v4/content/ContextCompat.java
+++ b/compat/java/android/support/v4/content/ContextCompat.java
@@ -30,7 +30,6 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.app.ActivityOptionsCompat;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.os.EnvironmentCompat;
 import android.util.Log;
 import android.util.TypedValue;
@@ -557,7 +556,7 @@
      * @see Context#startService()
      */
     public static void startForegroundService(Context context, Intent intent) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             context.startForegroundService(intent);
         } else {
             // Pre-O behavior.
diff --git a/compat/java/android/support/v4/content/pm/ShortcutManagerCompat.java b/compat/java/android/support/v4/content/pm/ShortcutManagerCompat.java
index ecc9344..9cfc3d3 100644
--- a/compat/java/android/support/v4/content/pm/ShortcutManagerCompat.java
+++ b/compat/java/android/support/v4/content/pm/ShortcutManagerCompat.java
@@ -24,11 +24,11 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutManager;
+import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
 import android.support.v4.content.ContextCompat;
-import android.support.v4.os.BuildCompat;
 import android.text.TextUtils;
 
 /**
@@ -51,7 +51,7 @@
      * {@code false} otherwise
      */
     public static boolean isRequestPinShortcutSupported(@NonNull Context context) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return context.getSystemService(ShortcutManager.class).isRequestPinShortcutSupported();
         }
 
@@ -88,7 +88,7 @@
      */
     public static boolean requestPinShortcut(@NonNull final Context context,
             @NonNull ShortcutInfoCompat shortcut, @Nullable final IntentSender callback) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return context.getSystemService(ShortcutManager.class).requestPinShortcut(
                     shortcut.toShortcutInfo(), callback);
         }
@@ -132,7 +132,7 @@
     public static Intent createShortcutResultIntent(@NonNull Context context,
             @NonNull ShortcutInfoCompat shortcut) {
         Intent result = null;
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             result = context.getSystemService(ShortcutManager.class)
                     .createShortcutResultIntent(shortcut.toShortcutInfo());
         }
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompat.java b/compat/java/android/support/v4/graphics/TypefaceCompat.java
index e9b2edd..59be452 100644
--- a/compat/java/android/support/v4/graphics/TypefaceCompat.java
+++ b/compat/java/android/support/v4/graphics/TypefaceCompat.java
@@ -21,7 +21,8 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Typeface;
-import android.net.Uri;
+import android.os.Build;
+import android.os.CancellationSignal;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
@@ -33,8 +34,7 @@
 import android.support.v4.util.LruCache;
 import android.widget.TextView;
 
-import java.nio.ByteBuffer;
-import java.util.Map;
+import java.io.File;
 
 /**
  * Helper for accessing features in {@link Typeface} in a backwards compatible fashion.
@@ -42,8 +42,18 @@
  */
 @RestrictTo(LIBRARY_GROUP)
 public class TypefaceCompat {
-    // TODO(nona): Introduce API 24 implementation.
-    private static final TypefaceCompatImpl sTypefaceCompatImpl = new TypefaceCompatBaseImpl();
+    private static final String TAG = "TypefaceCompat";
+
+    private static final TypefaceCompatImpl sTypefaceCompatImpl;
+    static {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && TypefaceCompatApi24Impl.isUsable()) {
+            sTypefaceCompatImpl = new TypefaceCompatApi24Impl();
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            sTypefaceCompatImpl = new TypefaceCompatApi21Impl();
+        } else {
+            sTypefaceCompatImpl = new TypefaceCompatBaseImpl();
+        }
+    }
 
     /**
      * Cache for Typeface objects dynamically loaded from assets.
@@ -51,17 +61,14 @@
     private static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16);
 
     interface TypefaceCompatImpl {
-        // Create Typeface from font file in res/font directory.
-        Typeface createFromResourcesFontFile(Context context, Resources resources, int id,
-                int style);
-
         // Create Typeface from XML which root node is "font-family"
         Typeface createFromFontFamilyFilesResourceEntry(
-                Context context, FontFamilyFilesResourceEntry entry, Resources resources, int id,
+                Context context, FontFamilyFilesResourceEntry entry, Resources resources,
                 int style);
 
-        Typeface createTypeface(Context context, @NonNull FontInfo[] fonts,
-                Map<Uri, ByteBuffer> uriBuffer);
+        Typeface createFromFontInfo(Context context,
+                @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts,
+                int style);
     }
 
     private TypefaceCompat() {}
@@ -103,7 +110,7 @@
                     providerEntry.getTimeout(), style);
         } else {
             typeface = sTypefaceCompatImpl.createFromFontFamilyFilesResourceEntry(
-                    context, (FontFamilyFilesResourceEntry) entry, resources, id, style);
+                    context, (FontFamilyFilesResourceEntry) entry, resources, style);
         }
         if (typeface != null) {
             sTypefaceCache.put(createResourceUid(resources, id, style), typeface);
@@ -117,19 +124,34 @@
     @Nullable
     public static Typeface createFromResourcesFontFile(
             Context context, Resources resources, int id, int style) {
-        Typeface typeface = sTypefaceCompatImpl.createFromResourcesFontFile(
-                context, resources, id, style);
-        if (typeface != null) {
-            sTypefaceCache.put(createResourceUid(resources, id, style), typeface);
+        final File tmpFile = TypefaceCompatUtil.getTempFile(context);
+        if (tmpFile == null) {
+            return null;
         }
-        return typeface;
+        try {
+            if (!TypefaceCompatUtil.copyToFile(tmpFile, resources, id)) {
+                return null;
+            }
+            Typeface typeface = Typeface.createFromFile(tmpFile.getPath());
+            if (typeface != null) {
+                sTypefaceCache.put(createResourceUid(resources, id, style), typeface);
+            }
+            return typeface;
+        } catch (RuntimeException e) {
+            // This was thrown from Typeface.createFromFile when a Typeface could not be loaded.
+            // such as due to an invalid ttf or unreadable file. We don't want to throw that
+            // exception anymore.
+            return null;
+        } finally {
+            tmpFile.delete();
+        }
     }
 
     /**
      * Create a Typeface from a given FontInfo list and a map that matches them to ByteBuffers.
      */
-    public static Typeface createTypeface(Context context, @NonNull FontInfo[] fonts,
-            Map<Uri, ByteBuffer> uriBuffer) {
-        return sTypefaceCompatImpl.createTypeface(context, fonts, uriBuffer);
+    public static Typeface createFromFontInfo(Context context,
+            @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) {
+        return sTypefaceCompatImpl.createFromFontInfo(context, cancellationSignal, fonts, style);
     }
 }
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompatApi21Impl.java b/compat/java/android/support/v4/graphics/TypefaceCompatApi21Impl.java
new file mode 100644
index 0000000..a742004
--- /dev/null
+++ b/compat/java/android/support/v4/graphics/TypefaceCompatApi21Impl.java
@@ -0,0 +1,85 @@
+/*
+ * 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.v4.graphics;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.v4.provider.FontsContractCompat.FontInfo;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+
+/**
+ * Implementation of the Typeface compat methods for API 21 and above.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(21)
+class TypefaceCompatApi21Impl extends TypefaceCompatBaseImpl {
+    private static final String TAG = "TypefaceCompatApi21Impl";
+
+    private File getFile(ParcelFileDescriptor fd) {
+        try {
+            final String path = Os.readlink("/proc/self/fd/" + fd.getFd());
+            // Check if the symbolic link points the regular file.
+            if (OsConstants.S_ISREG(Os.stat(path).st_mode)) {
+                return new File(path);
+            } else {
+                return null;
+            }
+        } catch (ErrnoException e) {
+            return null;  // Mostly permission error.
+        }
+    }
+
+    @Override
+    public Typeface createFromFontInfo(Context context, CancellationSignal cancellationSignal,
+            @NonNull FontInfo[] fonts, int style) {
+        if (fonts.length < 1) {
+            return null;
+        }
+        final FontInfo bestFont = findBestInfo(fonts, style);
+        final ContentResolver resolver = context.getContentResolver();
+        try (ParcelFileDescriptor pfd =
+                     resolver.openFileDescriptor(bestFont.getUri(), "r", cancellationSignal)) {
+            final File file = getFile(pfd);
+            if (file == null || !file.canRead()) {
+                // Unable to use the real file for creating Typeface. Fallback to copying
+                // implementation.
+                try (FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())) {
+                    return super.createFromInputStream(context, fis);
+                }
+            }
+            return Typeface.createFromFile(file);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+}
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompatApi24Impl.java b/compat/java/android/support/v4/graphics/TypefaceCompatApi24Impl.java
new file mode 100644
index 0000000..22d5f9d
--- /dev/null
+++ b/compat/java/android/support/v4/graphics/TypefaceCompatApi24Impl.java
@@ -0,0 +1,161 @@
+/*
+ * 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.v4.graphics;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.CancellationSignal;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
+import android.support.v4.content.res.FontResourcesParserCompat.FontFileResourceEntry;
+import android.support.v4.provider.FontsContractCompat.FontInfo;
+import android.support.v4.util.SimpleArrayMap;
+import android.util.Log;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+
+/**
+ * Implementation of the Typeface compat methods for API 24 and above.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(24)
+class TypefaceCompatApi24Impl implements TypefaceCompat.TypefaceCompatImpl {
+    private static final String TAG = "TypefaceCompatApi24Impl";
+
+    private static final String FONT_FAMILY_CLASS = "android.graphics.FontFamily";
+    private static final String ADD_FONT_WEIGHT_STYLE_METHOD = "addFontWeightStyle";
+    private static final String CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD =
+            "createFromFamiliesWithDefault";
+    private static final Class sFontFamily;
+    private static final Constructor sFontFamilyCtor;
+    private static final Method sAddFontWeightStyle;
+    private static final Method sCreateFromFamiliesWithDefault;
+
+    static {
+        Class fontFamilyClass;
+        Constructor fontFamilyCtor;
+        Method addFontMethod;
+        Method createFromFamiliesWithDefaultMethod;
+        try {
+            fontFamilyClass = Class.forName(FONT_FAMILY_CLASS);
+            fontFamilyCtor = fontFamilyClass.getConstructor();
+            addFontMethod = fontFamilyClass.getMethod(ADD_FONT_WEIGHT_STYLE_METHOD,
+                    ByteBuffer.class, Integer.TYPE, List.class, Integer.TYPE, Boolean.TYPE);
+            Object familyArray = Array.newInstance(fontFamilyClass, 1);
+            createFromFamiliesWithDefaultMethod =
+                    Typeface.class.getMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
+                          familyArray.getClass());
+        } catch (ClassNotFoundException | NoSuchMethodException e) {
+            Log.e(TAG, e.getClass().getName(), e);
+            fontFamilyClass = null;
+            fontFamilyCtor = null;
+            addFontMethod = null;
+            createFromFamiliesWithDefaultMethod = null;
+        }
+        sFontFamilyCtor = fontFamilyCtor;
+        sFontFamily = fontFamilyClass;
+        sAddFontWeightStyle = addFontMethod;
+        sCreateFromFamiliesWithDefault = createFromFamiliesWithDefaultMethod;
+    }
+
+    /**
+     * Returns true if API24 implementation is usable.
+     */
+    public static boolean isUsable() {
+        return sAddFontWeightStyle != null;
+    }
+
+    private static Object newFamily() {
+        try {
+            return sFontFamilyCtor.newInstance();
+        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static boolean addFontWeightStyle(Object family, ByteBuffer buffer, int ttcIndex,
+            int weight, boolean style) {
+        try {
+            final Boolean result = (Boolean) sAddFontWeightStyle.invoke(
+                    family, buffer, ttcIndex, null /* variation axis */, weight, style);
+            return result.booleanValue();
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Typeface createFromFamiliesWithDefault(Object family) {
+        try {
+            Object familyArray = Array.newInstance(sFontFamily, 1);
+            Array.set(familyArray, 0, family);
+            return (Typeface) sCreateFromFamiliesWithDefault.invoke(
+                    null /* static method */, familyArray);
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Typeface createFromFontInfo(Context context,
+            @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) {
+        Object family = newFamily();
+        SimpleArrayMap<Uri, ByteBuffer> bufferCache = new SimpleArrayMap<>();
+
+        for (final FontInfo font : fonts) {
+            final Uri uri = font.getUri();
+            ByteBuffer buffer = bufferCache.get(uri);
+            if (buffer == null) {
+                buffer = TypefaceCompatUtil.mmap(context, cancellationSignal, uri);
+                bufferCache.put(uri, buffer);
+            }
+            if (!addFontWeightStyle(family, buffer, font.getTtcIndex(), font.getWeight(),
+                    font.isItalic())) {
+                return null;
+            }
+        }
+        return createFromFamiliesWithDefault(family);
+    }
+
+    @Override
+    public Typeface createFromFontFamilyFilesResourceEntry(Context context,
+            FontFamilyFilesResourceEntry entry, Resources resources, int style) {
+        Object family = newFamily();
+        for (final FontFileResourceEntry e : entry.getEntries()) {
+            final ByteBuffer buffer =
+                    TypefaceCompatUtil.copyToDirectBuffer(context, resources, e.getResourceId());
+            // TODO: support ttc index.
+            if (!addFontWeightStyle(family, buffer, 0, e.getWeight(), e.isItalic())) {
+                return null;
+            }
+        }
+        return createFromFamiliesWithDefault(family);
+    }
+}
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompatBaseImpl.java b/compat/java/android/support/v4/graphics/TypefaceCompatBaseImpl.java
index 86bcae8..55289eb 100644
--- a/compat/java/android/support/v4/graphics/TypefaceCompatBaseImpl.java
+++ b/compat/java/android/support/v4/graphics/TypefaceCompatBaseImpl.java
@@ -21,7 +21,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Typeface;
-import android.net.Uri;
+import android.os.CancellationSignal;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RequiresApi;
@@ -29,15 +29,10 @@
 import android.support.v4.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
 import android.support.v4.content.res.FontResourcesParserCompat.FontFileResourceEntry;
 import android.support.v4.provider.FontsContractCompat.FontInfo;
-import android.util.Log;
 
-import java.io.Closeable;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.Map;
 
 /**
  * Implementation of the Typeface compat methods for API 14 and above.
@@ -49,169 +44,107 @@
     private static final String TAG = "TypefaceCompatBaseImpl";
     private static final String CACHE_FILE_PREFIX = "cached_font_";
 
+    private interface StyleExtractor<T> {
+        int getWeight(T t);
+        boolean isItalic(T t);
+    }
+
+    private static <T> T findBestFont(T[] fonts, int style, StyleExtractor<T> extractor) {
+        final int targetWeight = (style & Typeface.BOLD) == 0 ? 400 : 700;
+        final boolean isTargetItalic = (style & Typeface.ITALIC) != 0;
+
+        T best = null;
+        int bestScore = Integer.MAX_VALUE;  // smaller is better
+
+        for (final T font : fonts) {
+            final int score = (Math.abs(extractor.getWeight(font) - targetWeight) * 2)
+                    + (extractor.isItalic(font) == isTargetItalic ? 0 : 1);
+
+            if (best == null || bestScore > score) {
+                best = font;
+                bestScore = score;
+            }
+        }
+        return best;
+    }
+
+    protected FontInfo findBestInfo(FontInfo[] fonts, int style) {
+        return findBestFont(fonts, style, new StyleExtractor<FontInfo>() {
+            @Override
+            public int getWeight(FontInfo info) {
+                return info.getWeight();
+            }
+
+            @Override
+            public boolean isItalic(FontInfo info) {
+                return info.isItalic();
+            }
+        });
+    }
+
+    // Caller must close the stream.
+    protected Typeface createFromInputStream(Context context, InputStream is) {
+        final File tmpFile = TypefaceCompatUtil.getTempFile(context);
+        if (tmpFile == null) {
+            return null;
+        }
+        try {
+            if (!TypefaceCompatUtil.copyToFile(tmpFile, is)) {
+                return null;
+            }
+            return Typeface.createFromFile(tmpFile.getPath());
+        } catch (RuntimeException e) {
+            // This was thrown from Typeface.createFromFile when a Typeface could not be loaded,
+            // such as due to an invalid ttf or unreadable file. We don't want to throw that
+            // exception anymore.
+            return null;
+        } finally {
+            tmpFile.delete();
+        }
+    }
+
     @Override
-    public Typeface createTypeface(Context context, @NonNull FontInfo[] fonts,
-            Map<Uri, ByteBuffer> uriBuffer) {
+    public Typeface createFromFontInfo(Context context,
+            @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) {
         // When we load from file, we can only load one font so just take the first one.
         if (fonts.length < 1) {
             return null;
         }
-        Typeface typeface = null;
-        FontInfo font = fonts[0];
-        ByteBuffer buffer = uriBuffer.get(font.getUri());
-        File tmpFile = copyToCacheFile(context, buffer);
-        if (tmpFile != null) {
-            try {
-                typeface = Typeface.createFromFile(tmpFile.getPath());
-            } catch (RuntimeException e) {
-                // This was thrown from Typeface.createFromFile when a Typeface could not be loaded,
-                // such as due to an invalid ttf or unreadable file. We don't want to throw that
-                // exception anymore.
-                return null;
-            } finally {
-                tmpFile.delete();
-            }
-        }
-        return typeface;
-    }
-
-    private static File copyToCacheFile(Context context, final InputStream is) {
-        FileOutputStream fos = null;
-        File cacheFile;
-        try {
-            cacheFile = new File(context.getCacheDir(),
-                    CACHE_FILE_PREFIX + Thread.currentThread().getId());
-            fos = new FileOutputStream(cacheFile, false);
-
-            byte[] buffer = new byte[1024];
-            int readLen;
-            while ((readLen = is.read(buffer)) != -1) {
-                fos.write(buffer, 0, readLen);
-            }
-        } catch (IOException e) {
-            Log.e(TAG, "Error copying font file descriptor to temp local file.", e);
-            return null;
-        } finally {
-            closeQuietly(is);
-            closeQuietly(fos);
-        }
-        return cacheFile;
-    }
-
-    private static File copyToCacheFile(Context context, final ByteBuffer is) {
-        FileOutputStream fos = null;
-        File cacheFile;
-        try {
-            cacheFile = new File(context.getCacheDir(),
-                    CACHE_FILE_PREFIX + Thread.currentThread().getId());
-            fos = new FileOutputStream(cacheFile, false);
-
-            byte[] buffer = new byte[1024];
-            while (is.hasRemaining()) {
-                int len = Math.min(1024, is.remaining());
-                is.get(buffer, 0, len);
-                fos.write(buffer, 0, len);
-            }
-        } catch (IOException e) {
-            Log.e(TAG, "Error copying font file descriptor to temp local file.", e);
-            return null;
-        } finally {
-            closeQuietly(fos);
-        }
-        return cacheFile;
-    }
-
-    private static void closeQuietly(InputStream is) {
-        if (is != null) {
-            try {
-                is.close();
-            } catch (IOException io) {
-                Log.e(TAG, "Error closing input stream", io);
-            }
-        }
-    }
-
-    @Nullable
-    @Override
-    public Typeface createFromResourcesFontFile(Context context, Resources resources, int id,
-            int style) {
+        FontInfo font = findBestInfo(fonts, style);
         InputStream is = null;
         try {
-            is = resources.openRawResource(id);
-            return createTypeface(context, resources, is);
+            is = context.getContentResolver().openInputStream(font.getUri());
+            return createFromInputStream(context, is);
         } catch (IOException e) {
             return null;
         } finally {
-            closeQuietly(is);
+            TypefaceCompatUtil.closeQuietly(is);
         }
     }
 
-    private FontFileResourceEntry findBestEntry(FontFamilyFilesResourceEntry entry,
-            int targetWeight, boolean isTargetItalic) {
-        FontFileResourceEntry bestEntry = null;
-        int bestScore = Integer.MAX_VALUE;  // smaller is better
-
-        for (final FontFileResourceEntry e : entry.getEntries()) {
-            final int score = (Math.abs(e.getWeight() - targetWeight) * 2)
-                    + (isTargetItalic == e.isItalic() ? 0 : 1);
-
-            if (bestEntry == null || bestScore > score) {
-                bestEntry = e;
-                bestScore = score;
+    private FontFileResourceEntry findBestEntry(FontFamilyFilesResourceEntry entry, int style) {
+        return findBestFont(entry.getEntries(), style, new StyleExtractor<FontFileResourceEntry>() {
+            @Override
+            public int getWeight(FontFileResourceEntry entry) {
+                return entry.getWeight();
             }
-        }
-        return bestEntry;
+
+            @Override
+            public boolean isItalic(FontFileResourceEntry entry) {
+                return entry.isItalic();
+            }
+        });
     }
 
     @Nullable
     @Override
     public Typeface createFromFontFamilyFilesResourceEntry(Context context,
-            FontFamilyFilesResourceEntry entry, Resources resources, int id, int style) {
-        FontFileResourceEntry best = findBestEntry(
-                entry, ((style & Typeface.BOLD) == 0) ? 400 : 700, (style & Typeface.ITALIC) != 0);
+            FontFamilyFilesResourceEntry entry, Resources resources, int style) {
+        FontFileResourceEntry best = findBestEntry(entry, style);
         if (best == null) {
             return null;
         }
-
-        InputStream is = null;
-        try {
-            is = resources.openRawResource(best.getResourceId());
-            return createTypeface(context, resources, is);
-        } catch (IOException e) {
-            // This is fine. The resource can be string type which indicates a name of Typeface.
-        } finally {
-            closeQuietly(is);
-        }
-        return null;
-    }
-
-    // Caller must close "is"
-    Typeface createTypeface(Context context, Resources resources, InputStream is)
-            throws IOException {
-        File tmpFile = copyToCacheFile(context, is);
-        if (tmpFile != null) {
-            try {
-                return Typeface.createFromFile(tmpFile.getPath());
-            } catch (RuntimeException e) {
-                // This was thrown from Typeface.createFromFile when a Typeface could not be loaded,
-                // such as due to an invalid ttf or unreadable file. We don't want to throw that
-                // exception anymore.
-                android.util.Log.e(TAG, "Failed to create font", e);
-                return null;
-            } finally {
-                tmpFile.delete();
-            }
-        }
-        return null;
-    }
-
-    static void closeQuietly(Closeable stream) {
-        if (stream != null) {
-            try {
-                stream.close();
-            } catch (IOException io) {
-                Log.e(TAG, "Error closing stream", io);
-            }
-        }
+        return TypefaceCompat.createFromResourcesFontFile(
+                context, resources, best.getResourceId(), style);
     }
 }
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompatUtil.java b/compat/java/android/support/v4/graphics/TypefaceCompatUtil.java
new file mode 100644
index 0000000..8d8beb2
--- /dev/null
+++ b/compat/java/android/support/v4/graphics/TypefaceCompatUtil.java
@@ -0,0 +1,164 @@
+/*
+ * 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.v4.graphics;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+/**
+ * Utility methods for TypefaceCompat.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+class TypefaceCompatUtil {
+    private static final String TAG = "TypefaceCompatUtil";
+
+    private TypefaceCompatUtil() {}  // Do not instantiate.
+
+    private static final String CACHE_FILE_PREFIX = ".font";
+
+    /**
+     * Creates a temp file.
+     *
+     * Returns null if failed to create temp file.
+     */
+    public static File getTempFile(Context context) {
+        final String prefix = CACHE_FILE_PREFIX + Process.myPid() + "-" + Process.myTid() + "-";
+        for (int i = 0; i < 100; ++i) {
+            final File file = new File(context.getCacheDir(), prefix + i);
+            try {
+                if (file.createNewFile()) {
+                    return file;
+                }
+            } catch (IOException e) {
+                // ignore. Try next file.
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Copy the file contents to the direct byte buffer.
+     */
+    @RequiresApi(19)
+    private static ByteBuffer mmap(File file) {
+        try (FileInputStream fis = new FileInputStream(file)) {
+            FileChannel channel = fis.getChannel();
+            final long size = channel.size();
+            return channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Copy the file contents to the direct byte buffer.
+     */
+    @RequiresApi(19)
+    public static ByteBuffer mmap(Context context, CancellationSignal cancellationSignal, Uri uri) {
+        final ContentResolver resolver = context.getContentResolver();
+        try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r", cancellationSignal);
+                FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())) {
+            FileChannel channel = fis.getChannel();
+            final long size = channel.size();
+            return channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Copy the resource contents to the direct byte buffer.
+     */
+    @RequiresApi(19)
+    public static ByteBuffer copyToDirectBuffer(Context context, Resources res, int id) {
+        File tmpFile = getTempFile(context);
+        if (tmpFile == null) {
+            return null;
+        }
+        try {
+            if (!copyToFile(tmpFile, res, id)) {
+                return null;
+            }
+            return mmap(tmpFile);
+        } finally {
+            tmpFile.delete();
+        }
+    }
+
+    /**
+     * Copy the input stream contents to file.
+     */
+    public static boolean copyToFile(File file, InputStream is) {
+        FileOutputStream os = null;
+        try {
+            os = new FileOutputStream(file, false);
+            byte[] buffer = new byte[1024];
+            int readLen;
+            while ((readLen = is.read(buffer)) != -1) {
+                os.write(buffer, 0, readLen);
+            }
+            return true;
+        } catch (IOException e) {
+            Log.e(TAG, "Error copying resource contents to temp file: " + e.getMessage());
+            return false;
+        } finally {
+            closeQuietly(os);
+        }
+    }
+
+    /**
+     * Copy the resource contents to file.
+     */
+    public static boolean copyToFile(File file, Resources res, int id) {
+        InputStream is = null;
+        try {
+            is = res.openRawResource(id);
+            return copyToFile(file, is);
+        } finally {
+            closeQuietly(is);
+        }
+    }
+
+    public static void closeQuietly(Closeable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException e) {
+            }
+        }
+    }
+}
diff --git a/compat/java/android/support/v4/graphics/drawable/IconCompat.java b/compat/java/android/support/v4/graphics/drawable/IconCompat.java
index c820366..c268d8b 100644
--- a/compat/java/android/support/v4/graphics/drawable/IconCompat.java
+++ b/compat/java/android/support/v4/graphics/drawable/IconCompat.java
@@ -34,7 +34,6 @@
 import android.support.annotation.DrawableRes;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
-import android.support.v4.os.BuildCompat;
 
 /**
  * Helper for accessing features in {@link android.graphics.drawable.Icon}
@@ -186,7 +185,7 @@
             case TYPE_BITMAP:
                 return Icon.createWithBitmap((Bitmap) mObj1);
             case TYPE_ADAPTIVE_BITMAP:
-                if (BuildCompat.isAtLeastO()) {
+                if (Build.VERSION.SDK_INT >= 26) {
                     return Icon.createWithAdaptiveBitmap((Bitmap) mObj1);
                 } else {
                     return Icon.createWithBitmap(createLegacyIconFromAdaptiveIcon((Bitmap) mObj1));
diff --git a/compat/java/android/support/v4/os/LocaleHelper.java b/compat/java/android/support/v4/os/LocaleHelper.java
index 539106c..f6fe7da 100644
--- a/compat/java/android/support/v4/os/LocaleHelper.java
+++ b/compat/java/android/support/v4/os/LocaleHelper.java
@@ -62,7 +62,7 @@
         StringBuilder buf = new StringBuilder();
         buf.append(locale.getLanguage());
         final String country = locale.getCountry();
-        if (country != null && !country.equals("")) {
+        if (country != null && !country.isEmpty()) {
             buf.append("-");
             buf.append(locale.getCountry());
         }
diff --git a/compat/java/android/support/v4/os/LocaleListCompat.java b/compat/java/android/support/v4/os/LocaleListCompat.java
index 1185e98..d4a32f2 100644
--- a/compat/java/android/support/v4/os/LocaleListCompat.java
+++ b/compat/java/android/support/v4/os/LocaleListCompat.java
@@ -285,7 +285,7 @@
      */
     @NonNull
     public static LocaleListCompat forLanguageTags(@Nullable String list) {
-        if (list == null || list.equals("")) {
+        if (list == null || list.isEmpty()) {
             return getEmptyLocaleList();
         } else {
             final String[] tags = list.split(",");
diff --git a/compat/java/android/support/v4/os/LocaleListHelper.java b/compat/java/android/support/v4/os/LocaleListHelper.java
index 133ecfe..cfb24fb 100644
--- a/compat/java/android/support/v4/os/LocaleListHelper.java
+++ b/compat/java/android/support/v4/os/LocaleListHelper.java
@@ -271,7 +271,7 @@
     @RestrictTo(LIBRARY_GROUP)
     @NonNull
     static LocaleListHelper forLanguageTags(@Nullable String list) {
-        if (list == null || list.equals("")) {
+        if (list == null || list.isEmpty()) {
             return getEmptyLocaleList();
         } else {
             final String[] tags = list.split(",");
diff --git a/compat/java/android/support/v4/provider/FontsContractCompat.java b/compat/java/android/support/v4/provider/FontsContractCompat.java
index ba44019..afa565c 100644
--- a/compat/java/android/support/v4/provider/FontsContractCompat.java
+++ b/compat/java/android/support/v4/provider/FontsContractCompat.java
@@ -19,6 +19,7 @@
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 import static android.support.v4.content.res.FontResourcesParserCompat.FetchStrategy;
 
+import android.annotation.SuppressLint;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.Context;
@@ -51,19 +52,13 @@
 import android.support.v4.util.SimpleArrayMap;
 import android.widget.TextView;
 
-import java.io.FileInputStream;
-import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.Callable;
 
 /**
@@ -178,7 +173,8 @@
             new SelfDestructiveThread("fonts", Process.THREAD_PRIORITY_BACKGROUND,
                     BACKGROUND_THREAD_KEEP_ALIVE_DURATION_MS);
 
-    private static Typeface getFontInternal(final Context context, final FontRequest request) {
+    private static Typeface getFontInternal(final Context context, final FontRequest request,
+            int style) {
         FontFamilyResult result;
         try {
             result = fetchFonts(context, null /* CancellationSignal */, request);
@@ -186,7 +182,8 @@
             return null;
         }
         if (result.getStatusCode() == FontFamilyResult.STATUS_OK) {
-            return buildTypeface(context, null /* CancellationSignal */, result.getFonts());
+            return TypefaceCompat.createFromFontInfo(context, null /* CancellationSignal */,
+                    result.getFonts(), style);
         }
         return null;
     }
@@ -200,7 +197,7 @@
     @RestrictTo(LIBRARY_GROUP)
     public static Typeface getFontSync(final Context context, final FontRequest request,
             final TextView targetView, @FetchStrategy int strategy, int timeout, final int style) {
-        final String id = request.getIdentifier();
+        final String id = request.getIdentifier() + "-" + style;
         Typeface cached = sTypefaceCache.get(id);
         if (cached != null) {
             return cached;
@@ -211,13 +208,13 @@
 
         if (isBlockingFetch && timeout == FontResourcesParserCompat.INFINITE_TIMEOUT_VALUE) {
             // Wait forever. No need to post to the thread.
-            return getFontInternal(context, request);
+            return getFontInternal(context, request, style);
         }
 
         final Callable<Typeface> fetcher = new Callable<Typeface>() {
             @Override
             public Typeface call() throws Exception {
-                Typeface typeface = getFontInternal(context, request);
+                Typeface typeface = getFontInternal(context, request, style);
                 if (typeface != null) {
                     sTypefaceCache.put(id, typeface);
                 }
@@ -581,55 +578,6 @@
     }
 
     /**
-     * A helper function to create a mapping from {@link Uri} to {@link ByteBuffer}.
-     *
-     * Skip if the file contents is not ready to be read.
-     *
-     * @param context A {@link Context} to be used for resolving content URI in
-     *                {@link FontInfo}.
-     * @param fonts An array of {@link FontInfo}.
-     * @return A map from {@link Uri} to {@link ByteBuffer}.
-     */
-    private static Map<Uri, ByteBuffer> prepareFontData(Context context, FontInfo[] fonts,
-            CancellationSignal cancellationSignal) {
-        final HashMap<Uri, ByteBuffer> out = new HashMap<>();
-        final ContentResolver resolver = context.getContentResolver();
-
-        for (FontInfo font : fonts) {
-            if (font.getResultCode() != Columns.RESULT_CODE_OK) {
-                continue;
-            }
-
-            final Uri uri = font.getUri();
-            if (out.containsKey(uri)) {
-                continue;
-            }
-
-            ByteBuffer buffer = null;
-            ParcelFileDescriptor pfd = null;
-            FileInputStream fis = null;
-            try {
-                if (Build.VERSION.SDK_INT > 19) {
-                    pfd = resolver.openFileDescriptor(uri, "r", cancellationSignal);
-                } else {
-                    pfd = resolver.openFileDescriptor(uri, "r");
-                }
-                fis = new FileInputStream(pfd.getFileDescriptor());
-                final FileChannel fileChannel = fis.getChannel();
-                final long size = fileChannel.size();
-                buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
-            } catch (IOException e) {
-                // ignore
-            }
-
-            // TODO: try other approach?, e.g. read all contents instead of mmap.
-
-            out.put(uri, buffer);
-        }
-        return Collections.unmodifiableMap(out);
-    }
-
-    /**
      * Build a Typeface from an array of {@link FontInfo}
      *
      * Results that are marked as not ready will be skipped.
@@ -643,9 +591,8 @@
      */
     public static Typeface buildTypeface(@NonNull Context context,
             @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts) {
-        final Map<Uri, ByteBuffer> uriBuffer =
-                prepareFontData(context, fonts, cancellationSignal);
-        return TypefaceCompat.createTypeface(context, fonts, uriBuffer);
+        return TypefaceCompat.createFromFontInfo(context, cancellationSignal, fonts,
+                Typeface.NORMAL);
     }
 
     /**
@@ -699,6 +646,8 @@
         }
 
         List<byte[]> signatures;
+        // We correctly check all signatures returned, as advised in the lint error.
+        @SuppressLint("PackageManagerGetSignatures")
         PackageInfo packageInfo = packageManager.getPackageInfo(info.packageName,
                 PackageManager.GET_SIGNATURES);
         signatures = convertToByteArrayList(packageInfo.signatures);
diff --git a/compat/java/android/support/v4/text/util/LinkifyCompat.java b/compat/java/android/support/v4/text/util/LinkifyCompat.java
index d242780..23b3e49 100644
--- a/compat/java/android/support/v4/text/util/LinkifyCompat.java
+++ b/compat/java/android/support/v4/text/util/LinkifyCompat.java
@@ -18,6 +18,7 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import android.os.Build;
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
@@ -96,6 +97,9 @@
      *  @return True if at least one link is found and applied.
      */
     public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return Linkify.addLinks(text, mask);
+        }
         if (mask == 0) {
             return false;
         }
@@ -157,6 +161,9 @@
      *  @return True if at least one link is found and applied.
      */
     public static final boolean addLinks(@NonNull TextView text, @LinkifyMask int mask) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return Linkify.addLinks(text, mask);
+        }
         if (mask == 0) {
             return false;
         }
@@ -197,6 +204,10 @@
      */
     public static final void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
             @Nullable String scheme) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            Linkify.addLinks(text, pattern, scheme);
+            return;
+        }
         addLinks(text, pattern, scheme, null, null, null);
     }
 
@@ -217,6 +228,10 @@
     public static final void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
             @Nullable String scheme, @Nullable MatchFilter matchFilter,
             @Nullable TransformFilter transformFilter) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            Linkify.addLinks(text, pattern, scheme, matchFilter, transformFilter);
+            return;
+        }
         addLinks(text, pattern, scheme, null, matchFilter, transformFilter);
     }
 
@@ -238,8 +253,12 @@
      *  @param transformFilter Filter to allow the client code to update the link found.
      */
     public static final void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
-            @Nullable  String defaultScheme, @Nullable String[] schemes,
+            @Nullable String defaultScheme, @Nullable String[] schemes,
             @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            Linkify.addLinks(text, pattern, defaultScheme, schemes, matchFilter, transformFilter);
+            return;
+        }
         SpannableString spannable = SpannableString.valueOf(text.getText());
 
         boolean linksAdded = addLinks(spannable, pattern, defaultScheme, schemes, matchFilter,
@@ -261,6 +280,9 @@
      */
     public static final boolean addLinks(@NonNull Spannable text, @NonNull Pattern pattern,
             @Nullable String scheme) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return Linkify.addLinks(text, pattern, scheme);
+        }
         return addLinks(text, pattern, scheme, null, null, null);
     }
 
@@ -282,6 +304,9 @@
     public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
             @Nullable String scheme, @Nullable MatchFilter matchFilter,
             @Nullable TransformFilter transformFilter) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return Linkify.addLinks(spannable, pattern, scheme, matchFilter, transformFilter);
+        }
         return addLinks(spannable, pattern, scheme, null, matchFilter,
                 transformFilter);
     }
@@ -305,6 +330,10 @@
     public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
             @Nullable  String defaultScheme, @Nullable String[] schemes,
             @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return Linkify.addLinks(spannable, pattern, defaultScheme, schemes, matchFilter,
+                    transformFilter);
+        }
         final String[] schemesCopy;
         if (defaultScheme == null) defaultScheme = "";
         if (schemes == null || schemes.length < 1) {
diff --git a/compat/java/android/support/v4/view/MenuItemCompat.java b/compat/java/android/support/v4/view/MenuItemCompat.java
index 649c413..d7e96cc 100644
--- a/compat/java/android/support/v4/view/MenuItemCompat.java
+++ b/compat/java/android/support/v4/view/MenuItemCompat.java
@@ -19,9 +19,9 @@
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.support.annotation.RequiresApi;
 import android.support.v4.internal.view.SupportMenuItem;
-import android.support.v4.os.BuildCompat;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.Menu;
@@ -274,7 +274,7 @@
      */
     static final MenuVersionImpl IMPL;
     static {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             IMPL = new MenuItemCompatApi26Impl();
         } else {
             IMPL = new MenuItemCompatBaseImpl();
diff --git a/compat/java/android/support/v4/view/ViewCompat.java b/compat/java/android/support/v4/view/ViewCompat.java
index efbf848..a4035b2 100644
--- a/compat/java/android/support/v4/view/ViewCompat.java
+++ b/compat/java/android/support/v4/view/ViewCompat.java
@@ -36,7 +36,6 @@
 import android.support.annotation.Nullable;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
 import android.util.Log;
@@ -1576,7 +1575,7 @@
 
     static final ViewCompatBaseImpl IMPL;
     static {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             IMPL = new ViewCompatApi26Impl();
         } else if (Build.VERSION.SDK_INT >= 24) {
             IMPL = new ViewCompatApi24Impl();
diff --git a/compat/java/android/support/v4/view/ViewConfigurationCompat.java b/compat/java/android/support/v4/view/ViewConfigurationCompat.java
index 683a380..4ae2260 100644
--- a/compat/java/android/support/v4/view/ViewConfigurationCompat.java
+++ b/compat/java/android/support/v4/view/ViewConfigurationCompat.java
@@ -17,8 +17,8 @@
 package android.support.v4.view;
 
 import android.content.Context;
+import android.os.Build;
 import android.support.annotation.NonNull;
-import android.support.v4.os.BuildCompat;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.ViewConfiguration;
@@ -37,7 +37,7 @@
     private static Method sGetScaledScrollFactorMethod;
 
     static {
-        if (android.os.Build.VERSION.SDK_INT >= 25 && !BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT == 25) {
             try {
                 sGetScaledScrollFactorMethod =
                         ViewConfiguration.class.getDeclaredMethod("getScaledScrollFactor");
@@ -79,7 +79,7 @@
      */
     public static float getScaledHorizontalScrollFactor(@NonNull ViewConfiguration config,
             @NonNull Context context) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return config.getScaledHorizontalScrollFactor();
         } else {
             return getLegacyScrollFactor(config, context);
@@ -96,7 +96,7 @@
      */
     public static float getScaledVerticalScrollFactor(@NonNull ViewConfiguration config,
             @NonNull Context context) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return config.getScaledVerticalScrollFactor();
         } else {
             return getLegacyScrollFactor(config, context);
diff --git a/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java b/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java
index bf313a0..426dad9 100644
--- a/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java
@@ -36,14 +36,7 @@
     public static final String TAG = "NotificationCompat";
 
     // Extras keys used for Jellybean SDK and above.
-    static final String EXTRA_LOCAL_ONLY = "android.support.localOnly";
-    static final String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
-    static final String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
     static final String EXTRA_DATA_ONLY_REMOTE_INPUTS = "android.support.dataRemoteInputs";
-    static final String EXTRA_GROUP_KEY = "android.support.groupKey";
-    static final String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
-    static final String EXTRA_SORT_KEY = "android.support.sortKey";
-    static final String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
     static final String EXTRA_ALLOW_GENERATED_REPLIES = "android.support.allowGeneratedReplies";
 
     // Bundle keys for storing action fields in a bundle
@@ -112,18 +105,18 @@
                 mExtras.putAll(extras);
             }
             if (localOnly) {
-                mExtras.putBoolean(EXTRA_LOCAL_ONLY, true);
+                mExtras.putBoolean(NotificationCompatExtras.EXTRA_LOCAL_ONLY, true);
             }
             if (groupKey != null) {
-                mExtras.putString(EXTRA_GROUP_KEY, groupKey);
+                mExtras.putString(NotificationCompatExtras.EXTRA_GROUP_KEY, groupKey);
                 if (groupSummary) {
-                    mExtras.putBoolean(EXTRA_GROUP_SUMMARY, true);
+                    mExtras.putBoolean(NotificationCompatExtras.EXTRA_GROUP_SUMMARY, true);
                 } else {
-                    mExtras.putBoolean(EXTRA_USE_SIDE_CHANNEL, true);
+                    mExtras.putBoolean(NotificationManagerCompat.EXTRA_USE_SIDE_CHANNEL, true);
                 }
             }
             if (sortKey != null) {
-                mExtras.putString(EXTRA_SORT_KEY, sortKey);
+                mExtras.putString(NotificationCompatExtras.EXTRA_SORT_KEY, sortKey);
             }
             mContentView = contentView;
             mBigContentView = bigContentView;
@@ -155,7 +148,8 @@
             SparseArray<Bundle> actionExtrasMap = buildActionExtrasMap(mActionExtrasList);
             if (actionExtrasMap != null) {
                 // Add the action extras sparse array if any action was added with extras.
-                getExtras(notif).putSparseParcelableArray(EXTRA_ACTION_EXTRAS, actionExtrasMap);
+                getExtras(notif).putSparseParcelableArray(
+                        NotificationCompatExtras.EXTRA_ACTION_EXTRAS, actionExtrasMap);
             }
             if (mContentView != null) {
                 notif.contentView = mContentView;
@@ -266,7 +260,8 @@
         boolean allowGeneratedReplies = false;
         if (extras != null) {
             remoteInputs = RemoteInputCompatJellybean.fromBundleArray(
-                    BundleUtil.getBundleArrayFromBundle(extras, EXTRA_REMOTE_INPUTS),
+                    BundleUtil.getBundleArrayFromBundle(extras,
+                            NotificationCompatExtras.EXTRA_REMOTE_INPUTS),
                     remoteInputFactory);
             dataOnlyRemoteInputs = RemoteInputCompatJellybean.fromBundleArray(
                     BundleUtil.getBundleArrayFromBundle(extras, EXTRA_DATA_ONLY_REMOTE_INPUTS),
@@ -282,7 +277,7 @@
         builder.addAction(action.getIcon(), action.getTitle(), action.getActionIntent());
         Bundle actionExtras = new Bundle(action.getExtras());
         if (action.getRemoteInputs() != null) {
-            actionExtras.putParcelableArray(EXTRA_REMOTE_INPUTS,
+            actionExtras.putParcelableArray(NotificationCompatExtras.EXTRA_REMOTE_INPUTS,
                     RemoteInputCompatJellybean.toBundleArray(action.getRemoteInputs()));
         }
         if (action.getDataOnlyRemoteInputs() != null) {
@@ -313,7 +308,7 @@
                     Bundle extras = getExtras(notif);
                     if (extras != null) {
                         SparseArray<Bundle> actionExtrasMap = extras.getSparseParcelableArray(
-                                EXTRA_ACTION_EXTRAS);
+                                NotificationCompatExtras.EXTRA_ACTION_EXTRAS);
                         if (actionExtrasMap != null) {
                             actionExtras = actionExtrasMap.get(actionIndex);
                         }
diff --git a/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java b/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java
index 558a42b..0402a91 100644
--- a/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java
@@ -31,12 +31,6 @@
 
 @RequiresApi(16)
 class RemoteInputCompatJellybean {
-    /** Label used to denote the clip data type used for remote input transport */
-    public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
-
-    /** Extra added to a clip data intent object to hold the results bundle. */
-    public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
-
     /** Extra added to a clip data intent object to hold the data results bundle. */
     private static final String EXTRA_DATA_TYPE_RESULTS_DATA =
             "android.remoteinput.dataTypeResultsData";
@@ -112,7 +106,7 @@
         if (clipDataIntent == null) {
             return null;
         }
-        return clipDataIntent.getExtras().getParcelable(EXTRA_RESULTS_DATA);
+        return clipDataIntent.getExtras().getParcelable(RemoteInput.EXTRA_RESULTS_DATA);
     }
 
     static Map<String, Uri> getDataResultsFromIntent(Intent intent, String remoteInputResultKey) {
@@ -145,7 +139,7 @@
         if (clipDataIntent == null) {
             clipDataIntent = new Intent();  // First time we've added a result.
         }
-        Bundle resultsBundle = clipDataIntent.getBundleExtra(EXTRA_RESULTS_DATA);
+        Bundle resultsBundle = clipDataIntent.getBundleExtra(RemoteInput.EXTRA_RESULTS_DATA);
         if (resultsBundle == null) {
             resultsBundle = new Bundle();
         }
@@ -155,8 +149,8 @@
                 resultsBundle.putCharSequence(remoteInput.getResultKey(), (CharSequence) result);
             }
         }
-        clipDataIntent.putExtra(EXTRA_RESULTS_DATA, resultsBundle);
-        intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent));
+        clipDataIntent.putExtra(RemoteInput.EXTRA_RESULTS_DATA, resultsBundle);
+        intent.setClipData(ClipData.newIntent(RemoteInput.RESULTS_CLIP_LABEL, clipDataIntent));
     }
 
     /**
@@ -186,7 +180,7 @@
             resultsBundle.putString(remoteInput.getResultKey(), uri.toString());
             clipDataIntent.putExtra(getExtraResultsKeyForData(mimeType), resultsBundle);
         }
-        intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent));
+        intent.setClipData(ClipData.newIntent(RemoteInput.RESULTS_CLIP_LABEL, clipDataIntent));
     }
 
     private static String getExtraResultsKeyForData(String mimeType) {
@@ -202,7 +196,7 @@
         if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
             return null;
         }
-        if (!clipDescription.getLabel().equals(RESULTS_CLIP_LABEL)) {
+        if (!clipDescription.getLabel().equals(RemoteInput.RESULTS_CLIP_LABEL)) {
             return null;
         }
         return clipData.getItemAt(0).getIntent();
diff --git a/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java b/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java
index 34de842..f40fd1b 100644
--- a/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java
@@ -81,18 +81,18 @@
                         people.toArray(new String[people.size()]));
             }
             if (localOnly) {
-                mExtras.putBoolean(NotificationCompatJellybean.EXTRA_LOCAL_ONLY, true);
+                mExtras.putBoolean(NotificationCompatExtras.EXTRA_LOCAL_ONLY, true);
             }
             if (groupKey != null) {
-                mExtras.putString(NotificationCompatJellybean.EXTRA_GROUP_KEY, groupKey);
+                mExtras.putString(NotificationCompatExtras.EXTRA_GROUP_KEY, groupKey);
                 if (groupSummary) {
-                    mExtras.putBoolean(NotificationCompatJellybean.EXTRA_GROUP_SUMMARY, true);
+                    mExtras.putBoolean(NotificationCompatExtras.EXTRA_GROUP_SUMMARY, true);
                 } else {
-                    mExtras.putBoolean(NotificationCompatJellybean.EXTRA_USE_SIDE_CHANNEL, true);
+                    mExtras.putBoolean(NotificationManagerCompat.EXTRA_USE_SIDE_CHANNEL, true);
                 }
             }
             if (sortKey != null) {
-                mExtras.putString(NotificationCompatJellybean.EXTRA_SORT_KEY, sortKey);
+                mExtras.putString(NotificationCompatExtras.EXTRA_SORT_KEY, sortKey);
             }
             mContentView = contentView;
             mBigContentView = bigContentView;
@@ -115,7 +115,7 @@
             if (actionExtrasMap != null) {
                 // Add the action extras sparse array if any action was added with extras.
                 mExtras.putSparseParcelableArray(
-                        NotificationCompatJellybean.EXTRA_ACTION_EXTRAS, actionExtrasMap);
+                        NotificationCompatExtras.EXTRA_ACTION_EXTRAS, actionExtrasMap);
             }
             b.setExtras(mExtras);
             Notification notification = b.build();
@@ -135,7 +135,7 @@
         Notification.Action action = notif.actions[actionIndex];
         Bundle actionExtras = null;
         SparseArray<Bundle> actionExtrasMap = notif.extras.getSparseParcelableArray(
-                NotificationCompatJellybean.EXTRA_ACTION_EXTRAS);
+                NotificationCompatExtras.EXTRA_ACTION_EXTRAS);
         if (actionExtrasMap != null) {
             actionExtras = actionExtrasMap.get(actionIndex);
         }
diff --git a/compat/tests/assets/fonts/large_a.ttf b/compat/tests/assets/fonts/large_a.ttf
new file mode 100644
index 0000000..0e778b6
--- /dev/null
+++ b/compat/tests/assets/fonts/large_a.ttf
Binary files differ
diff --git a/compat/tests/assets/fonts/large_a.ttx b/compat/tests/assets/fonts/large_a.ttx
new file mode 100644
index 0000000..c453414
--- /dev/null
+++ b/compat/tests/assets/fonts/large_a.ttx
@@ -0,0 +1,189 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="3em" />
+      <map code="0x0062" name="1em" />
+      <map code="0x0063" name="1em" />
+      <map code="0x0064" name="1em" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Source code is available at platform/frameworks/support/compat/tests/assets/fonts/large_a.ttx.
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/compat/tests/assets/fonts/large_b.ttf b/compat/tests/assets/fonts/large_b.ttf
new file mode 100644
index 0000000..b8ce6ce
--- /dev/null
+++ b/compat/tests/assets/fonts/large_b.ttf
Binary files differ
diff --git a/compat/tests/assets/fonts/large_b.ttx b/compat/tests/assets/fonts/large_b.ttx
new file mode 100644
index 0000000..0274e9c
--- /dev/null
+++ b/compat/tests/assets/fonts/large_b.ttx
@@ -0,0 +1,189 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="1em" />
+      <map code="0x0062" name="3em" />
+      <map code="0x0063" name="1em" />
+      <map code="0x0064" name="1em" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Source code is available at platform/frameworks/support/compat/tests/assets/fonts/large_b.ttx.
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/compat/tests/assets/fonts/large_c.ttf b/compat/tests/assets/fonts/large_c.ttf
new file mode 100644
index 0000000..dd5fa50
--- /dev/null
+++ b/compat/tests/assets/fonts/large_c.ttf
Binary files differ
diff --git a/compat/tests/assets/fonts/large_c.ttx b/compat/tests/assets/fonts/large_c.ttx
new file mode 100644
index 0000000..d3657b1
--- /dev/null
+++ b/compat/tests/assets/fonts/large_c.ttx
@@ -0,0 +1,189 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="1em" />
+      <map code="0x0062" name="1em" />
+      <map code="0x0063" name="3em" />
+      <map code="0x0064" name="1em" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Source code is available at platform/frameworks/support/compat/tests/assets/fonts/large_c.ttx.
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/compat/tests/assets/fonts/large_d.ttf b/compat/tests/assets/fonts/large_d.ttf
new file mode 100644
index 0000000..b791100
--- /dev/null
+++ b/compat/tests/assets/fonts/large_d.ttf
Binary files differ
diff --git a/compat/tests/assets/fonts/large_d.ttx b/compat/tests/assets/fonts/large_d.ttx
new file mode 100644
index 0000000..12f668c
--- /dev/null
+++ b/compat/tests/assets/fonts/large_d.ttx
@@ -0,0 +1,189 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="1em" />
+      <map code="0x0062" name="1em" />
+      <map code="0x0063" name="1em" />
+      <map code="0x0064" name="3em" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Source code is available at platform/frameworks/support/compat/tests/assets/fonts/large_d.ttx.
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/compat/tests/java/android/support/v4/graphics/TypefaceCompatTest.java b/compat/tests/java/android/support/v4/graphics/TypefaceCompatTest.java
new file mode 100644
index 0000000..a2f2c7c
--- /dev/null
+++ b/compat/tests/java/android/support/v4/graphics/TypefaceCompatTest.java
@@ -0,0 +1,277 @@
+/*
+ * 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.v4.graphics;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.res.Resources;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.support.compat.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.testutils.PollingCheck;
+import android.support.v4.content.res.FontResourcesParserCompat;
+import android.support.v4.content.res.FontResourcesParserCompat.FamilyResourceEntry;
+import android.support.v4.content.res.FontResourcesParserCompat.ProviderResourceEntry;
+import android.support.v4.provider.FontRequest;
+import android.support.v4.provider.MockFontProvider;
+import android.widget.TextView;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@SdkSuppress(maxSdkVersion = 25)  // on API 26, use platform implementation.
+@SmallTest
+public class TypefaceCompatTest {
+    private static final String AUTHORITY = "android.provider.fonts.font";
+    private static final String PACKAGE = "android.support.compat.test";
+
+    public Context mContext;
+    public Resources mResources;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mResources = mContext.getResources();
+        MockFontProvider.prepareFontFiles(mContext);
+    }
+
+    @After
+    public void tearDown() {
+        MockFontProvider.cleanUpFontFiles(mContext);
+    }
+
+    // Signature to be used for authentication to access content provider.
+    // In this test case, the content provider and consumer live in the same package, self package's
+    // signature works.
+    private static final List<List<byte[]>> SIGNATURE;
+    static {
+        final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        try {
+            PackageManager manager = context.getPackageManager();
+            PackageInfo info = manager.getPackageInfo(
+                    context.getPackageName(), PackageManager.GET_SIGNATURES);
+            ArrayList<byte[]> out = new ArrayList<>();
+            for (Signature sig : info.signatures) {
+                out.add(sig.toByteArray());
+            }
+            SIGNATURE = new ArrayList<>();
+            SIGNATURE.add(out);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Helper method to get the used font resource id by typeface.
+     *
+     * If the typeface is created from one of the R.font.large_a, R.font.large_b, R.font.large_c or
+     * R.font.large_d resource, this method returns the resource id used by the typeface.
+     */
+    private static int getSelectedFontResourceId(Typeface typeface) {
+        // The glyph for "a" in R.font.large_a font has a 3em width and glyph for "b", "c" and "d"
+        // have 1em width. Similarly, The glyph for "b" in R.font.large_b font, the glyph for "c"
+        // in R.font.large_c font, the glyph for "d" in R.font.large_d font has 3em width and the
+        // glyph for the rest characters have 1em. Thus we can get the resource id of the source
+        // font file by comparing width of "a", "b", "c" and "d".
+        Paint p = new Paint();
+        p.setTypeface(typeface);
+        final int[] ids = { R.font.large_a, R.font.large_b, R.font.large_c, R.font.large_d };
+        final float[] widths = {
+            p.measureText("a"), p.measureText("b"), p.measureText("c"), p.measureText("d")
+        };
+
+        int maxIndex = Integer.MIN_VALUE;
+        float maxValue = Float.MIN_VALUE;
+        for (int i = 0; i < widths.length; ++i) {
+            if (maxValue < widths[i]) {
+                maxIndex = i;
+                maxValue = widths[i];
+            }
+        }
+        return ids[maxIndex];
+    }
+
+    /**
+     * Helper method to obtain ProviderResourceEntry with overwriting correct signatures.
+     */
+    private ProviderResourceEntry getProviderResourceEntry(int id) {
+        final ProviderResourceEntry entry;
+        try {
+            entry = (ProviderResourceEntry) FontResourcesParserCompat.parse(
+                    mResources.getXml(id), mResources);
+        } catch (XmlPullParserException | IOException e) {
+            throw new RuntimeException(e);
+        }
+        final FontRequest parsedRequest = entry.getRequest();
+        final FontRequest request = new FontRequest(parsedRequest.getProviderAuthority(),
+                parsedRequest.getProviderPackage(), parsedRequest.getQuery(), SIGNATURE);
+        return new ProviderResourceEntry(request, entry.getFetchStrategy(), entry.getTimeout());
+    }
+
+    @Test
+    public void testCreateFromResourcesFamilyXml_resourceFont_syncloading() throws Exception {
+        Typeface typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources,
+                R.font.styletest_sync_providerfont, Typeface.NORMAL, null /* TextView */);
+        typeface = Typeface.create(typeface, Typeface.NORMAL);
+        assertEquals(R.font.large_a, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources,
+                R.font.styletest_sync_providerfont, Typeface.ITALIC, null /* TextView */);
+        typeface = Typeface.create(typeface, Typeface.ITALIC);
+        assertEquals(R.font.large_b, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources,
+                R.font.styletest_sync_providerfont, Typeface.BOLD, null /* TextView */);
+        typeface = Typeface.create(typeface, Typeface.BOLD);
+        assertEquals(R.font.large_c, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources,
+                R.font.styletest_sync_providerfont, Typeface.BOLD_ITALIC, null /* TextView */);
+        typeface = Typeface.create(typeface, Typeface.BOLD_ITALIC);
+        assertEquals(R.font.large_d, getSelectedFontResourceId(typeface));
+    }
+
+    @Test
+    public void testCreateFromResourcesFamilyXml_resourceFont_asyncloading() throws Exception {
+        Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+        final TextView textView = new TextView(mContext);
+        PollingCheck.PollingCheckCondition condition = new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return textView.getTypeface() != null;
+            }
+        };
+
+        textView.setTypeface(null);
+        inst.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                        getProviderResourceEntry(R.font.styletest_async_providerfont), mResources,
+                        R.font.styletest_async_providerfont, Typeface.NORMAL, textView);
+            }
+        });
+        PollingCheck.waitFor(condition);
+        assertEquals(R.font.large_a, getSelectedFontResourceId(textView.getTypeface()));
+
+        textView.setTypeface(null);
+        inst.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                        getProviderResourceEntry(R.font.styletest_async_providerfont), mResources,
+                        R.font.styletest_async_providerfont, Typeface.ITALIC, textView);
+            }
+        });
+        PollingCheck.waitFor(condition);
+        assertEquals(R.font.large_b, getSelectedFontResourceId(textView.getTypeface()));
+
+        textView.setTypeface(null);
+        inst.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                        getProviderResourceEntry(R.font.styletest_async_providerfont), mResources,
+                        R.font.styletest_async_providerfont, Typeface.BOLD, textView);
+            }
+        });
+        PollingCheck.waitFor(condition);
+        assertEquals(R.font.large_c, getSelectedFontResourceId(textView.getTypeface()));
+
+        textView.setTypeface(null);
+        inst.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                TypefaceCompat.createFromResourcesFamilyXml(mContext,
+                        getProviderResourceEntry(R.font.styletest_async_providerfont), mResources,
+                        R.font.styletest_async_providerfont, Typeface.BOLD_ITALIC, textView);
+            }
+        });
+        PollingCheck.waitFor(condition);
+        assertEquals(R.font.large_d, getSelectedFontResourceId(textView.getTypeface()));
+    }
+
+    @Test
+    public void testCreateFromResourcesFamilyXml_resourceFont() throws Exception {
+        final FamilyResourceEntry entry = FontResourcesParserCompat.parse(
+                mResources.getXml(R.font.styletestfont), mResources);
+        Typeface typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources,
+                R.font.styletestfont, Typeface.NORMAL, null /* text view */);
+        assertEquals(typeface, TypefaceCompat.findFromCache(
+                mResources, R.font.styletestfont, Typeface.NORMAL));
+        typeface = Typeface.create(typeface, Typeface.NORMAL);
+        // styletestfont has a node of fontStyle="normal" fontWeight="400" font="@font/large_a".
+        assertEquals(R.font.large_a, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources,
+                R.font.styletestfont, Typeface.ITALIC, null);
+        assertEquals(typeface, TypefaceCompat.findFromCache(
+                mResources, R.font.styletestfont, Typeface.ITALIC));
+        typeface = Typeface.create(typeface, Typeface.ITALIC);
+        // styletestfont has a node of fontStyle="italic" fontWeight="400" font="@font/large_b".
+        assertEquals(R.font.large_b, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources,
+                R.font.styletestfont, Typeface.BOLD, null);
+        assertEquals(typeface, TypefaceCompat.findFromCache(
+                mResources, R.font.styletestfont, Typeface.BOLD));
+        typeface = Typeface.create(typeface, Typeface.BOLD);
+        // styletestfont has a node of fontStyle="normal" fontWeight="700" font="@font/large_c".
+        assertEquals(R.font.large_c, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources,
+                R.font.styletestfont, Typeface.BOLD_ITALIC, null);
+        assertEquals(typeface, TypefaceCompat.findFromCache(
+                mResources, R.font.styletestfont, Typeface.BOLD_ITALIC));
+        typeface = Typeface.create(typeface, Typeface.BOLD_ITALIC);
+        // styletestfont has a node of fontStyle="italic" fontWeight="700" font="@font/large_d".
+        assertEquals(R.font.large_d, getSelectedFontResourceId(typeface));
+    }
+
+    @Test
+    public void testCreateFromResourcesFontFile() {
+        Typeface typeface = TypefaceCompat.createFromResourcesFontFile(
+                mContext, mResources, R.font.large_a, Typeface.NORMAL);
+        assertEquals(typeface, TypefaceCompat.findFromCache(
+                mResources, R.font.large_a, Typeface.NORMAL));
+        assertEquals(R.font.large_a, getSelectedFontResourceId(typeface));
+
+        typeface = TypefaceCompat.createFromResourcesFontFile(
+                mContext, mResources, R.font.large_b, Typeface.NORMAL);
+        assertEquals(typeface, TypefaceCompat.findFromCache(
+                mResources, R.font.large_b, Typeface.NORMAL));
+        assertEquals(R.font.large_b, getSelectedFontResourceId(typeface));
+    }
+}
diff --git a/compat/tests/java/android/support/v4/provider/MockFontProvider.java b/compat/tests/java/android/support/v4/provider/MockFontProvider.java
index 14b16a5..f07d92d 100644
--- a/compat/tests/java/android/support/v4/provider/MockFontProvider.java
+++ b/compat/tests/java/android/support/v4/provider/MockFontProvider.java
@@ -41,9 +41,13 @@
  */
 public class MockFontProvider extends ContentProvider {
     static final String[] FONT_FILES = {
-            "samplefont.ttf",
+            "samplefont.ttf", "large_a.ttf", "large_b.ttf", "large_c.ttf", "large_d.ttf"
     };
     private static final int SAMPLE_FONT_FILE_0_ID = 0;
+    private static final int LARGE_A_FILE_ID = 1;
+    private static final int LARGE_B_FILE_ID = 2;
+    private static final int LARGE_C_FILE_ID = 3;
+    private static final int LARGE_D_FILE_ID = 4;
 
     static final String SINGLE_FONT_FAMILY_QUERY = "singleFontFamily";
     static final String SINGLE_FONT_FAMILY2_QUERY = "singleFontFamily2";
@@ -54,6 +58,7 @@
     static final String NOT_FOUND_THIRD_QUERY = "notFoundThird";
     static final String NEGATIVE_ERROR_CODE_QUERY = "negativeCode";
     static final String MANDATORY_FIELDS_ONLY_QUERY = "mandatoryFields";
+    static final String STYLE_TEST_QUERY = "styleTest";
 
     static class Font {
         Font(int id, int fileId, int ttcIndex, String varSettings, int weight, int italic,
@@ -160,6 +165,17 @@
                         Columns.RESULT_CODE_OK, false),
         });
 
+        map.put(STYLE_TEST_QUERY, new Font[] {
+                new Font(id++, LARGE_A_FILE_ID, 0, null, 400, 0 /* normal */,
+                        Columns.RESULT_CODE_OK, true),
+                new Font(id++, LARGE_B_FILE_ID, 0, null, 400, 1 /* italic */,
+                        Columns.RESULT_CODE_OK, true),
+                new Font(id++, LARGE_C_FILE_ID, 0, null, 700, 0 /* normal */,
+                        Columns.RESULT_CODE_OK, true),
+                new Font(id++, LARGE_D_FILE_ID, 0, null, 700, 1 /* italic */,
+                        Columns.RESULT_CODE_OK, true),
+        });
+
         QUERY_MAP = Collections.unmodifiableMap(map);
     }
 
@@ -188,7 +204,15 @@
             InputStream is = null;
             try {
                 is = mgr.open("fonts/" + file);
-                copy(is, getCopiedFile(context, file));
+                File copied = getCopiedFile(context, file);
+                File parent = copied.getParentFile();
+                if (!parent.isDirectory()) {
+                    parent.mkdirs();
+                    parent.setReadable(true, false);
+                    parent.setExecutable(true, false);
+                }
+                copy(is, copied);
+                copied.setReadable(true, false);
             } catch (IOException e) {
                 throw new RuntimeException(e);
             } finally {
@@ -229,7 +253,8 @@
     }
 
     public static File getCopiedFile(Context context, String path) {
-        return new File(context.getFilesDir(), path);
+        final File cacheDir = new File(context.getFilesDir(), "fontCache");
+        return new File(cacheDir, path);
     }
 
     @Override
diff --git a/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java b/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java
index 51bb7d0..d79c789 100644
--- a/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java
+++ b/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java
@@ -245,12 +245,21 @@
             assertTrue(LinkifyCompat.addLinks(spannable, Linkify.ALL));
             URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class);
             assertEquals(3, spans.length);
-            assertEquals("tel:8005551233", spans[0].getURL());
-            assertEquals("mailto:800-555-1211@gmail.com", spans[1].getURL());
-            assertEquals("http://800-555-1222.com", spans[2].getURL());
+            assertTrue(containsUrl(spans, "tel:8005551233"));
+            assertTrue(containsUrl(spans, "mailto:800-555-1211@gmail.com"));
+            assertTrue(containsUrl(spans, "http://800-555-1222.com"));
         }
     }
 
+    private boolean containsUrl(URLSpan[] spans, String expectedValue) {
+        for (URLSpan span : spans) {
+            if (span.getURL().equals(expectedValue)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Test
     public void testAddLinks_addsLinksWhenDefaultSchemeIsNull() {
         Spannable spannable = new SpannableString("any https://android.com any android.com any");
diff --git a/compat/tests/res/font/dummyproviderfont.xml b/compat/tests/res/font/dummyproviderfont.xml
new file mode 100644
index 0000000..b0b41c3
--- /dev/null
+++ b/compat/tests/res/font/dummyproviderfont.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:app="http://schemas.android.com/apk/res-auto"
+         android:fontProviderAuthority="android.provider.fonts.font"
+         android:fontProviderPackage="android.support.compat.test"
+         android:fontProviderQuery="styleTest"
+         app:fontProviderAuthority="android.provider.fonts.font"
+         app:fontProviderPackage="android.support.compat.test"
+         app:fontProviderQuery="styleTest" >
+</font-family>
diff --git a/compat/tests/res/font/large_a.ttf b/compat/tests/res/font/large_a.ttf
new file mode 100644
index 0000000..0e778b6
--- /dev/null
+++ b/compat/tests/res/font/large_a.ttf
Binary files differ
diff --git a/compat/tests/res/font/large_b.ttf b/compat/tests/res/font/large_b.ttf
new file mode 100644
index 0000000..b8ce6ce
--- /dev/null
+++ b/compat/tests/res/font/large_b.ttf
Binary files differ
diff --git a/compat/tests/res/font/large_c.ttf b/compat/tests/res/font/large_c.ttf
new file mode 100644
index 0000000..dd5fa50
--- /dev/null
+++ b/compat/tests/res/font/large_c.ttf
Binary files differ
diff --git a/compat/tests/res/font/large_d.ttf b/compat/tests/res/font/large_d.ttf
new file mode 100644
index 0000000..b791100
--- /dev/null
+++ b/compat/tests/res/font/large_d.ttf
Binary files differ
diff --git a/compat/tests/res/font/styletest_async_providerfont.xml b/compat/tests/res/font/styletest_async_providerfont.xml
new file mode 100644
index 0000000..8d934cb
--- /dev/null
+++ b/compat/tests/res/font/styletest_async_providerfont.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:app="http://schemas.android.com/apk/res-auto"
+         android:fontProviderAuthority="android.support.provider.fonts.font"
+         android:fontProviderPackage="android.support.compat.test"
+         android:fontProviderQuery="styleTest"
+         app:fontProviderAuthority="android.support.provider.fonts.font"
+         app:fontProviderPackage="android.support.compat.test"
+         app:fontProviderQuery="styleTest"
+         app:fontProviderFetchStrategy="async">
+</font-family>
diff --git a/compat/tests/res/font/styletest_sync_providerfont.xml b/compat/tests/res/font/styletest_sync_providerfont.xml
new file mode 100644
index 0000000..4e9c7d6
--- /dev/null
+++ b/compat/tests/res/font/styletest_sync_providerfont.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:app="http://schemas.android.com/apk/res-auto"
+         android:fontProviderAuthority="android.support.provider.fonts.font"
+         android:fontProviderPackage="android.support.compat.test"
+         android:fontProviderQuery="styleTest"
+         app:fontProviderAuthority="android.support.provider.fonts.font"
+         app:fontProviderPackage="android.support.compat.test"
+         app:fontProviderQuery="styleTest"
+         app:fontProviderFetchStrategy="blocking"
+         app:fontProviderFetchTimeout="forever">
+</font-family>
diff --git a/compat/tests/res/font/styletestfont.xml b/compat/tests/res/font/styletestfont.xml
new file mode 100644
index 0000000..f1326d5
--- /dev/null
+++ b/compat/tests/res/font/styletestfont.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:app="http://schemas.android.com/apk/res-auto">
+    <font app:fontStyle="normal" app:fontWeight="400" app:font="@font/large_a"
+          android:fontStyle="normal" android:fontWeight="400" android:font="@font/large_a" />
+    <font app:fontStyle="italic" app:fontWeight="400" app:font="@font/large_b"
+          android:fontStyle="italic" android:fontWeight="400" android:font="@font/large_b" />
+    <font app:fontStyle="normal" app:fontWeight="700" app:font="@font/large_c"
+          android:fontStyle="normal" android:fontWeight="700" android:font="@font/large_c" />
+    <font app:fontStyle="italic" app:fontWeight="700" app:font="@font/large_d"
+          android:fontStyle="italic" android:fontWeight="700" android:font="@font/large_d" />
+</font-family>
diff --git a/core-ui/ics/android/support/v4/app/ActionBarDrawerToggleIcs.java b/core-ui/ics/android/support/v4/app/ActionBarDrawerToggleIcs.java
index 7826f86..a332b8a 100644
--- a/core-ui/ics/android/support/v4/app/ActionBarDrawerToggleIcs.java
+++ b/core-ui/ics/android/support/v4/app/ActionBarDrawerToggleIcs.java
@@ -39,7 +39,7 @@
  */
 @RequiresApi(14)
 class ActionBarDrawerToggleIcs {
-    private static final String TAG = "ActionBarDrawerToggleHoneycomb";
+    private static final String TAG = "ActionBarDrawerToggle";
 
     private static final int[] THEME_ATTRS = new int[] {
             R.attr.homeAsUpIndicator
diff --git a/core-ui/java/android/support/v4/widget/ExploreByTouchHelper.java b/core-ui/java/android/support/v4/widget/ExploreByTouchHelper.java
index 780d34e..8a29eff 100644
--- a/core-ui/java/android/support/v4/widget/ExploreByTouchHelper.java
+++ b/core-ui/java/android/support/v4/widget/ExploreByTouchHelper.java
@@ -28,7 +28,6 @@
 import android.support.v4.view.ViewCompat.FocusRealDirection;
 import android.support.v4.view.ViewParentCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityManagerCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
@@ -177,8 +176,7 @@
      * @return Whether the hover event was handled.
      */
     public final boolean dispatchHoverEvent(@NonNull MotionEvent event) {
-        if (!mManager.isEnabled()
-                || !AccessibilityManagerCompat.isTouchExplorationEnabled(mManager)) {
+        if (!mManager.isEnabled() || !mManager.isTouchExplorationEnabled()) {
             return false;
         }
 
@@ -650,7 +648,7 @@
      */
     private AccessibilityEvent createEventForHost(int eventType) {
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
-        ViewCompat.onInitializeAccessibilityEvent(mHost, event);
+        mHost.onInitializeAccessibilityEvent(event);
         return event;
     }
 
@@ -941,7 +939,7 @@
         ViewParent viewParent = mHost.getParent();
         while (viewParent instanceof View) {
             final View view = (View) viewParent;
-            if ((ViewCompat.getAlpha(view) <= 0) || (view.getVisibility() != View.VISIBLE)) {
+            if ((view.getAlpha() <= 0) || (view.getVisibility() != View.VISIBLE)) {
                 return false;
             }
             viewParent = view.getParent();
@@ -964,8 +962,7 @@
      * @return whether this virtual view actually took accessibility focus
      */
     private boolean requestAccessibilityFocus(int virtualViewId) {
-        if (!mManager.isEnabled()
-                || !AccessibilityManagerCompat.isTouchExplorationEnabled(mManager)) {
+        if (!mManager.isEnabled() || !mManager.isTouchExplorationEnabled()) {
             return false;
         }
         // TODO: Check virtual view visibility.
diff --git a/core-ui/java/android/support/v4/widget/SlidingPaneLayout.java b/core-ui/java/android/support/v4/widget/SlidingPaneLayout.java
index 0e482d7..c949cbb 100644
--- a/core-ui/java/android/support/v4/widget/SlidingPaneLayout.java
+++ b/core-ui/java/android/support/v4/widget/SlidingPaneLayout.java
@@ -16,10 +16,8 @@
 
 package android.support.v4.widget;
 
-import android.support.annotation.RequiresApi;
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -32,6 +30,7 @@
 import android.os.Parcelable;
 import android.support.annotation.ColorInt;
 import android.support.annotation.DrawableRes;
+import android.support.annotation.RequiresApi;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.view.AbsSavedState;
 import android.support.v4.view.AccessibilityDelegateCompat;
@@ -41,7 +40,6 @@
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
@@ -192,8 +190,7 @@
 
     private final Rect mTmpRect = new Rect();
 
-    final ArrayList<DisableLayerRunnable> mPostedRunnables =
-            new ArrayList<DisableLayerRunnable>();
+    final ArrayList<DisableLayerRunnable> mPostedRunnables = new ArrayList<>();
 
     static final SlidingPanelLayoutImpl IMPL;
 
@@ -262,8 +259,6 @@
         final float density = context.getResources().getDisplayMetrics().density;
         mOverhangSize = (int) (DEFAULT_OVERHANG_SIZE * density + 0.5f);
 
-        final ViewConfiguration viewConfig = ViewConfiguration.get(context);
-
         setWillNotDraw(false);
 
         ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegate());
@@ -1012,28 +1007,7 @@
             canvas.clipRect(mTmpRect);
         }
 
-        if (Build.VERSION.SDK_INT >= 11) { // HC
-            result = super.drawChild(canvas, child, drawingTime);
-        } else {
-            if (lp.dimWhenOffset && mSlideOffset > 0) {
-                if (!child.isDrawingCacheEnabled()) {
-                    child.setDrawingCacheEnabled(true);
-                }
-                final Bitmap cache = child.getDrawingCache();
-                if (cache != null) {
-                    canvas.drawBitmap(cache, child.getLeft(), child.getTop(), lp.dimPaint);
-                    result = false;
-                } else {
-                    Log.e(TAG, "drawChild: child view " + child + " returned null drawing cache");
-                    result = super.drawChild(canvas, child, drawingTime);
-                }
-            } else {
-                if (child.isDrawingCacheEnabled()) {
-                    child.setDrawingCacheEnabled(false);
-                }
-                result = super.drawChild(canvas, child, drawingTime);
-            }
-        }
+        result = super.drawChild(canvas, child, drawingTime);
 
         canvas.restoreToCount(save);
 
diff --git a/core-ui/java/android/support/v4/widget/Space.java b/core-ui/java/android/support/v4/widget/Space.java
index 4857479..77a2d2e 100644
--- a/core-ui/java/android/support/v4/widget/Space.java
+++ b/core-ui/java/android/support/v4/widget/Space.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.widget;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
@@ -48,6 +49,7 @@
      * @param canvas an unused parameter.
      */
     @Override
+    @SuppressLint("MissingSuperCall")
     public void draw(Canvas canvas) {
     }
 
diff --git a/core-ui/java/android/support/v4/widget/SwipeRefreshLayout.java b/core-ui/java/android/support/v4/widget/SwipeRefreshLayout.java
index 8432658..6a5a629 100644
--- a/core-ui/java/android/support/v4/widget/SwipeRefreshLayout.java
+++ b/core-ui/java/android/support/v4/widget/SwipeRefreshLayout.java
@@ -527,7 +527,7 @@
      * @deprecated Use {@link #setColorSchemeResources(int...)}
      */
     @Deprecated
-    public void setColorScheme(@ColorInt int... colors) {
+    public void setColorScheme(@ColorRes int... colors) {
         setColorSchemeResources(colors);
     }
 
diff --git a/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java b/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java
index 1544e76..855d01d 100644
--- a/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java
+++ b/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java
@@ -1076,7 +1076,7 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     public void testKeyboardNavigation() {
         ButtonPagerAdapter adapter = new ButtonPagerAdapter();
         adapter.add("Red", Color.RED);
diff --git a/core-utils/api20/android/support/v4/print/PrintHelperApi20.java b/core-utils/api20/android/support/v4/print/PrintHelperApi20.java
deleted file mode 100644
index 54699a5..0000000
--- a/core-utils/api20/android/support/v4/print/PrintHelperApi20.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.print;
-
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-
-/**
- * Api20 specific PrintManager API implementation.
- */
-@RequiresApi(20)
-class PrintHelperApi20 extends PrintHelperKitkat {
-    PrintHelperApi20(Context context) {
-        super(context);
-
-        /**
-         * There is a bug in the PrintActivity that causes it to ignore the orientation
-         */
-        mPrintActivityRespectsOrientation = false;
-    }
-}
\ No newline at end of file
diff --git a/core-utils/api23/android/support/v4/print/PrintHelperApi23.java b/core-utils/api23/android/support/v4/print/PrintHelperApi23.java
deleted file mode 100644
index bd949b9..0000000
--- a/core-utils/api23/android/support/v4/print/PrintHelperApi23.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.print;
-
-import android.content.Context;
-import android.print.PrintAttributes;
-import android.support.annotation.RequiresApi;
-
-/**
- * Api23 specific PrintManager API implementation.
- */
-@RequiresApi(23)
-class PrintHelperApi23 extends PrintHelperApi20 {
-    @Override
-    protected PrintAttributes.Builder copyAttributes(PrintAttributes other) {
-        PrintAttributes.Builder b = super.copyAttributes(other);
-
-        if (other.getDuplexMode() != 0) {
-            b.setDuplexMode(other.getDuplexMode());
-        }
-
-        return b;
-    }
-
-    PrintHelperApi23(Context context) {
-        super(context);
-
-        mIsMinMarginsHandlingCorrect = false;
-    }
-}
diff --git a/core-utils/api24/android/support/v4/print/PrintHelperApi24.java b/core-utils/api24/android/support/v4/print/PrintHelperApi24.java
deleted file mode 100644
index 9ae32b4..0000000
--- a/core-utils/api24/android/support/v4/print/PrintHelperApi24.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.print;
-
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-
-/**
- * Api24 specific PrintManager API implementation.
- */
-@RequiresApi(24)
-class PrintHelperApi24 extends PrintHelperApi23 {
-    PrintHelperApi24(Context context) {
-        super(context);
-
-        mIsMinMarginsHandlingCorrect = true;
-        mPrintActivityRespectsOrientation = true;
-    }
-}
\ No newline at end of file
diff --git a/core-utils/java/android/support/v4/print/PrintHelper.java b/core-utils/java/android/support/v4/print/PrintHelper.java
index fb8bc12..3dddca5 100644
--- a/core-utils/java/android/support/v4/print/PrintHelper.java
+++ b/core-utils/java/android/support/v4/print/PrintHelper.java
@@ -16,14 +16,35 @@
 
 package android.support.v4.print;
 
-import android.support.annotation.RequiresApi;
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.os.Build;
-
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.pdf.PdfDocument;
 import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentInfo;
+import android.print.PrintManager;
+import android.print.pdf.PrintedPdfDocument;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
 
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
 
 /**
  * Helper for printing bitmaps.
@@ -264,6 +285,714 @@
     }
 
     /**
+     * Kitkat specific PrintManager API implementation.
+     */
+    @RequiresApi(19)
+    private static class PrintHelperKitkat {
+        private static final String LOG_TAG = "PrintHelperKitkat";
+        // will be <= 300 dpi on A4 (8.3×11.7) paper (worst case of 150 dpi)
+        private static final int MAX_PRINT_SIZE = 3500;
+        final Context mContext;
+        BitmapFactory.Options mDecodeOptions = null;
+        private final Object mLock = new Object();
+        /**
+         * image will be scaled but leave white space
+         */
+        public static final int SCALE_MODE_FIT = 1;
+        /**
+         * image will fill the paper and be cropped (default)
+         */
+        public static final int SCALE_MODE_FILL = 2;
+
+        /**
+         * select landscape (default)
+         */
+        public static final int ORIENTATION_LANDSCAPE = 1;
+
+        /**
+         * select portrait
+         */
+        public static final int ORIENTATION_PORTRAIT = 2;
+
+        /**
+         * this is a black and white image
+         */
+        public static final int COLOR_MODE_MONOCHROME = 1;
+        /**
+         * this is a color image (default)
+         */
+        public static final int COLOR_MODE_COLOR = 2;
+
+        public interface OnPrintFinishCallback {
+            void onFinish();
+        }
+
+        /**
+         * Whether the PrintActivity respects the suggested orientation
+         */
+        protected boolean mPrintActivityRespectsOrientation;
+
+        /**
+         * Whether the print subsystem handles min margins correctly. If not the print helper needs
+         * to fake this.
+         */
+        protected boolean mIsMinMarginsHandlingCorrect;
+
+        int mScaleMode = SCALE_MODE_FILL;
+
+        int mColorMode = COLOR_MODE_COLOR;
+
+        int mOrientation;
+
+        PrintHelperKitkat(Context context) {
+            mPrintActivityRespectsOrientation = true;
+            mIsMinMarginsHandlingCorrect = true;
+
+            mContext = context;
+        }
+
+        /**
+         * Selects whether the image will fill the paper and be cropped
+         * <p/>
+         * {@link #SCALE_MODE_FIT}
+         * or whether the image will be scaled but leave white space
+         * {@link #SCALE_MODE_FILL}.
+         *
+         * @param scaleMode {@link #SCALE_MODE_FIT} or
+         *                  {@link #SCALE_MODE_FILL}
+         */
+        public void setScaleMode(int scaleMode) {
+            mScaleMode = scaleMode;
+        }
+
+        /**
+         * Returns the scale mode with which the image will fill the paper.
+         *
+         * @return The scale Mode: {@link #SCALE_MODE_FIT} or
+         * {@link #SCALE_MODE_FILL}
+         */
+        public int getScaleMode() {
+            return mScaleMode;
+        }
+
+        /**
+         * Sets whether the image will be printed in color (default)
+         * {@link #COLOR_MODE_COLOR} or in back and white
+         * {@link #COLOR_MODE_MONOCHROME}.
+         *
+         * @param colorMode The color mode which is one of
+         *                  {@link #COLOR_MODE_COLOR} and {@link #COLOR_MODE_MONOCHROME}.
+         */
+        public void setColorMode(int colorMode) {
+            mColorMode = colorMode;
+        }
+
+        /**
+         * Sets whether to select landscape (default), {@link #ORIENTATION_LANDSCAPE}
+         * or portrait {@link #ORIENTATION_PORTRAIT}
+         * @param orientation The page orientation which is one of
+         *                    {@link #ORIENTATION_LANDSCAPE} or {@link #ORIENTATION_PORTRAIT}.
+         */
+        public void setOrientation(int orientation) {
+            mOrientation = orientation;
+        }
+
+        /**
+         * Gets the page orientation with which the image will be printed.
+         *
+         * @return The preferred orientation which is one of
+         * {@link #ORIENTATION_LANDSCAPE} or {@link #ORIENTATION_PORTRAIT}
+         */
+        public int getOrientation() {
+            /// Unset defaults to landscape but might turn image
+            if (mOrientation == 0) {
+                return ORIENTATION_LANDSCAPE;
+            }
+            return mOrientation;
+        }
+
+        /**
+         * Gets the color mode with which the image will be printed.
+         *
+         * @return The color mode which is one of {@link #COLOR_MODE_COLOR}
+         * and {@link #COLOR_MODE_MONOCHROME}.
+         */
+        public int getColorMode() {
+            return mColorMode;
+        }
+
+        /**
+         * Check if the supplied bitmap should best be printed on a portrait orientation paper.
+         *
+         * @param bitmap The bitmap to be printed.
+         * @return true iff the picture should best be printed on a portrait orientation paper.
+         */
+        private static boolean isPortrait(Bitmap bitmap) {
+            return bitmap.getWidth() <= bitmap.getHeight();
+        }
+
+        /**
+         * Create a build with a copy from the other print attributes.
+         *
+         * @param other The other print attributes
+         *
+         * @return A builder that will build print attributes that match the other attributes
+         */
+        protected PrintAttributes.Builder copyAttributes(PrintAttributes other) {
+            PrintAttributes.Builder b = (new PrintAttributes.Builder())
+                    .setMediaSize(other.getMediaSize())
+                    .setResolution(other.getResolution())
+                    .setMinMargins(other.getMinMargins());
+
+            if (other.getColorMode() != 0) {
+                b.setColorMode(other.getColorMode());
+            }
+
+            return b;
+        }
+
+        /**
+         * Prints a bitmap.
+         *
+         * @param jobName The print job name.
+         * @param bitmap  The bitmap to print.
+         * @param callback Optional callback to observe when printing is finished.
+         */
+        public void printBitmap(final String jobName, final Bitmap bitmap,
+                final PrintHelperKitkat.OnPrintFinishCallback callback) {
+            if (bitmap == null) {
+                return;
+            }
+            final int fittingMode = mScaleMode; // grab the fitting mode at time of call
+            PrintManager printManager =
+                    (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
+            PrintAttributes.MediaSize mediaSize;
+            if (isPortrait(bitmap)) {
+                mediaSize = PrintAttributes.MediaSize.UNKNOWN_PORTRAIT;
+            } else {
+                mediaSize = PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE;
+            }
+            PrintAttributes attr = new PrintAttributes.Builder()
+                    .setMediaSize(mediaSize)
+                    .setColorMode(mColorMode)
+                    .build();
+
+            printManager.print(jobName,
+                    new PrintDocumentAdapter() {
+                        private PrintAttributes mAttributes;
+
+                        @Override
+                        public void onLayout(PrintAttributes oldPrintAttributes,
+                                PrintAttributes newPrintAttributes,
+                                CancellationSignal cancellationSignal,
+                                LayoutResultCallback layoutResultCallback,
+                                Bundle bundle) {
+
+                            mAttributes = newPrintAttributes;
+
+                            PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
+                                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+                                    .setPageCount(1)
+                                    .build();
+                            boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
+                            layoutResultCallback.onLayoutFinished(info, changed);
+                        }
+
+                        @Override
+                        public void onWrite(PageRange[] pageRanges,
+                                ParcelFileDescriptor fileDescriptor,
+                                CancellationSignal cancellationSignal,
+                                WriteResultCallback writeResultCallback) {
+                            writeBitmap(mAttributes, fittingMode, bitmap, fileDescriptor,
+                                    cancellationSignal, writeResultCallback);
+                        }
+
+                        @Override
+                        public void onFinish() {
+                            if (callback != null) {
+                                callback.onFinish();
+                            }
+                        }
+                    }, attr);
+        }
+
+        /**
+         * Calculates the transform the print an Image to fill the page
+         *
+         * @param imageWidth  with of bitmap
+         * @param imageHeight height of bitmap
+         * @param content     The output page dimensions
+         * @param fittingMode The mode of fitting {@link #SCALE_MODE_FILL} vs
+         *                    {@link #SCALE_MODE_FIT}
+         * @return Matrix to be used in canvas.drawBitmap(bitmap, matrix, null) call
+         */
+        private Matrix getMatrix(int imageWidth, int imageHeight, RectF content, int fittingMode) {
+            Matrix matrix = new Matrix();
+
+            // Compute and apply scale to fill the page.
+            float scale = content.width() / imageWidth;
+            if (fittingMode == SCALE_MODE_FILL) {
+                scale = Math.max(scale, content.height() / imageHeight);
+            } else {
+                scale = Math.min(scale, content.height() / imageHeight);
+            }
+            matrix.postScale(scale, scale);
+
+            // Center the content.
+            final float translateX = (content.width()
+                    - imageWidth * scale) / 2;
+            final float translateY = (content.height()
+                    - imageHeight * scale) / 2;
+            matrix.postTranslate(translateX, translateY);
+            return matrix;
+        }
+
+        /**
+         * Write a bitmap for a PDF document.
+         *
+         * @param attributes          The print attributes
+         * @param fittingMode         How to fit the bitmap
+         * @param bitmap              The bitmap to write
+         * @param fileDescriptor      The file to write to
+         * @param cancellationSignal  Signal cancelling operation
+         * @param writeResultCallback Callback to call once written
+         */
+        private void writeBitmap(final PrintAttributes attributes, final int fittingMode,
+                final Bitmap bitmap, final ParcelFileDescriptor fileDescriptor,
+                final CancellationSignal cancellationSignal,
+                final PrintDocumentAdapter.WriteResultCallback writeResultCallback) {
+            final PrintAttributes pdfAttributes;
+            if (mIsMinMarginsHandlingCorrect) {
+                pdfAttributes = attributes;
+            } else {
+                // If the handling of any margin != 0 is broken, strip the margins and add them to
+                // the bitmap later
+                pdfAttributes = copyAttributes(attributes)
+                        .setMinMargins(new PrintAttributes.Margins(0, 0, 0, 0)).build();
+            }
+
+            (new AsyncTask<Void, Void, Throwable>() {
+                @Override
+                protected Throwable doInBackground(Void... params) {
+                    try {
+                        if (cancellationSignal.isCanceled()) {
+                            return null;
+                        }
+
+                        PrintedPdfDocument pdfDocument = new PrintedPdfDocument(mContext,
+                                pdfAttributes);
+
+                        Bitmap maybeGrayscale = convertBitmapForColorMode(bitmap,
+                                pdfAttributes.getColorMode());
+
+                        if (cancellationSignal.isCanceled()) {
+                            return null;
+                        }
+
+                        try {
+                            PdfDocument.Page page = pdfDocument.startPage(1);
+
+                            RectF contentRect;
+                            if (mIsMinMarginsHandlingCorrect) {
+                                contentRect = new RectF(page.getInfo().getContentRect());
+                            } else {
+                                // Create dummy doc that has the margins to compute correctly sized
+                                // content rectangle
+                                PrintedPdfDocument dummyDocument = new PrintedPdfDocument(mContext,
+                                        attributes);
+                                PdfDocument.Page dummyPage = dummyDocument.startPage(1);
+                                contentRect = new RectF(dummyPage.getInfo().getContentRect());
+                                dummyDocument.finishPage(dummyPage);
+                                dummyDocument.close();
+                            }
+
+                            // Resize bitmap
+                            Matrix matrix = getMatrix(
+                                    maybeGrayscale.getWidth(), maybeGrayscale.getHeight(),
+                                    contentRect, fittingMode);
+
+                            if (mIsMinMarginsHandlingCorrect) {
+                                // The pdfDocument takes care of the positioning and margins
+                            } else {
+                                // Move it to the correct position.
+                                matrix.postTranslate(contentRect.left, contentRect.top);
+
+                                // Cut off margins
+                                page.getCanvas().clipRect(contentRect);
+                            }
+
+                            // Draw the bitmap.
+                            page.getCanvas().drawBitmap(maybeGrayscale, matrix, null);
+
+                            // Finish the page.
+                            pdfDocument.finishPage(page);
+
+                            if (cancellationSignal.isCanceled()) {
+                                return null;
+                            }
+
+                            // Write the document.
+                            pdfDocument.writeTo(
+                                    new FileOutputStream(fileDescriptor.getFileDescriptor()));
+                            return null;
+                        } finally {
+                            pdfDocument.close();
+
+                            if (fileDescriptor != null) {
+                                try {
+                                    fileDescriptor.close();
+                                } catch (IOException ioe) {
+                                    // ignore
+                                }
+                            }
+                            // If we created a new instance for grayscaling, then recycle it here.
+                            if (maybeGrayscale != bitmap) {
+                                maybeGrayscale.recycle();
+                            }
+                        }
+                    } catch (Throwable t) {
+                        return t;
+                    }
+                }
+
+                @Override
+                protected void onPostExecute(Throwable throwable) {
+                    if (cancellationSignal.isCanceled()) {
+                        // Cancelled.
+                        writeResultCallback.onWriteCancelled();
+                    } else if (throwable == null) {
+                        // Done.
+                        writeResultCallback.onWriteFinished(
+                                new PageRange[] { PageRange.ALL_PAGES });
+                    } else {
+                        // Failed.
+                        Log.e(LOG_TAG, "Error writing printed content", throwable);
+                        writeResultCallback.onWriteFailed(null);
+                    }
+                }
+            }).execute();
+        }
+
+        /**
+         * Prints an image located at the Uri. Image types supported are those of
+         * <code>BitmapFactory.decodeStream</code> (JPEG, GIF, PNG, BMP, WEBP)
+         *
+         * @param jobName   The print job name.
+         * @param imageFile The <code>Uri</code> pointing to an image to print.
+         * @param callback Optional callback to observe when printing is finished.
+         * @throws FileNotFoundException if <code>Uri</code> is not pointing to a valid image.
+         */
+        public void printBitmap(final String jobName, final Uri imageFile,
+                final PrintHelperKitkat.OnPrintFinishCallback callback)
+                throws FileNotFoundException {
+            final int fittingMode = mScaleMode;
+
+            PrintDocumentAdapter printDocumentAdapter = new PrintDocumentAdapter() {
+                private PrintAttributes mAttributes;
+                AsyncTask<Uri, Boolean, Bitmap> mLoadBitmap;
+                Bitmap mBitmap = null;
+
+                @Override
+                public void onLayout(final PrintAttributes oldPrintAttributes,
+                        final PrintAttributes newPrintAttributes,
+                        final CancellationSignal cancellationSignal,
+                        final LayoutResultCallback layoutResultCallback,
+                        Bundle bundle) {
+
+                    synchronized (this) {
+                        mAttributes = newPrintAttributes;
+                    }
+
+                    if (cancellationSignal.isCanceled()) {
+                        layoutResultCallback.onLayoutCancelled();
+                        return;
+                    }
+                    // we finished the load
+                    if (mBitmap != null) {
+                        PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
+                                .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+                                .setPageCount(1)
+                                .build();
+                        boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
+                        layoutResultCallback.onLayoutFinished(info, changed);
+                        return;
+                    }
+
+                    mLoadBitmap = new AsyncTask<Uri, Boolean, Bitmap>() {
+                        @Override
+                        protected void onPreExecute() {
+                            // First register for cancellation requests.
+                            cancellationSignal.setOnCancelListener(
+                                    new CancellationSignal.OnCancelListener() {
+                                        @Override
+                                        public void onCancel() { // on different thread
+                                            cancelLoad();
+                                            cancel(false);
+                                        }
+                                    });
+                        }
+
+                        @Override
+                        protected Bitmap doInBackground(Uri... uris) {
+                            try {
+                                return loadConstrainedBitmap(imageFile, MAX_PRINT_SIZE);
+                            } catch (FileNotFoundException e) {
+                          /* ignore */
+                            }
+                            return null;
+                        }
+
+                        @Override
+                        protected void onPostExecute(Bitmap bitmap) {
+                            super.onPostExecute(bitmap);
+
+                            // If orientation was not set by the caller, try to fit the bitmap on
+                            // the current paper by potentially rotating the bitmap by 90 degrees.
+                            if (bitmap != null
+                                    && (!mPrintActivityRespectsOrientation || mOrientation == 0)) {
+                                PrintAttributes.MediaSize mediaSize;
+
+                                synchronized (this) {
+                                    mediaSize = mAttributes.getMediaSize();
+                                }
+
+                                if (mediaSize != null) {
+                                    if (mediaSize.isPortrait() != isPortrait(bitmap)) {
+                                        Matrix rotation = new Matrix();
+
+                                        rotation.postRotate(90);
+                                        bitmap = Bitmap.createBitmap(bitmap, 0, 0,
+                                                bitmap.getWidth(), bitmap.getHeight(), rotation,
+                                                true);
+                                    }
+                                }
+                            }
+
+                            mBitmap = bitmap;
+                            if (bitmap != null) {
+                                PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
+                                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+                                        .setPageCount(1)
+                                        .build();
+
+                                boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
+
+                                layoutResultCallback.onLayoutFinished(info, changed);
+
+                            } else {
+                                layoutResultCallback.onLayoutFailed(null);
+                            }
+                            mLoadBitmap = null;
+                        }
+
+                        @Override
+                        protected void onCancelled(Bitmap result) {
+                            // Task was cancelled, report that.
+                            layoutResultCallback.onLayoutCancelled();
+                            mLoadBitmap = null;
+                        }
+                    }.execute();
+                }
+
+                private void cancelLoad() {
+                    synchronized (mLock) { // prevent race with set null below
+                        if (mDecodeOptions != null) {
+                            mDecodeOptions.requestCancelDecode();
+                            mDecodeOptions = null;
+                        }
+                    }
+                }
+
+                @Override
+                public void onFinish() {
+                    super.onFinish();
+                    cancelLoad();
+                    if (mLoadBitmap != null) {
+                        mLoadBitmap.cancel(true);
+                    }
+                    if (callback != null) {
+                        callback.onFinish();
+                    }
+                    if (mBitmap != null) {
+                        mBitmap.recycle();
+                        mBitmap = null;
+                    }
+                }
+
+                @Override
+                public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
+                        CancellationSignal cancellationSignal,
+                        WriteResultCallback writeResultCallback) {
+                    writeBitmap(mAttributes, fittingMode, mBitmap, fileDescriptor,
+                            cancellationSignal, writeResultCallback);
+                }
+            };
+
+            PrintManager printManager =
+                    (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
+            PrintAttributes.Builder builder = new PrintAttributes.Builder();
+            builder.setColorMode(mColorMode);
+
+            if (mOrientation == ORIENTATION_LANDSCAPE || mOrientation == 0) {
+                builder.setMediaSize(PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE);
+            } else if (mOrientation == ORIENTATION_PORTRAIT) {
+                builder.setMediaSize(PrintAttributes.MediaSize.UNKNOWN_PORTRAIT);
+            }
+            PrintAttributes attr = builder.build();
+
+            printManager.print(jobName, printDocumentAdapter, attr);
+        }
+
+        /**
+         * Loads a bitmap while limiting its size
+         *
+         * @param uri           location of a valid image
+         * @param maxSideLength the maximum length of a size
+         * @return the Bitmap
+         * @throws FileNotFoundException if the Uri does not point to an image
+         */
+        private Bitmap loadConstrainedBitmap(Uri uri, int maxSideLength)
+                throws FileNotFoundException {
+            if (maxSideLength <= 0 || uri == null || mContext == null) {
+                throw new IllegalArgumentException("bad argument to getScaledBitmap");
+            }
+            // Get width and height of stored bitmap
+            BitmapFactory.Options opt = new BitmapFactory.Options();
+            opt.inJustDecodeBounds = true;
+            loadBitmap(uri, opt);
+
+            int w = opt.outWidth;
+            int h = opt.outHeight;
+
+            // If bitmap cannot be decoded, return null
+            if (w <= 0 || h <= 0) {
+                return null;
+            }
+
+            // Find best downsampling size
+            int imageSide = Math.max(w, h);
+
+            int sampleSize = 1;
+            while (imageSide > maxSideLength) {
+                imageSide >>>= 1;
+                sampleSize <<= 1;
+            }
+
+            // Make sure sample size is reasonable
+            if (sampleSize <= 0 || 0 >= (int) (Math.min(w, h) / sampleSize)) {
+                return null;
+            }
+            BitmapFactory.Options decodeOptions = null;
+            synchronized (mLock) { // prevent race with set null below
+                mDecodeOptions = new BitmapFactory.Options();
+                mDecodeOptions.inMutable = true;
+                mDecodeOptions.inSampleSize = sampleSize;
+                decodeOptions = mDecodeOptions;
+            }
+            try {
+                return loadBitmap(uri, decodeOptions);
+            } finally {
+                synchronized (mLock) {
+                    mDecodeOptions = null;
+                }
+            }
+        }
+
+        /**
+         * Returns the bitmap from the given uri loaded using the given options.
+         * Returns null on failure.
+         */
+        private Bitmap loadBitmap(Uri uri, BitmapFactory.Options o) throws FileNotFoundException {
+            if (uri == null || mContext == null) {
+                throw new IllegalArgumentException("bad argument to loadBitmap");
+            }
+            InputStream is = null;
+            try {
+                is = mContext.getContentResolver().openInputStream(uri);
+                return BitmapFactory.decodeStream(is, null, o);
+            } finally {
+                if (is != null) {
+                    try {
+                        is.close();
+                    } catch (IOException t) {
+                        Log.w(LOG_TAG, "close fail ", t);
+                    }
+                }
+            }
+        }
+
+        private Bitmap convertBitmapForColorMode(Bitmap original, int colorMode) {
+            if (colorMode != COLOR_MODE_MONOCHROME) {
+                return original;
+            }
+            // Create a grayscale bitmap
+            Bitmap grayscale = Bitmap.createBitmap(original.getWidth(), original.getHeight(),
+                    Bitmap.Config.ARGB_8888);
+            Canvas c = new Canvas(grayscale);
+            Paint p = new Paint();
+            ColorMatrix cm = new ColorMatrix();
+            cm.setSaturation(0);
+            ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
+            p.setColorFilter(f);
+            c.drawBitmap(original, 0, 0, p);
+            c.setBitmap(null);
+
+            return grayscale;
+        }
+    }
+
+    /**
+     * Api20 specific PrintManager API implementation.
+     */
+    @RequiresApi(20)
+    private static class PrintHelperApi20 extends PrintHelperKitkat {
+        PrintHelperApi20(Context context) {
+            super(context);
+
+            /**
+             * There is a bug in the PrintActivity that causes it to ignore the orientation
+             */
+            mPrintActivityRespectsOrientation = false;
+        }
+    }
+
+    /**
+     * Api23 specific PrintManager API implementation.
+     */
+    @RequiresApi(23)
+    private static class PrintHelperApi23 extends PrintHelperApi20 {
+        @Override
+        protected PrintAttributes.Builder copyAttributes(PrintAttributes other) {
+            PrintAttributes.Builder b = super.copyAttributes(other);
+
+            if (other.getDuplexMode() != 0) {
+                b.setDuplexMode(other.getDuplexMode());
+            }
+
+            return b;
+        }
+
+        PrintHelperApi23(Context context) {
+            super(context);
+
+            mIsMinMarginsHandlingCorrect = false;
+        }
+    }
+
+    /**
+     * Api24 specific PrintManager API implementation.
+     */
+    @RequiresApi(24)
+    private static class PrintHelperApi24 extends PrintHelperApi23 {
+        PrintHelperApi24(Context context) {
+            super(context);
+
+            mIsMinMarginsHandlingCorrect = true;
+            mPrintActivityRespectsOrientation = true;
+        }
+    }
+
+    /**
      * Returns the PrintHelper that can be used to print images.
      *
      * @param context A context for accessing system resources.
diff --git a/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java b/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java
deleted file mode 100644
index 355e878..0000000
--- a/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java
+++ /dev/null
@@ -1,701 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.print;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.graphics.pdf.PdfDocument.Page;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.print.PageRange;
-import android.print.PrintAttributes;
-import android.print.PrintAttributes.MediaSize;
-import android.print.PrintDocumentAdapter;
-import android.print.PrintDocumentInfo;
-import android.print.PrintManager;
-import android.print.pdf.PrintedPdfDocument;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Kitkat specific PrintManager API implementation.
- */
-@RequiresApi(19)
-class PrintHelperKitkat {
-    private static final String LOG_TAG = "PrintHelperKitkat";
-    // will be <= 300 dpi on A4 (8.3×11.7) paper (worst case of 150 dpi)
-    private final static int MAX_PRINT_SIZE = 3500;
-    final Context mContext;
-    BitmapFactory.Options mDecodeOptions = null;
-    private final Object mLock = new Object();
-    /**
-     * image will be scaled but leave white space
-     */
-    public static final int SCALE_MODE_FIT = 1;
-    /**
-     * image will fill the paper and be cropped (default)
-     */
-    public static final int SCALE_MODE_FILL = 2;
-
-    /**
-     * select landscape (default)
-     */
-    public static final int ORIENTATION_LANDSCAPE = 1;
-
-    /**
-     * select portrait
-     */
-    public static final int ORIENTATION_PORTRAIT = 2;
-
-    /**
-     * this is a black and white image
-     */
-    public static final int COLOR_MODE_MONOCHROME = 1;
-    /**
-     * this is a color image (default)
-     */
-    public static final int COLOR_MODE_COLOR = 2;
-
-    public interface OnPrintFinishCallback {
-        public void onFinish();
-    }
-
-    /**
-     * Whether the PrintActivity respects the suggested orientation
-     */
-    protected boolean mPrintActivityRespectsOrientation;
-
-    /**
-     * Whether the print subsystem handles min margins correctly. If not the print helper needs to
-     * fake this.
-     */
-    protected boolean mIsMinMarginsHandlingCorrect;
-
-    int mScaleMode = SCALE_MODE_FILL;
-
-    int mColorMode = COLOR_MODE_COLOR;
-
-    int mOrientation;
-
-    PrintHelperKitkat(Context context) {
-        mPrintActivityRespectsOrientation = true;
-        mIsMinMarginsHandlingCorrect = true;
-
-        mContext = context;
-    }
-
-    /**
-     * Selects whether the image will fill the paper and be cropped
-     * <p/>
-     * {@link #SCALE_MODE_FIT}
-     * or whether the image will be scaled but leave white space
-     * {@link #SCALE_MODE_FILL}.
-     *
-     * @param scaleMode {@link #SCALE_MODE_FIT} or
-     *                  {@link #SCALE_MODE_FILL}
-     */
-    public void setScaleMode(int scaleMode) {
-        mScaleMode = scaleMode;
-    }
-
-    /**
-     * Returns the scale mode with which the image will fill the paper.
-     *
-     * @return The scale Mode: {@link #SCALE_MODE_FIT} or
-     * {@link #SCALE_MODE_FILL}
-     */
-    public int getScaleMode() {
-        return mScaleMode;
-    }
-
-    /**
-     * Sets whether the image will be printed in color (default)
-     * {@link #COLOR_MODE_COLOR} or in back and white
-     * {@link #COLOR_MODE_MONOCHROME}.
-     *
-     * @param colorMode The color mode which is one of
-     *                  {@link #COLOR_MODE_COLOR} and {@link #COLOR_MODE_MONOCHROME}.
-     */
-    public void setColorMode(int colorMode) {
-        mColorMode = colorMode;
-    }
-
-    /**
-     * Sets whether to select landscape (default), {@link #ORIENTATION_LANDSCAPE}
-     * or portrait {@link #ORIENTATION_PORTRAIT}
-     * @param orientation The page orientation which is one of
-     *                    {@link #ORIENTATION_LANDSCAPE} or {@link #ORIENTATION_PORTRAIT}.
-     */
-    public void setOrientation(int orientation) {
-        mOrientation = orientation;
-    }
-
-    /**
-     * Gets the page orientation with which the image will be printed.
-     *
-     * @return The preferred orientation which is one of
-     * {@link #ORIENTATION_LANDSCAPE} or {@link #ORIENTATION_PORTRAIT}
-     */
-    public int getOrientation() {
-        /// Unset defaults to landscape but might turn image
-        if (mOrientation == 0) {
-            return ORIENTATION_LANDSCAPE;
-        }
-        return mOrientation;
-    }
-
-    /**
-     * Gets the color mode with which the image will be printed.
-     *
-     * @return The color mode which is one of {@link #COLOR_MODE_COLOR}
-     * and {@link #COLOR_MODE_MONOCHROME}.
-     */
-    public int getColorMode() {
-        return mColorMode;
-    }
-
-    /**
-     * Check if the supplied bitmap should best be printed on a portrait orientation paper.
-     *
-     * @param bitmap The bitmap to be printed.
-     * @return true iff the picture should best be printed on a portrait orientation paper.
-     */
-    private static boolean isPortrait(Bitmap bitmap) {
-        if (bitmap.getWidth() <= bitmap.getHeight()) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Create a build with a copy from the other print attributes.
-     *
-     * @param other The other print attributes
-     *
-     * @return A builder that will build print attributes that match the other attributes
-     */
-    protected PrintAttributes.Builder copyAttributes(PrintAttributes other) {
-        PrintAttributes.Builder b = (new PrintAttributes.Builder())
-                .setMediaSize(other.getMediaSize())
-                .setResolution(other.getResolution())
-                .setMinMargins(other.getMinMargins());
-
-        if (other.getColorMode() != 0) {
-            b.setColorMode(other.getColorMode());
-        }
-
-        return b;
-    }
-
-    /**
-     * Prints a bitmap.
-     *
-     * @param jobName The print job name.
-     * @param bitmap  The bitmap to print.
-     * @param callback Optional callback to observe when printing is finished.
-     */
-    public void printBitmap(final String jobName, final Bitmap bitmap,
-            final OnPrintFinishCallback callback) {
-        if (bitmap == null) {
-            return;
-        }
-        final int fittingMode = mScaleMode; // grab the fitting mode at time of call
-        PrintManager printManager = (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
-        PrintAttributes.MediaSize mediaSize;
-        if (isPortrait(bitmap)) {
-            mediaSize = PrintAttributes.MediaSize.UNKNOWN_PORTRAIT;
-        } else {
-            mediaSize = PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE;
-        }
-        PrintAttributes attr = new PrintAttributes.Builder()
-                .setMediaSize(mediaSize)
-                .setColorMode(mColorMode)
-                .build();
-
-        printManager.print(jobName,
-                new PrintDocumentAdapter() {
-                    private PrintAttributes mAttributes;
-
-                    @Override
-                    public void onLayout(PrintAttributes oldPrintAttributes,
-                                         PrintAttributes newPrintAttributes,
-                                         CancellationSignal cancellationSignal,
-                                         LayoutResultCallback layoutResultCallback,
-                                         Bundle bundle) {
-
-                        mAttributes = newPrintAttributes;
-
-                        PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
-                                .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
-                                .setPageCount(1)
-                                .build();
-                        boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
-                        layoutResultCallback.onLayoutFinished(info, changed);
-                    }
-
-                    @Override
-                    public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
-                                        CancellationSignal cancellationSignal,
-                                        WriteResultCallback writeResultCallback) {
-                        writeBitmap(mAttributes, fittingMode, bitmap, fileDescriptor,
-                                cancellationSignal, writeResultCallback);
-                    }
-
-                    @Override
-                    public void onFinish() {
-                        if (callback != null) {
-                            callback.onFinish();
-                        }
-                    }
-                }, attr);
-    }
-
-    /**
-     * Calculates the transform the print an Image to fill the page
-     *
-     * @param imageWidth  with of bitmap
-     * @param imageHeight height of bitmap
-     * @param content     The output page dimensions
-     * @param fittingMode The mode of fitting {@link #SCALE_MODE_FILL} vs {@link #SCALE_MODE_FIT}
-     * @return Matrix to be used in canvas.drawBitmap(bitmap, matrix, null) call
-     */
-    private Matrix getMatrix(int imageWidth, int imageHeight, RectF content, int fittingMode) {
-        Matrix matrix = new Matrix();
-
-        // Compute and apply scale to fill the page.
-        float scale = content.width() / imageWidth;
-        if (fittingMode == SCALE_MODE_FILL) {
-            scale = Math.max(scale, content.height() / imageHeight);
-        } else {
-            scale = Math.min(scale, content.height() / imageHeight);
-        }
-        matrix.postScale(scale, scale);
-
-        // Center the content.
-        final float translateX = (content.width()
-                - imageWidth * scale) / 2;
-        final float translateY = (content.height()
-                - imageHeight * scale) / 2;
-        matrix.postTranslate(translateX, translateY);
-        return matrix;
-    }
-
-    /**
-     * Write a bitmap for a PDF document.
-     *
-     * @param attributes          The print attributes
-     * @param fittingMode         How to fit the bitmap
-     * @param bitmap              The bitmap to write
-     * @param fileDescriptor      The file to write to
-     * @param cancellationSignal  Signal cancelling operation
-     * @param writeResultCallback Callback to call once written
-     */
-    private void writeBitmap(final PrintAttributes attributes, final int fittingMode,
-            final Bitmap bitmap, final ParcelFileDescriptor fileDescriptor,
-            final CancellationSignal cancellationSignal,
-            final PrintDocumentAdapter.WriteResultCallback writeResultCallback) {
-        final PrintAttributes pdfAttributes;
-        if (mIsMinMarginsHandlingCorrect) {
-            pdfAttributes = attributes;
-        } else {
-            // If the handling of any margin != 0 is broken, strip the margins and add them to the
-            // bitmap later
-            pdfAttributes = copyAttributes(attributes)
-                    .setMinMargins(new PrintAttributes.Margins(0,0,0,0)).build();
-        }
-
-        (new AsyncTask<Void, Void, Throwable>() {
-            @Override
-            protected Throwable doInBackground(Void... params) {
-                try {
-                    if (cancellationSignal.isCanceled()) {
-                        return null;
-                    }
-
-                    PrintedPdfDocument pdfDocument = new PrintedPdfDocument(mContext,
-                            pdfAttributes);
-
-                    Bitmap maybeGrayscale = convertBitmapForColorMode(bitmap,
-                            pdfAttributes.getColorMode());
-
-                    if (cancellationSignal.isCanceled()) {
-                        return null;
-                    }
-
-                    try {
-                        Page page = pdfDocument.startPage(1);
-
-                        RectF contentRect;
-                        if (mIsMinMarginsHandlingCorrect) {
-                            contentRect = new RectF(page.getInfo().getContentRect());
-                        } else {
-                            // Create dummy doc that has the margins to compute correctly sized
-                            // content rectangle
-                            PrintedPdfDocument dummyDocument = new PrintedPdfDocument(mContext,
-                                    attributes);
-                            Page dummyPage = dummyDocument.startPage(1);
-                            contentRect = new RectF(dummyPage.getInfo().getContentRect());
-                            dummyDocument.finishPage(dummyPage);
-                            dummyDocument.close();
-                        }
-
-                        // Resize bitmap
-                        Matrix matrix = getMatrix(
-                                maybeGrayscale.getWidth(), maybeGrayscale.getHeight(),
-                                contentRect, fittingMode);
-
-                        if (mIsMinMarginsHandlingCorrect) {
-                            // The pdfDocument takes care of the positioning and margins
-                        } else {
-                            // Move it to the correct position.
-                            matrix.postTranslate(contentRect.left, contentRect.top);
-
-                            // Cut off margins
-                            page.getCanvas().clipRect(contentRect);
-                        }
-
-                        // Draw the bitmap.
-                        page.getCanvas().drawBitmap(maybeGrayscale, matrix, null);
-
-                        // Finish the page.
-                        pdfDocument.finishPage(page);
-
-                        if (cancellationSignal.isCanceled()) {
-                            return null;
-                        }
-
-                        // Write the document.
-                        pdfDocument
-                                .writeTo(new FileOutputStream(fileDescriptor.getFileDescriptor()));
-                        return null;
-                    } finally {
-                        pdfDocument.close();
-
-                        if (fileDescriptor != null) {
-                            try {
-                                fileDescriptor.close();
-                            } catch (IOException ioe) {
-                                // ignore
-                            }
-                        }
-                        // If we created a new instance for grayscaling, then recycle it here.
-                        if (maybeGrayscale != bitmap) {
-                            maybeGrayscale.recycle();
-                        }
-                    }
-                } catch (Throwable t) {
-                    return t;
-                }
-            }
-
-            @Override
-            protected void onPostExecute(Throwable throwable) {
-                if (cancellationSignal.isCanceled()) {
-                    // Cancelled.
-                    writeResultCallback.onWriteCancelled();
-                } else if (throwable == null) {
-                    // Done.
-                    writeResultCallback.onWriteFinished(new PageRange[] { PageRange.ALL_PAGES });
-                } else {
-                    // Failed.
-                    Log.e(LOG_TAG, "Error writing printed content", throwable);
-                    writeResultCallback.onWriteFailed(null);
-                }
-            }
-        }).execute();
-    }
-
-    /**
-     * Prints an image located at the Uri. Image types supported are those of
-     * <code>BitmapFactory.decodeStream</code> (JPEG, GIF, PNG, BMP, WEBP)
-     *
-     * @param jobName   The print job name.
-     * @param imageFile The <code>Uri</code> pointing to an image to print.
-     * @param callback Optional callback to observe when printing is finished.
-     * @throws FileNotFoundException if <code>Uri</code> is not pointing to a valid image.
-     */
-    public void printBitmap(final String jobName, final Uri imageFile,
-            final OnPrintFinishCallback callback) throws FileNotFoundException {
-        final int fittingMode = mScaleMode;
-
-        PrintDocumentAdapter printDocumentAdapter = new PrintDocumentAdapter() {
-            private PrintAttributes mAttributes;
-            AsyncTask<Uri, Boolean, Bitmap> mLoadBitmap;
-            Bitmap mBitmap = null;
-
-            @Override
-            public void onLayout(final PrintAttributes oldPrintAttributes,
-                                 final PrintAttributes newPrintAttributes,
-                                 final CancellationSignal cancellationSignal,
-                                 final LayoutResultCallback layoutResultCallback,
-                                 Bundle bundle) {
-
-                synchronized (this) {
-                    mAttributes = newPrintAttributes;
-                }
-
-                if (cancellationSignal.isCanceled()) {
-                    layoutResultCallback.onLayoutCancelled();
-                    return;
-                }
-                // we finished the load
-                if (mBitmap != null) {
-                    PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
-                            .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
-                            .setPageCount(1)
-                            .build();
-                    boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
-                    layoutResultCallback.onLayoutFinished(info, changed);
-                    return;
-                }
-
-                mLoadBitmap = new AsyncTask<Uri, Boolean, Bitmap>() {
-                    @Override
-                    protected void onPreExecute() {
-                        // First register for cancellation requests.
-                        cancellationSignal.setOnCancelListener(
-                                new CancellationSignal.OnCancelListener() {
-                                    @Override
-                                    public void onCancel() { // on different thread
-                                        cancelLoad();
-                                        cancel(false);
-                                    }
-                                });
-                    }
-
-                    @Override
-                    protected Bitmap doInBackground(Uri... uris) {
-                        try {
-                            return loadConstrainedBitmap(imageFile, MAX_PRINT_SIZE);
-                        } catch (FileNotFoundException e) {
-                          /* ignore */
-                        }
-                        return null;
-                    }
-
-                    @Override
-                    protected void onPostExecute(Bitmap bitmap) {
-                        super.onPostExecute(bitmap);
-
-                        // If orientation was not set by the caller, try to fit the bitmap on
-                        // the current paper by potentially rotating the bitmap by 90 degrees.
-                        if (bitmap != null
-                                && (!mPrintActivityRespectsOrientation || mOrientation == 0)) {
-                            MediaSize mediaSize;
-
-                            synchronized (this) {
-                                mediaSize = mAttributes.getMediaSize();
-                            }
-
-                            if (mediaSize != null) {
-                                if (mediaSize.isPortrait() != isPortrait(bitmap)) {
-                                    Matrix rotation = new Matrix();
-
-                                    rotation.postRotate(90);
-                                    bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
-                                            bitmap.getHeight(), rotation, true);
-                                }
-                            }
-                        }
-
-                        mBitmap = bitmap;
-                        if (bitmap != null) {
-                            PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
-                                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
-                                    .setPageCount(1)
-                                    .build();
-
-                            boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
-
-                            layoutResultCallback.onLayoutFinished(info, changed);
-
-                        } else {
-                            layoutResultCallback.onLayoutFailed(null);
-                        }
-                        mLoadBitmap = null;
-                    }
-
-                    @Override
-                    protected void onCancelled(Bitmap result) {
-                        // Task was cancelled, report that.
-                        layoutResultCallback.onLayoutCancelled();
-                        mLoadBitmap = null;
-                    }
-                }.execute();
-            }
-
-            private void cancelLoad() {
-                synchronized (mLock) { // prevent race with set null below
-                    if (mDecodeOptions != null) {
-                        mDecodeOptions.requestCancelDecode();
-                        mDecodeOptions = null;
-                    }
-                }
-            }
-
-            @Override
-            public void onFinish() {
-                super.onFinish();
-                cancelLoad();
-                if (mLoadBitmap != null) {
-                    mLoadBitmap.cancel(true);
-                }
-                if (callback != null) {
-                    callback.onFinish();
-                }
-                if (mBitmap != null) {
-                    mBitmap.recycle();
-                    mBitmap = null;
-                }
-            }
-
-            @Override
-            public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
-                                CancellationSignal cancellationSignal,
-                                WriteResultCallback writeResultCallback) {
-                writeBitmap(mAttributes, fittingMode, mBitmap, fileDescriptor, cancellationSignal,
-                        writeResultCallback);
-            }
-        };
-
-        PrintManager printManager = (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
-        PrintAttributes.Builder builder = new PrintAttributes.Builder();
-        builder.setColorMode(mColorMode);
-
-        if (mOrientation == ORIENTATION_LANDSCAPE || mOrientation == 0) {
-            builder.setMediaSize(PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE);
-        } else if (mOrientation == ORIENTATION_PORTRAIT) {
-            builder.setMediaSize(PrintAttributes.MediaSize.UNKNOWN_PORTRAIT);
-        }
-        PrintAttributes attr = builder.build();
-
-        printManager.print(jobName, printDocumentAdapter, attr);
-    }
-
-    /**
-     * Loads a bitmap while limiting its size
-     *
-     * @param uri           location of a valid image
-     * @param maxSideLength the maximum length of a size
-     * @return the Bitmap
-     * @throws FileNotFoundException if the Uri does not point to an image
-     */
-    private Bitmap loadConstrainedBitmap(Uri uri, int maxSideLength) throws FileNotFoundException {
-        if (maxSideLength <= 0 || uri == null || mContext == null) {
-            throw new IllegalArgumentException("bad argument to getScaledBitmap");
-        }
-        // Get width and height of stored bitmap
-        BitmapFactory.Options opt = new BitmapFactory.Options();
-        opt.inJustDecodeBounds = true;
-        loadBitmap(uri, opt);
-
-        int w = opt.outWidth;
-        int h = opt.outHeight;
-
-        // If bitmap cannot be decoded, return null
-        if (w <= 0 || h <= 0) {
-            return null;
-        }
-
-        // Find best downsampling size
-        int imageSide = Math.max(w, h);
-
-        int sampleSize = 1;
-        while (imageSide > maxSideLength) {
-            imageSide >>>= 1;
-            sampleSize <<= 1;
-        }
-
-        // Make sure sample size is reasonable
-        if (sampleSize <= 0 || 0 >= (int) (Math.min(w, h) / sampleSize)) {
-            return null;
-        }
-        BitmapFactory.Options decodeOptions = null;
-        synchronized (mLock) { // prevent race with set null below
-            mDecodeOptions = new BitmapFactory.Options();
-            mDecodeOptions.inMutable = true;
-            mDecodeOptions.inSampleSize = sampleSize;
-            decodeOptions = mDecodeOptions;
-        }
-        try {
-            return loadBitmap(uri, decodeOptions);
-        } finally {
-            synchronized (mLock) {
-                mDecodeOptions = null;
-            }
-        }
-    }
-
-    /**
-     * Returns the bitmap from the given uri loaded using the given options.
-     * Returns null on failure.
-     */
-    private Bitmap loadBitmap(Uri uri, BitmapFactory.Options o) throws FileNotFoundException {
-        if (uri == null || mContext == null) {
-            throw new IllegalArgumentException("bad argument to loadBitmap");
-        }
-        InputStream is = null;
-        try {
-            is = mContext.getContentResolver().openInputStream(uri);
-            return BitmapFactory.decodeStream(is, null, o);
-        } finally {
-            if (is != null) {
-                try {
-                    is.close();
-                } catch (IOException t) {
-                    Log.w(LOG_TAG, "close fail ", t);
-                }
-            }
-        }
-    }
-
-    private Bitmap convertBitmapForColorMode(Bitmap original, int colorMode) {
-        if (colorMode != COLOR_MODE_MONOCHROME) {
-            return original;
-        }
-        // Create a grayscale bitmap
-        Bitmap grayscale = Bitmap.createBitmap(original.getWidth(), original.getHeight(),
-                Config.ARGB_8888);
-        Canvas c = new Canvas(grayscale);
-        Paint p = new Paint();
-        ColorMatrix cm = new ColorMatrix();
-        cm.setSaturation(0);
-        ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
-        p.setColorFilter(f);
-        c.drawBitmap(original, 0, 0, p);
-        c.setBitmap(null);
-
-        return grayscale;
-    }
-}
diff --git a/design/src/android/support/design/widget/AppBarLayout.java b/design/src/android/support/design/widget/AppBarLayout.java
index afdfafb..8e465de 100644
--- a/design/src/android/support/design/widget/AppBarLayout.java
+++ b/design/src/android/support/design/widget/AppBarLayout.java
@@ -34,7 +34,6 @@
 import android.support.annotation.VisibleForTesting;
 import android.support.design.R;
 import android.support.v4.math.MathUtils;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.view.AbsSavedState;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.WindowInsetsCompat;
@@ -179,7 +178,7 @@
             ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(
                     this, a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0));
         }
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             // In O+, we have these values set in the style. Since there is no defStyleAttr for
             // AppBarLayout at the AppCompat level, check for these attributes here.
             if (a.hasValue(R.styleable.AppBarLayout_android_keyboardNavigationCluster)) {
diff --git a/development/refaster/IsAtLeastO.java b/development/refaster/IsAtLeastO.java
new file mode 100644
index 0000000..9198441
--- /dev/null
+++ b/development/refaster/IsAtLeastO.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+import android.os.Build.VERSION;
+import android.support.v4.os.BuildCompat;
+
+import com.google.errorprone.refaster.annotation.AfterTemplate;
+import com.google.errorprone.refaster.annotation.AlsoNegation;
+import com.google.errorprone.refaster.annotation.BeforeTemplate;
+
+/**
+ * Replace usages of BuildCompat.isAtLeastO() with SDK_INT check.
+ */
+public class IsAtLeastO {
+    @BeforeTemplate
+    boolean usingAtLeastO() {
+        return BuildCompat.isAtLeastO();
+    }
+
+    @AfterTemplate
+    @AlsoNegation
+    boolean optimizedMethod() {
+        return VERSION.SDK_INT >= 26;
+    }
+}
diff --git a/development/refaster/README b/development/refaster/README
new file mode 100644
index 0000000..daf8f0d
--- /dev/null
+++ b/development/refaster/README
@@ -0,0 +1,25 @@
+Author: aurimas@google.com
+Updated: 6/6/2017
+
+Instructions on how to compile and apply refaster rules to support library
+
+0. Download error-prone and refaster jars
+http://errorprone.info/docs/refaster will have up to date instructions
+
+1. Compile the refaster rule (in this example IsAtLeastO.java)
+java -cp /path/to/android.jar:/path/to/support-compat.jar:javac-9-dev-r3297-4.jar:error_prone_refaster-2.0.18.jar com.google.errorprone.refaster.RefasterRuleCompiler IsAtLeastO.java --out `pwd`/myrule.refaster
+
+2. Update build to use the refaster rule
+Add compiler args to error-prone in SupportLibraryPlugin.groovy
+'-XepPatchChecks:refaster:/path/to/refaster/myrule.refaster',
+'-XepPatchLocation:' + project.projectDir
+
+3. Compile support library using the refaster rule
+./gradlew assembleErrorProne
+
+4. Apply patches
+error-prone will produce patch files like "design/error-prone.patch" and to apply them, cd into the
+directory e.g. "design" and then run:
+patch -p0 -u -i error-prone.patch
+
+5. Rules have been applied! Celebrate!
\ No newline at end of file
diff --git a/droiddoc.mk b/droiddoc.mk
index bf48544..0913f5f 100644
--- a/droiddoc.mk
+++ b/droiddoc.mk
@@ -36,7 +36,7 @@
     -since $(SUPPORT_PATH)/api/25.1.0.txt 25.1.0 \
     -since $(SUPPORT_PATH)/api/25.2.0.txt 25.2.0 \
     -since $(SUPPORT_PATH)/api/25.3.0.txt 25.3.0 \
-    -since $(SUPPORT_PATH)/api/25.3.0.txt 25.4.0 \
+    -since $(SUPPORT_PATH)/api/25.4.0.txt 25.4.0 \
     -since $(SUPPORT_PATH)/api/26.0.0-alpha1.txt 26.0.0-alpha1 \
     -since $(SUPPORT_PATH)/api/26.0.0-beta1.txt 26.0.0-beta1 \
     -since $(SUPPORT_PATH)/api/26.0.0-beta2.txt 26.0.0-beta2
diff --git a/emoji/bundled/src/android/support/text/emoji/bundled/BundledEmojiCompatConfig.java b/emoji/bundled/src/android/support/text/emoji/bundled/BundledEmojiCompatConfig.java
index 37fc444..97ea363 100644
--- a/emoji/bundled/src/android/support/text/emoji/bundled/BundledEmojiCompatConfig.java
+++ b/emoji/bundled/src/android/support/text/emoji/bundled/BundledEmojiCompatConfig.java
@@ -43,7 +43,7 @@
         super(new BundledMetadataLoader(context));
     }
 
-    private static class BundledMetadataLoader implements EmojiCompat.MetadataLoader {
+    private static class BundledMetadataLoader implements EmojiCompat.MetadataRepoLoader {
         private final Context mContext;
 
         private BundledMetadataLoader(@NonNull Context context) {
@@ -52,7 +52,7 @@
 
         @Override
         @RequiresApi(19)
-        public void load(@NonNull EmojiCompat.LoaderCallback loaderCallback) {
+        public void load(@NonNull EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
             Preconditions.checkNotNull(loaderCallback, "loaderCallback cannot be null");
             final InitRunnable runnable = new InitRunnable(mContext, loaderCallback);
             final Thread thread = new Thread(runnable);
@@ -64,11 +64,11 @@
     @RequiresApi(19)
     private static class InitRunnable implements Runnable {
         private static final String FONT_NAME = "NotoColorEmojiCompat.ttf";
-        private final EmojiCompat.LoaderCallback mLoaderCallback;
+        private final EmojiCompat.MetadataRepoLoaderCallback mLoaderCallback;
         private final Context mContext;
 
         private InitRunnable(final Context context,
-                final EmojiCompat.LoaderCallback loaderCallback) {
+                final EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
             mContext = context;
             mLoaderCallback = loaderCallback;
         }
diff --git a/emoji/core/src/android/support/text/emoji/EmojiCompat.java b/emoji/core/src/android/support/text/emoji/EmojiCompat.java
index b5db6f6..26efbba 100644
--- a/emoji/core/src/android/support/text/emoji/EmojiCompat.java
+++ b/emoji/core/src/android/support/text/emoji/EmojiCompat.java
@@ -22,6 +22,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.support.annotation.AnyThread;
+import android.support.annotation.CheckResult;
 import android.support.annotation.ColorInt;
 import android.support.annotation.GuardedBy;
 import android.support.annotation.IntDef;
@@ -101,19 +102,31 @@
     /**
      * EmojiCompat successfully initialized.
      */
+    public static final int LOAD_STATE_SUCCEEDED = 1;
+
+    /**
+     * @deprecated Use {@link #LOAD_STATE_SUCCEEDED} instead.
+     */
+    @Deprecated
     public static final int LOAD_STATE_SUCCESS = 1;
 
     /**
      * An unrecoverable error occurred during initialization of EmojiCompat. Calls to functions
      * such as {@link #process(CharSequence)} will fail.
      */
+    public static final int LOAD_STATE_FAILED = 2;
+
+    /**
+     * @deprecated Use {@link #LOAD_STATE_FAILED} instead.
+     */
+    @Deprecated
     public static final int LOAD_STATE_FAILURE = 2;
 
     /**
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP)
-    @IntDef({LOAD_STATE_LOADING, LOAD_STATE_SUCCESS, LOAD_STATE_FAILURE})
+    @IntDef({LOAD_STATE_LOADING, LOAD_STATE_SUCCEEDED, LOAD_STATE_FAILED})
     @Retention(RetentionPolicy.SOURCE)
     public @interface LoadState {
     }
@@ -167,9 +180,16 @@
     private final CompatInternal mHelper;
 
     /**
-     * MetadataLoader instance given in the Config instance.
+     * Metadata loader instance given in the Config instance.
      */
-    private final MetadataLoader mMetadataLoader;
+    private final MetadataRepoLoader mMetadataLoader;
+
+    /**
+     * Old metadata loader instance given in the Config instance.
+     * @deprecated Will be removed soon.
+     */
+    @Deprecated
+    private final MetadataLoader mLegacyMetadataLoader;
 
     /**
      * @see Config#setReplaceAll(boolean)
@@ -197,6 +217,7 @@
         mEmojiSpanIndicatorEnabled = config.mEmojiSpanIndicatorEnabled;
         mEmojiSpanIndicatorColor = config.mEmojiSpanIndicatorColor;
         mMetadataLoader = config.mMetadataLoader;
+        mLegacyMetadataLoader = config.mLegacyMetadataLoader;
         mMainHandler = new Handler(Looper.getMainLooper());
         mInitCallbacks = new ArraySet<>();
         if (config.mInitCallbacks != null && !config.mInitCallbacks.isEmpty()) {
@@ -209,8 +230,8 @@
 
     /**
      * Initialize the singleton instance with a configuration. When used on devices running API 18
-     * or below, the singleton instance is immediately moved into {@link #LOAD_STATE_SUCCESS} state
-     * without loading any metadata.
+     * or below, the singleton instance is immediately moved into {@link #LOAD_STATE_SUCCEEDED}
+     * state without loading any metadata.
      *
      * @see EmojiCompat.Config
      */
@@ -296,7 +317,7 @@
         final Collection<InitCallback> initCallbacks = new ArrayList<>();
         mInitLock.writeLock().lock();
         try {
-            mLoadState = LOAD_STATE_SUCCESS;
+            mLoadState = LOAD_STATE_SUCCEEDED;
             initCallbacks.addAll(mInitCallbacks);
             mInitCallbacks.clear();
         } finally {
@@ -310,7 +331,7 @@
         final Collection<InitCallback> initCallbacks = new ArrayList<>();
         mInitLock.writeLock().lock();
         try {
-            mLoadState = LOAD_STATE_FAILURE;
+            mLoadState = LOAD_STATE_FAILED;
             initCallbacks.addAll(mInitCallbacks);
             mInitCallbacks.clear();
         } finally {
@@ -337,7 +358,7 @@
 
         mInitLock.writeLock().lock();
         try {
-            if (mLoadState == LOAD_STATE_SUCCESS || mLoadState == LOAD_STATE_FAILURE) {
+            if (mLoadState == LOAD_STATE_SUCCEEDED || mLoadState == LOAD_STATE_FAILED) {
                 mMainHandler.post(new ListenerDispatcher(initCallback, mLoadState));
             } else {
                 mInitCallbacks.add(initCallback);
@@ -364,10 +385,10 @@
 
     /**
      * Returns loading state of the EmojiCompat instance. When used on devices running API 18 or
-     * below always returns {@link #LOAD_STATE_SUCCESS}.
+     * below always returns {@link #LOAD_STATE_SUCCEEDED}.
      *
-     * @return one of {@link #LOAD_STATE_LOADING}, {@link #LOAD_STATE_SUCCESS},
-     * {@link #LOAD_STATE_FAILURE}
+     * @return one of {@link #LOAD_STATE_LOADING}, {@link #LOAD_STATE_SUCCEEDED},
+     * {@link #LOAD_STATE_FAILED}
      */
     public @LoadState int getLoadState() {
         mInitLock.readLock().lock();
@@ -382,7 +403,7 @@
      * @return {@code true} if EmojiCompat is successfully initialized
      */
     private boolean isInitialized() {
-        return getLoadState() == LOAD_STATE_SUCCESS;
+        return getLoadState() == LOAD_STATE_SUCCEEDED;
     }
 
     /**
@@ -507,6 +528,7 @@
      * @throws IllegalStateException if not initialized yet
      * @see #process(CharSequence, int, int)
      */
+    @CheckResult
     public CharSequence process(@NonNull final CharSequence charSequence) {
         // since charSequence might be null here we have to check it. Passing through here to the
         // main function so that it can do all the checks including isInitialized. It will also
@@ -541,6 +563,7 @@
      *                                  {@code start > charSequence.length()},
      *                                  {@code end > charSequence.length()}
      */
+    @CheckResult
     public CharSequence process(@NonNull final CharSequence charSequence,
             @IntRange(from = 0) final int start, @IntRange(from = 0) final int end) {
         return process(charSequence, start, end, EmojiProcessor.EMOJI_COUNT_UNLIMITED);
@@ -575,6 +598,7 @@
      *                                  {@code end > charSequence.length()}
      *                                  {@code maxEmojiCount < 0}
      */
+    @CheckResult
     public CharSequence process(@NonNull final CharSequence charSequence,
             @IntRange(from = 0) final int start, @IntRange(from = 0) final int end,
             @IntRange(from = 0) final int maxEmojiCount) {
@@ -614,6 +638,7 @@
      *                                  {@code end > charSequence.length()}
      *                                  {@code maxEmojiCount < 0}
      */
+    @CheckResult
     public CharSequence process(@NonNull final CharSequence charSequence,
             @IntRange(from = 0) final int start, @IntRange(from = 0) final int end,
             @IntRange(from = 0) final int maxEmojiCount, @ReplaceStrategy int replaceStrategy) {
@@ -716,6 +741,44 @@
     /**
      * Interface to load emoji metadata.
      */
+    public interface MetadataRepoLoader {
+        /**
+         * Start loading the metadata. When the loading operation is finished {@link
+         * MetadataRepoLoaderCallback#onLoaded(MetadataRepo)} or
+         * {@link MetadataRepoLoaderCallback#onFailed(Throwable)} should be called. When used on
+         * devices running API 18 or below, this function is never called.
+         *
+         * @param loaderCallback callback to signal the loading state
+         */
+        void load(@NonNull MetadataRepoLoaderCallback loaderCallback);
+    }
+
+    /**
+     * Callback to inform EmojiCompat about the state of the metadata load. Passed to
+     * MetadataRepoLoader during {@link MetadataRepoLoader#load(MetadataRepoLoaderCallback)} call.
+     */
+    public abstract static class MetadataRepoLoaderCallback {
+        /**
+         * Called by {@link MetadataRepoLoader} when metadata is loaded successfully.
+         *
+         * @param metadataRepo MetadataRepo instance, cannot be {@code null}
+         */
+        public abstract void onLoaded(@NonNull MetadataRepo metadataRepo);
+
+        /**
+         * Called by {@link MetadataRepoLoader} if an error occurs while loading the metadata.
+         *
+         * @param throwable the exception that caused the failure, {@code nullable}
+         */
+        public abstract void onFailed(@Nullable Throwable throwable);
+    }
+
+    /**
+     * Interface to load emoji metadata.
+     *
+     * @deprecated Use {@link MetadataRepoLoader} instead.
+     */
+    @Deprecated
     public interface MetadataLoader {
         /**
          * Start loading the metadata. When the loading operation is finished {@link
@@ -731,7 +794,10 @@
     /**
      * Callback to inform EmojiCompat about the state of the metadata load. Passed to MetadataLoader
      * during {@link MetadataLoader#load(LoaderCallback)} call.
+     *
+     * @deprecated Use {@link MetadataRepoLoaderCallback} instead.
      */
+    @Deprecated
     public abstract static class LoaderCallback {
         /**
          * Called by {@link MetadataLoader} when metadata is loaded successfully.
@@ -755,7 +821,8 @@
      * @see #init(EmojiCompat.Config)
      */
     public abstract static class Config {
-        private final MetadataLoader mMetadataLoader;
+        private final MetadataRepoLoader mMetadataLoader;
+        private final MetadataLoader mLegacyMetadataLoader;
         private boolean mReplaceAll;
         private Set<InitCallback> mInitCallbacks;
         private boolean mEmojiSpanIndicatorEnabled;
@@ -764,11 +831,25 @@
         /**
          * Default constructor.
          *
+         * @param metadataLoader MetadataRepoLoader instance, cannot be {@code null}
+         */
+        protected Config(@NonNull final MetadataRepoLoader metadataLoader) {
+            Preconditions.checkNotNull(metadataLoader, "metadataLoader cannot be null.");
+            mMetadataLoader = metadataLoader;
+            mLegacyMetadataLoader = null;
+        }
+
+        /**
+         * Default constructor.
+         *
          * @param metadataLoader MetadataLoader instance, cannot be {@code null}
+         *
+         * @deprecated Use constructor with MetadataRepoLoader instead.
          */
         protected Config(@NonNull final MetadataLoader metadataLoader) {
             Preconditions.checkNotNull(metadataLoader, "metadataLoader cannot be null.");
-            mMetadataLoader = metadataLoader;
+            mLegacyMetadataLoader = metadataLoader;
+            mMetadataLoader = null;
         }
 
         /**
@@ -843,11 +924,11 @@
         }
 
         /**
-         * Returns the {@link MetadataLoader}.
+         * Returns the {@link MetadataRepoLoader}.
          * @hide
          */
         @RestrictTo(LIBRARY_GROUP)
-        public final MetadataLoader getMetadataLoader() {
+        public final MetadataRepoLoader getMetadataLoader() {
             return mMetadataLoader;
         }
     }
@@ -884,12 +965,12 @@
         public void run() {
             final int size = mInitCallbacks.size();
             switch (mLoadState) {
-                case LOAD_STATE_SUCCESS:
+                case LOAD_STATE_SUCCEEDED:
                     for (int i = 0; i < size; i++) {
                         mInitCallbacks.get(i).onInitialized();
                     }
                     break;
-                case LOAD_STATE_FAILURE:
+                case LOAD_STATE_FAILED:
                 default:
                     for (int i = 0; i < size; i++) {
                         mInitCallbacks.get(i).onFailed(mThrowable);
@@ -961,17 +1042,34 @@
         @Override
         void loadMetadata() {
             try {
-                mEmojiCompat.mMetadataLoader.load(new LoaderCallback() {
-                    @Override
-                    public void onLoaded(@NonNull MetadataRepo metadataRepo) {
-                        onMetadataLoadSuccess(metadataRepo);
-                    }
+                if (mEmojiCompat.mMetadataLoader != null) {
+                    final MetadataRepoLoaderCallback callback = new MetadataRepoLoaderCallback() {
+                        @Override
+                        public void onLoaded(@NonNull MetadataRepo metadataRepo) {
+                            onMetadataLoadSuccess(metadataRepo);
+                        }
 
-                    @Override
-                    public void onFailed(@Nullable Throwable throwable) {
-                        mEmojiCompat.onMetadataLoadFailed(throwable);
-                    }
-                });
+                        @Override
+                        public void onFailed(@Nullable Throwable throwable) {
+                            mEmojiCompat.onMetadataLoadFailed(throwable);
+                        }
+                    };
+                    mEmojiCompat.mMetadataLoader.load(callback);
+                } else {
+                    final LoaderCallback callback = new LoaderCallback() {
+                        @Override
+                        public void onLoaded(@NonNull MetadataRepo metadataRepo) {
+                            onMetadataLoadSuccess(metadataRepo);
+                        }
+
+                        @Override
+                        public void onFailed(@Nullable Throwable throwable) {
+                            mEmojiCompat.onMetadataLoadFailed(throwable);
+                        }
+                    };
+                    mEmojiCompat.mLegacyMetadataLoader.load(callback);
+                }
+
             } catch (Throwable t) {
                 mEmojiCompat.onMetadataLoadFailed(t);
             }
diff --git a/emoji/core/src/android/support/text/emoji/FontRequestEmojiCompatConfig.java b/emoji/core/src/android/support/text/emoji/FontRequestEmojiCompatConfig.java
index ae7cd14..f5d7b82 100644
--- a/emoji/core/src/android/support/text/emoji/FontRequestEmojiCompatConfig.java
+++ b/emoji/core/src/android/support/text/emoji/FontRequestEmojiCompatConfig.java
@@ -20,17 +20,14 @@
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Typeface;
-import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
-import android.support.v4.graphics.TypefaceCompat;
 import android.support.v4.provider.FontRequest;
 import android.support.v4.provider.FontsContractCompat;
 import android.support.v4.provider.FontsContractCompat.FontFamilyResult;
 import android.support.v4.provider.FontsContractCompat.FontInfo;
-import android.support.v4.util.ArrayMap;
 import android.support.v4.util.Preconditions;
 
 import java.io.FileInputStream;
@@ -64,10 +61,10 @@
 
 
     /**
-     * MetadataLoader implementation that uses FontsContractCompat and TypefaceCompat to load a
+     * MetadataRepoLoader implementation that uses FontsContractCompat and TypefaceCompat to load a
      * given FontRequest.
      */
-    private static class FontRequestMetadataLoader implements EmojiCompat.MetadataLoader {
+    private static class FontRequestMetadataLoader implements EmojiCompat.MetadataRepoLoader {
         private final Context mContext;
         private final FontRequest mRequest;
         private final FontsContractDelegate mFontsContract;
@@ -83,7 +80,7 @@
 
         @Override
         @RequiresApi(19)
-        public void load(@NonNull final EmojiCompat.LoaderCallback loaderCallback) {
+        public void load(@NonNull final EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
             Preconditions.checkNotNull(loaderCallback, "LoaderCallback cannot be null");
             final InitRunnable runnable =
                     new InitRunnable(mContext, mRequest, mFontsContract, loaderCallback);
@@ -98,7 +95,7 @@
      */
     @RequiresApi(19)
     private static class InitRunnable implements Runnable {
-        private final EmojiCompat.LoaderCallback mLoaderCallback;
+        private final EmojiCompat.MetadataRepoLoaderCallback mLoaderCallback;
         private final Context mContext;
         private final FontsContractDelegate mFontsContract;
         private final FontRequest mFontRequest;
@@ -106,7 +103,7 @@
         private InitRunnable(final Context context,
                 final FontRequest fontRequest,
                 final FontsContractDelegate fontsContract,
-                final EmojiCompat.LoaderCallback loaderCallback) {
+                final EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
             mContext = context;
             mFontRequest = fontRequest;
             mFontsContract = fontsContract;
@@ -145,12 +142,10 @@
                     throwException("Unable to open file.");
                 }
 
-                // TypefaceCompat.buildTypeface opens file descriptor again, so bypass the
-                // FontsContract.prepareFontData and create FontInfo and ByteBuffer directly.
-                final ArrayMap<Uri, ByteBuffer> bufferMap = new ArrayMap<>();
-                bufferMap.put(font.getUri(), buffer.duplicate());
-                final Typeface typeface = TypefaceCompat.createTypeface(mContext,
-                        new FontInfo[] { font }, bufferMap);
+                // TODO(nona): Introduce public API to make Typeface from filedescriptor so that we
+                // can stop opening file descriptor twice.
+                final Typeface typeface = FontsContractCompat.buildTypeface(mContext,
+                        null /* cancellation signal */, fonts);
                 if (typeface == null) {
                     throwException("Failed to create Typeface.");
                 }
diff --git a/emoji/core/src/android/support/text/emoji/widget/EditTextAttributeHelper.java b/emoji/core/src/android/support/text/emoji/widget/EditTextAttributeHelper.java
index 49cd7c8..3ee7546 100644
--- a/emoji/core/src/android/support/text/emoji/widget/EditTextAttributeHelper.java
+++ b/emoji/core/src/android/support/text/emoji/widget/EditTextAttributeHelper.java
@@ -33,7 +33,7 @@
  */
 @RestrictTo(LIBRARY_GROUP)
 public class EditTextAttributeHelper {
-
+    static final int MAX_EMOJI_COUNT = Integer.MAX_VALUE;
     private int mMaxEmojiCount;
 
     public EditTextAttributeHelper(@NonNull View view, AttributeSet attrs, int defStyleAttr) {
@@ -41,8 +41,7 @@
             final Context context = view.getContext();
             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.EmojiEditText,
                     defStyleAttr, 0);
-            mMaxEmojiCount = a.getInteger(R.styleable.EmojiEditText_maxEmojiCount,
-                    EmojiTextWatcher.MAX_EMOJI_COUNT);
+            mMaxEmojiCount = a.getInteger(R.styleable.EmojiEditText_maxEmojiCount, MAX_EMOJI_COUNT);
             a.recycle();
         }
     }
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiInputFilter.java b/emoji/core/src/android/support/text/emoji/widget/EmojiInputFilter.java
index a1a6fc8..4dec230 100644
--- a/emoji/core/src/android/support/text/emoji/widget/EmojiInputFilter.java
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiInputFilter.java
@@ -58,7 +58,7 @@
 
 
         switch (EmojiCompat.get().getLoadState()){
-            case EmojiCompat.LOAD_STATE_SUCCESS:
+            case EmojiCompat.LOAD_STATE_SUCCEEDED:
                 boolean process = true;
                 if (destEnd == 0 && destStart == 0 && dest.length() == 0) {
                     final CharSequence oldText = mTextView.getText();
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiTextWatcher.java b/emoji/core/src/android/support/text/emoji/widget/EmojiTextWatcher.java
index d261c0c..8c224fe 100644
--- a/emoji/core/src/android/support/text/emoji/widget/EmojiTextWatcher.java
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiTextWatcher.java
@@ -37,10 +37,9 @@
 @RestrictTo(LIBRARY_GROUP)
 @RequiresApi(19)
 final class EmojiTextWatcher implements android.text.TextWatcher {
-    static final int MAX_EMOJI_COUNT = Integer.MAX_VALUE;
     private final EditText mEditText;
     private InitCallback mInitCallback;
-    private int mMaxEmojiCount = MAX_EMOJI_COUNT;
+    private int mMaxEmojiCount = EditTextAttributeHelper.MAX_EMOJI_COUNT;
 
     EmojiTextWatcher(EditText editText) {
         mEditText = editText;
@@ -64,7 +63,7 @@
         //before > after --> a deletion occured
         if (before <= after && charSequence instanceof Spannable) {
             switch (EmojiCompat.get().getLoadState()){
-                case EmojiCompat.LOAD_STATE_SUCCESS:
+                case EmojiCompat.LOAD_STATE_SUCCEEDED:
                     final Spannable s = (Spannable) charSequence;
                     EmojiCompat.get().process(s, start, start + after, mMaxEmojiCount);
                     break;
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiTransformationMethod.java b/emoji/core/src/android/support/text/emoji/widget/EmojiTransformationMethod.java
index b7e7531..6e66462 100644
--- a/emoji/core/src/android/support/text/emoji/widget/EmojiTransformationMethod.java
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiTransformationMethod.java
@@ -52,7 +52,7 @@
 
         if (source != null) {
             switch (EmojiCompat.get().getLoadState()){
-                case EmojiCompat.LOAD_STATE_SUCCESS:
+                case EmojiCompat.LOAD_STATE_SUCCEEDED:
                     return EmojiCompat.get().process(source);
             }
         }
diff --git a/emoji/core/tests/java/android/support/text/emoji/ConfigTest.java b/emoji/core/tests/java/android/support/text/emoji/ConfigTest.java
index 75692af..538ce99 100644
--- a/emoji/core/tests/java/android/support/text/emoji/ConfigTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/ConfigTest.java
@@ -102,9 +102,9 @@
     public void testInitCallback_callsFailCallback() {
         final EmojiCompat.InitCallback initCallback1 = mock(EmojiCompat.InitCallback.class);
         final EmojiCompat.InitCallback initCallback2 = mock(EmojiCompat.InitCallback.class);
-        final EmojiCompat.MetadataLoader loader = mock(EmojiCompat.MetadataLoader.class);
-        doThrow(new RuntimeException("")).when(loader).load(any(EmojiCompat.LoaderCallback
-                .class));
+        final EmojiCompat.MetadataRepoLoader loader = mock(EmojiCompat.MetadataRepoLoader.class);
+        doThrow(new RuntimeException("")).when(loader)
+                .load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
 
         final EmojiCompat.Config config = new TestConfigBuilder.TestConfig(loader)
                 .registerInitCallback(initCallback1)
diff --git a/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java b/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
index 29964ce..eb4ce2e 100644
--- a/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
@@ -589,7 +589,7 @@
     @Test
     @SdkSuppress(maxSdkVersion = 18)
     public void testGetLoadState_returnsSuccess_pre19() {
-        assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_SUCCESS);
+        assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_SUCCEEDED);
     }
 
     @Test
@@ -605,7 +605,7 @@
         metadataLoader.getTestLatch().await();
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
-        assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_SUCCESS);
+        assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_SUCCEEDED);
     }
 
     @Test
@@ -621,7 +621,7 @@
         metadataLoader.getTestLatch().await();
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
-        assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_FAILURE);
+        assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_FAILED);
     }
 
     @Test
diff --git a/emoji/core/tests/java/android/support/text/emoji/FontRequestEmojiCompatConfigTest.java b/emoji/core/tests/java/android/support/text/emoji/FontRequestEmojiCompatConfigTest.java
index 72602c5..8201e96 100644
--- a/emoji/core/tests/java/android/support/text/emoji/FontRequestEmojiCompatConfigTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/FontRequestEmojiCompatConfigTest.java
@@ -203,7 +203,7 @@
         assertThat(argumentCaptor.getValue().getMessage(), containsString(exceptionMessage));
     }
 
-    public static class WaitingLoaderCallback extends EmojiCompat.LoaderCallback {
+    public static class WaitingLoaderCallback extends EmojiCompat.MetadataRepoLoaderCallback {
         final CountDownLatch mLatch;
 
         public WaitingLoaderCallback() {
diff --git a/emoji/core/tests/java/android/support/text/emoji/InitCallbackTest.java b/emoji/core/tests/java/android/support/text/emoji/InitCallbackTest.java
index e11f4da..10abb01 100644
--- a/emoji/core/tests/java/android/support/text/emoji/InitCallbackTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/InitCallbackTest.java
@@ -57,9 +57,9 @@
     public void testRegisterInitCallback_callsFailCallback() {
         final EmojiCompat.InitCallback initCallback1 = mock(EmojiCompat.InitCallback.class);
         final EmojiCompat.InitCallback initCallback2 = mock(EmojiCompat.InitCallback.class);
-        final EmojiCompat.MetadataLoader loader = mock(EmojiCompat.MetadataLoader.class);
-        doThrow(new RuntimeException("")).when(loader).load(any(EmojiCompat.LoaderCallback
-                .class));
+        final EmojiCompat.MetadataRepoLoader loader = mock(EmojiCompat.MetadataRepoLoader.class);
+        doThrow(new RuntimeException("")).when(loader)
+                .load(any(EmojiCompat.MetadataRepoLoaderCallback.class));
 
         final EmojiCompat.Config config = new TestConfig(loader);
         final EmojiCompat emojiCompat = EmojiCompat.reset(config);
@@ -76,9 +76,9 @@
     @SdkSuppress(minSdkVersion = 19)
     public void testRegisterInitCallback_callsFailCallback_whenOnFailCalledByLoader() {
         final EmojiCompat.InitCallback initCallback = mock(EmojiCompat.InitCallback.class);
-        final EmojiCompat.MetadataLoader loader = new EmojiCompat.MetadataLoader() {
+        final EmojiCompat.MetadataRepoLoader loader = new EmojiCompat.MetadataRepoLoader() {
             @Override
-            public void load(@NonNull EmojiCompat.LoaderCallback loaderCallback) {
+            public void load(@NonNull EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
                 loaderCallback.onFailed(new RuntimeException(""));
             }
         };
@@ -95,9 +95,9 @@
     @SdkSuppress(minSdkVersion = 19)
     public void testRegisterInitCallback_callsFailCallback_whenMetadataRepoIsNull() {
         final EmojiCompat.InitCallback initCallback = mock(EmojiCompat.InitCallback.class);
-        final EmojiCompat.MetadataLoader loader = new EmojiCompat.MetadataLoader() {
+        final EmojiCompat.MetadataRepoLoader loader = new EmojiCompat.MetadataRepoLoader() {
             @Override
-            public void load(@NonNull EmojiCompat.LoaderCallback loaderCallback) {
+            public void load(@NonNull EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
                 loaderCallback.onLoaded(null);
             }
         };
diff --git a/emoji/core/tests/java/android/support/text/emoji/TestConfigBuilder.java b/emoji/core/tests/java/android/support/text/emoji/TestConfigBuilder.java
index 202b2e4..242d62b 100644
--- a/emoji/core/tests/java/android/support/text/emoji/TestConfigBuilder.java
+++ b/emoji/core/tests/java/android/support/text/emoji/TestConfigBuilder.java
@@ -34,12 +34,12 @@
             super(new TestEmojiDataLoader());
         }
 
-        TestConfig(final EmojiCompat.MetadataLoader metadataLoader) {
+        TestConfig(final EmojiCompat.MetadataRepoLoader metadataLoader) {
             super(metadataLoader);
         }
     }
 
-    public static class WaitingDataLoader implements EmojiCompat.MetadataLoader {
+    public static class WaitingDataLoader implements EmojiCompat.MetadataRepoLoader {
         private final CountDownLatch mLoaderLatch;
         private final CountDownLatch mTestLatch;
         private final boolean mSuccess;
@@ -63,7 +63,7 @@
         }
 
         @Override
-        public void load(@NonNull final EmojiCompat.LoaderCallback loaderCallback) {
+        public void load(@NonNull final EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
             new Thread(new Runnable() {
                 @Override
                 public void run() {
@@ -84,7 +84,7 @@
         }
     }
 
-    public static class TestEmojiDataLoader implements EmojiCompat.MetadataLoader {
+    public static class TestEmojiDataLoader implements EmojiCompat.MetadataRepoLoader {
         static final Object sMetadataRepoLock = new Object();
         // keep a static instance to in order not to slow down the tests
         @GuardedBy("sMetadataRepoLock")
@@ -94,7 +94,7 @@
         }
 
         @Override
-        public void load(@NonNull EmojiCompat.LoaderCallback loaderCallback) {
+        public void load(@NonNull EmojiCompat.MetadataRepoLoaderCallback loaderCallback) {
             if (sMetadataRepo == null) {
                 synchronized (sMetadataRepoLock) {
                     if (sMetadataRepo == null) {
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputFilterTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputFilterTest.java
index e9c9418..ca594cf 100644
--- a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputFilterTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputFilterTest.java
@@ -54,7 +54,7 @@
         final TextView textView = mock(TextView.class);
         mEmojiCompat = mock(EmojiCompat.class);
         EmojiCompat.reset(mEmojiCompat);
-        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_SUCCESS);
+        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_SUCCEEDED);
         mInputFilter = new EmojiInputFilter(textView);
     }
 
@@ -106,7 +106,7 @@
     @Test
     public void testFilter_whenEmojiCompatLoadFailed() {
         final Spannable testString = new SpannableString("abc");
-        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_FAILURE);
+        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_FAILED);
 
         final CharSequence result = mInputFilter.filter(testString, 0, 1, null, 0, 1);
 
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextWatcherTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextWatcherTest.java
index 6fc7347..728947c 100644
--- a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextWatcherTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextWatcherTest.java
@@ -55,7 +55,7 @@
     @Test
     public void testOnTextChanged_callsProcess() {
         final Spannable testString = new SpannableString("abc");
-        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_SUCCESS);
+        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_SUCCEEDED);
 
         mTextWatcher.onTextChanged(testString, 0, 0, 1);
 
@@ -78,7 +78,7 @@
     @Test
     public void testOnTextChanged_whenEmojiCompatLoadFailed() {
         final Spannable testString = new SpannableString("abc");
-        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_FAILURE);
+        when(mEmojiCompat.getLoadState()).thenReturn(EmojiCompat.LOAD_STATE_FAILED);
 
         mTextWatcher.onTextChanged(testString, 0, 0, 1);
 
diff --git a/exifinterface/src/android/support/media/ExifInterface.java b/exifinterface/src/android/support/media/ExifInterface.java
index ec59c63..dac55bd 100644
--- a/exifinterface/src/android/support/media/ExifInterface.java
+++ b/exifinterface/src/android/support/media/ExifInterface.java
@@ -2603,9 +2603,9 @@
     }
 
     private void addDefaultValuesForCompatibility() {
-        // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag.
+        // If DATETIME tag has no value, then set the value to DATETIME_ORIGINAL tag's.
         String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL);
-        if (valueOfDateTimeOriginal != null) {
+        if (valueOfDateTimeOriginal != null && getAttribute(TAG_DATETIME) == null) {
             mAttributes[IFD_TYPE_PRIMARY].put(TAG_DATETIME,
                     ExifAttribute.createString(valueOfDateTimeOriginal));
         }
diff --git a/exifinterface/tests/src/android/support/media/ExifInterfaceTest.java b/exifinterface/tests/src/android/support/media/ExifInterfaceTest.java
index ee9e38e..4af8a4a 100644
--- a/exifinterface/tests/src/android/support/media/ExifInterfaceTest.java
+++ b/exifinterface/tests/src/android/support/media/ExifInterfaceTest.java
@@ -82,7 +82,7 @@
             ExifInterface.TAG_MAKE,
             ExifInterface.TAG_MODEL,
             ExifInterface.TAG_F_NUMBER,
-            ExifInterface.TAG_DATETIME,
+            ExifInterface.TAG_DATETIME_ORIGINAL,
             ExifInterface.TAG_EXPOSURE_TIME,
             ExifInterface.TAG_FLASH,
             ExifInterface.TAG_FOCAL_LENGTH,
@@ -118,7 +118,7 @@
         public final String make;
         public final String model;
         public final float aperture;
-        public final String datetime;
+        public final String dateTimeOriginal;
         public final float exposureTime;
         public final float flash;
         public final String focalLength;
@@ -161,7 +161,7 @@
             make = getString(typedArray, 7);
             model = getString(typedArray, 8);
             aperture = typedArray.getFloat(9, 0f);
-            datetime = getString(typedArray, 10);
+            dateTimeOriginal = getString(typedArray, 10);
             exposureTime = typedArray.getFloat(11, 0f);
             flash = typedArray.getFloat(12, 0f);
             focalLength = getString(typedArray, 13);
@@ -295,6 +295,33 @@
         }
     }
 
+    @Test
+    @SmallTest
+    public void testSetDateTime() throws IOException {
+        final String dateTimeValue = "2017:02:02 22:22:22";
+        final String dateTimeOriginalValue = "2017:01:01 11:11:11";
+
+        File imageFile = new File(
+                Environment.getExternalStorageDirectory(), EXIF_BYTE_ORDER_II_JPEG);
+        ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
+        exif.setAttribute(ExifInterface.TAG_DATETIME, dateTimeValue);
+        exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL, dateTimeOriginalValue);
+        exif.saveAttributes();
+
+        // Check that the DATETIME value is not overwritten by DATETIME_ORIGINAL's value.
+        exif = new ExifInterface(imageFile.getAbsolutePath());
+        assertEquals(dateTimeValue, exif.getAttribute(ExifInterface.TAG_DATETIME));
+        assertEquals(dateTimeOriginalValue, exif.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL));
+
+        // Now remove the DATETIME value.
+        exif.setAttribute(ExifInterface.TAG_DATETIME, null);
+        exif.saveAttributes();
+
+        // When the DATETIME has no value, then it should be set to DATETIME_ORIGINAL's value.
+        exif = new ExifInterface(imageFile.getAbsolutePath());
+        assertEquals(dateTimeOriginalValue, exif.getAttribute(ExifInterface.TAG_DATETIME));
+    }
+
     private void printExifTagsAndValues(String fileName, ExifInterface exifInterface) {
         // Prints thumbnail information.
         if (exifInterface.hasThumbnail()) {
@@ -390,7 +417,8 @@
         assertStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedValue.make);
         assertStringTag(exifInterface, ExifInterface.TAG_MODEL, expectedValue.model);
         assertFloatTag(exifInterface, ExifInterface.TAG_F_NUMBER, expectedValue.aperture);
-        assertStringTag(exifInterface, ExifInterface.TAG_DATETIME, expectedValue.datetime);
+        assertStringTag(exifInterface, ExifInterface.TAG_DATETIME_ORIGINAL,
+                expectedValue.dateTimeOriginal);
         assertFloatTag(exifInterface, ExifInterface.TAG_EXPOSURE_TIME, expectedValue.exposureTime);
         assertFloatTag(exifInterface, ExifInterface.TAG_FLASH, expectedValue.flash);
         assertStringTag(exifInterface, ExifInterface.TAG_FOCAL_LENGTH, expectedValue.focalLength);
diff --git a/fragment/java/android/support/v4/app/FragmentManager.java b/fragment/java/android/support/v4/app/FragmentManager.java
index c49b69b..460d474 100644
--- a/fragment/java/android/support/v4/app/FragmentManager.java
+++ b/fragment/java/android/support/v4/app/FragmentManager.java
@@ -662,7 +662,8 @@
     ArrayList<Integer> mAvailBackStackIndices;
 
     ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
-    private CopyOnWriteArrayList<Pair<FragmentLifecycleCallbacks, Boolean>> mLifecycleCallbacks;
+    private final CopyOnWriteArrayList<Pair<FragmentLifecycleCallbacks, Boolean>>
+            mLifecycleCallbacks = new CopyOnWriteArrayList<>();
 
     int mCurState = Fragment.INITIALIZING;
     FragmentHostCallback mHost;
@@ -3350,18 +3351,11 @@
     @Override
     public void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb,
             boolean recursive) {
-        if (mLifecycleCallbacks == null) {
-            mLifecycleCallbacks = new CopyOnWriteArrayList<>();
-        }
         mLifecycleCallbacks.add(new Pair<>(cb, recursive));
     }
 
     @Override
     public void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb) {
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
-
         synchronized (mLifecycleCallbacks) {
             for (int i = 0, N = mLifecycleCallbacks.size(); i < N; i++) {
                 if (mLifecycleCallbacks.get(i).first == cb) {
@@ -3380,9 +3374,6 @@
                         .dispatchOnFragmentPreAttached(f, context, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentPreAttached(this, f, context);
@@ -3398,9 +3389,6 @@
                         .dispatchOnFragmentAttached(f, context, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentAttached(this, f, context);
@@ -3417,9 +3405,6 @@
                         .dispatchOnFragmentPreCreated(f, savedInstanceState, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentPreCreated(this, f, savedInstanceState);
@@ -3435,9 +3420,6 @@
                         .dispatchOnFragmentCreated(f, savedInstanceState, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentCreated(this, f, savedInstanceState);
@@ -3454,9 +3436,6 @@
                         .dispatchOnFragmentActivityCreated(f, savedInstanceState, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentActivityCreated(this, f, savedInstanceState);
@@ -3473,9 +3452,6 @@
                         .dispatchOnFragmentViewCreated(f, v, savedInstanceState, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentViewCreated(this, f, v, savedInstanceState);
@@ -3491,9 +3467,6 @@
                         .dispatchOnFragmentStarted(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentStarted(this, f);
@@ -3509,9 +3482,6 @@
                         .dispatchOnFragmentResumed(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentResumed(this, f);
@@ -3527,9 +3497,6 @@
                         .dispatchOnFragmentPaused(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentPaused(this, f);
@@ -3545,9 +3512,6 @@
                         .dispatchOnFragmentStopped(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentStopped(this, f);
@@ -3563,9 +3527,6 @@
                         .dispatchOnFragmentSaveInstanceState(f, outState, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentSaveInstanceState(this, f, outState);
@@ -3581,9 +3542,6 @@
                         .dispatchOnFragmentViewDestroyed(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentViewDestroyed(this, f);
@@ -3599,9 +3557,6 @@
                         .dispatchOnFragmentDestroyed(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentDestroyed(this, f);
@@ -3617,9 +3572,6 @@
                         .dispatchOnFragmentDetached(f, true);
             }
         }
-        if (mLifecycleCallbacks == null) {
-            return;
-        }
         for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
             if (!onlyRecursive || p.second) {
                 p.first.onFragmentDetached(this, f);
diff --git a/media-compat/java/android/support/v4/media/MediaBrowserCompat.java b/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
index 98d6452..93ee3e0 100644
--- a/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
@@ -24,7 +24,8 @@
 import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_REMOVE_SUBSCRIPTION;
 import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_SEARCH;
 import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_SEND_CUSTOM_ACTION;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER;
+import static android.support.v4.media.MediaBrowserProtocol
+        .CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER;
 import static android.support.v4.media.MediaBrowserProtocol.CLIENT_VERSION_CURRENT;
 import static android.support.v4.media.MediaBrowserProtocol.DATA_CALLBACK_TOKEN;
 import static android.support.v4.media.MediaBrowserProtocol.DATA_CUSTOM_ACTION;
@@ -69,7 +70,6 @@
 import android.support.v4.media.session.IMediaSession;
 import android.support.v4.media.session.MediaControllerCompat.TransportControls;
 import android.support.v4.media.session.MediaSessionCompat;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.os.ResultReceiver;
 import android.support.v4.util.ArrayMap;
 import android.text.TextUtils;
@@ -181,7 +181,7 @@
             ConnectionCallback callback, Bundle rootHints) {
         // To workaround an issue of {@link #unsubscribe(String, SubscriptionCallback)} on API 24
         // and 25 devices, use the support library version of implementation on those devices.
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             mImpl = new MediaBrowserImplApi24(context, serviceComponent, callback, rootHints);
         } else if (Build.VERSION.SDK_INT >= 23) {
             mImpl = new MediaBrowserImplApi23(context, serviceComponent, callback, rootHints);
@@ -676,7 +676,7 @@
         WeakReference<Subscription> mSubscriptionRef;
 
         public SubscriptionCallback() {
-            if (BuildCompat.isAtLeastO()) {
+            if (Build.VERSION.SDK_INT >= 26) {
                 mSubscriptionCallbackObj =
                         MediaBrowserCompatApi24.createSubscriptionCallback(new StubApi24());
                 mToken = null;
diff --git a/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java b/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
index 12617b7..7c4c761 100644
--- a/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
@@ -25,7 +25,8 @@
 import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_REMOVE_SUBSCRIPTION;
 import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_SEARCH;
 import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_SEND_CUSTOM_ACTION;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER;
+import static android.support.v4.media.MediaBrowserProtocol
+        .CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER;
 import static android.support.v4.media.MediaBrowserProtocol.DATA_CALLBACK_TOKEN;
 import static android.support.v4.media.MediaBrowserProtocol.DATA_CALLING_UID;
 import static android.support.v4.media.MediaBrowserProtocol.DATA_CUSTOM_ACTION;
@@ -68,7 +69,6 @@
 import android.support.v4.app.BundleCompat;
 import android.support.v4.media.session.IMediaSession;
 import android.support.v4.media.session.MediaSessionCompat;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.os.ResultReceiver;
 import android.support.v4.util.ArrayMap;
 import android.support.v4.util.Pair;
@@ -977,7 +977,7 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             mImpl = new MediaBrowserServiceImplApi24();
         } else if (Build.VERSION.SDK_INT >= 23) {
             mImpl = new MediaBrowserServiceImplApi23();
diff --git a/media-compat/java/android/support/v4/media/session/MediaButtonReceiver.java b/media-compat/java/android/support/v4/media/session/MediaButtonReceiver.java
index afb72945..d411131 100644
--- a/media-compat/java/android/support/v4/media/session/MediaButtonReceiver.java
+++ b/media-compat/java/android/support/v4/media/session/MediaButtonReceiver.java
@@ -24,11 +24,11 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.Build;
 import android.os.RemoteException;
 import android.support.v4.media.MediaBrowserCompat;
 import android.support.v4.media.MediaBrowserServiceCompat;
 import android.support.v4.media.session.PlaybackStateCompat.MediaKeyAction;
-import android.support.v4.os.BuildCompat;
 import android.util.Log;
 import android.view.KeyEvent;
 
@@ -284,7 +284,7 @@
     }
 
     private static void startForegroundService(Context context, Intent intent) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             context.startForegroundService(intent);
         } else {
             context.startService(intent);
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/accessibility/AccessibilityManagerSupportActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/accessibility/AccessibilityManagerSupportActivity.java
index 4ffc0ef..061ed21 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/accessibility/AccessibilityManagerSupportActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/accessibility/AccessibilityManagerSupportActivity.java
@@ -22,7 +22,6 @@
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.support.v4.accessibilityservice.AccessibilityServiceInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityManagerCompat;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.TextView;
 import android.widget.Toast;
@@ -79,20 +78,16 @@
      * when the global accessibility state on the device changes.
      */
     private void registerAccessibilityStateChangeListener() {
-        // The AccessibilityStateChange listener APIs were added in ICS. Therefore to be
-        // backwards compatible we use the APIs in the support library. Note that if the
-        // platform API version is lower and the called API is not available no listener
-        // is added and you will not receive a call of onAccessibilityStateChanged.
-        AccessibilityManagerCompat.addAccessibilityStateChangeListener(mAccessibilityManager,
-                new AccessibilityManagerCompat.AccessibilityStateChangeListener() {
-            @Override
-            public void onAccessibilityStateChanged(boolean enabled) {
-                Toast.makeText(AccessibilityManagerSupportActivity.this,
-                        getString(R.string.accessibility_manager_accessibility_state,
-                                Boolean.toString(enabled)),
-                        Toast.LENGTH_SHORT).show();
-            }
-        });
+        mAccessibilityManager.addAccessibilityStateChangeListener(
+                new AccessibilityManager.AccessibilityStateChangeListener() {
+                    @Override
+                    public void onAccessibilityStateChanged(boolean enabled) {
+                        Toast.makeText(AccessibilityManagerSupportActivity.this,
+                                getString(R.string.accessibility_manager_accessibility_state,
+                                        Boolean.toString(enabled)),
+                                Toast.LENGTH_SHORT).show();
+                    }
+                });
     }
 
     /**
@@ -100,13 +95,9 @@
      * accessibility services.
      */
     private void updateAccessibilityStateView() {
-        // The API for getting the enabled accessibility services based on feedback
-        // type was added in ICS. Therefore to be backwards compatible we use the
-        // APIs in the support library. Note that if the platform API version is lower
-        // and the called API is not available an empty list of services is returned.
         List<AccessibilityServiceInfo> enabledServices =
-            AccessibilityManagerCompat.getEnabledAccessibilityServiceList(mAccessibilityManager,
-                    AccessibilityServiceInfo.FEEDBACK_SPOKEN);
+                mAccessibilityManager.getEnabledAccessibilityServiceList(
+                        AccessibilityServiceInfo.FEEDBACK_SPOKEN);
         if (!enabledServices.isEmpty()) {
             StringBuilder builder = new StringBuilder();
             final int enabledServiceCount = enabledServices.size();
diff --git a/samples/SupportTransitionDemos/res/layout/change_transform.xml b/samples/SupportTransitionDemos/res/layout/change_transform.xml
index 35909e3..c9559ab 100644
--- a/samples/SupportTransitionDemos/res/layout/change_transform.xml
+++ b/samples/SupportTransitionDemos/res/layout/change_transform.xml
@@ -32,6 +32,8 @@
         android:id="@+id/container_1"
         android:layout_width="match_parent"
         android:layout_height="0dp"
+        android:layout_marginEnd="128dp"
+        android:layout_marginRight="128dp"
         android:layout_weight="1"
         android:background="#BBDEFB"/>
 
@@ -39,6 +41,8 @@
         android:id="@+id/container_2"
         android:layout_width="match_parent"
         android:layout_height="0dp"
+        android:layout_marginLeft="128dp"
+        android:layout_marginStart="128dp"
         android:layout_weight="1"
         android:background="#FFCC80"/>
 
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeTransformUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeTransformUsage.java
index 67aa409..af1bafd 100644
--- a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeTransformUsage.java
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeTransformUsage.java
@@ -17,8 +17,10 @@
 package com.example.android.support.transition.widget;
 
 import android.os.Bundle;
+import android.support.transition.ArcMotion;
 import android.support.transition.ChangeTransform;
 import android.support.transition.TransitionManager;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -45,6 +47,8 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mChangeTransform = new ChangeTransform();
+        mChangeTransform.setInterpolator(new FastOutSlowInInterpolator());
+        mChangeTransform.setPathMotion(new ArcMotion());
         mRoot = findViewById(R.id.root);
         mContainer1 = findViewById(R.id.container_1);
         mContainer2 = findViewById(R.id.container_2);
diff --git a/samples/SupportWearDemos/AndroidManifest.xml b/samples/SupportWearDemos/AndroidManifest.xml
index df77ef7..eb10f0a 100644
--- a/samples/SupportWearDemos/AndroidManifest.xml
+++ b/samples/SupportWearDemos/AndroidManifest.xml
@@ -18,9 +18,12 @@
     package="com.example.android.support.wear" >
     <uses-feature android:name="android.hardware.type.watch" />
 
-    <application android:icon="@drawable/app_sample_code" android:label="SupportWearDemos" >
+    <application android:icon="@drawable/app_sample_code" android:label="SupportWearDemos"
+            android:theme="@android:style/Theme.DeviceDefault">
         <activity android:name="com.example.android.support.wear.app.SimpleWearableRecyclerViewDemo">
         </activity>
+        <activity android:name="com.example.android.support.wear.app.WearableSwitchDemo">
+        </activity>
         <activity android:name="com.example.android.support.wear.app.MainDemoActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/samples/SupportWearDemos/build.gradle b/samples/SupportWearDemos/build.gradle
index 2819768..0eac8a1 100644
--- a/samples/SupportWearDemos/build.gradle
+++ b/samples/SupportWearDemos/build.gradle
@@ -24,7 +24,7 @@
     compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
-        minSdkVersion 23
+        minSdkVersion 24
         targetSdkVersion project.ext.currentSdk
     }
 
diff --git a/samples/SupportWearDemos/res/layout/switch_demo.xml b/samples/SupportWearDemos/res/layout/switch_demo.xml
new file mode 100644
index 0000000..921568c
--- /dev/null
+++ b/samples/SupportWearDemos/res/layout/switch_demo.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" >
+    <Switch
+            style="@style/Widget.Wear.RoundSwitch"
+            android:layout_gravity="center"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"/>
+</merge>
diff --git a/samples/SupportWearDemos/src/com/example/android/support/wear/app/MainDemoActivity.java b/samples/SupportWearDemos/src/com/example/android/support/wear/app/MainDemoActivity.java
index 26dbef7..c9512a2 100644
--- a/samples/SupportWearDemos/src/com/example/android/support/wear/app/MainDemoActivity.java
+++ b/samples/SupportWearDemos/src/com/example/android/support/wear/app/MainDemoActivity.java
@@ -50,9 +50,10 @@
 
     private Map<String, Intent> createContentMap() {
         Map<String, Intent> contentMap = new HashMap<>();
-        Intent intent = new Intent();
-        intent.setClass(MainDemoActivity.this, SimpleWearableRecyclerViewDemo.class);
-        contentMap.put("Wearable Recycler View", intent);
+        contentMap.put("Wearable Recycler View", new Intent(
+                this, SimpleWearableRecyclerViewDemo.class));
+        contentMap.put("Wearable Switch", new Intent(
+                this, WearableSwitchDemo.class));
 
         return contentMap;
     }
diff --git a/samples/SupportWearDemos/src/com/example/android/support/wear/app/WearableSwitchDemo.java b/samples/SupportWearDemos/src/com/example/android/support/wear/app/WearableSwitchDemo.java
new file mode 100644
index 0000000..774021b
--- /dev/null
+++ b/samples/SupportWearDemos/src/com/example/android/support/wear/app/WearableSwitchDemo.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 com.example.android.support.wear.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.example.android.support.wear.R;
+
+/** Main activity for the Switch demo. */
+public class WearableSwitchDemo extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.switch_demo);
+    }
+}
diff --git a/transition/api14/android/support/transition/GhostViewApi14.java b/transition/api14/android/support/transition/GhostViewApi14.java
index 9c91c99..c918651 100644
--- a/transition/api14/android/support/transition/GhostViewApi14.java
+++ b/transition/api14/android/support/transition/GhostViewApi14.java
@@ -150,6 +150,8 @@
         final int[] viewLocation = new int[2];
         getLocationOnScreen(location);
         mView.getLocationOnScreen(viewLocation);
+        viewLocation[0] -= mView.getTranslationX();
+        viewLocation[1] -= mView.getTranslationY();
         mDeltaX = viewLocation[0] - location[0];
         mDeltaY = viewLocation[1] - location[1];
         // Monitor invalidation of the target view.
diff --git a/transition/api14/android/support/transition/ObjectAnimatorUtilsApi14.java b/transition/api14/android/support/transition/ObjectAnimatorUtilsApi14.java
index a51c4d6..a1038de 100644
--- a/transition/api14/android/support/transition/ObjectAnimatorUtilsApi14.java
+++ b/transition/api14/android/support/transition/ObjectAnimatorUtilsApi14.java
@@ -18,7 +18,6 @@
 
 import android.animation.ObjectAnimator;
 import android.graphics.Path;
-import android.graphics.PathMeasure;
 import android.graphics.PointF;
 import android.support.annotation.RequiresApi;
 import android.util.Property;
@@ -31,47 +30,4 @@
         return ObjectAnimator.ofFloat(target, new PathProperty<>(property, path), 0f, 1f);
     }
 
-    /**
-     * A special {@link Property} that can animate a pair of properties bi-dimensionally along the
-     * specified path.
-     * <p>
-     * This property should always be used with Animator that sets float fractions between
-     * {@code 0.f} and {@code 1.f}. For example, setting {@code 0.5f} to this property sets the
-     * values right in the middle of the specified path to the underlying properties.
-     * <p>
-     * Unlike many of the platform built-in properties, instances of this class cannot be reused
-     * for later animations.
-     */
-    private static class PathProperty<T> extends Property<T, Float> {
-
-        private final Property<T, PointF> mProperty;
-        private final PathMeasure mPathMeasure;
-        private final float mPathLength;
-        private final float[] mPosition = new float[2];
-        private final PointF mPointF = new PointF();
-        private float mCurrentFraction;
-
-        PathProperty(Property<T, PointF> property, Path path) {
-            super(Float.class, property.getName());
-            mProperty = property;
-            mPathMeasure = new PathMeasure(path, false);
-            mPathLength = mPathMeasure.getLength();
-        }
-
-        @Override
-        public Float get(T object) {
-            return mCurrentFraction;
-        }
-
-        @Override
-        public void set(T target, Float fraction) {
-            mCurrentFraction = fraction;
-            mPathMeasure.getPosTan(mPathLength * fraction, mPosition, null);
-            mPointF.x = mPosition[0];
-            mPointF.y = mPosition[1];
-            mProperty.set(target, mPointF);
-        }
-
-    }
-
 }
diff --git a/transition/api14/android/support/transition/PathProperty.java b/transition/api14/android/support/transition/PathProperty.java
new file mode 100644
index 0000000..c5e7429
--- /dev/null
+++ b/transition/api14/android/support/transition/PathProperty.java
@@ -0,0 +1,65 @@
+/*
+ * 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.transition;
+
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.graphics.PointF;
+import android.util.Property;
+
+/**
+ * A special {@link Property} that can animate a pair of properties bi-dimensionally along the
+ * specified path.
+ * <p>
+ * This property should always be used with Animator that sets float fractions between
+ * {@code 0.f} and {@code 1.f}. For example, setting {@code 0.5f} to this property sets the
+ * values right in the middle of the specified path to the underlying properties.
+ * <p>
+ * Unlike many of the platform built-in properties, instances of this class cannot be reused
+ * for later animations.
+ */
+class PathProperty<T> extends Property<T, Float> {
+
+    private final Property<T, PointF> mProperty;
+    private final PathMeasure mPathMeasure;
+    private final float mPathLength;
+    private final float[] mPosition = new float[2];
+    private final PointF mPointF = new PointF();
+    private float mCurrentFraction;
+
+    PathProperty(Property<T, PointF> property, Path path) {
+        super(Float.class, property.getName());
+        mProperty = property;
+        mPathMeasure = new PathMeasure(path, false);
+        mPathLength = mPathMeasure.getLength();
+    }
+
+    @Override
+    public Float get(T object) {
+        return mCurrentFraction;
+    }
+
+    @Override
+    public void set(T target, Float fraction) {
+        mCurrentFraction = fraction;
+        mPathMeasure.getPosTan(mPathLength * fraction, mPosition, null);
+        mPointF.x = mPosition[0];
+        mPointF.y = mPosition[1];
+        mProperty.set(target, mPointF);
+    }
+
+}
diff --git a/transition/api14/android/support/transition/PropertyValuesHolderUtilsApi14.java b/transition/api14/android/support/transition/PropertyValuesHolderUtilsApi14.java
index 7bcd36e..072178b 100644
--- a/transition/api14/android/support/transition/PropertyValuesHolderUtilsApi14.java
+++ b/transition/api14/android/support/transition/PropertyValuesHolderUtilsApi14.java
@@ -17,9 +17,7 @@
 package android.support.transition;
 
 import android.animation.PropertyValuesHolder;
-import android.animation.TypeEvaluator;
 import android.graphics.Path;
-import android.graphics.PathMeasure;
 import android.graphics.PointF;
 import android.support.annotation.RequiresApi;
 import android.util.Property;
@@ -29,28 +27,7 @@
 
     @Override
     public PropertyValuesHolder ofPointF(Property<?, PointF> property, Path path) {
-        return PropertyValuesHolder.ofObject(property, new PathEvaluator(path));
-    }
-
-    private static class PathEvaluator implements TypeEvaluator<PointF> {
-
-        private final PointF mPointF = new PointF();
-        private final PathMeasure mPathMeasure;
-        private final float mPathLength;
-        private final float[] mPosition = new float[2];
-
-        PathEvaluator(Path path) {
-            mPathMeasure = new PathMeasure(path, false);
-            mPathLength = mPathMeasure.getLength();
-        }
-
-        @Override
-        public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
-            mPathMeasure.getPosTan(mPathLength * fraction, mPosition, null);
-            mPointF.set(mPosition[0], mPosition[1]);
-            return mPointF;
-        }
-
+        return PropertyValuesHolder.ofFloat(new PathProperty<>(property, path), 0f, 1f);
     }
 
 }
diff --git a/transition/src/android/support/transition/ArcMotion.java b/transition/src/android/support/transition/ArcMotion.java
index a89ce4c..42f667c 100644
--- a/transition/src/android/support/transition/ArcMotion.java
+++ b/transition/src/android/support/transition/ArcMotion.java
@@ -37,6 +37,14 @@
  * {@link #setMinimumVerticalAngle(float)} may be used to set the minimum angle of the
  * arc between two points.
  * </p>
+ * <p>This may be used in XML as an element inside a transition.</p>
+ * <pre>{@code
+ * <changeBounds>
+ *   <arcMotion android:minimumHorizontalAngle="15"
+ *              android:minimumVerticalAngle="0"
+ *              android:maximumAngle="90"/>
+ * </changeBounds>}
+ * </pre>
  */
 public class ArcMotion extends PathMotion {
 
diff --git a/transition/src/android/support/transition/ChangeTransform.java b/transition/src/android/support/transition/ChangeTransform.java
index 539a89e..f0adc08 100644
--- a/transition/src/android/support/transition/ChangeTransform.java
+++ b/transition/src/android/support/transition/ChangeTransform.java
@@ -19,10 +19,12 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
+import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Matrix;
+import android.graphics.Path;
+import android.graphics.PointF;
 import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.v4.content.res.TypedArrayUtils;
@@ -59,16 +61,35 @@
             PROPNAME_PARENT_MATRIX,
     };
 
-    private static final Property<View, Matrix> ANIMATION_MATRIX_PROPERTY =
-            new Property<View, Matrix>(Matrix.class, "animationMatrix") {
+    /**
+     * This property sets the animation matrix properties that are not translations.
+     */
+    private static final Property<PathAnimatorMatrix, float[]> NON_TRANSLATIONS_PROPERTY =
+            new Property<PathAnimatorMatrix, float[]>(float[].class, "nonTranslations") {
                 @Override
-                public Matrix get(View view) {
+                public float[] get(PathAnimatorMatrix object) {
                     return null;
                 }
 
                 @Override
-                public void set(View view, Matrix matrix) {
-                    ViewUtils.setAnimationMatrix(view, matrix);
+                public void set(PathAnimatorMatrix object, float[] value) {
+                    object.setValues(value);
+                }
+            };
+
+    /**
+     * This property sets the translation animation matrix properties.
+     */
+    private static final Property<PathAnimatorMatrix, PointF> TRANSLATIONS_PROPERTY =
+            new Property<PathAnimatorMatrix, PointF>(PointF.class, "translations") {
+                @Override
+                public PointF get(PathAnimatorMatrix object) {
+                    return null;
+                }
+
+                @Override
+                public void set(PathAnimatorMatrix object, PointF value) {
+                    object.setTranslation(value);
                 }
             };
 
@@ -278,8 +299,23 @@
         final View view = endValues.view;
         setIdentityTransforms(view);
 
-        ObjectAnimator animator = ObjectAnimator.ofObject(view, ANIMATION_MATRIX_PROPERTY,
-                new TransitionUtils.MatrixEvaluator(), startMatrix, endMatrix);
+        final float[] startMatrixValues = new float[9];
+        startMatrix.getValues(startMatrixValues);
+        final float[] endMatrixValues = new float[9];
+        endMatrix.getValues(endMatrixValues);
+        final PathAnimatorMatrix pathAnimatorMatrix =
+                new PathAnimatorMatrix(view, startMatrixValues);
+
+        PropertyValuesHolder valuesProperty = PropertyValuesHolder.ofObject(
+                NON_TRANSLATIONS_PROPERTY, new FloatArrayEvaluator(new float[9]),
+                startMatrixValues, endMatrixValues);
+        Path path = getPathMotion().getPath(startMatrixValues[Matrix.MTRANS_X],
+                startMatrixValues[Matrix.MTRANS_Y], endMatrixValues[Matrix.MTRANS_X],
+                endMatrixValues[Matrix.MTRANS_Y]);
+        PropertyValuesHolder translationProperty = PropertyValuesHolderUtils.ofPointF(
+                TRANSLATIONS_PROPERTY, path);
+        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(pathAnimatorMatrix,
+                valuesProperty, translationProperty);
 
         final Matrix finalEndMatrix = endMatrix;
 
@@ -302,14 +338,13 @@
                         view.setTag(R.id.parent_matrix, null);
                     }
                 }
-                ANIMATION_MATRIX_PROPERTY.set(view, null);
+                ViewUtils.setAnimationMatrix(view, null);
                 transforms.restore(view);
             }
 
             @Override
             public void onAnimationPause(Animator animation) {
-                ValueAnimator animator = (ValueAnimator) animation;
-                Matrix currentMatrix = (Matrix) animator.getAnimatedValue();
+                Matrix currentMatrix = pathAnimatorMatrix.getMatrix();
                 setCurrentMatrix(currentMatrix);
             }
 
@@ -471,7 +506,7 @@
 
     }
 
-    private static class GhostListener extends Transition.TransitionListenerAdapter {
+    private static class GhostListener extends TransitionListenerAdapter {
 
         private View mView;
         private GhostViewImpl mGhostView;
@@ -501,4 +536,48 @@
 
     }
 
+    /**
+     * PathAnimatorMatrix allows the translations and the rest of the matrix to be set
+     * separately. This allows the PathMotion to affect the translations while scale
+     * and rotation are evaluated separately.
+     */
+    private static class PathAnimatorMatrix {
+
+        private final Matrix mMatrix = new Matrix();
+        private final View mView;
+        private final float[] mValues;
+        private float mTranslationX;
+        private float mTranslationY;
+
+        PathAnimatorMatrix(View view, float[] values) {
+            mView = view;
+            mValues = values.clone();
+            mTranslationX = mValues[Matrix.MTRANS_X];
+            mTranslationY = mValues[Matrix.MTRANS_Y];
+            setAnimationMatrix();
+        }
+
+        void setValues(float[] values) {
+            System.arraycopy(values, 0, mValues, 0, values.length);
+            setAnimationMatrix();
+        }
+
+        void setTranslation(PointF translation) {
+            mTranslationX = translation.x;
+            mTranslationY = translation.y;
+            setAnimationMatrix();
+        }
+
+        private void setAnimationMatrix() {
+            mValues[Matrix.MTRANS_X] = mTranslationX;
+            mValues[Matrix.MTRANS_Y] = mTranslationY;
+            mMatrix.setValues(mValues);
+            ViewUtils.setAnimationMatrix(mView, mMatrix);
+        }
+
+        Matrix getMatrix() {
+            return mMatrix;
+        }
+    }
+
 }
diff --git a/transition/src/android/support/transition/FloatArrayEvaluator.java b/transition/src/android/support/transition/FloatArrayEvaluator.java
new file mode 100644
index 0000000..81b97b7
--- /dev/null
+++ b/transition/src/android/support/transition/FloatArrayEvaluator.java
@@ -0,0 +1,70 @@
+/*
+ * 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.transition;
+
+import android.animation.TypeEvaluator;
+
+/**
+ * This evaluator can be used to perform type interpolation between <code>float[]</code> values.
+ * Each index into the array is treated as a separate value to interpolate. For example,
+ * evaluating <code>{100, 200}</code> and <code>{300, 400}</code> will interpolate the value at
+ * the first index between 100 and 300 and the value at the second index value between 200 and 400.
+ */
+class FloatArrayEvaluator implements TypeEvaluator<float[]> {
+
+    private float[] mArray;
+
+    /**
+     * Create a FloatArrayEvaluator that reuses <code>reuseArray</code> for every evaluate() call.
+     * Caution must be taken to ensure that the value returned from
+     * {@link android.animation.ValueAnimator#getAnimatedValue()} is not cached, modified, or
+     * used across threads. The value will be modified on each <code>evaluate()</code> call.
+     *
+     * @param reuseArray The array to modify and return from <code>evaluate</code>.
+     */
+    FloatArrayEvaluator(float[] reuseArray) {
+        mArray = reuseArray;
+    }
+
+    /**
+     * Interpolates the value at each index by the fraction. If
+     * {@link #FloatArrayEvaluator(float[])} was used to construct this object,
+     * <code>reuseArray</code> will be returned, otherwise a new <code>float[]</code>
+     * will be returned.
+     *
+     * @param fraction   The fraction from the starting to the ending values
+     * @param startValue The start value.
+     * @param endValue   The end value.
+     * @return A <code>float[]</code> where each element is an interpolation between
+     * the same index in startValue and endValue.
+     */
+    @Override
+    public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
+        float[] array = mArray;
+        if (array == null) {
+            array = new float[startValue.length];
+        }
+
+        for (int i = 0; i < array.length; i++) {
+            float start = startValue[i];
+            float end = endValue[i];
+            array[i] = start + (fraction * (end - start));
+        }
+        return array;
+    }
+
+}
diff --git a/transition/src/android/support/transition/PathMotion.java b/transition/src/android/support/transition/PathMotion.java
index 513b61e..d270a08 100644
--- a/transition/src/android/support/transition/PathMotion.java
+++ b/transition/src/android/support/transition/PathMotion.java
@@ -29,6 +29,14 @@
  * have these motions move in a curve can change how Views interpolate in two dimensions
  * by extending PathMotion and implementing {@link #getPath(float, float, float, float)}.
  * </p>
+ * <p>This may be used in XML as an element inside a transition.</p>
+ * <pre>
+ * {@code
+ * <changeBounds>
+ *     <pathMotion class="my.app.transition.MyPathMotion"/>
+ * </changeBounds>
+ * }
+ * </pre>
  */
 public abstract class PathMotion {
 
diff --git a/transition/src/android/support/transition/PatternPathMotion.java b/transition/src/android/support/transition/PatternPathMotion.java
index a8c6078..2ac6e72 100644
--- a/transition/src/android/support/transition/PatternPathMotion.java
+++ b/transition/src/android/support/transition/PatternPathMotion.java
@@ -31,6 +31,12 @@
  * A PathMotion that takes a Path pattern and applies it to the separation between two points.
  * The starting point of the Path will be moved to the origin and the end point will be scaled
  * and rotated so that it matches with the target end point.
+ * <p>This may be used in XML as an element inside a transition.</p>
+ * <pre>{@code
+ * <changeBounds>
+ *     <patternPathMotion android:patternPathData="M0 0 L0 100 L100 100"/>
+ * </changeBounds>}
+ * </pre>
  */
 public class PatternPathMotion extends PathMotion {
 
diff --git a/transition/src/android/support/transition/Transition.java b/transition/src/android/support/transition/Transition.java
index a2b38bc..04cc57b 100644
--- a/transition/src/android/support/transition/Transition.java
+++ b/transition/src/android/support/transition/Transition.java
@@ -2087,7 +2087,7 @@
      * @see PatternPathMotion
      * @see android.transition.PathMotion
      */
-    @Nullable
+    @NonNull
     public PathMotion getPathMotion() {
         return mPathMotion;
     }
@@ -2335,36 +2335,6 @@
     }
 
     /**
-     * Utility adapter class to avoid having to override all three methods
-     * whenever someone just wants to listen for a single event.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class TransitionListenerAdapter implements TransitionListener {
-
-        @Override
-        public void onTransitionStart(@NonNull Transition transition) {
-        }
-
-        @Override
-        public void onTransitionEnd(@NonNull Transition transition) {
-        }
-
-        @Override
-        public void onTransitionCancel(@NonNull Transition transition) {
-        }
-
-        @Override
-        public void onTransitionPause(@NonNull Transition transition) {
-        }
-
-        @Override
-        public void onTransitionResume(@NonNull Transition transition) {
-        }
-    }
-
-    /**
      * Holds information about each animator used when a new transition starts
      * while other transitions are still running to determine whether a running
      * animation should be canceled or a new animation noop'd. The structure holds
diff --git a/transition/src/android/support/transition/TransitionListenerAdapter.java b/transition/src/android/support/transition/TransitionListenerAdapter.java
new file mode 100644
index 0000000..333fbfb
--- /dev/null
+++ b/transition/src/android/support/transition/TransitionListenerAdapter.java
@@ -0,0 +1,49 @@
+/*
+ * 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.transition;
+
+import android.support.annotation.NonNull;
+
+/**
+ * This adapter class provides empty implementations of the methods from {@link
+ * Transition.TransitionListener}.
+ * Any custom listener that cares only about a subset of the methods of this listener can
+ * simply subclass this adapter class instead of implementing the interface directly.
+ */
+public class TransitionListenerAdapter implements Transition.TransitionListener {
+
+    @Override
+    public void onTransitionStart(@NonNull Transition transition) {
+    }
+
+    @Override
+    public void onTransitionEnd(@NonNull Transition transition) {
+    }
+
+    @Override
+    public void onTransitionCancel(@NonNull Transition transition) {
+    }
+
+    @Override
+    public void onTransitionPause(@NonNull Transition transition) {
+    }
+
+    @Override
+    public void onTransitionResume(@NonNull Transition transition) {
+    }
+
+}
diff --git a/transition/src/android/support/transition/TransitionManager.java b/transition/src/android/support/transition/TransitionManager.java
index f65a464..d5f46ab 100644
--- a/transition/src/android/support/transition/TransitionManager.java
+++ b/transition/src/android/support/transition/TransitionManager.java
@@ -273,7 +273,7 @@
                 previousRunningTransitions = new ArrayList<>(currentTransitions);
             }
             currentTransitions.add(mTransition);
-            mTransition.addListener(new Transition.TransitionListenerAdapter() {
+            mTransition.addListener(new TransitionListenerAdapter() {
                 @Override
                 public void onTransitionEnd(@NonNull Transition transition) {
                     ArrayList<Transition> currentTransitions = runningTransitions.get(mSceneRoot);
diff --git a/transition/src/android/support/transition/ViewUtils.java b/transition/src/android/support/transition/ViewUtils.java
index bf671d0..66c3076 100644
--- a/transition/src/android/support/transition/ViewUtils.java
+++ b/transition/src/android/support/transition/ViewUtils.java
@@ -20,6 +20,7 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.v4.view.ViewCompat;
 import android.util.Log;
 import android.util.Property;
@@ -188,7 +189,7 @@
      * @param v The view
      * @param m The matrix
      */
-    static void setAnimationMatrix(@NonNull View v, @NonNull Matrix m) {
+    static void setAnimationMatrix(@NonNull View v, @Nullable Matrix m) {
         IMPL.setAnimationMatrix(v, m);
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
index 65d2c44..262a5a6 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
@@ -37,7 +37,6 @@
 import android.support.v17.leanback.widget.BackgroundHelper;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.view.animation.FastOutLinearInInterpolator;
 import android.util.Log;
 import android.view.View;
@@ -674,7 +673,7 @@
         // Activity transition below O has ghost effect for null window background where we
         // need set a transparent background to force redraw the whole window.
         mContext.getWindow().getDecorView().setBackground(
-                BuildCompat.isAtLeastO() ? null : new ColorDrawable(Color.TRANSPARENT));
+                Build.VERSION.SDK_INT >= 26 ? null : new ColorDrawable(Color.TRANSPARENT));
     }
 
     void attachToViewInternal(View sceneRoot) {
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 35238cf..ff2d999 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -1882,7 +1882,9 @@
             View view = getChildAt(index);
             position = getAdapterPositionByView(view);
             Grid.Location location = mGrid.getLocation(position);
-            if (location == null) {
+            // position could be NO_POSITION for the views off the screen that were cached. For
+            // these views markKnownViewsInvalid invalidates them.
+            if (location == null || (position == NO_POSITION)) {
                 if (DEBUG) Log.w(getTag(), "fastRelayout(): no Location at " + position);
                 invalidateAfter = true;
                 break;
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
index 5bc5af4..a6f005d 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
@@ -778,6 +778,39 @@
         verifyEdgesSame(endEdges, endEdges2);
     }
 
+
+    @Test
+    public void testLayoutWhenAViewIsInvalidated() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 10);
+        mNumRows = 1;
+        initActivity(intent);
+        mOrientation = BaseGridView.VERTICAL;
+        waitOneUiCycle();
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                RecyclerView.ViewHolder vh = mGridView.findViewHolderForAdapterPosition(3);
+                // The following is nowhere ideal since we can't override GridLayoutManager to
+                // obtain a reference to Recycler. Ideally, this test should call
+                // addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID) on the ViewHolder to
+                // simulate markKnownViewsInvalid on the cached views in RecyclerView.
+                mLayoutManager.stopIgnoringView(vh.itemView);
+            }
+        });
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.requestLayout();
+            }
+        });
+        waitForScrollIdle();
+    }
+
+
     void preparePredictiveLayout() throws Throwable {
         Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
index 78136e2..c8d7b0a 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
@@ -31,7 +31,6 @@
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.NavUtils;
 import android.support.v4.app.TaskStackBuilder;
-import android.support.v4.os.BuildCompat;
 import android.support.v7.view.ActionMode;
 import android.support.v7.widget.Toolbar;
 import android.support.v7.widget.VectorEnabledTintResources;
@@ -549,7 +548,7 @@
      * and perform the corresponding action.
      */
     private boolean performMenuItemShortcut(int keycode, KeyEvent event) {
-        if (!BuildCompat.isAtLeastO() && !event.isCtrlPressed()
+        if (!(Build.VERSION.SDK_INT >= 26) && !event.isCtrlPressed()
                 && !KeyEvent.metaStateHasNoModifiers(event.getMetaState())
                 && event.getRepeatCount() == 0
                 && !KeyEvent.isModifierKey(event.getKeyCode())) {
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
index e79a906..2a4022c 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
@@ -26,11 +26,11 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.VisibleForTesting;
-import android.support.v4.os.BuildCompat;
 import android.support.v7.view.SupportActionModeWrapper;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -217,7 +217,7 @@
                 res.updateConfiguration(config, metrics);
 
                 // We may need to flush the Resources' drawable cache due to framework bugs.
-                if (!BuildCompat.isAtLeastO()) {
+                if (!(Build.VERSION.SDK_INT >= 26)) {
                     ResourcesFlusher.flush(res);
                 }
             }
diff --git a/v7/appcompat/src/android/support/v7/view/menu/CascadingMenuPopup.java b/v7/appcompat/src/android/support/v7/view/menu/CascadingMenuPopup.java
index fda1a97..73499cf 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/CascadingMenuPopup.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/CascadingMenuPopup.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -28,7 +29,6 @@
 import android.support.annotation.Nullable;
 import android.support.annotation.StyleRes;
 import android.support.v4.internal.view.SupportMenu;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.appcompat.R;
@@ -406,7 +406,7 @@
 
             final int parentOffsetLeft;
             final int parentOffsetTop;
-            if (BuildCompat.isAtLeastO()) {
+            if (Build.VERSION.SDK_INT >= 26) {
                 // Anchor the submenu directly to the parent menu item view. This allows for
                 // accurate submenu positioning when the parent menu is being moved.
                 popupWindow.setAnchorView(parentView);
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
index c5f6e17..afd59bd 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
@@ -27,7 +27,6 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.widget.TextViewCompat;
 import android.support.v7.appcompat.R;
 import android.text.method.PasswordTransformationMethod;
@@ -171,7 +170,7 @@
 
         mAutoSizeTextHelper.loadFromAttributes(attrs, defStyleAttr);
 
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             // Delegate auto-size functionality to the framework implementation.
             if (mAutoSizeTextHelper.getAutoSizeTextType()
                     != TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) {
@@ -200,6 +199,7 @@
 
         if (a.hasValue(R.styleable.TextAppearance_android_fontFamily)
                 || a.hasValue(R.styleable.TextAppearance_fontFamily)) {
+            mFontTypeface = null;
             int fontFamilyId = a.hasValue(R.styleable.TextAppearance_android_fontFamily)
                     ? R.styleable.TextAppearance_android_fontFamily
                     : R.styleable.TextAppearance_fontFamily;
@@ -283,45 +283,33 @@
     @RestrictTo(LIBRARY_GROUP)
     void onLayout(boolean changed, int left, int top, int right, int bottom) {
         // Auto-size is supported by the framework starting from Android O.
-        if (!BuildCompat.isAtLeastO()) {
-            if (isAutoSizeEnabled()) {
-                if (getNeedsAutoSizeText()) {
-                    // Call auto-size after the width and height have been calculated.
-                    autoSizeText();
-                }
-                // Always try to auto-size if enabled. Functions that do not want to trigger
-                // auto-sizing after the next layout round should set this to false.
-                setNeedsAutoSizeText(true);
-            }
+        if (!(Build.VERSION.SDK_INT >= 26)) {
+            autoSizeText();
         }
     }
 
     /** @hide */
     @RestrictTo(LIBRARY_GROUP)
     void setTextSize(int unit, float size) {
-        if (!BuildCompat.isAtLeastO()) {
+        if (!(Build.VERSION.SDK_INT >= 26)) {
             if (!isAutoSizeEnabled()) {
                 setTextSizeInternal(unit, size);
             }
         }
     }
 
-    private boolean isAutoSizeEnabled() {
-        return mAutoSizeTextHelper.isAutoSizeEnabled();
-    }
-
-    private boolean getNeedsAutoSizeText() {
-        return mAutoSizeTextHelper.getNeedsAutoSizeText();
-    }
-
-    private void setNeedsAutoSizeText(boolean needsAutoSizeText) {
-        mAutoSizeTextHelper.setNeedsAutoSizeText(needsAutoSizeText);
-    }
-
-    private void autoSizeText() {
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    void autoSizeText() {
         mAutoSizeTextHelper.autoSizeText();
     }
 
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    boolean isAutoSizeEnabled() {
+        return mAutoSizeTextHelper.isAutoSizeEnabled();
+    }
+
     private void setTextSizeInternal(int unit, float size) {
         mAutoSizeTextHelper.setTextSizeInternal(unit, size);
     }
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java
index 992ddc6..39f8c1f 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java
@@ -22,11 +22,11 @@
 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.RestrictTo;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.view.TintableBackgroundView;
 import android.support.v4.widget.AutoSizeableTextView;
 import android.support.v4.widget.TextViewCompat;
@@ -183,7 +183,7 @@
 
     @Override
     public void setTextSize(int unit, float size) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             super.setTextSize(unit, size);
         } else {
             if (mTextHelper != null) {
@@ -192,6 +192,14 @@
         }
     }
 
+    @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();
+        }
+    }
+
     /**
      * This should be accessed via
      * {@link android.support.v4.widget.TextViewCompat#setAutoSizeTextTypeWithDefaults(
@@ -203,7 +211,7 @@
     @Override
     public void setAutoSizeTextTypeWithDefaults(
             @TextViewCompat.AutoSizeTextType int autoSizeTextType) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             super.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
         } else {
             if (mTextHelper != null) {
@@ -226,7 +234,7 @@
             int autoSizeMaxTextSize,
             int autoSizeStepGranularity,
             int unit) throws IllegalArgumentException {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             super.setAutoSizeTextTypeUniformWithConfiguration(
                     autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
         } else {
@@ -248,7 +256,7 @@
     @Override
     public void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit)
             throws IllegalArgumentException {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             super.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
         } else {
             if (mTextHelper != null) {
@@ -267,7 +275,7 @@
     @Override
     @TextViewCompat.AutoSizeTextType
     public int getAutoSizeTextType() {
-        if (BuildCompat.isAtLeastO()) {
+        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;
@@ -288,7 +296,7 @@
     @RestrictTo(LIBRARY_GROUP)
     @Override
     public int getAutoSizeStepGranularity() {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return super.getAutoSizeStepGranularity();
         } else {
             if (mTextHelper != null) {
@@ -307,7 +315,7 @@
     @RestrictTo(LIBRARY_GROUP)
     @Override
     public int getAutoSizeMinTextSize() {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return super.getAutoSizeMinTextSize();
         } else {
             if (mTextHelper != null) {
@@ -326,7 +334,7 @@
     @RestrictTo(LIBRARY_GROUP)
     @Override
     public int getAutoSizeMaxTextSize() {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return super.getAutoSizeMaxTextSize();
         } else {
             if (mTextHelper != null) {
@@ -345,7 +353,7 @@
     @RestrictTo(LIBRARY_GROUP)
     @Override
     public int[] getAutoSizeTextAvailableSizes() {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return super.getAutoSizeTextAvailableSizes();
         } else {
             if (mTextHelper != null) {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java
index 068366a..a648aaf 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java
@@ -26,7 +26,6 @@
 import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
-import android.support.v4.os.BuildCompat;
 import android.support.v4.widget.TextViewCompat;
 import android.support.v7.appcompat.R;
 import android.text.Layout;
@@ -210,7 +209,9 @@
                             autoSizeMinTextSizeInPx,
                             autoSizeMaxTextSizeInPx,
                             DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX);
-                    setupAutoSizeText();
+                    if (setupAutoSizeText()) {
+                        autoSizeText();
+                    }
                     break;
                 default:
                     throw new IllegalArgumentException(
@@ -266,7 +267,9 @@
             validateAndSetAutoSizeTextTypeUniformConfiguration(autoSizeMinTextSizeInPx,
                     autoSizeMaxTextSizeInPx,
                     autoSizeStepGranularityInPx);
-            setupAutoSizeText();
+            if (setupAutoSizeText()) {
+                autoSizeText();
+            }
         }
     }
 
@@ -320,7 +323,10 @@
             } else {
                 mHasPresetAutoSizeValues = false;
             }
-            setupAutoSizeText();
+
+            if (setupAutoSizeText()) {
+                autoSizeText();
+            }
         }
     }
 
@@ -493,20 +499,19 @@
         mHasPresetAutoSizeValues = false;
     }
 
-    private void setupAutoSizeText() {
+    private boolean setupAutoSizeText() {
         if (supportsAutoSizeText()
                 && mAutoSizeTextType == TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM) {
             // Calculate the sizes set based on minimum size, maximum size and step size if we do
             // not have a predefined set of sizes or if the current sizes array is empty.
             if (!mHasPresetAutoSizeValues || mAutoSizeTextSizesInPx.length == 0) {
                 // Calculate sizes to choose from based on the current auto-size configuration.
-                int autoSizeValuesLength = (int) Math.ceil(
-                        (mAutoSizeMaxTextSizeInPx - mAutoSizeMinTextSizeInPx)
-                                / mAutoSizeStepGranularityInPx);
-                // Also reserve a slot for the max size if it fits.
-                if ((mAutoSizeMaxTextSizeInPx - mAutoSizeMinTextSizeInPx)
-                        % mAutoSizeStepGranularityInPx == 0) {
+                int autoSizeValuesLength = 1;
+                float currentSize = Math.round(mAutoSizeMinTextSizeInPx);
+                while (Math.round(currentSize + mAutoSizeStepGranularityInPx)
+                        <= Math.round(mAutoSizeMaxTextSizeInPx)) {
                     autoSizeValuesLength++;
+                    currentSize += mAutoSizeStepGranularityInPx;
                 }
                 int[] autoSizeTextSizesInPx = new int[autoSizeValuesLength];
                 float sizeToAdd = mAutoSizeMinTextSizeInPx;
@@ -518,17 +523,11 @@
             }
 
             mNeedsAutoSizeText = true;
-
-            // If the build version is at least 26 there is no need to auto-size using this
-            // helper because the job has been delegated to the actual TextView but the
-            // configuration still needs to be done for the case where this function is called
-            // from {@link #loadFromAttributes}, in which case the auto-size configuration
-            // attributes set up in this function will be read by {@link AppCompatTextHelper}
-            // and after passed on to the actual TextView which will take care of auto-sizing.
-            if (!BuildCompat.isAtLeastO()) {
-                autoSizeText();
-            }
+        } else {
+            mNeedsAutoSizeText = false;
         }
+
+        return mNeedsAutoSizeText;
     }
 
     /**
@@ -538,31 +537,44 @@
      */
     @RestrictTo(LIBRARY_GROUP)
     void autoSizeText() {
-        if (mTextView.getMeasuredHeight() <= 0 || mTextView.getMeasuredWidth() <= 0) {
+        if (!isAutoSizeEnabled()) {
             return;
         }
 
-        final int maxWidth = mTextView.getWidth() - mTextView.getTotalPaddingLeft()
-                - mTextView.getTotalPaddingRight();
-        final int maxHeight = Build.VERSION.SDK_INT >= 21
-                ? mTextView.getHeight() - mTextView.getExtendedPaddingBottom()
-                        - mTextView.getExtendedPaddingBottom()
-                : mTextView.getHeight() - mTextView.getCompoundPaddingBottom()
-                        - mTextView.getCompoundPaddingTop();
+        if (mNeedsAutoSizeText) {
+            if (mTextView.getMeasuredHeight() <= 0 || mTextView.getMeasuredWidth() <= 0) {
+                return;
+            }
 
-        if (maxWidth <= 0 || maxHeight <= 0) {
-            return;
-        }
+            final boolean horizontallyScrolling = invokeAndReturnWithDefault(
+                    mTextView, "getHorizontallyScrolling", false);
+            final int availableWidth = horizontallyScrolling
+                    ? VERY_WIDE
+                    : mTextView.getMeasuredWidth() - mTextView.getTotalPaddingLeft()
+                            - mTextView.getTotalPaddingRight();
+            final int availableHeight = Build.VERSION.SDK_INT >= 21
+                    ? mTextView.getHeight() - mTextView.getExtendedPaddingBottom()
+                    - mTextView.getExtendedPaddingBottom()
+                    : mTextView.getHeight() - mTextView.getCompoundPaddingBottom()
+                            - mTextView.getCompoundPaddingTop();
 
-        synchronized (TEMP_RECTF) {
-            TEMP_RECTF.setEmpty();
-            TEMP_RECTF.right = maxWidth;
-            TEMP_RECTF.bottom = maxHeight;
-            final float optimalTextSize = findLargestTextSizeWhichFits(TEMP_RECTF);
-            if (optimalTextSize != mTextView.getTextSize()) {
-                setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, optimalTextSize);
+            if (availableWidth <= 0 || availableHeight <= 0) {
+                return;
+            }
+
+            synchronized (TEMP_RECTF) {
+                TEMP_RECTF.setEmpty();
+                TEMP_RECTF.right = availableWidth;
+                TEMP_RECTF.bottom = availableHeight;
+                final float optimalTextSize = findLargestTextSizeWhichFits(TEMP_RECTF);
+                if (optimalTextSize != mTextView.getTextSize()) {
+                    setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, optimalTextSize);
+                }
             }
         }
+        // Always try to auto-size if enabled. Functions that do not want to trigger auto-sizing
+        // after the next layout pass should set this to false.
+        mNeedsAutoSizeText = true;
     }
 
     private void clearAutoSizeConfiguration() {
@@ -588,6 +600,11 @@
         if (size != mTextView.getPaint().getTextSize()) {
             mTextView.getPaint().setTextSize(size);
 
+            boolean isInLayout = false;
+            if (Build.VERSION.SDK_INT >= 18) {
+                isInLayout = mTextView.isInLayout();
+            }
+
             if (mTextView.getLayout() != null) {
                 // Do not auto-size right after setting the text size.
                 mNeedsAutoSizeText = false;
@@ -611,7 +628,12 @@
                     // Nothing to do.
                 }
 
-                mTextView.requestLayout();
+                if (!isInLayout) {
+                    mTextView.requestLayout();
+                } else {
+                    mTextView.forceLayout();
+                }
+
                 mTextView.invalidate();
             }
         }
@@ -648,12 +670,6 @@
     private boolean suggestedSizeFitsInSpace(int suggestedSizeInPx, RectF availableSpace) {
         final CharSequence text = mTextView.getText();
         final int maxLines = Build.VERSION.SDK_INT >= 16 ? mTextView.getMaxLines() : -1;
-        final boolean horizontallyScrolling = invokeAndReturnWithDefault(
-                mTextView, "getHorizontallyScrolling", false);
-        final int availableWidth = horizontallyScrolling
-                ? VERY_WIDE
-                : mTextView.getMeasuredWidth() - mTextView.getTotalPaddingLeft()
-                        - mTextView.getTotalPaddingRight();
         if (mTempTextPaint == null) {
             mTempTextPaint = new TextPaint();
         } else {
@@ -666,8 +682,10 @@
         Layout.Alignment alignment = invokeAndReturnWithDefault(
                 mTextView, "getLayoutAlignment", Layout.Alignment.ALIGN_NORMAL);
         final StaticLayout layout = Build.VERSION.SDK_INT >= 23
-                ? createStaticLayoutForMeasuring(text, alignment, availableWidth, maxLines)
-                : createStaticLayoutForMeasuringPre23(text, alignment, availableWidth);
+                ? createStaticLayoutForMeasuring(
+                        text, alignment, Math.round(availableSpace.right), maxLines)
+                : createStaticLayoutForMeasuringPre23(
+                        text, alignment, Math.round(availableSpace.right));
 
         // Lines overflow.
         if (maxLines != -1 && layout.getLineCount() > maxLines) {
@@ -779,18 +797,6 @@
                 && mAutoSizeTextType != TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
     }
 
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    boolean getNeedsAutoSizeText() {
-        return mNeedsAutoSizeText;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    void setNeedsAutoSizeText(boolean needsAutoSizeText) {
-        mNeedsAutoSizeText = needsAutoSizeText;
-    }
-
     /**
      * @return {@code true} if this TextView supports auto-sizing text to fit within its container.
      */
diff --git a/v7/appcompat/src/android/support/v7/widget/LinearLayoutCompat.java b/v7/appcompat/src/android/support/v7/widget/LinearLayoutCompat.java
index 6abdb75..bb400f1 100644
--- a/v7/appcompat/src/android/support/v7/widget/LinearLayoutCompat.java
+++ b/v7/appcompat/src/android/support/v7/widget/LinearLayoutCompat.java
@@ -1054,8 +1054,7 @@
 
             final int margin = lp.topMargin + lp.bottomMargin;
             final int childHeight = child.getMeasuredHeight() + margin;
-            childState = ViewUtils.combineMeasuredStates(childState,
-                    child.getMeasuredState());
+            childState = View.combineMeasuredStates(childState, child.getMeasuredState());
 
             if (baselineAligned) {
                 final int childBaseline = child.getBaseline();
diff --git a/v7/appcompat/src/android/support/v7/widget/SuggestionsAdapter.java b/v7/appcompat/src/android/support/v7/widget/SuggestionsAdapter.java
index 24dafb3..d6f0099 100644
--- a/v7/appcompat/src/android/support/v7/widget/SuggestionsAdapter.java
+++ b/v7/appcompat/src/android/support/v7/widget/SuggestionsAdapter.java
@@ -503,7 +503,7 @@
      * @return a Drawable, or null if none found
      */
     private Drawable getDrawableFromResourceValue(String drawableId) {
-        if (drawableId == null || drawableId.length() == 0 || "0".equals(drawableId)) {
+        if (drawableId == null || drawableId.isEmpty() || "0".equals(drawableId)) {
             return null;
         }
         try {
diff --git a/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java b/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
index b920505..0a86600 100644
--- a/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
+++ b/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
@@ -31,7 +31,6 @@
 import android.support.annotation.RestrictTo;
 import android.support.annotation.StyleableRes;
 import android.support.v4.content.res.ResourcesCompat;
-import android.support.v4.os.BuildCompat;
 import android.support.v7.content.res.AppCompatResources;
 import android.util.AttributeSet;
 import android.util.TypedValue;
@@ -110,7 +109,7 @@
      */
     @Nullable
     public Typeface getFont(@StyleableRes int index, int style, @NonNull TextView targetView) {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             return mWrapped.getFont(index);
         }
         final int resourceId = mWrapped.getResourceId(index, 0);
diff --git a/v7/appcompat/src/android/support/v7/widget/TooltipCompat.java b/v7/appcompat/src/android/support/v7/widget/TooltipCompat.java
index f7466bc..470c3b2 100644
--- a/v7/appcompat/src/android/support/v7/widget/TooltipCompat.java
+++ b/v7/appcompat/src/android/support/v7/widget/TooltipCompat.java
@@ -17,9 +17,9 @@
 package android.support.v7.widget;
 
 import android.annotation.TargetApi;
+import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.v4.os.BuildCompat;
 import android.view.View;
 
 /**
@@ -49,7 +49,7 @@
 
     private static final ViewCompatImpl IMPL;
     static {
-        if (BuildCompat.isAtLeastO()) {
+        if (Build.VERSION.SDK_INT >= 26) {
             IMPL = new Api26ViewCompatImpl();
         } else {
             IMPL = new BaseViewCompatImpl();
diff --git a/v7/appcompat/src/android/support/v7/widget/TooltipCompatHandler.java b/v7/appcompat/src/android/support/v7/widget/TooltipCompatHandler.java
index 4670534..b7f0c2c 100644
--- a/v7/appcompat/src/android/support/v7/widget/TooltipCompatHandler.java
+++ b/v7/appcompat/src/android/support/v7/widget/TooltipCompatHandler.java
@@ -22,7 +22,6 @@
 import android.content.Context;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityManagerCompat;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -116,8 +115,7 @@
         }
         AccessibilityManager manager = (AccessibilityManager)
                 mAnchor.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (manager.isEnabled()
-                && AccessibilityManagerCompat.isTouchExplorationEnabled(manager)) {
+        if (manager.isEnabled() && manager.isTouchExplorationEnabled()) {
             return false;
         }
         switch (event.getAction()) {
diff --git a/v7/appcompat/src/android/support/v7/widget/ViewUtils.java b/v7/appcompat/src/android/support/v7/widget/ViewUtils.java
index 58acb03..0f910c3 100644
--- a/v7/appcompat/src/android/support/v7/widget/ViewUtils.java
+++ b/v7/appcompat/src/android/support/v7/widget/ViewUtils.java
@@ -58,21 +58,6 @@
     }
 
     /**
-     * Merge two states as returned by {@link View#getMeasuredState()} ()}.
-     * @param curState The current state as returned from a view or the result
-     * of combining multiple views.
-     * @param newState The new view state to combine.
-     * @return Returns a new integer reflecting the combination of the two
-     * states.
-     *
-     * @deprecated Use {@link View#combineMeasuredStates(int, int)} directly.
-     */
-    @Deprecated
-    public static int combineMeasuredStates(int curState, int newState) {
-        return View.combineMeasuredStates(curState, newState);
-    }
-
-    /**
      * Allow calling the hidden method {@code computeFitSystemWindows(Rect, Rect)} through
      * reflection on {@code view}.
      */
diff --git a/v7/appcompat/tests/res/values/strings.xml b/v7/appcompat/tests/res/values/strings.xml
index 8a1746b..86386e2 100644
--- a/v7/appcompat/tests/res/values/strings.xml
+++ b/v7/appcompat/tests/res/values/strings.xml
@@ -95,4 +95,5 @@
     <string name="menu6">menu six</string>
 
     <string name="font_sans_serif">sans-serif</string>
+    <string name="font_serif">serif</string>
 </resources>
diff --git a/v7/appcompat/tests/res/values/styles.xml b/v7/appcompat/tests/res/values/styles.xml
index 81f8493..c08930a 100644
--- a/v7/appcompat/tests/res/values/styles.xml
+++ b/v7/appcompat/tests/res/values/styles.xml
@@ -70,4 +70,12 @@
         <item name="fontFamily">@font/samplexmlfont</item>
         <item name="android:textStyle">italic</item>
     </style>
+
+    <style name="TextView_SansSerif">
+        <item name="android:fontFamily">@string/font_sans_serif</item>
+    </style>
+
+    <style name="TextView_Serif">
+        <item name="android:fontFamily">@string/font_serif</item>
+    </style>
 </resources>
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java
index 8afd231..690cc26 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java
@@ -45,6 +45,8 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.support.test.espresso.DataInteraction;
 import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.TestUtilsMatchers;
 import android.view.View;
@@ -56,13 +58,17 @@
 import org.hamcrest.Matcher;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.io.File;
 
 @MediumTest
-public class AlertDialogCursorTest
-        extends BaseInstrumentationTestCase<AlertDialogTestActivity> {
+@RunWith(AndroidJUnit4.class)
+public class AlertDialogCursorTest {
+    @Rule
+    public final ActivityTestRule<AlertDialogTestActivity> mActivityTestRule;
 
     private Button mButton;
 
@@ -82,7 +88,7 @@
     private AlertDialog mAlertDialog;
 
     public AlertDialogCursorTest() {
-        super(AlertDialogTestActivity.class);
+        mActivityTestRule = new ActivityTestRule<>(AlertDialogTestActivity.class);
     }
 
     @Before
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogTest.java b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogTest.java
index 1d4b244..53cc4b5 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogTest.java
@@ -60,6 +60,7 @@
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.TestUtilsMatchers;
 import android.text.TextUtils;
@@ -76,6 +77,7 @@
 import org.hamcrest.Matcher;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
@@ -95,13 +97,16 @@
  *     is rendered by a single <code>CheckedTextView</code>.</li>
  * </ul>
  */
-public class AlertDialogTest extends BaseInstrumentationTestCase<AlertDialogTestActivity> {
+public class AlertDialogTest {
+    @Rule
+    public final ActivityTestRule<AlertDialogTestActivity> mActivityTestRule;
+
     private Button mButton;
 
     private AlertDialog mAlertDialog;
 
     public AlertDialogTest() {
-        super(AlertDialogTestActivity.class);
+        mActivityTestRule = new ActivityTestRule<>(AlertDialogTestActivity.class);
     }
 
     @Before
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java b/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java
index 905521d..84816ae 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java
@@ -24,17 +24,23 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 import android.widget.ImageView;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @SmallTest
-public class AppCompatVectorDrawableIntegrationTest
-        extends BaseInstrumentationTestCase<AppCompatVectorDrawableIntegrationActivity> {
+@RunWith(AndroidJUnit4.class)
+public class AppCompatVectorDrawableIntegrationTest {
+    @Rule
+    public final ActivityTestRule<AppCompatVectorDrawableIntegrationActivity> mActivityTestRule;
 
     private Bitmap mBitmap;
     private Canvas mCanvas;
@@ -46,7 +52,8 @@
     private static final int CENTER_Y = HEIGHT / 2;
 
     public AppCompatVectorDrawableIntegrationTest() {
-        super(AppCompatVectorDrawableIntegrationActivity.class);
+        mActivityTestRule =
+                new ActivityTestRule<>(AppCompatVectorDrawableIntegrationActivity.class);
     }
 
     @Before
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
index 6112d34..c634097 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
@@ -42,6 +42,8 @@
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.custom.FitWindowsContentLayout;
 import android.support.v7.testutils.BaseTestActivity;
@@ -50,24 +52,31 @@
 import android.view.View;
 import android.view.WindowInsets;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @SmallTest
-public abstract class BaseBasicsTestCase<A extends BaseTestActivity>
-        extends BaseInstrumentationTestCase<A> {
+@RunWith(AndroidJUnit4.class)
+public abstract class BaseBasicsTestCase<A extends BaseTestActivity> {
+    @Rule
+    public final ActivityTestRule<A> mActivityTestRule;
+
 
     protected BaseBasicsTestCase(Class<A> activityClass) {
-        super(activityClass);
+        mActivityTestRule = new ActivityTestRule<>(activityClass);
     }
 
     @Test
     public void testActionBarExists() {
-        assertNotNull("ActionBar is not null", getActivity().getSupportActionBar());
+        assertNotNull("ActionBar is not null",
+                mActivityTestRule.getActivity().getSupportActionBar());
     }
 
     @Test
     public void testDefaultActionBarTitle() {
-        assertEquals(getActivity().getTitle(), getActivity().getSupportActionBar().getTitle());
+        assertEquals(mActivityTestRule.getActivity().getTitle(),
+                mActivityTestRule.getActivity().getSupportActionBar().getTitle());
     }
 
     @UiThreadTest
@@ -84,10 +93,10 @@
     @RequiresApi(16)
     public void testFitSystemWindowsReachesContent() {
         final FitWindowsContentLayout content =
-                (FitWindowsContentLayout) getActivity().findViewById(R.id.test_content);
+                mActivityTestRule.getActivity().findViewById(R.id.test_content);
         assertNotNull(content);
 
-        if (!canShowSystemUi(getActivity())) {
+        if (!canShowSystemUi(mActivityTestRule.getActivity())) {
             // Device cannot show system UI so setSystemUiVisibility will do nothing.
             return;
         }
@@ -103,10 +112,10 @@
     @SdkSuppress(minSdkVersion = 21)
     @RequiresApi(21)
     public void testOnApplyWindowInsetsReachesContent() {
-        final View content = getActivity().findViewById(R.id.test_content);
+        final View content = mActivityTestRule.getActivity().findViewById(R.id.test_content);
         assertNotNull(content);
 
-        if (!canShowSystemUi(getActivity())) {
+        if (!canShowSystemUi(mActivityTestRule.getActivity())) {
             // Device cannot show system UI so setSystemUiVisibility will do nothing.
             return;
         }
@@ -128,7 +137,7 @@
     @Test
     @UiThreadTest
     public void testSupportActionModeCallbacks() {
-        final A activity = getActivity();
+        final A activity = mActivityTestRule.getActivity();
 
         // Create a mock action mode callback which returns true from onCreateActionMode
         final ActionMode.Callback callback = mock(ActionMode.Callback.class);
@@ -152,7 +161,7 @@
     @Test
     @UiThreadTest
     public void testSupportActionModeCallbacksInvalidate() {
-        final A activity = getActivity();
+        final A activity = mActivityTestRule.getActivity();
 
         // Create a mock action mode callback which returns true from onCreateActionMode
         final ActionMode.Callback callback = mock(ActionMode.Callback.class);
@@ -176,7 +185,7 @@
     @Test
     @UiThreadTest
     public void testSupportActionModeCallbacksWithFalseOnCreate() {
-        final A activity = getActivity();
+        final A activity = mActivityTestRule.getActivity();
 
         // Create a mock action mode callback which returns true from onCreateActionMode
         final ActionMode.Callback callback = mock(ActionMode.Callback.class);
@@ -206,7 +215,7 @@
     }
 
     protected void testSupportActionModeAppCompatCallbacks(final boolean fromWindow) {
-        final A activity = getActivity();
+        final A activity = mActivityTestRule.getActivity();
 
         // Create a mock action mode callback which returns true from onCreateActionMode
         final ActionMode.Callback amCallback = mock(ActionMode.Callback.class);
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseInstrumentationTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseInstrumentationTestCase.java
deleted file mode 100644
index c98df27..0000000
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseInstrumentationTestCase.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v7.app;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public abstract class BaseInstrumentationTestCase<A extends Activity> {
-
-    @Rule
-    public final ActivityTestRule<A> mActivityTestRule;
-
-    protected BaseInstrumentationTestCase(Class<A> activityClass) {
-        mActivityTestRule = new ActivityTestRule<A>(activityClass);
-    }
-
-    @Deprecated
-    public A getActivity() {
-        return mActivityTestRule.getActivity();
-    }
-
-    @Deprecated
-    public Instrumentation getInstrumentation() {
-        return InstrumentationRegistry.getInstrumentation();
-    }
-}
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
index 5e10e97..135f65d 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
@@ -30,8 +30,12 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.view.MenuItemCompat;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
@@ -41,15 +45,29 @@
 import android.view.MenuItem;
 
 import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
-public abstract class BaseKeyEventsTestCase<A extends BaseTestActivity>
-        extends BaseInstrumentationTestCase<A> {
+@RunWith(AndroidJUnit4.class)
+public abstract class BaseKeyEventsTestCase<A extends BaseTestActivity> {
+    @Rule
+    public final ActivityTestRule<A> mActivityTestRule;
+
+    private Instrumentation mInstrumentation;
+    private A mActivity;
 
     protected BaseKeyEventsTestCase(Class<A> activityClass) {
-        super(activityClass);
+        mActivityTestRule = new ActivityTestRule<>(activityClass);
+    }
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityTestRule.getActivity();
     }
 
     @Test
@@ -57,10 +75,10 @@
     public void testBackDismissesActionMode() {
         final AtomicBoolean destroyed = new AtomicBoolean();
 
-        getActivity().runOnUiThread(new Runnable() {
+        mActivity.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                getActivity().startSupportActionMode(new ActionMode.Callback() {
+                mActivity.startSupportActionMode(new ActionMode.Callback() {
                     @Override
                     public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                         mode.getMenuInflater().inflate(R.menu.sample_actions, menu);
@@ -85,11 +103,11 @@
             }
         });
 
-        getInstrumentation().waitForIdleSync();
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+        mInstrumentation.waitForIdleSync();
 
-        assertFalse("Activity was not finished", getActivity().isFinishing());
+        assertFalse("Activity was not finished", mActivity.isFinishing());
         assertTrue("ActionMode was destroyed", destroyed.get());
     }
 
@@ -103,14 +121,14 @@
                 .check(matches(isDisplayed()));
 
         // Let things settle
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         // Now send a back event to collapse the custom action view
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+        mInstrumentation.waitForIdleSync();
 
         // Check that the Activity is still running
-        assertFalse(getActivity().isFinishing());
-        assertFalse(getActivity().isDestroyed());
+        assertFalse(mActivity.isFinishing());
+        assertFalse(mActivity.isDestroyed());
         // ... and that our action view is not attached
         onView(withClassName(Matchers.is(CustomCollapsibleView.class.getName())))
                 .check(doesNotExist());
@@ -119,24 +137,24 @@
     @Test
     @SmallTest
     public void testMenuPressInvokesPanelCallbacks() throws InterruptedException {
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-        getInstrumentation().waitForIdleSync();
-        assertTrue("onMenuOpened called", getActivity().wasOnMenuOpenedCalled());
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        mInstrumentation.waitForIdleSync();
+        assertTrue("onMenuOpened called", mActivity.wasOnMenuOpenedCalled());
 
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-        getInstrumentation().waitForIdleSync();
-        assertTrue("onPanelClosed called", getActivity().wasOnPanelClosedCalled());
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        mInstrumentation.waitForIdleSync();
+        assertTrue("onPanelClosed called", mActivity.wasOnPanelClosedCalled());
     }
 
     @Test
     @SmallTest
     public void testBackPressWithMenuInvokesOnPanelClosed() throws InterruptedException {
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        mInstrumentation.waitForIdleSync();
 
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-        getInstrumentation().waitForIdleSync();
-        assertTrue("onPanelClosed called", getActivity().wasOnPanelClosedCalled());
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+        mInstrumentation.waitForIdleSync();
+        assertTrue("onPanelClosed called", mActivity.wasOnPanelClosedCalled());
     }
 
     @Test
@@ -144,25 +162,25 @@
     public void testBackPressWithEmptyMenuFinishesActivity() throws InterruptedException {
         repopulateWithEmptyMenu();
 
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        mInstrumentation.waitForIdleSync();
 
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-        assertTrue(getActivity().isFinishing());
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+        assertTrue(mActivity.isFinishing());
     }
 
     @Test
     @SmallTest
     public void testDelKeyEventReachesActivity() {
         // First send the event
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DEL);
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DEL);
+        mInstrumentation.waitForIdleSync();
 
-        KeyEvent downEvent = getActivity().getInvokedKeyDownEvent();
+        KeyEvent downEvent = mActivity.getInvokedKeyDownEvent();
         assertNotNull("onKeyDown called", downEvent);
         assertEquals("onKeyDown event matches", KeyEvent.KEYCODE_DEL, downEvent.getKeyCode());
 
-        KeyEvent upEvent = getActivity().getInvokedKeyUpEvent();
+        KeyEvent upEvent = mActivity.getInvokedKeyUpEvent();
         assertNotNull("onKeyUp called", upEvent);
         assertEquals("onKeyUp event matches", KeyEvent.KEYCODE_DEL, upEvent.getKeyCode());
     }
@@ -170,14 +188,14 @@
     @Test
     @SmallTest
     public void testMenuKeyEventReachesActivity() throws InterruptedException {
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        mInstrumentation.waitForIdleSync();
 
-        KeyEvent downEvent = getActivity().getInvokedKeyDownEvent();
+        KeyEvent downEvent = mActivity.getInvokedKeyDownEvent();
         assertNotNull("onKeyDown called", downEvent);
         assertEquals("onKeyDown event matches", KeyEvent.KEYCODE_MENU, downEvent.getKeyCode());
 
-        KeyEvent upEvent = getActivity().getInvokedKeyUpEvent();
+        KeyEvent upEvent = mActivity.getInvokedKeyUpEvent();
         assertNotNull("onKeyUp called", upEvent);
         assertEquals("onKeyDown event matches", KeyEvent.KEYCODE_MENU, upEvent.getKeyCode());
     }
@@ -193,7 +211,7 @@
                 .check(matches(isDisplayed()))
                 .check(matches(withContentDescription((String) null)));
 
-        Menu menu = getActivity().getMenu();
+        Menu menu = mActivity.getMenu();
         final MenuItem alphaItem = menu.findItem(R.id.action_alpha_shortcut);
         assertNotNull(alphaItem);
 
@@ -201,9 +219,9 @@
             @Override
             public void run() {
                 MenuItemCompat.setContentDescription(alphaItem,
-                        getActivity().getString(R.string.alpha_menu_description));
+                        mActivity.getString(R.string.alpha_menu_description));
                 MenuItemCompat.setTooltipText(alphaItem,
-                        getActivity().getString(R.string.alpha_menu_tooltip));
+                        mActivity.getString(R.string.alpha_menu_tooltip));
             }
         });
 
@@ -214,9 +232,9 @@
 
     private void repopulateWithEmptyMenu() throws InterruptedException {
         int count = 0;
-        getActivity().setShouldPopulateOptionsMenu(false);
+        mActivity.setShouldPopulateOptionsMenu(false);
         while (count++ < 10) {
-            Menu menu = getActivity().getMenu();
+            Menu menu = mActivity.getMenu();
             if (menu == null || menu.size() != 0) {
                 Thread.sleep(100);
             } else {
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java
index cb21347..88216d1 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java
@@ -19,21 +19,29 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
+import android.app.Instrumentation;
 import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MenuItem;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public abstract class BaseKeyboardShortcutsTestCase<A extends BaseTestActivity>
-        extends BaseInstrumentationTestCase<A> {
+@RunWith(AndroidJUnit4.class)
+public abstract class BaseKeyboardShortcutsTestCase<A extends BaseTestActivity> {
+    @Rule
+    public final ActivityTestRule<A> mActivityTestRule;
 
     protected BaseKeyboardShortcutsTestCase(Class<A> activityClass) {
-        super(activityClass);
+        mActivityTestRule = new ActivityTestRule<>(activityClass);
     }
 
     @Test
@@ -45,18 +53,19 @@
     }
 
     private void testKeyboardShortcut(final int keyCode, final int meta, final int expectedId) {
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
         final long downTime = SystemClock.uptimeMillis();
         final KeyEvent downEvent = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
                 keyCode, 0, meta, KeyCharacterMap.VIRTUAL_KEYBOARD, 0);
-        getInstrumentation().sendKeySync(downEvent);
-        getInstrumentation().waitForIdleSync();
+        instrumentation.sendKeySync(downEvent);
+        instrumentation.waitForIdleSync();
 
         final KeyEvent upEvent = new KeyEvent(downTime, downTime + 500, KeyEvent.ACTION_UP,
                 keyCode, 0, meta, KeyCharacterMap.VIRTUAL_KEYBOARD, 0);
-        getInstrumentation().sendKeySync(upEvent);
-        getInstrumentation().waitForIdleSync();
+        instrumentation.sendKeySync(upEvent);
+        instrumentation.waitForIdleSync();
 
-        MenuItem selectedItem = getActivity().getOptionsItemSelected();
+        MenuItem selectedItem = mActivityTestRule.getActivity().getOptionsItemSelected();
         assertNotNull("Options item selected", selectedItem);
         assertEquals("Correct options item selected", selectedItem.getItemId(), expectedId);
     }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DialogTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/DialogTestCase.java
index 8230cbc..ce9c157 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/DialogTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DialogTestCase.java
@@ -21,25 +21,30 @@
 
 import android.app.Dialog;
 import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @MediumTest
-public class DialogTestCase extends BaseInstrumentationTestCase<WindowDecorAppCompatActivity> {
-
-    public DialogTestCase() {
-        super(WindowDecorAppCompatActivity.class);
-    }
+@RunWith(AndroidJUnit4.class)
+public class DialogTestCase {
+    @Rule
+    public final ActivityTestRule<WindowDecorAppCompatActivity> mActivityTestRule =
+            new ActivityTestRule<>(WindowDecorAppCompatActivity.class);
 
     @Test
     public void testDialogFragmentShows() {
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         TestDialogFragment fragment = new TestDialogFragment();
-        fragment.show(getActivity().getSupportFragmentManager(), null);
+        fragment.show(mActivityTestRule.getActivity().getSupportFragmentManager(), null);
 
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         assertNotNull("Dialog was null", fragment.getDialog());
         assertTrue("Dialog was not being shown", fragment.getDialog().isShowing());
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutTest.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutTest.java
index 198c63a..54597c0 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutTest.java
@@ -28,6 +28,8 @@
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.appcompat.test.R;
 import android.view.View;
@@ -37,18 +39,20 @@
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.After;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Test cases to verify that <code>DrawerLayout</code> only supports configurations
  * with at most one drawer child along each vertical (left / right) edge.
  */
 @SmallTest
-public class DrawerDynamicLayoutTest
-        extends BaseInstrumentationTestCase<DrawerDynamicLayoutActivity> {
-    public DrawerDynamicLayoutTest() {
-        super(DrawerDynamicLayoutActivity.class);
-    }
+@RunWith(AndroidJUnit4.class)
+public class DrawerDynamicLayoutTest {
+    @Rule
+    public final ActivityTestRule<DrawerDynamicLayoutActivity> mActivityTestRule =
+            new ActivityTestRule<>(DrawerDynamicLayoutActivity.class);
 
     @UiThreadTest
     @After
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleTest.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleTest.java
index 2f43430..8578798 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleTest.java
@@ -28,6 +28,8 @@
 
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.appcompat.test.R;
@@ -35,10 +37,16 @@
 import android.view.View;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class DrawerLayoutDoubleTest
-        extends BaseInstrumentationTestCase<DrawerLayoutDoubleActivity> {
+@RunWith(AndroidJUnit4.class)
+public class DrawerLayoutDoubleTest {
+    @Rule
+    public final ActivityTestRule<DrawerLayoutDoubleActivity> mActivityTestRule =
+            new ActivityTestRule<>(DrawerLayoutDoubleActivity.class);
+
     private CustomDrawerLayout mDrawerLayout;
 
     private View mStartDrawer;
@@ -47,10 +55,6 @@
 
     private View mContentView;
 
-    public DrawerLayoutDoubleTest() {
-        super(DrawerLayoutDoubleActivity.class);
-    }
-
     @Before
     public void setUp() {
         final DrawerLayoutDoubleActivity activity = mActivityTestRule.getActivity();
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
index 34c5d64..f43c909 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
@@ -49,6 +49,8 @@
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.Suppress;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.appcompat.test.R;
@@ -56,21 +58,24 @@
 import android.view.View;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 
-public class DrawerLayoutTest extends BaseInstrumentationTestCase<DrawerLayoutActivity> {
+@RunWith(AndroidJUnit4.class)
+public class DrawerLayoutTest {
+    @Rule
+    public final ActivityTestRule<DrawerLayoutActivity> mActivityTestRule =
+            new ActivityTestRule<DrawerLayoutActivity>(DrawerLayoutActivity.class);
+
     private CustomDrawerLayout mDrawerLayout;
 
     private View mStartDrawer;
 
     private View mContentView;
 
-    public DrawerLayoutTest() {
-        super(DrawerLayoutActivity.class);
-    }
-
     @Before
     public void setUp() {
         final DrawerLayoutActivity activity = mActivityTestRule.getActivity();
diff --git a/v7/appcompat/tests/src/android/support/v7/app/FragmentContentIdTest.java b/v7/appcompat/tests/src/android/support/v7/app/FragmentContentIdTest.java
index c2e2d16..c316e29 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/FragmentContentIdTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/FragmentContentIdTest.java
@@ -23,15 +23,19 @@
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class FragmentContentIdTest extends BaseInstrumentationTestCase<FragmentContentIdActivity> {
-
-    public FragmentContentIdTest() {
-        super(FragmentContentIdActivity.class);
-    }
+@RunWith(AndroidJUnit4.class)
+public class FragmentContentIdTest {
+    @Rule
+    public final ActivityTestRule<FragmentContentIdActivity> mActivityTestRule =
+            new ActivityTestRule<>(FragmentContentIdActivity.class);
 
     @SmallTest
     @Test
@@ -48,5 +52,4 @@
         // And that fragment_b is displayed
         onView(withId(R.id.fragment_b)).check(matches(isDisplayed()));
     }
-
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java b/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java
index 80cfcaa..e7d485b 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java
@@ -16,14 +16,15 @@
 
 package android.support.v7.app;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.v7.widget.Toolbar;
 import android.view.KeyEvent;
 import android.view.Window;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import org.junit.Test;
 
 public class KeyEventsTestCaseWithToolbar extends BaseKeyEventsTestCase<ToolbarAppCompatActivity> {
@@ -46,12 +47,12 @@
         Toolbar toolbar = mActivityTestRule.getActivity().getToolbar();
         assertFalse(toolbar.isOverflowMenuShowing());
 
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
         assertTrue(toolbar.isOverflowMenuShowing());
 
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
         assertFalse(toolbar.isOverflowMenuShowing());
     }
 
@@ -70,7 +71,7 @@
                 mActivityTestRule.getActivity().openOptionsMenu();
             }
         });
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
         assertTrue(toolbar.isOverflowMenuShowing());
 
         mActivityTestRule.runOnUiThread(new Runnable() {
@@ -79,7 +80,7 @@
                 mActivityTestRule.getActivity().closeOptionsMenu();
             }
         });
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
         assertFalse(toolbar.isOverflowMenuShowing());
     }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java b/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java
index fc2b2ae..4f450ff 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java
@@ -22,7 +22,10 @@
 
 import android.os.Build;
 import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.testutils.BaseTestActivity;
 import android.support.v7.widget.Toolbar;
 import android.view.KeyEvent;
@@ -30,13 +33,15 @@
 import android.view.View;
 import android.view.Window;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class KeyboardShortcutsTestCaseWithToolbar
-        extends BaseKeyboardShortcutsTestCase<ToolbarAppCompatActivity> {
-    public KeyboardShortcutsTestCaseWithToolbar() {
-        super(ToolbarAppCompatActivity.class);
-    }
+@RunWith(AndroidJUnit4.class)
+public class KeyboardShortcutsTestCaseWithToolbar {
+    @Rule
+    public final ActivityTestRule<ToolbarAppCompatActivity> mActivityTestRule =
+            new ActivityTestRule<>(ToolbarAppCompatActivity.class);
 
     @Test
     @SmallTest
@@ -45,7 +50,7 @@
         if (Build.VERSION.SDK_INT <= 25) {
             return;
         }
-        final BaseTestActivity activity = getActivity();
+        final BaseTestActivity activity = mActivityTestRule.getActivity();
 
         final View editText = activity.findViewById(android.support.v7.appcompat.test.R.id.editText);
         mActivityTestRule.runOnUiThread(new Runnable() {
@@ -55,9 +60,9 @@
             }
         });
 
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
         sendMetaKey(KeyEvent.KEYCODE_TAB);
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -71,7 +76,7 @@
         // navigation won't leaves it.
         sendMetaKey(KeyEvent.KEYCODE_TAB);
 
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         // Should jump to the first view again.
         mActivityTestRule.runOnUiThread(new Runnable() {
@@ -85,10 +90,10 @@
     @Test
     @SmallTest
     public void testKeyShortcuts() throws Throwable {
-        final ToolbarAppCompatActivity activity = getActivity();
+        final ToolbarAppCompatActivity activity = mActivityTestRule.getActivity();
 
         final Toolbar toolbar =
-                (Toolbar) activity.findViewById(android.support.v7.appcompat.test.R.id.toolbar);
+                activity.findViewById(android.support.v7.appcompat.test.R.id.toolbar);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -135,7 +140,7 @@
         activity.resetCounters();
 
         // Make sure that unhandled shortcuts don't prepare menus (since toolbar is handling that).
-        getInstrumentation().sendKeySync(unhandledShortcutKey);
+        InstrumentationRegistry.getInstrumentation().sendKeySync(unhandledShortcutKey);
         assertEquals(1, activity.mKeyShortcutCount);
         assertEquals(0, activity.mPrepareMenuCount);
         assertEquals(0, activity.mCreateMenuCount);
@@ -145,10 +150,10 @@
         long time = SystemClock.uptimeMillis();
         KeyEvent keyDown = new KeyEvent(time, time, KeyEvent.ACTION_DOWN, keyCode,
                 0, KeyEvent.META_META_ON);
-        getInstrumentation().sendKeySync(keyDown);
+        InstrumentationRegistry.getInstrumentation().sendKeySync(keyDown);
         time = SystemClock.uptimeMillis();
         KeyEvent keyUp = new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode,
                 0, KeyEvent.META_META_ON);
-        getInstrumentation().sendKeySync(keyUp);
+        InstrumentationRegistry.getInstrumentation().sendKeySync(keyUp);
     }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
index 64c63a0..892a554 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
@@ -20,12 +20,14 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
-import android.support.annotation.RequiresApi;
 import android.content.Context;
 import android.content.res.Resources;
+import android.support.annotation.RequiresApi;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.custom.ContextWrapperFrameLayout;
 import android.support.v7.widget.AppCompatAutoCompleteTextView;
@@ -45,14 +47,15 @@
 import android.widget.LinearLayout;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class LayoutInflaterFactoryTestCase
-        extends BaseInstrumentationTestCase<LayoutInflaterFactoryTestActivity> {
-
-    public LayoutInflaterFactoryTestCase() {
-        super(LayoutInflaterFactoryTestActivity.class);
-    }
+@RunWith(AndroidJUnit4.class)
+public class LayoutInflaterFactoryTestCase {
+    @Rule
+    public final ActivityTestRule<LayoutInflaterFactoryTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(LayoutInflaterFactoryTestActivity.class);
 
     @Before
     public void setup() {
@@ -64,7 +67,7 @@
     @Test
     @SmallTest
     public void testAndroidThemeInflation() {
-        final LayoutInflater inflater = LayoutInflater.from(getActivity());
+        final LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
         assertThemedContext(inflater.inflate(R.layout.layout_android_theme, null));
     }
 
@@ -72,7 +75,7 @@
     @Test
     @SmallTest
     public void testAppThemeInflation() {
-        final LayoutInflater inflater = LayoutInflater.from(getActivity());
+        final LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
         assertThemedContext(inflater.inflate(R.layout.layout_app_theme, null));
     }
 
@@ -83,7 +86,7 @@
     @Test
     @SmallTest
     public void testAndroidThemeWithChildrenInflation() {
-        LayoutInflater inflater = LayoutInflater.from(getActivity());
+        LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
         final ViewGroup root = (ViewGroup) inflater.inflate(
                 R.layout.layout_android_theme_children, null);
         assertThemedContext(root);
@@ -93,7 +96,7 @@
     @Test
     @SmallTest
     public void testThemedInflationWithUnattachedParent() {
-        final Context activity = getActivity();
+        final Context activity = mActivityTestRule.getActivity();
 
         // Create a parent but not attached
         final LinearLayout parent = new LinearLayout(activity);
@@ -195,15 +198,15 @@
     @Test
     @SmallTest
     public void testDeclarativeOnClickWithContextWrapper() {
-        LayoutInflater inflater = LayoutInflater.from(getActivity());
+        LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
         View view = inflater.inflate(R.layout.layout_button_themed_onclick, null);
 
         assertTrue(view.performClick());
-        assertTrue(getActivity().wasDeclarativeOnClickCalled());
+        assertTrue(mActivityTestRule.getActivity().wasDeclarativeOnClickCalled());
     }
 
     private void verifyAppCompatWidgetInflation(final int layout, final Class<?> expectedClass) {
-        LayoutInflater inflater = LayoutInflater.from(getActivity());
+        LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
         View view = inflater.inflate(layout, null);
         assertSame("View is " + expectedClass.getSimpleName(), expectedClass,
                 view.getClass());
diff --git a/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
index 2168069..2981ad4 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
@@ -28,22 +28,27 @@
 import android.app.Instrumentation;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
-import android.support.test.filters.SdkSuppress;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.content.ContextCompat;
 import android.support.v7.appcompat.test.R;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @LargeTest
-@SdkSuppress(minSdkVersion = 14)
-public class NightModeTestCase extends BaseInstrumentationTestCase<NightModeActivity> {
+@RunWith(AndroidJUnit4.class)
+public class NightModeTestCase {
+    @Rule
+    public final ActivityTestRule<NightModeActivity> mActivityTestRule;
 
     private static final String STRING_DAY = "DAY";
     private static final String STRING_NIGHT = "NIGHT";
 
     public NightModeTestCase() {
-        super(NightModeActivity.class);
+        mActivityTestRule = new ActivityTestRule<>(NightModeActivity.class);
     }
 
     @Before
@@ -116,7 +121,7 @@
         });
 
         // Now wait for the recreate
-        getInstrumentation().waitForIdleSync();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         // Now check that the text has changed, signifying that night resources are being used
         onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)));
@@ -128,7 +133,7 @@
         final FakeTwilightManager twilightManager = new FakeTwilightManager();
         TwilightManager.setInstance(twilightManager);
 
-        final NightModeActivity activity = getActivity();
+        final NightModeActivity activity = mActivityTestRule.getActivity();
 
         // Set MODE_NIGHT_AUTO so that we will change to night mode automatically
         setLocalNightModeAndWaitForRecreate(activity, AppCompatDelegate.MODE_NIGHT_AUTO);
@@ -138,7 +143,8 @@
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                final Instrumentation instrumentation = getInstrumentation();
+                final Instrumentation instrumentation =
+                        InstrumentationRegistry.getInstrumentation();
                 // Now fool the Activity into thinking that it has gone into the background
                 instrumentation.callActivityOnPause(activity);
                 instrumentation.callActivityOnStop(activity);
diff --git a/v7/appcompat/tests/src/android/support/v7/res/content/AppCompatResourcesTestCase.java b/v7/appcompat/tests/src/android/support/v7/res/content/AppCompatResourcesTestCase.java
index bfb456a..d6b9f83 100644
--- a/v7/appcompat/tests/src/android/support/v7/res/content/AppCompatResourcesTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/res/content/AppCompatResourcesTestCase.java
@@ -23,25 +23,31 @@
 import android.content.res.ColorStateList;
 import android.graphics.Color;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.graphics.ColorUtils;
 import android.support.v7.app.AppCompatActivity;
-import android.support.v7.app.BaseInstrumentationTestCase;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.content.res.AppCompatResources;
 import android.support.v7.testutils.TestUtils;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @SmallTest
-public class AppCompatResourcesTestCase extends BaseInstrumentationTestCase<AppCompatActivity> {
+@RunWith(AndroidJUnit4.class)
+public class AppCompatResourcesTestCase {
+    @Rule
+    public final ActivityTestRule<AppCompatActivity> mActivityTestRule;
 
     public AppCompatResourcesTestCase() {
-        super(AppCompatActivity.class);
+        mActivityTestRule = new ActivityTestRule<>(AppCompatActivity.class);
     }
 
     @Test
     public void testGetColorStateListWithThemedAttributes() {
-        final Activity context = getActivity();
+        final Activity context = mActivityTestRule.getActivity();
 
         final int colorForegound = TestUtils.getThemeAttrColor(
                 context, android.R.attr.colorForeground);
@@ -65,7 +71,7 @@
 
     @Test
     public void testGetDrawableVectorResource() {
-        final Activity context = getActivity();
+        final Activity context = mActivityTestRule.getActivity();
         assertNotNull(AppCompatResources.getDrawable(context, R.drawable.test_vector_off));
     }
 
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseImageViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseImageViewTest.java
index c8398ed..431dc0e 100755
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseImageViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseImageViewTest.java
@@ -66,7 +66,7 @@
     @SmallTest
     public void testImageTintingAcrossStateChange() {
         final @IdRes int viewId = R.id.view_tinted_source;
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivity.getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
         @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null);
@@ -144,7 +144,7 @@
     @SmallTest
     public void testImageTintingAcrossModeChange() {
         final @IdRes int viewId = R.id.view_untinted_source;
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivity.getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
         @ColorInt int emeraldDefault = ResourcesCompat.getColor(
@@ -216,7 +216,7 @@
     @SmallTest
     public void testImageOpaqueTintingAcrossImageChange() {
         final @IdRes int viewId = R.id.view_tinted_no_source;
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivity.getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
         @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null);
@@ -271,7 +271,7 @@
     @SmallTest
     public void testImageTranslucentTintingAcrossImageChange() {
         final @IdRes int viewId = R.id.view_untinted_no_source;
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivity.getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
         @ColorInt int emeraldDefault = ResourcesCompat.getColor(
@@ -361,7 +361,7 @@
     @SmallTest
     public void testImageTintingAcrossBackgroundTintingChange() {
         final @IdRes int viewId = R.id.view_untinted_source;
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivity.getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
         @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null);
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java
index 74a73d3..cafe2cf 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java
@@ -36,9 +36,10 @@
 import android.support.annotation.IdRes;
 import android.support.annotation.NonNull;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.content.res.ResourcesCompat;
 import android.support.v4.graphics.ColorUtils;
-import android.support.v7.app.BaseInstrumentationTestCase;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.AppCompatTintableViewActions;
 import android.support.v7.testutils.BaseTestActivity;
@@ -47,7 +48,9 @@
 import android.view.ViewGroup;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Base class for testing custom view extensions in appcompat-v7 that implement the
@@ -55,15 +58,18 @@
  * from here and add test cases specific to the functionality they add to the relevant
  * base view class (such as <code>AppCompatTextView</code>'s all-caps support).
  */
-public abstract class AppCompatBaseViewTest<A extends BaseTestActivity, T extends View>
-        extends BaseInstrumentationTestCase<A> {
+@RunWith(AndroidJUnit4.class)
+public abstract class AppCompatBaseViewTest<A extends BaseTestActivity, T extends View> {
+    @Rule
+    public final ActivityTestRule<A> mActivityTestRule;
+
     protected ViewGroup mContainer;
 
     protected A mActivity;
     protected Resources mResources;
 
     public AppCompatBaseViewTest(Class clazz) {
-        super(clazz);
+        mActivityTestRule = new ActivityTestRule<A>(clazz);
     }
 
     @Before
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 b8f082e..78f7524 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewAutoSizeTest.java
@@ -22,15 +22,18 @@
 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.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.app.BaseInstrumentationTestCase;
 import android.support.v7.appcompat.test.R;
 import android.text.TextUtils;
 import android.text.method.SingleLineTransformationMethod;
@@ -41,29 +44,123 @@
 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
-public class AppCompatTextViewAutoSizeTest extends
-        BaseInstrumentationTestCase<AppCompatTextViewAutoSizeActivity> {
+@RunWith(AndroidJUnit4.class)
+public class AppCompatTextViewAutoSizeTest {
+    @Rule
+    public final ActivityTestRule<AppCompatTextViewAutoSizeActivity> mActivityTestRule;
+
+    private Instrumentation mInstrumentation;
+    private AppCompatTextViewAutoSizeActivity mActivity;
 
     public AppCompatTextViewAutoSizeTest() {
-        super(AppCompatTextViewAutoSizeActivity.class);
+        mActivityTestRule = new ActivityTestRule<>(AppCompatTextViewAutoSizeActivity.class);
+    }
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityTestRule.getActivity();
+    }
+
+    @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 = getActivity().getResources().getDisplayMetrics();
+        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(getActivity());
+        final AppCompatTextView granularityTextView = new AppCompatTextView(mActivity);
         granularityTextView.setAutoSizeTextTypeUniformWithConfiguration(
                 minTextSize, maxTextSize, granularity, unit);
 
-        final AppCompatTextView presetTextView = new AppCompatTextView(getActivity());
+        final AppCompatTextView presetTextView = new AppCompatTextView(mActivity);
         presetTextView.setAutoSizeTextTypeUniformWithPresetSizes(
                 new int[]{minTextSize, 12, 14, 16, 18, maxTextSize}, unit);
 
@@ -95,33 +192,32 @@
                 presetTextView.getAutoSizeTextAvailableSizes());
 
         final String someText = "This is a string";
-        final int widthHeight = 600;
+        final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                500, 500);
         // Configure identically and attach to layout.
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                LinearLayout ll = getActivity().findViewById(R.id.layout_textviewtest);
+                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);
-                granularityTextView.setWidth(widthHeight);
-                granularityTextView.setHeight(widthHeight);
-
                 presetTextView.setText(someText);
-                presetTextView.setWidth(widthHeight);
-                presetTextView.setHeight(widthHeight);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         assertEquals(granularityTextView.getTextSize(), presetTextView.getTextSize(), 0f);
     }
 
     @Test
     public void testAutoSize_notSupportedByEditText() throws Throwable {
-        final AppCompatEditText autoSizeEditText = (AppCompatEditText) getActivity().findViewById(
+        final AppCompatEditText autoSizeEditText = (AppCompatEditText) mActivity.findViewById(
                 R.id.edittext_autosize_uniform);
         // Do not force exact height only.
         final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
@@ -133,7 +229,7 @@
                 autoSizeEditText.setLayoutParams(layoutParams);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         final float initialTextSize = autoSizeEditText.getTextSize();
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -141,7 +237,7 @@
                 autoSizeEditText.setHeight(autoSizeEditText.getHeight() / 4);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         assertEquals(initialTextSize, autoSizeEditText.getTextSize(), 0f);
     }
@@ -160,7 +256,7 @@
                 autoSizeTextView.setLayoutParams(layoutParams);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         final float initialTextSize = autoSizeTextView.getTextSize();
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -168,7 +264,7 @@
                 autoSizeTextView.setHeight(autoSizeTextView.getHeight() / 4);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
     }
@@ -178,7 +274,7 @@
         final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
                 R.id.textview_autosize_uniform, false);
         final float initialTextSize = autoSizeTextView.getTextSize();
-        final Drawable drawable = ResourcesCompat.getDrawable(getActivity().getResources(),
+        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() {
@@ -187,7 +283,7 @@
                 autoSizeTextView.setCompoundDrawables(drawable, drawable, drawable, drawable);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
     }
@@ -198,7 +294,7 @@
             final AppCompatTextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
                     R.id.textview_autosize_uniform, false);
             final float initialTextSize = autoSizeTextView.getTextSize();
-            final Drawable drawable = ResourcesCompat.getDrawable(getActivity().getResources(),
+            final Drawable drawable = ResourcesCompat.getDrawable(mActivity.getResources(),
                     R.drawable.test_drawable_red, null);
             drawable.setBounds(0, 0, autoSizeTextView.getWidth() / 3,
                     autoSizeTextView.getHeight() / 3);
@@ -211,7 +307,7 @@
                     }
                 }
             });
-            getInstrumentation().waitForIdleSync();
+            mInstrumentation.waitForIdleSync();
 
             assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
         }
@@ -229,10 +325,10 @@
                 autoSizeTextView.setHeight(autoSizeTextView.getHeight() * 2);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        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(getActivity().getResources(),
+        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() {
@@ -242,7 +338,7 @@
                         drawable, drawable, drawable, drawable);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         final float initialTextSize = autoSizeTextView.getTextSize();
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -251,7 +347,7 @@
                         autoSizeTextView.getCompoundDrawablePadding() + 10);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
     }
@@ -269,7 +365,7 @@
                         autoSizeTextView.getWidth() / 3, autoSizeTextView.getHeight() / 3);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
     }
@@ -291,7 +387,7 @@
                     }
                 }
             });
-            getInstrumentation().waitForIdleSync();
+            mInstrumentation.waitForIdleSync();
 
             assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
         }
@@ -312,7 +408,7 @@
                         + "sentence to make sure this test is not flaky. Not flaky at all.");
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         final float initialTextSize = autoSizeTextView.getTextSize();
 
         mActivityTestRule.runOnUiThread(new Runnable() {
@@ -325,7 +421,7 @@
                 autoSizeTextView.setTypeface(differentTypeface);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         final float changedTextSize = autoSizeTextView.getTextSize();
 
         // Don't really know if it is larger or smaller (depends on the typeface chosen above),
@@ -338,7 +434,7 @@
                 autoSizeTextView.setTypeface(autoSizeTextView.getTypeface());
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         assertEquals(changedTextSize, autoSizeTextView.getTextSize(), 0f);
     }
@@ -359,7 +455,7 @@
                     }
                 }
             });
-            getInstrumentation().waitForIdleSync();
+            mInstrumentation.waitForIdleSync();
             final float changedTextSize = autoSizeTextView.getTextSize();
 
             assertTrue(changedTextSize < initialTextSize);
@@ -372,7 +468,7 @@
                     }
                 }
             });
-            getInstrumentation().waitForIdleSync();
+            mInstrumentation.waitForIdleSync();
 
             assertEquals(changedTextSize, autoSizeTextView.getTextSize(), 0f);
         }
@@ -392,7 +488,7 @@
                 autoSizeTextView.setLayoutParams(layoutParams);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         final float initialTextSize = autoSizeTextView.getTextSize();
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -401,7 +497,7 @@
                         autoSizeTextView.getHeight() / 4);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         assertTrue(autoSizeTextView.getTextSize() < initialTextSize);
     }
@@ -420,7 +516,7 @@
                 autoSizeTextView.setLayoutParams(layoutParams);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         final float initialTextSize = autoSizeTextView.getTextSize();
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -429,7 +525,7 @@
                         autoSizeTextView.getWidth() / 4);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         assertTrue(autoSizeTextView.getTextSize() != initialTextSize);
     }
@@ -448,7 +544,7 @@
                 autoSizeTextView.setLayoutParams(layoutParams);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         final float initialTextSize = autoSizeTextView.getTextSize();
         mActivityTestRule.runOnUiThread(new Runnable() {
@@ -458,7 +554,7 @@
                         autoSizeTextView.getWidth() / 4);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         assertTrue(autoSizeTextView.getTextSize() != initialTextSize);
     }
@@ -475,7 +571,7 @@
                 autoSizeTextView.setTextSize(initialTextSize + 123f);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         assertEquals(initialTextSize, autoSizeTextView.getTextSize(), 0f);
     }
@@ -492,7 +588,7 @@
                 autoSizeTextView.setHorizontallyScrolling(true);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         assertTrue(autoSizeTextView.getTextSize() > initialTextSize);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
@@ -501,13 +597,13 @@
                 autoSizeTextView.setHorizontallyScrolling(false);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         Assert.assertEquals(initialTextSize, autoSizeTextView.getTextSize(), 0f);
     }
 
     @Test
     public void testAutoSize_setEllipsize() throws Throwable {
-        final AppCompatTextView textView = (AppCompatTextView) getActivity().findViewById(
+        final AppCompatTextView textView = (AppCompatTextView) mActivity.findViewById(
                 R.id.textview_autosize_uniform_predef_sizes);
         final int initialAutoSizeType = textView.getAutoSizeTextType();
         final int initialMinTextSize = textView.getAutoSizeMinTextSize();
@@ -531,7 +627,7 @@
                 textView.setEllipsize(newEllipsizeValue);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         Assert.assertEquals(newEllipsizeValue, textView.getEllipsize());
         // Beside the ellipsis no auto-size attribute has changed.
         Assert.assertEquals(initialAutoSizeType, textView.getAutoSizeTextType());
@@ -544,7 +640,7 @@
     @Test
     public void testEllipsize_setAutoSize() throws Throwable {
         final AppCompatTextView textView =
-                (AppCompatTextView) getActivity().findViewById(R.id.textview_text);
+                (AppCompatTextView) mActivity.findViewById(R.id.textview_text);
         final TextUtils.TruncateAt newEllipsizeValue = TextUtils.TruncateAt.END;
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -552,7 +648,7 @@
                 textView.setEllipsize(newEllipsizeValue);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         Assert.assertEquals(newEllipsizeValue, textView.getEllipsize());
         Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
                 textView.getAutoSizeTextType());
@@ -568,7 +664,7 @@
                         TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         Assert.assertEquals(newEllipsizeValue, textView.getEllipsize());
         Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
                 textView.getAutoSizeTextType());
@@ -581,9 +677,9 @@
 
     @Test
     public void testAutoSizeUniform_obtainStyledAttributesUsingPredefinedSizes() {
-        DisplayMetrics m = getActivity().getResources().getDisplayMetrics();
+        DisplayMetrics m = mActivity.getResources().getDisplayMetrics();
         final AppCompatTextView autoSizeTextViewUniform =
-                (AppCompatTextView) getActivity().findViewById(
+                (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.
@@ -610,7 +706,7 @@
 
     @Test
     public void testAutoSizeUniform_obtainStyledAttributesPredefinedSizesFiltering() {
-        AppCompatTextView autoSizeTextViewUniform = (AppCompatTextView) getActivity().findViewById(
+        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.
@@ -620,7 +716,7 @@
 
     @Test
     public void testAutoSizeUniform_predefinedSizesFilteringAndSorting() throws Throwable {
-        final AppCompatTextView textView = (AppCompatTextView) getActivity().findViewById(
+        final AppCompatTextView textView = (AppCompatTextView) mActivity.findViewById(
                 R.id.textview_text);
         Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
                 textView.getAutoSizeTextType());
@@ -633,13 +729,13 @@
                         predefinedSizes, TypedValue.COMPLEX_UNIT_PX);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         assertArrayEquals(new int[] {10, 40, 400}, textView.getAutoSizeTextAvailableSizes());
     }
 
     @Test(expected = NullPointerException.class)
     public void testAutoSizeUniform_predefinedSizesNullArray() throws Throwable {
-        final AppCompatTextView textView = (AppCompatTextView) getActivity().findViewById(
+        final AppCompatTextView textView = (AppCompatTextView) mActivity.findViewById(
                 R.id.textview_text);
         Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
                 textView.getAutoSizeTextType());
@@ -652,12 +748,12 @@
                         predefinedSizes, TypedValue.COMPLEX_UNIT_PX);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
     }
 
     @Test
     public void testAutoSizeUniform_predefinedSizesEmptyArray() throws Throwable {
-        final AppCompatTextView textView = (AppCompatTextView) getActivity().findViewById(
+        final AppCompatTextView textView = (AppCompatTextView) mActivity.findViewById(
                 R.id.textview_text);
         Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
                 textView.getAutoSizeTextType());
@@ -669,7 +765,7 @@
                         TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         final int[] defaultSizes = textView.getAutoSizeTextAvailableSizes();
         assertNotNull(defaultSizes);
@@ -683,7 +779,7 @@
                         predefinedSizes, TypedValue.COMPLEX_UNIT_PX);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         final int[] newSizes = textView.getAutoSizeTextAvailableSizes();
         assertNotNull(defaultSizes);
@@ -693,7 +789,7 @@
     @Test
     public void testAutoSizeUniform_buildsSizes() throws Throwable {
         final AppCompatTextView autoSizeTextViewUniform =
-                (AppCompatTextView) getActivity().findViewById(
+                (AppCompatTextView) mActivity.findViewById(
                         R.id.textview_autosize_uniform);
 
         // Verify that the interval limits are both included.
@@ -705,7 +801,7 @@
                                 TypedValue.COMPLEX_UNIT_PX);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         assertArrayEquals(
                 new int[] {10, 12, 14, 16, 18, 20},
                 autoSizeTextViewUniform.getAutoSizeTextAvailableSizes());
@@ -721,7 +817,7 @@
                                 TypedValue.COMPLEX_UNIT_PX);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         assertArrayEquals(
                 new int[] {10, 12, 14, 16, 18},
                 autoSizeTextViewUniform.getAutoSizeTextAvailableSizes());
@@ -737,7 +833,7 @@
                                 TypedValue.COMPLEX_UNIT_PX);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         assertArrayEquals(
                 new int[] {10, 12, 14, 16, 18, 20},
                 autoSizeTextViewUniform.getAutoSizeTextAvailableSizes());
@@ -745,7 +841,7 @@
 
     @Test
     public void testAutoSizeUniform_getSetAutoSizeTextDefaults() {
-        final AppCompatTextView textView = new AppCompatTextView(getActivity());
+        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.
@@ -774,7 +870,7 @@
 
     @Test
     public void testAutoSizeUniform_getSetAutoSizeStepGranularity() {
-        final AppCompatTextView textView = new AppCompatTextView(getActivity());
+        final AppCompatTextView textView = new AppCompatTextView(mActivity);
         Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE,
                 textView.getAutoSizeTextType());
         final int initialValue = -1;
@@ -799,7 +895,7 @@
 
     @Test
     public void testAutoSizeUniform_getSetAutoSizeMinTextSize() {
-        final AppCompatTextView textView = new AppCompatTextView(getActivity());
+        final AppCompatTextView textView = new AppCompatTextView(mActivity);
         textView.setAutoSizeTextTypeWithDefaults(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
         Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
                 textView.getAutoSizeTextType());
@@ -831,41 +927,41 @@
         // 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, getActivity().getResources().getDisplayMetrics())),
+                newMinSize, mActivity.getResources().getDisplayMetrics())),
                         textView.getAutoSizeMinTextSize());
     }
 
     @Test(expected = IllegalArgumentException.class)
     public void testAutoSizeUniform_throwsException_whenMaxLessThanMin() {
-        final AppCompatTextView textView = new AppCompatTextView(getActivity());
+        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(getActivity());
+        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(getActivity());
+        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(getActivity());
+        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(getActivity());
+        final AppCompatTextView textView = new AppCompatTextView(mActivity);
         textView.setAutoSizeTextTypeWithDefaults(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
         Assert.assertEquals(TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM,
                 textView.getAutoSizeTextType());
@@ -892,13 +988,13 @@
         // 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, getActivity().getResources().getDisplayMetrics())),
+                newMaxSize, mActivity.getResources().getDisplayMetrics())),
                         textView.getAutoSizeMaxTextSize());
     }
 
     @Test
     public void testAutoSizeUniform_autoSizeCalledWhenTypeChanged() throws Throwable {
-        final AppCompatTextView textView = (AppCompatTextView) getActivity().findViewById(
+        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,
@@ -911,7 +1007,7 @@
                 textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, customTextSize);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         Assert.assertEquals(customTextSize, textView.getTextSize(), 0f);
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -920,7 +1016,7 @@
                         TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         // The size of the text should have changed.
         assertNotEquals(customTextSize, textView.getTextSize(), 0f);
     }
@@ -936,7 +1032,7 @@
                 textView.setAutoSizeTextTypeWithDefaults(TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
         final float newTextSizeInPx = 123f;
         assertNotEquals(newTextSizeInPx, textView.getTextSize(), 0f);
 
@@ -946,7 +1042,7 @@
                 textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, newTextSizeInPx);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
         assertEquals(newTextSizeInPx, textView.getTextSize(), 0f);
     }
@@ -966,17 +1062,17 @@
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                LinearLayout ll = (LinearLayout) getActivity().findViewById(
+                LinearLayout ll = (LinearLayout) mActivity.findViewById(
                         android.support.v7.appcompat.test.R.id.layout_textviewtest);
                 AppCompatTextView targetedTextView =
-                        (AppCompatTextView) getActivity().findViewById(viewId);
+                        (AppCompatTextView) mActivity.findViewById(viewId);
                 ll.removeAllViews();
                 ll.addView(targetedTextView);
             }
         });
-        getInstrumentation().waitForIdleSync();
+        mInstrumentation.waitForIdleSync();
 
-        final AppCompatTextView textView = getActivity().findViewById(viewId);
+        final AppCompatTextView textView = mActivity.findViewById(viewId);
         if (shouldWrapLayoutContent) {
             // Do not force exact width or height.
             final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
@@ -988,7 +1084,7 @@
                     textView.setLayoutParams(layoutParams);
                 }
             });
-            getInstrumentation().waitForIdleSync();
+            mInstrumentation.waitForIdleSync();
         }
 
         return textView;
@@ -997,7 +1093,7 @@
     @Test
     public void testAutoSizeWithMaxLines_shouldNotThrowException() throws Throwable {
         // the layout contains an instance of CustomTextViewWithTransformationMethod
-        final AppCompatTextView textView = (AppCompatTextView) mActivityTestRule.getActivity()
+        final AppCompatTextView textView = (AppCompatTextView) mActivity
                 .getLayoutInflater().inflate(R.layout.textview_autosize_maxlines, null);
         assertTrue(textView instanceof CustomTextViewWithTransformationMethod);
         // Method added in API 16.
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
index d1b00ad..5470329 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 
+import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
 import android.graphics.Typeface;
@@ -244,4 +245,17 @@
 
         assertNotEquals(Typeface.DEFAULT, textView.getTypeface());
     }
+
+    @Test
+    @UiThreadTest
+    public void testSetTextAppearance_resetTypeface() throws PackageManager.NameNotFoundException {
+        TextView textView = mContainer.findViewById(R.id.textview_simple);
+
+        TextViewCompat.setTextAppearance(textView, R.style.TextView_SansSerif);
+        Typeface firstTypeface = textView.getTypeface();
+
+        TextViewCompat.setTextAppearance(textView, R.style.TextView_Serif);
+        Typeface secondTypeface = textView.getTypeface();
+        assertNotEquals(firstTypeface, secondTypeface);
+    }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java b/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java
index 907e653..3b01e4b 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java
@@ -46,7 +46,8 @@
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
-import android.support.v7.app.BaseInstrumentationTestCase;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.appcompat.test.R;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -60,9 +61,16 @@
 import android.widget.TextView;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class ListPopupWindowTest extends BaseInstrumentationTestCase<PopupTestActivity> {
+@RunWith(AndroidJUnit4.class)
+public class ListPopupWindowTest {
+    @Rule
+    public final ActivityTestRule<PopupTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(PopupTestActivity.class);
+
     private FrameLayout mContainer;
 
     private Button mButton;
@@ -86,15 +94,11 @@
         }
     }
 
-    public ListPopupWindowTest() {
-        super(PopupTestActivity.class);
-    }
-
     @Before
     public void setUp() throws Exception {
         final PopupTestActivity activity = mActivityTestRule.getActivity();
-        mContainer = (FrameLayout) activity.findViewById(R.id.container);
-        mButton = (Button) mContainer.findViewById(R.id.test_button);
+        mContainer = activity.findViewById(R.id.container);
+        mButton = mContainer.findViewById(R.id.test_button);
         mItemClickListener = new PopupItemClickListener();
     }
 
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/PopupMenuTest.java b/v7/appcompat/tests/src/android/support/v7/widget/PopupMenuTest.java
index 8186e3a..7be67a6 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/PopupMenuTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/PopupMenuTest.java
@@ -51,8 +51,9 @@
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.view.MenuItemCompat;
-import android.support.v7.app.BaseInstrumentationTestCase;
 import android.support.v7.appcompat.test.R;
 import android.text.TextUtils;
 import android.view.MenuInflater;
@@ -69,9 +70,16 @@
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class PopupMenuTest extends BaseInstrumentationTestCase<PopupTestActivity> {
+@RunWith(AndroidJUnit4.class)
+public class PopupMenuTest {
+    @Rule
+    public final ActivityTestRule<PopupTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(PopupTestActivity.class);
+
     // Since PopupMenu doesn't expose any access to the underlying
     // implementation (like ListPopupWindow.getListView), we're relying on the
     // class name of the list view from MenuPopupWindow that is being used
@@ -89,15 +97,11 @@
 
     private View mMainDecorView;
 
-    public PopupMenuTest() {
-        super(PopupTestActivity.class);
-    }
-
     @Before
     public void setUp() throws Exception {
         final PopupTestActivity activity = mActivityTestRule.getActivity();
-        mContainer = (FrameLayout) activity.findViewById(R.id.container);
-        mButton = (Button) mContainer.findViewById(R.id.test_button);
+        mContainer = activity.findViewById(R.id.container);
+        mButton = mContainer.findViewById(R.id.test_button);
         mResources = mActivityTestRule.getActivity().getResources();
         mMainDecorView = mActivityTestRule.getActivity().getWindow().getDecorView();
     }
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/TintResourcesTest.java b/v7/appcompat/tests/src/android/support/v7/widget/TintResourcesTest.java
index eb2d462..700fe26 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/TintResourcesTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/TintResourcesTest.java
@@ -22,26 +22,31 @@
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.app.AppCompatActivity;
-import android.support.v7.app.BaseInstrumentationTestCase;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @SmallTest
-public class TintResourcesTest extends BaseInstrumentationTestCase<AppCompatActivity> {
-
-    public TintResourcesTest() {
-        super(AppCompatActivity.class);
-    }
+@RunWith(AndroidJUnit4.class)
+public class TintResourcesTest {
+    @Rule
+    public final ActivityTestRule<AppCompatActivity> mActivityTestRule =
+            new ActivityTestRule<>(AppCompatActivity.class);
 
     @Test
     public void testTintResourcesDelegateBackToOriginalResources() {
-        final TestResources testResources = new TestResources(getActivity().getResources());
+        final TestResources testResources =
+                new TestResources(mActivityTestRule.getActivity().getResources());
         // First make sure that the flag is false
         assertFalse(testResources.wasGetDrawableCalled());
 
         // Now wrap in a TintResources instance and get a Drawable
-        final Resources tintResources = new TintResources(getActivity(), testResources);
+        final Resources tintResources =
+                new TintResources(mActivityTestRule.getActivity(), testResources);
         tintResources.getDrawable(android.R.drawable.ic_delete);
 
         // ...and assert that the flag was flipped
diff --git a/v7/cardview/src/android/support/v7/widget/CardView.java b/v7/cardview/src/android/support/v7/widget/CardView.java
index fab9453..3df45d9 100644
--- a/v7/cardview/src/android/support/v7/widget/CardView.java
+++ b/v7/cardview/src/android/support/v7/widget/CardView.java
@@ -196,6 +196,9 @@
                     widthMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(minWidth,
                             MeasureSpec.getSize(widthMeasureSpec)), widthMode);
                     break;
+                case MeasureSpec.UNSPECIFIED:
+                    // Do nothing
+                    break;
             }
 
             final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
@@ -206,6 +209,9 @@
                     heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(minHeight,
                             MeasureSpec.getSize(heightMeasureSpec)), heightMode);
                     break;
+                case MeasureSpec.UNSPECIFIED:
+                    // Do nothing
+                    break;
             }
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         } else {
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
index a7e8cb5..00d15c9 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
@@ -489,7 +489,8 @@
             setEnabled(mRouter.isRouteAvailable(mSelector,
                     MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE));
         }
-        if (mRemoteIndicator.getCurrent() instanceof AnimationDrawable) {
+        if (mRemoteIndicator != null
+                && mRemoteIndicator.getCurrent() instanceof AnimationDrawable) {
             AnimationDrawable curDrawable = (AnimationDrawable) mRemoteIndicator.getCurrent();
             if (mAttachedToWindow) {
                 if ((needsRefresh || isConnecting) && !curDrawable.isRunning()) {
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
index e2c9b87..e052035 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
@@ -2560,6 +2560,20 @@
         }
 
         private void setSelectedRouteInternal(RouteInfo route, int unselectReason) {
+            // TODO: Remove the following logging when no longer needed.
+            if (mBluetoothRoute != null && route.isDefault()) {
+                final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+                StringBuffer sb = new StringBuffer();
+                // callStack[3] is the caller of this method.
+                for (int i = 3; i < callStack.length; i++) {
+                    StackTraceElement caller = callStack[i];
+                    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 (mSelectedRoute != route) {
                 if (mSelectedRoute != null) {
                     if (DEBUG) {
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index e01cd83..e452009 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -677,7 +677,7 @@
             int defStyleAttr, int defStyleRes) {
         if (className != null) {
             className = className.trim();
-            if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
+            if (!className.isEmpty()) {
                 className = getFullClassName(context, className);
                 try {
                     ClassLoader classLoader;
diff --git a/wear/AndroidManifest.xml b/wear/AndroidManifest.xml
index fda54e2..5fa5a83 100644
--- a/wear/AndroidManifest.xml
+++ b/wear/AndroidManifest.xml
@@ -15,7 +15,6 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.support.wear">
-    <uses-sdk android:minSdkVersion="22"/>
     <application>
         <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
     </application>
diff --git a/wear/build.gradle b/wear/build.gradle
index b320ab7..07e579e 100644
--- a/wear/build.gradle
+++ b/wear/build.gradle
@@ -19,7 +19,7 @@
 
 android {
     defaultConfig {
-        minSdkVersion 23
+        minSdkVersion 24
     }
 
     sourceSets {
diff --git a/wear/res/layout-v23/action_drawer_title_view.xml b/wear/res-public/values/public_styles.xml
similarity index 64%
copy from wear/res/layout-v23/action_drawer_title_view.xml
copy to wear/res-public/values/public_styles.xml
index a2be3e1..d4575e4 100644
--- a/wear/res/layout-v23/action_drawer_title_view.xml
+++ b/wear/res-public/values/public_styles.xml
@@ -13,11 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/wearable_support_action_drawer_title"
-    android:layout_width="wrap_content"
-    android:layout_height="@dimen/action_drawer_item_icon_size"
-    android:layout_gravity="center"
-    android:gravity="center"
-    style="@style/WearableActionDrawerTitleText" />
+<!-- Definitions of styles to be exposed as public -->
+<resources>
+    <public type="style" name="Widget.Wear.RoundSwitch"/>
+</resources>
diff --git a/wear/res/layout-v23/action_drawer_title_view.xml b/wear/res/color/ws_switch_thumb_color_material.xml
similarity index 63%
copy from wear/res/layout-v23/action_drawer_title_view.xml
copy to wear/res/color/ws_switch_thumb_color_material.xml
index a2be3e1..0707d69 100644
--- a/wear/res/layout-v23/action_drawer_title_view.xml
+++ b/wear/res/color/ws_switch_thumb_color_material.xml
@@ -13,11 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/wearable_support_action_drawer_title"
-    android:layout_width="wrap_content"
-    android:layout_height="@dimen/action_drawer_item_icon_size"
-    android:layout_gravity="center"
-    android:gravity="center"
-    style="@style/WearableActionDrawerTitleText" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="?android:attr/colorButtonNormal"
+            android:alpha="?android:attr/disabledAlpha" android:state_enabled="false" />
+    <item android:color="?android:attr/colorControlActivated" android:state_checked="true" />
+    <item android:color="?android:attr/colorButtonNormal" />
+</selector>
diff --git a/wear/res/layout-v23/action_drawer_title_view.xml b/wear/res/color/ws_switch_track_color_material.xml
similarity index 65%
copy from wear/res/layout-v23/action_drawer_title_view.xml
copy to wear/res/color/ws_switch_track_color_material.xml
index a2be3e1..214bf4a 100644
--- a/wear/res/layout-v23/action_drawer_title_view.xml
+++ b/wear/res/color/ws_switch_track_color_material.xml
@@ -13,11 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/wearable_support_action_drawer_title"
-    android:layout_width="wrap_content"
-    android:layout_height="@dimen/action_drawer_item_icon_size"
-    android:layout_gravity="center"
-    android:gravity="center"
-    style="@style/WearableActionDrawerTitleText" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?android:attr/disabledAlpha"
+          android:color="?android:attr/colorPrimary" />
+    <item android:color="?android:attr/colorPrimary" />
+</selector>
diff --git a/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_14w.png b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_14w.png
new file mode 100644
index 0000000..371469c
--- /dev/null
+++ b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_14w.png
Binary files differ
diff --git a/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_15w.png b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_15w.png
new file mode 100644
index 0000000..e477260
--- /dev/null
+++ b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_15w.png
Binary files differ
diff --git a/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_16w.png b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_16w.png
new file mode 100644
index 0000000..19a1bd3
--- /dev/null
+++ b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_16w.png
Binary files differ
diff --git a/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_17w.png b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_17w.png
new file mode 100644
index 0000000..79dc733
--- /dev/null
+++ b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_17w.png
Binary files differ
diff --git a/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_18w.png b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_18w.png
new file mode 100644
index 0000000..6d921c0
--- /dev/null
+++ b/wear/res/drawable-hdpi/ws_switch_thumb_mtrl_18w.png
Binary files differ
diff --git a/wear/res/drawable-hdpi/ws_switch_track_mtrl.png b/wear/res/drawable-hdpi/ws_switch_track_mtrl.png
new file mode 100644
index 0000000..ecee3e1
--- /dev/null
+++ b/wear/res/drawable-hdpi/ws_switch_track_mtrl.png
Binary files differ
diff --git a/wear/res/drawable-v21/ic_expand_more_white_22.xml b/wear/res/drawable-v21/ws_ic_expand_more_white_22.xml
similarity index 100%
rename from wear/res/drawable-v21/ic_expand_more_white_22.xml
rename to wear/res/drawable-v21/ws_ic_expand_more_white_22.xml
diff --git a/wear/res/drawable-v21/ws_switch_thumb_material_anim.xml b/wear/res/drawable-v21/ws_switch_thumb_material_anim.xml
new file mode 100644
index 0000000..15423e5
--- /dev/null
+++ b/wear/res/drawable-v21/ws_switch_thumb_material_anim.xml
@@ -0,0 +1,95 @@
+<?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.
+-->
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:constantSize="true">
+    <item
+        android:id="@+id/off"
+        android:drawable="@drawable/ws_switch_thumb_mtrl_14w"
+        android:state_enabled="false" />
+    <item
+        android:id="@+id/on"
+        android:drawable="@drawable/ws_switch_thumb_mtrl_14w"
+        android:state_checked="true" />
+    <item
+        android:id="@+id/off"
+        android:drawable="@drawable/ws_switch_thumb_mtrl_14w" />
+    <transition
+        android:fromId="@id/off"
+        android:toId="@id/on">
+        <animation-list>
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_14w"
+                android:duration="30" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_15w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_16w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_17w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_18w"
+                android:duration="75" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_17w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_16w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_15w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_14w"
+                android:duration="30" />
+        </animation-list>
+    </transition>
+    <transition
+        android:fromId="@id/on"
+        android:toId="@id/off">
+        <animation-list>
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_14w"
+                android:duration="30" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_15w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_16w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_17w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_18w"
+                android:duration="75" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_17w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_16w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_15w"
+                android:duration="15" />
+            <item
+                android:drawable="@drawable/ws_switch_thumb_mtrl_14w"
+                android:duration="30" />
+        </animation-list>
+    </transition>
+</animated-selector>
diff --git a/wear/res/drawable-v23/action_item_background.xml b/wear/res/drawable-v23/ws_action_item_background.xml
similarity index 100%
rename from wear/res/drawable-v23/action_item_background.xml
rename to wear/res/drawable-v23/ws_action_item_background.xml
diff --git a/wear/res/drawable-v23/action_item_icon_background.xml b/wear/res/drawable-v23/ws_action_item_icon_background.xml
similarity index 100%
rename from wear/res/drawable-v23/action_item_icon_background.xml
rename to wear/res/drawable-v23/ws_action_item_icon_background.xml
diff --git a/wear/res/drawable-v23/ic_expand_less_white_22.xml b/wear/res/drawable-v23/ws_ic_expand_less_white_22.xml
similarity index 100%
rename from wear/res/drawable-v23/ic_expand_less_white_22.xml
rename to wear/res/drawable-v23/ws_ic_expand_less_white_22.xml
diff --git a/wear/res/drawable-v23/ic_more_horiz_24dp_wht.xml b/wear/res/drawable-v23/ws_ic_more_horiz_24dp_wht.xml
similarity index 100%
rename from wear/res/drawable-v23/ic_more_horiz_24dp_wht.xml
rename to wear/res/drawable-v23/ws_ic_more_horiz_24dp_wht.xml
diff --git a/wear/res/drawable-v23/ic_more_vert_24dp_wht.xml b/wear/res/drawable-v23/ws_ic_more_vert_24dp_wht.xml
similarity index 100%
rename from wear/res/drawable-v23/ic_more_vert_24dp_wht.xml
rename to wear/res/drawable-v23/ws_ic_more_vert_24dp_wht.xml
diff --git a/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_14w.png b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_14w.png
new file mode 100644
index 0000000..7f7ca14
--- /dev/null
+++ b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_14w.png
Binary files differ
diff --git a/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_15w.png b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_15w.png
new file mode 100644
index 0000000..52120b8
--- /dev/null
+++ b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_15w.png
Binary files differ
diff --git a/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_16w.png b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_16w.png
new file mode 100644
index 0000000..d6e9be9
--- /dev/null
+++ b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_16w.png
Binary files differ
diff --git a/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_17w.png b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_17w.png
new file mode 100644
index 0000000..8d76393
--- /dev/null
+++ b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_17w.png
Binary files differ
diff --git a/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_18w.png b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_18w.png
new file mode 100644
index 0000000..ca9c66e
--- /dev/null
+++ b/wear/res/drawable-xhdpi/ws_switch_thumb_mtrl_18w.png
Binary files differ
diff --git a/wear/res/drawable-xhdpi/ws_switch_track_mtrl.png b/wear/res/drawable-xhdpi/ws_switch_track_mtrl.png
new file mode 100644
index 0000000..1aa5442
--- /dev/null
+++ b/wear/res/drawable-xhdpi/ws_switch_track_mtrl.png
Binary files differ
diff --git a/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_14w.png b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_14w.png
new file mode 100644
index 0000000..c0d72d7
--- /dev/null
+++ b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_14w.png
Binary files differ
diff --git a/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_15w.png b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_15w.png
new file mode 100644
index 0000000..d7c0ec0
--- /dev/null
+++ b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_15w.png
Binary files differ
diff --git a/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_16w.png b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_16w.png
new file mode 100644
index 0000000..5815ba9
--- /dev/null
+++ b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_16w.png
Binary files differ
diff --git a/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_17w.png b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_17w.png
new file mode 100644
index 0000000..41da8c0
--- /dev/null
+++ b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_17w.png
Binary files differ
diff --git a/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_18w.png b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_18w.png
new file mode 100644
index 0000000..975eb01
--- /dev/null
+++ b/wear/res/drawable-xxhdpi/ws_switch_thumb_mtrl_18w.png
Binary files differ
diff --git a/wear/res/drawable-xxhdpi/ws_switch_track_mtrl.png b/wear/res/drawable-xxhdpi/ws_switch_track_mtrl.png
new file mode 100644
index 0000000..af2042b
--- /dev/null
+++ b/wear/res/drawable-xxhdpi/ws_switch_track_mtrl.png
Binary files differ
diff --git a/wear/res/layout-v23/action_drawer_item_view.xml b/wear/res/layout-v23/ws_action_drawer_item_view.xml
similarity index 70%
rename from wear/res/layout-v23/action_drawer_item_view.xml
rename to wear/res/layout-v23/ws_action_drawer_item_view.xml
index 068e364..fc84862 100644
--- a/wear/res/layout-v23/action_drawer_item_view.xml
+++ b/wear/res/layout-v23/ws_action_drawer_item_view.xml
@@ -18,24 +18,24 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@drawable/action_item_background"
+    android:background="@drawable/ws_action_item_background"
     android:orientation="horizontal">
 
     <ImageView
-        android:id="@+id/wearable_support_action_drawer_item_icon"
-        android:layout_width="@dimen/action_drawer_item_icon_size"
-        android:layout_height="@dimen/action_drawer_item_icon_size"
+        android:id="@+id/ws_action_drawer_item_icon"
+        android:layout_width="@dimen/ws_action_drawer_item_icon_size"
+        android:layout_height="@dimen/ws_action_drawer_item_icon_size"
         android:layout_gravity="center_vertical"
-        android:background="@drawable/action_item_icon_background"
+        android:background="@drawable/ws_action_item_icon_background"
         android:tint="?android:attr/colorBackground"
-        android:padding="@dimen/action_drawer_item_icon_padding"
+        android:padding="@dimen/ws_action_drawer_item_icon_padding"
         android:scaleType="fitCenter"
         tools:ignore="ContentDescription" />
 
     <TextView
-        android:id="@+id/wearable_support_action_drawer_item_text"
+        android:id="@+id/ws_action_drawer_item_text"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        style="@style/WearableActionDrawerItemText"/>
+        style="@style/WsWearableActionDrawerItemText"/>
 
 </LinearLayout>
diff --git a/wear/res/layout-v23/action_drawer_peek_view.xml b/wear/res/layout-v23/ws_action_drawer_peek_view.xml
similarity index 66%
rename from wear/res/layout-v23/action_drawer_peek_view.xml
rename to wear/res/layout-v23/ws_action_drawer_peek_view.xml
index 88946cb..ce66d73 100644
--- a/wear/res/layout-v23/action_drawer_peek_view.xml
+++ b/wear/res/layout-v23/ws_action_drawer_peek_view.xml
@@ -17,25 +17,25 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="wrap_content"
-    android:layout_height="@dimen/action_drawer_peek_view_height"
+    android:layout_height="@dimen/ws_action_drawer_peek_view_height"
     android:layout_gravity="center"
     android:gravity="center_vertical"
     android:orientation="vertical"
-    android:paddingTop="@dimen/action_drawer_peek_top_padding">
+    android:paddingTop="@dimen/ws_action_drawer_peek_top_padding">
 
     <ImageView
-        android:id="@+id/wearable_support_action_drawer_peek_action_icon"
-        android:layout_width="@dimen/peek_view_icon_size"
-        android:layout_height="@dimen/peek_view_icon_size"
+        android:id="@+id/ws_action_drawer_peek_action_icon"
+        android:layout_width="@dimen/ws_peek_view_icon_size"
+        android:layout_height="@dimen/ws_peek_view_icon_size"
         android:tint="?android:attr/colorForeground"
         tools:ignore="ContentDescription" />
 
     <ImageView
-        android:id="@+id/wearable_support_action_drawer_expand_icon"
-        android:layout_width="@dimen/peek_view_icon_size"
-        android:layout_height="@dimen/peek_view_icon_size"
-        android:layout_marginTop="@dimen/action_drawer_expand_icon_top_margin"
-        android:src="@drawable/ic_expand_less_white_22"
+        android:id="@+id/ws_action_drawer_expand_icon"
+        android:layout_width="@dimen/ws_peek_view_icon_size"
+        android:layout_height="@dimen/ws_peek_view_icon_size"
+        android:layout_marginTop="@dimen/ws_action_drawer_expand_icon_top_margin"
+        android:src="@drawable/ws_ic_expand_less_white_22"
         android:alpha="0.5"
         android:tint="?android:attr/colorForeground"
         tools:ignore="NegativeMargin,ContentDescription" />
diff --git a/wear/res/layout-v23/action_drawer_title_view.xml b/wear/res/layout-v23/ws_action_drawer_title_view.xml
similarity index 83%
rename from wear/res/layout-v23/action_drawer_title_view.xml
rename to wear/res/layout-v23/ws_action_drawer_title_view.xml
index a2be3e1..e5535c4 100644
--- a/wear/res/layout-v23/action_drawer_title_view.xml
+++ b/wear/res/layout-v23/ws_action_drawer_title_view.xml
@@ -15,9 +15,9 @@
 -->
 <TextView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/wearable_support_action_drawer_title"
+    android:id="@+id/ws_action_drawer_title"
     android:layout_width="wrap_content"
-    android:layout_height="@dimen/action_drawer_item_icon_size"
+    android:layout_height="@dimen/ws_action_drawer_item_icon_size"
     android:layout_gravity="center"
     android:gravity="center"
-    style="@style/WearableActionDrawerTitleText" />
+    style="@style/WsWearableActionDrawerTitleText" />
diff --git a/wear/res/layout-v23/wearable_drawer_view.xml b/wear/res/layout-v23/ws_wearable_drawer_view.xml
similarity index 74%
rename from wear/res/layout-v23/wearable_drawer_view.xml
rename to wear/res/layout-v23/ws_wearable_drawer_view.xml
index 8659aac..c7c5907 100644
--- a/wear/res/layout-v23/wearable_drawer_view.xml
+++ b/wear/res/layout-v23/ws_wearable_drawer_view.xml
@@ -19,16 +19,16 @@
 
     <!-- Layout gravity for peek is set in code by WearableDrawerView. -->
     <FrameLayout
-        android:id="@+id/wearable_support_drawer_view_peek_container"
+        android:id="@+id/ws_drawer_view_peek_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
 
         <ImageView
-            android:id="@+id/wearable_support_drawer_view_peek_icon"
-            android:layout_width="@dimen/peek_view_icon_size"
-            android:layout_height="@dimen/peek_view_icon_size"
-            android:layout_marginTop="@dimen/peek_view_top_padding"
-            android:layout_marginBottom="@dimen/peek_view_bottom_padding"
+            android:id="@+id/ws_drawer_view_peek_icon"
+            android:layout_width="@dimen/ws_peek_view_icon_size"
+            android:layout_height="@dimen/ws_peek_view_icon_size"
+            android:layout_marginTop="@dimen/ws_peek_view_top_padding"
+            android:layout_marginBottom="@dimen/ws_peek_view_bottom_padding"
             android:layout_gravity="center"
             android:tint="?android:attr/colorForeground"
             tools:ignore="ContentDescription" />
diff --git a/wear/res/layout/single_page_nav_drawer_5_item.xml b/wear/res/layout/single_page_nav_drawer_5_item.xml
deleted file mode 100644
index 2a8c3af..0000000
--- a/wear/res/layout/single_page_nav_drawer_5_item.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?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.
--->
-<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:clipChildren="false">
-
-    <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_0"
-        android:layout_alignParentStart="true"
-        android:layout_alignParentTop="true"
-        app:layout_marginStartPercent="@fraction/nav_drawer_margin_5_items_horizontal_outer_rows"
-        app:layout_marginTopPercent="@fraction/nav_drawer_margin_5_items_vertical"
-        tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle"/>
-
-    <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_1"
-        android:layout_alignParentEnd="true"
-        android:layout_alignParentTop="true"
-        app:layout_marginEndPercent="@fraction/nav_drawer_margin_5_items_horizontal_outer_rows"
-        app:layout_marginTopPercent="@fraction/nav_drawer_margin_5_items_vertical"
-        tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
-
-    <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_2"
-        android:layout_alignParentStart="true"
-        android:layout_centerVertical="true"
-        app:layout_marginStartPercent="@fraction/nav_drawer_margin_5_items_horizontal_middle_row"
-        tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
-
-    <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_3"
-        android:layout_centerInParent="true"
-        tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
-
-    <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_4"
-        android:layout_alignParentEnd="true"
-        android:layout_centerVertical="true"
-        app:layout_marginEndPercent="@fraction/nav_drawer_margin_5_items_horizontal_middle_row"
-        tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
-
-    <TextView
-        android:id="@+id/wearable_support_nav_drawer_text"
-        android:layout_below="@id/wearable_support_nav_drawer_icon_4"
-        style="@style/SinglePageNavDrawerTextStyle"/>
-
-</android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/single_page_nav_drawer_7_item.xml b/wear/res/layout/single_page_nav_drawer_7_item.xml
deleted file mode 100644
index 45e0c24..0000000
--- a/wear/res/layout/single_page_nav_drawer_7_item.xml
+++ /dev/null
@@ -1,81 +0,0 @@
-<?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.
--->
-<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:clipChildren="false">
-
-    <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_0"
-        android:layout_alignParentStart="true"
-        android:layout_alignParentTop="true"
-        app:layout_marginStartPercent="@fraction/nav_drawer_margin_7_items_horizontal_outer_rows"
-        app:layout_marginTopPercent="@fraction/nav_drawer_margin_7_items_vertical"
-        tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
-
-    <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_1"
-        android:layout_alignParentEnd="true"
-        android:layout_alignParentTop="true"
-        app:layout_marginEndPercent="@fraction/nav_drawer_margin_7_items_horizontal_outer_rows"
-        app:layout_marginTopPercent="@fraction/nav_drawer_margin_7_items_vertical"
-        tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
-
-    <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_2"
-        android:layout_alignParentStart="true"
-        android:layout_centerVertical="true"
-        app:layout_marginStartPercent="@fraction/nav_drawer_margin_7_items_horizontal_middle_row"
-        tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
-
-    <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_3"
-        android:layout_centerInParent="true"
-        tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
-
-    <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_4"
-        android:layout_alignParentEnd="true"
-        android:layout_centerVertical="true"
-        app:layout_marginEndPercent="@fraction/nav_drawer_margin_7_items_horizontal_middle_row"
-        tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
-
-    <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_5"
-        android:layout_alignParentStart="true"
-        android:layout_alignParentBottom="true"
-        app:layout_marginStartPercent="@fraction/nav_drawer_margin_7_items_horizontal_outer_rows"
-        app:layout_marginBottomPercent="@fraction/nav_drawer_margin_7_items_vertical"
-        tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
-
-    <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_6"
-        android:layout_alignParentEnd="true"
-        android:layout_alignParentBottom="true"
-        app:layout_marginEndPercent="@fraction/nav_drawer_margin_7_items_horizontal_outer_rows"
-        app:layout_marginBottomPercent="@fraction/nav_drawer_margin_7_items_vertical"
-        tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
-
-</android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/navigation_drawer_item_view.xml b/wear/res/layout/ws_navigation_drawer_item_view.xml
similarity index 86%
rename from wear/res/layout/navigation_drawer_item_view.xml
rename to wear/res/layout/ws_navigation_drawer_item_view.xml
index 9e4d86a..dcd5e41 100644
--- a/wear/res/layout/navigation_drawer_item_view.xml
+++ b/wear/res/layout/ws_navigation_drawer_item_view.xml
@@ -19,17 +19,17 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <ImageView
-        android:id="@+id/wearable_support_navigation_drawer_item_icon"
+        android:id="@+id/ws_navigation_drawer_item_icon"
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:layout_centerInParent="true"
         android:scaleType="fitCenter"
         tools:ignore="ContentDescription" />
     <TextView
-        android:id="@+id/wearable_support_navigation_drawer_item_text"
+        android:id="@+id/ws_navigation_drawer_item_text"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_below="@id/wearable_support_navigation_drawer_item_icon"
+        android:layout_below="@id/ws_navigation_drawer_item_icon"
         android:layout_marginTop="6dp"
         android:gravity="center_horizontal"
         android:textColor="?android:attr/textColorPrimary"/>
diff --git a/wear/res/layout/navigation_drawer_view.xml b/wear/res/layout/ws_navigation_drawer_view.xml
similarity index 78%
rename from wear/res/layout/navigation_drawer_view.xml
rename to wear/res/layout/ws_navigation_drawer_view.xml
index 9824bbf..92855eb 100644
--- a/wear/res/layout/navigation_drawer_view.xml
+++ b/wear/res/layout/ws_navigation_drawer_view.xml
@@ -20,15 +20,15 @@
     android:layout_height="match_parent" >
 
     <android.support.v4.view.ViewPager
-        android:id="@+id/wearable_support_navigation_drawer_view_pager"
+        android:id="@+id/ws_navigation_drawer_view_pager"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
     <android.support.wear.widget.drawer.PageIndicatorView
-        android:id="@+id/wearable_support_navigation_drawer_page_indicator"
+        android:id="@+id/ws_navigation_drawer_page_indicator"
         android:layout_width="wrap_content"
-        android:layout_height="@dimen/peek_view_icon_size"
-        android:layout_marginBottom="@dimen/peek_view_bottom_padding"
+        android:layout_height="@dimen/ws_peek_view_icon_size"
+        android:layout_marginBottom="@dimen/ws_peek_view_bottom_padding"
         android:layout_gravity="bottom|center_horizontal"
-        app:pageIndicatorDotFadeWhenIdle="false" />
+        app:dotFadeWhenIdle="false" />
 
 </FrameLayout>
diff --git a/wear/res/layout/single_page_nav_drawer_1_item.xml b/wear/res/layout/ws_single_page_nav_drawer_1_item.xml
similarity index 79%
rename from wear/res/layout/single_page_nav_drawer_1_item.xml
rename to wear/res/layout/ws_single_page_nav_drawer_1_item.xml
index 2911f43..550d737 100644
--- a/wear/res/layout/single_page_nav_drawer_1_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_1_item.xml
@@ -20,14 +20,14 @@
     android:clipChildren="false">
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_0"
+        android:id="@+id/ws_nav_drawer_icon_0"
         android:layout_centerInParent="true"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <TextView
-        android:id="@+id/wearable_support_nav_drawer_text"
-        android:layout_below="@id/wearable_support_nav_drawer_icon_0"
-        style="@style/SinglePageNavDrawerTextStyle"/>
+        android:id="@+id/ws_nav_drawer_text"
+        android:layout_below="@id/ws_nav_drawer_icon_0"
+        style="@style/WsSinglePageNavDrawerTextStyle"/>
 
 </android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/single_page_nav_drawer_2_item.xml b/wear/res/layout/ws_single_page_nav_drawer_2_item.xml
similarity index 71%
rename from wear/res/layout/single_page_nav_drawer_2_item.xml
rename to wear/res/layout/ws_single_page_nav_drawer_2_item.xml
index cbdbb24..63dbbe6 100644
--- a/wear/res/layout/single_page_nav_drawer_2_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_2_item.xml
@@ -21,24 +21,24 @@
     android:clipChildren="false">
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_0"
+        android:id="@+id/ws_nav_drawer_icon_0"
         android:layout_alignParentStart="true"
         android:layout_centerVertical="true"
-        app:layout_marginStartPercent="@fraction/nav_drawer_margin_2_items"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_2_items"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_1"
+        android:id="@+id/ws_nav_drawer_icon_1"
         android:layout_alignParentEnd="true"
         android:layout_centerVertical="true"
-        app:layout_marginEndPercent="@fraction/nav_drawer_margin_2_items"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_2_items"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <TextView
-        android:id="@+id/wearable_support_nav_drawer_text"
-        android:layout_below="@id/wearable_support_nav_drawer_icon_1"
-        style="@style/SinglePageNavDrawerTextStyle"/>
+        android:id="@+id/ws_nav_drawer_text"
+        android:layout_below="@id/ws_nav_drawer_icon_1"
+        style="@style/WsSinglePageNavDrawerTextStyle"/>
 
 </android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/single_page_nav_drawer_3_item.xml b/wear/res/layout/ws_single_page_nav_drawer_3_item.xml
similarity index 69%
rename from wear/res/layout/single_page_nav_drawer_3_item.xml
rename to wear/res/layout/ws_single_page_nav_drawer_3_item.xml
index 5e4fc52..209ec16 100644
--- a/wear/res/layout/single_page_nav_drawer_3_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_3_item.xml
@@ -21,30 +21,30 @@
     android:clipChildren="false">
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_0"
+        android:id="@+id/ws_nav_drawer_icon_0"
         android:layout_alignParentStart="true"
         android:layout_centerVertical="true"
-        app:layout_marginStartPercent="@fraction/nav_drawer_margin_3_items"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_3_items"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_1"
+        android:id="@+id/ws_nav_drawer_icon_1"
         android:layout_centerInParent="true"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_2"
+        android:id="@+id/ws_nav_drawer_icon_2"
         android:layout_alignParentEnd="true"
         android:layout_centerVertical="true"
-        app:layout_marginEndPercent="@fraction/nav_drawer_margin_3_items"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_3_items"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <TextView
-        android:id="@+id/wearable_support_nav_drawer_text"
-        android:layout_below="@id/wearable_support_nav_drawer_icon_2"
-        style="@style/SinglePageNavDrawerTextStyle"/>
+        android:id="@+id/ws_nav_drawer_text"
+        android:layout_below="@id/ws_nav_drawer_icon_2"
+        style="@style/WsSinglePageNavDrawerTextStyle"/>
 
 </android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/single_page_nav_drawer_4_item.xml b/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
similarity index 66%
rename from wear/res/layout/single_page_nav_drawer_4_item.xml
rename to wear/res/layout/ws_single_page_nav_drawer_4_item.xml
index 4acfdab..1b2c163 100644
--- a/wear/res/layout/single_page_nav_drawer_4_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
@@ -21,38 +21,38 @@
     android:clipChildren="false">
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_0"
+        android:id="@+id/ws_nav_drawer_icon_0"
         android:layout_alignParentTop="true"
         android:layout_centerHorizontal="true"
-        app:layout_marginTopPercent="@fraction/nav_drawer_margin_4_items_vertical"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_4_items_vertical"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_1"
+        android:id="@+id/ws_nav_drawer_icon_1"
         android:layout_alignParentStart="true"
         android:layout_centerVertical="true"
-        app:layout_marginStartPercent="@fraction/nav_drawer_margin_4_items_horizontal"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_4_items_horizontal"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_2"
+        android:id="@+id/ws_nav_drawer_icon_2"
         android:layout_centerInParent="true"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_3"
+        android:id="@+id/ws_nav_drawer_icon_3"
         android:layout_alignParentEnd="true"
         android:layout_centerVertical="true"
-        app:layout_marginEndPercent="@fraction/nav_drawer_margin_4_items_horizontal"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_4_items_horizontal"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <TextView
-        android:id="@+id/wearable_support_nav_drawer_text"
-        android:layout_below="@id/wearable_support_nav_drawer_icon_3"
-        style="@style/SinglePageNavDrawerTextStyle"/>
+        android:id="@+id/ws_nav_drawer_text"
+        android:layout_below="@id/ws_nav_drawer_icon_3"
+        style="@style/WsSinglePageNavDrawerTextStyle"/>
 
 </android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_5_item.xml b/wear/res/layout/ws_single_page_nav_drawer_5_item.xml
new file mode 100644
index 0000000..699533d
--- /dev/null
+++ b/wear/res/layout/ws_single_page_nav_drawer_5_item.xml
@@ -0,0 +1,68 @@
+<?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.
+-->
+<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false">
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_0"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_5_items_horizontal_outer_rows"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_5_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle"/>
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_1"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentTop="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_5_items_horizontal_outer_rows"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_5_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_2"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_5_items_horizontal_middle_row"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_3"
+        android:layout_centerInParent="true"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_4"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_5_items_horizontal_middle_row"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <TextView
+        android:id="@+id/ws_nav_drawer_text"
+        android:layout_below="@id/ws_nav_drawer_icon_4"
+        style="@style/WsSinglePageNavDrawerTextStyle"/>
+
+</android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/single_page_nav_drawer_6_item.xml b/wear/res/layout/ws_single_page_nav_drawer_6_item.xml
similarity index 60%
rename from wear/res/layout/single_page_nav_drawer_6_item.xml
rename to wear/res/layout/ws_single_page_nav_drawer_6_item.xml
index fd6cd65..00625a1 100644
--- a/wear/res/layout/single_page_nav_drawer_6_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_6_item.xml
@@ -21,56 +21,56 @@
     android:clipChildren="false">
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_0"
+        android:id="@+id/ws_nav_drawer_icon_0"
         android:layout_alignParentStart="true"
         android:layout_alignParentTop="true"
-        app:layout_marginStartPercent="@fraction/nav_drawer_margin_6_items_horizontal"
-        app:layout_marginTopPercent="@fraction/nav_drawer_margin_6_items_vertical"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_6_items_horizontal"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_1"
+        android:id="@+id/ws_nav_drawer_icon_1"
         android:layout_centerHorizontal="true"
         android:layout_alignParentTop="true"
-        app:layout_marginTopPercent="@fraction/nav_drawer_margin_6_items_vertical"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_2"
+        android:id="@+id/ws_nav_drawer_icon_2"
         android:layout_alignParentEnd="true"
         android:layout_alignParentTop="true"
-        app:layout_marginEndPercent="@fraction/nav_drawer_margin_6_items_horizontal"
-        app:layout_marginTopPercent="@fraction/nav_drawer_margin_6_items_vertical"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_6_items_horizontal"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_3"
+        android:id="@+id/ws_nav_drawer_icon_3"
         android:layout_alignParentStart="true"
         android:layout_alignParentBottom="true"
-        app:layout_marginStartPercent="@fraction/nav_drawer_margin_6_items_horizontal"
-        app:layout_marginBottomPercent="@fraction/nav_drawer_margin_6_items_vertical"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_6_items_horizontal"
+        app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_4"
+        android:id="@+id/ws_nav_drawer_icon_4"
         android:layout_centerHorizontal="true"
         android:layout_alignParentBottom="true"
-        app:layout_marginBottomPercent="@fraction/nav_drawer_margin_6_items_vertical"
+        app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
 
     <android.support.wear.widget.CircledImageView
-        android:id="@+id/wearable_support_nav_drawer_icon_5"
+        android:id="@+id/ws_nav_drawer_icon_5"
         android:layout_alignParentEnd="true"
         android:layout_alignParentBottom="true"
-        app:layout_marginEndPercent="@fraction/nav_drawer_margin_6_items_horizontal"
-        app:layout_marginBottomPercent="@fraction/nav_drawer_margin_6_items_vertical"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_6_items_horizontal"
+        app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_6_items_vertical"
         tools:ignore="ContentDescription"
-        style="@style/SinglePageNavDrawerIconStyle" />
+        style="@style/WsSinglePageNavDrawerIconStyle" />
 
 </android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/ws_single_page_nav_drawer_7_item.xml b/wear/res/layout/ws_single_page_nav_drawer_7_item.xml
new file mode 100644
index 0000000..5daef22
--- /dev/null
+++ b/wear/res/layout/ws_single_page_nav_drawer_7_item.xml
@@ -0,0 +1,81 @@
+<?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.
+-->
+<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false">
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_0"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_outer_rows"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_7_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_1"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentTop="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_outer_rows"
+        app:layout_marginTopPercent="@fraction/ws_nav_drawer_margin_7_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_2"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_middle_row"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_3"
+        android:layout_centerInParent="true"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_4"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_middle_row"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_5"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentBottom="true"
+        app:layout_marginStartPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_outer_rows"
+        app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_7_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+    <android.support.wear.widget.CircledImageView
+        android:id="@+id/ws_nav_drawer_icon_6"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentBottom="true"
+        app:layout_marginEndPercent="@fraction/ws_nav_drawer_margin_7_items_horizontal_outer_rows"
+        app:layout_marginBottomPercent="@fraction/ws_nav_drawer_margin_7_items_vertical"
+        tools:ignore="ContentDescription"
+        style="@style/WsSinglePageNavDrawerIconStyle" />
+
+</android.support.percent.PercentRelativeLayout>
diff --git a/wear/res/layout/single_page_nav_drawer_peek_view.xml b/wear/res/layout/ws_single_page_nav_drawer_peek_view.xml
similarity index 78%
rename from wear/res/layout/single_page_nav_drawer_peek_view.xml
rename to wear/res/layout/ws_single_page_nav_drawer_peek_view.xml
index e0ed880..8b11554 100644
--- a/wear/res/layout/single_page_nav_drawer_peek_view.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_peek_view.xml
@@ -20,13 +20,13 @@
     android:layout_height="wrap_content"
     android:gravity="center"
     android:orientation="vertical"
-    android:paddingTop="@dimen/peek_view_top_padding"
-    android:paddingBottom="@dimen/peek_view_bottom_padding">
+    android:paddingTop="@dimen/ws_peek_view_top_padding"
+    android:paddingBottom="@dimen/ws_peek_view_bottom_padding">
 
     <ImageView
-        android:layout_width="@dimen/peek_view_icon_size"
-        android:layout_height="@dimen/peek_view_icon_size"
-        android:src="@drawable/ic_expand_more_white_22"
+        android:layout_width="@dimen/ws_peek_view_icon_size"
+        android:layout_height="@dimen/ws_peek_view_icon_size"
+        android:src="@drawable/ws_ic_expand_more_white_22"
         android:tint="?android:attr/colorForeground"
         tools:ignore="ContentDescription"/>
 
diff --git a/wear/res/values-sw180dp-notround/dimens.xml b/wear/res/values-sw180dp-notround/dimens.xml
index 87938e5..f6395d3 100644
--- a/wear/res/values-sw180dp-notround/dimens.xml
+++ b/wear/res/values-sw180dp-notround/dimens.xml
@@ -15,11 +15,11 @@
 -->
 <resources>
 
-    <dimen name="action_drawer_item_top_padding">10dp</dimen>
-    <dimen name="action_drawer_item_bottom_padding">10dp</dimen>
-    <dimen name="action_drawer_item_icon_right_margin">12dp</dimen>
-    <dimen name="action_drawer_item_icon_size">40dp</dimen>
-    <dimen name="action_drawer_item_icon_padding">8dp</dimen>
-    <dimen name="action_drawer_item_text_size">16sp</dimen>
+    <dimen name="ws_action_drawer_item_top_padding">10dp</dimen>
+    <dimen name="ws_action_drawer_item_bottom_padding">10dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_right_margin">12dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_size">40dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_padding">8dp</dimen>
+    <dimen name="ws_action_drawer_item_text_size">16sp</dimen>
 
 </resources>
diff --git a/wear/res/values-sw210dp-round/dimens.xml b/wear/res/values-sw210dp-round/dimens.xml
index d6d4ed0..1a2581e 100644
--- a/wear/res/values-sw210dp-round/dimens.xml
+++ b/wear/res/values-sw210dp-round/dimens.xml
@@ -14,10 +14,10 @@
      limitations under the License.
 -->
 <resources>
-    <dimen name="action_drawer_item_top_padding">10dp</dimen>
-    <dimen name="action_drawer_item_bottom_padding">10dp</dimen>
-    <dimen name="action_drawer_item_icon_right_margin">12dp</dimen>
-    <dimen name="action_drawer_item_icon_size">40dp</dimen>
-    <dimen name="action_drawer_item_icon_padding">8dp</dimen>
-    <dimen name="action_drawer_item_text_size">16sp</dimen>
+    <dimen name="ws_action_drawer_item_top_padding">10dp</dimen>
+    <dimen name="ws_action_drawer_item_bottom_padding">10dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_right_margin">12dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_size">40dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_padding">8dp</dimen>
+    <dimen name="ws_action_drawer_item_text_size">16sp</dimen>
 </resources>
diff --git a/wear/res/values-sw210dp/dimens.xml b/wear/res/values-sw210dp/dimens.xml
index e8d7afc..8f69e0b 100644
--- a/wear/res/values-sw210dp/dimens.xml
+++ b/wear/res/values-sw210dp/dimens.xml
@@ -15,8 +15,8 @@
 -->
 <resources>
 
-    <dimen name="nav_drawer_text_size">14sp</dimen>
-    <dimen name="nav_drawer_single_page_icon_size">32dp</dimen>
-    <dimen name="nav_drawer_single_page_circle_radius">27dp</dimen>
+    <dimen name="ws_nav_drawer_text_size">14sp</dimen>
+    <dimen name="ws_nav_drawer_single_page_icon_size">32dp</dimen>
+    <dimen name="ws_nav_drawer_single_page_circle_radius">27dp</dimen>
 
 </resources>
diff --git a/wear/res/values-v20/styles.xml b/wear/res/values-v20/styles.xml
index aa442f7..2104f6d 100644
--- a/wear/res/values-v20/styles.xml
+++ b/wear/res/values-v20/styles.xml
@@ -15,20 +15,20 @@
 -->
 <resources>
 
-    <style name="PageIndicatorViewStyle">
-        <item name="pageIndicatorDotSpacing">7.8dp</item>
-        <item name="pageIndicatorDotRadius">2.1dp</item>
-        <item name="pageIndicatorDotRadiusSelected">3.1dp</item>
-        <item name="pageIndicatorDotColor">?android:attr/colorForeground</item>
-        <item name="pageIndicatorDotColorSelected">?android:attr/colorForeground</item>
-        <item name="pageIndicatorDotFadeOutDelay">1000</item>
-        <item name="pageIndicatorDotFadeOutDuration">250</item>
-        <item name="pageIndicatorDotFadeInDuration">100</item>
-        <item name="pageIndicatorDotFadeWhenIdle">true</item>
-        <item name="pageIndicatorDotShadowColor">#66000000</item>
-        <item name="pageIndicatorDotShadowRadius">1dp</item>
-        <item name="pageIndicatorDotShadowDx">0.5dp</item>
-        <item name="pageIndicatorDotShadowDy">0.5dp</item>
+    <style name="WsPageIndicatorViewStyle">
+        <item name="dotSpacing">7.8dp</item>
+        <item name="dotRadius">2.1dp</item>
+        <item name="dotRadiusSelected">3.1dp</item>
+        <item name="dotColor">?android:attr/colorForeground</item>
+        <item name="dotColorSelected">?android:attr/colorForeground</item>
+        <item name="dotFadeOutDelay">1000</item>
+        <item name="dotFadeOutDuration">250</item>
+        <item name="dotFadeInDuration">100</item>
+        <item name="dotFadeWhenIdle">true</item>
+        <item name="dotShadowColor">#66000000</item>
+        <item name="dotShadowRadius">1dp</item>
+        <item name="dotShadowDx">0.5dp</item>
+        <item name="dotShadowDy">0.5dp</item>
     </style>
 
 </resources>
diff --git a/wear/res/values-v23/styles.xml b/wear/res/values-v23/styles.xml
index 49a94eb..6bb1a51 100644
--- a/wear/res/values-v23/styles.xml
+++ b/wear/res/values-v23/styles.xml
@@ -14,15 +14,15 @@
      limitations under the License.
 -->
 <resources>
-    <style name="WearableActionDrawerItemText">
+    <style name="WsWearableActionDrawerItemText">
         <item name="android:layout_gravity">center_vertical</item>
         <item name="android:ellipsize">end</item>
         <item name="android:fontFamily">sans-serif-condensed-light</item>
         <item name="android:maxLines">3</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
-        <item name="android:textSize">@dimen/action_drawer_item_text_size</item>
+        <item name="android:textSize">@dimen/ws_action_drawer_item_text_size</item>
     </style>
-    <style name="WearableActionDrawerTitleText" parent="android:TextAppearance.Material.Subhead">
+    <style name="WsWearableActionDrawerTitleText" parent="android:TextAppearance.Material.Subhead">
         <item name="android:alpha">0.7</item>
     </style>
 </resources>
diff --git a/wear/res/values/attrs.xml b/wear/res/values/attrs.xml
index 673fda5..8688a52 100644
--- a/wear/res/values/attrs.xml
+++ b/wear/res/values/attrs.xml
@@ -101,70 +101,70 @@
 
     <declare-styleable name="PageIndicatorView">
         <!-- Sets the distance between dots. -->
-        <attr name="pageIndicatorDotSpacing" format="dimension" />
+        <attr name="dotSpacing" format="dimension" />
         <!-- Sets the radius of a dot when it is not selected. -->
-        <attr name="pageIndicatorDotRadius" format="dimension" />
+        <attr name="dotRadius" format="dimension" />
         <!-- Sets the radius of a dot when it is selected. -->
-        <attr name="pageIndicatorDotRadiusSelected" format="dimension" />
+        <attr name="dotRadiusSelected" format="dimension" />
         <!-- Sets the color of a dot when it is not selected. -->
-        <attr name="pageIndicatorDotColor" format="color" />
+        <attr name="dotColor" format="color" />
         <!-- Sets the color of a dot when it is selected. -->
-        <attr name="pageIndicatorDotColorSelected" format="color" />
+        <attr name="dotColorSelected" format="color" />
         <!-- Sets whether the dots should fade out after inactivity. -->
-        <attr name="pageIndicatorDotFadeWhenIdle" format="boolean" />
+        <attr name="dotFadeWhenIdle" format="boolean" />
         <!-- Sets the delay between the pager arriving at an idle state, and the fade out animation
              beginning, in milliseconds. -->
-        <attr name="pageIndicatorDotFadeOutDelay" format="integer" />
+        <attr name="dotFadeOutDelay" format="integer" />
         <!-- Sets the duration of the fade out animation. -->
-        <attr name="pageIndicatorDotFadeOutDuration" format="integer" />
+        <attr name="dotFadeOutDuration" format="integer" />
         <!-- Sets the duration of the fade in animation. -->
-        <attr name="pageIndicatorDotFadeInDuration" format="integer" />
+        <attr name="dotFadeInDuration" format="integer" />
         <!-- Sets the shadow color. -->
-        <attr name="pageIndicatorDotShadowColor" format="color" />
+        <attr name="dotShadowColor" format="color" />
         <!-- Sets the shadow radius. -->
-        <attr name="pageIndicatorDotShadowRadius" format="dimension" />
+        <attr name="dotShadowRadius" format="dimension" />
         <!-- Sets the horizontal shadow offset. -->
-        <attr name="pageIndicatorDotShadowDx" format="dimension" />
+        <attr name="dotShadowDx" format="dimension" />
         <!-- Sets the vertical shadow offset. -->
-        <attr name="pageIndicatorDotShadowDy" format="dimension" />
+        <attr name="dotShadowDy" format="dimension" />
     </declare-styleable>
 
     <declare-styleable name="CircledImageView">
         <attr name="android:src" />
         <!-- Sets the color of the circle. -->
-        <attr name="circle_color" format="color" />
+        <attr name="background_color" format="color" />
         <!-- Sets the radius of the circle. -->
-        <attr name="circle_radius" format="dimension" />
+        <attr name="background_radius" format="dimension" />
         <!-- Sets the radius of the circle while the circle is being pressed. -->
-        <attr name="circle_radius_pressed" format="dimension" />
+        <attr name="background_radius_pressed" format="dimension" />
         <!-- Sets the width of the border. -->
-        <attr name="circle_border_width" format="dimension" />
+        <attr name="background_border_width" format="dimension" />
         <!-- Sets the color of the border. -->
-        <attr name="circle_border_color" format="color" />
+        <attr name="background_border_color" format="color" />
         <!-- Sets the stroke cap for the border around the circle. -->
-        <attr name="circle_border_cap" format="enum">
+        <attr name="background_border_cap" format="enum">
             <enum name="butt" value="0" />
             <enum name="round" value="1" />
             <enum name="square" value="2" />
         </attr>
         <!-- Sets the padding between the edge of the circle and the start of the image. -->
-        <attr name="circle_padding" format="dimension" />
+        <attr name="img_padding" format="dimension" />
         <!-- Sets the width of the shadow. -->
-        <attr name="shadow_width" format="dimension" />
+        <attr name="background_shadow_width" format="dimension" />
         <!-- Sets the percentage of the circle which the image should occupy. -->
-        <attr name="image_circle_percentage" format="dimension" />
+        <attr name="img_circle_percentage" format="dimension" />
         <!-- Sets the percentage of the circle which the image should should be offset
              horizontally. -->
-        <attr name="image_horizontal_offcenter_percentage" format="dimension" />
+        <attr name="img_horizontal_offset_percentage" format="dimension" />
         <!-- Sets the tint color of the image. -->
-        <attr name="image_tint" format="color" />
+        <attr name="img_tint" format="color" />
         <!-- Sets the radius of the circle to be a percentage of the largest dimension of the
              view. -->
-        <attr name="circle_radius_percent" format="fraction" />
+        <attr name="background_radius_percent" format="fraction" />
         <!-- Sets the circle radius when pressed. -->
-        <attr name="circle_radius_pressed_percent" format="fraction" />
+        <attr name="background_radius_pressed_percent" format="fraction" />
         <!-- Sets which dimension to use if the image isn't square. -->
-        <attr name="square_dimen" format="enum">
+        <attr name="clip_dimen" format="enum">
             <enum name="none" value="0" />
             <enum name="height" value="1" />
             <enum name="width" value="2" />
diff --git a/wear/res/values/dimens.xml b/wear/res/values/dimens.xml
index c39fc77..4e5496d 100644
--- a/wear/res/values/dimens.xml
+++ b/wear/res/values/dimens.xml
@@ -15,50 +15,52 @@
 -->
 <resources>
     <!-- Values for the WearableRecyclerView. -->
-    <dimen name="wrv_curve_default_x_offset">24dp</dimen>
+    <dimen name="ws_wrv_curve_default_x_offset">24dp</dimen>
 
     <!-- Values for WearableDrawerView. -->
-    <dimen name="wearable_drawer_view_elevation">12dp</dimen>
+    <dimen name="ws_wearable_drawer_view_elevation">12dp</dimen>
 
     <!-- Values for WearableActionDrawerView. -->
-    <item name="action_drawer_item_first_item_top_padding" type="fraction">15%</item>
-    <item name="action_drawer_item_last_item_bottom_padding" type="fraction">15%</item>
-    <item name="action_drawer_item_left_padding" type="fraction">15%</item>
-    <item name="action_drawer_item_right_padding" type="fraction">10%</item>
-    <dimen name="action_drawer_item_top_padding">8dp</dimen>
-    <dimen name="action_drawer_item_bottom_padding">8dp</dimen>
-    <dimen name="action_drawer_item_icon_right_margin">8dp</dimen>
-    <dimen name="action_drawer_item_icon_size">36dp</dimen>
-    <dimen name="action_drawer_item_icon_padding">7dp</dimen>
-    <dimen name="action_drawer_item_text_size">14sp</dimen>
-    <dimen name="action_drawer_peek_view_height">43dp</dimen>
-    <dimen name="action_drawer_peek_top_padding">2dp</dimen>
-    <dimen name="action_drawer_expand_icon_top_margin">-3dp</dimen>
+    <item name="ws_action_drawer_item_first_item_top_padding" type="fraction">15%</item>
+    <item name="ws_action_drawer_item_last_item_bottom_padding" type="fraction">15%</item>
+    <item name="ws_action_drawer_item_left_padding" type="fraction">15%</item>
+    <item name="ws_action_drawer_item_right_padding" type="fraction">10%</item>
+    <dimen name="ws_action_drawer_item_top_padding">8dp</dimen>
+    <dimen name="ws_action_drawer_item_bottom_padding">8dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_right_margin">8dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_size">36dp</dimen>
+    <dimen name="ws_action_drawer_item_icon_padding">7dp</dimen>
+    <dimen name="ws_action_drawer_item_text_size">14sp</dimen>
+    <dimen name="ws_action_drawer_peek_view_height">43dp</dimen>
+    <dimen name="ws_action_drawer_peek_top_padding">2dp</dimen>
+    <dimen name="ws_action_drawer_expand_icon_top_margin">-3dp</dimen>
 
     <!-- Dimensions for the single page WearableNavigationDrawerView. -->
-    <dimen name="nav_drawer_single_page_icon_size">28dp</dimen>
-    <dimen name="nav_drawer_single_page_icon_padding">6dp</dimen>
-    <dimen name="nav_drawer_single_page_circle_radius">24dp</dimen>
-    <dimen name="nav_drawer_text_size">12sp</dimen>
-    <fraction name="nav_drawer_text_margin">13%</fraction>
-    <fraction name="nav_drawer_margin_2_items">20%</fraction>
-    <fraction name="nav_drawer_margin_3_items">16%</fraction>
-    <fraction name="nav_drawer_margin_4_items_vertical">16.9%</fraction>
-    <fraction name="nav_drawer_margin_4_items_horizontal">13%</fraction>
-    <fraction name="nav_drawer_margin_5_items_horizontal_outer_rows">27.4%</fraction>
-    <fraction name="nav_drawer_margin_5_items_horizontal_middle_row">13%</fraction>
-    <fraction name="nav_drawer_margin_5_items_vertical">16.9%</fraction>
-    <fraction name="nav_drawer_margin_6_items_horizontal">16%</fraction>
-    <fraction name="nav_drawer_margin_6_items_vertical">26.8%</fraction>
-    <fraction name="nav_drawer_margin_7_items_horizontal_outer_rows">27.4%</fraction>
-    <fraction name="nav_drawer_margin_7_items_horizontal_middle_row">13%</fraction>
-    <fraction name="nav_drawer_margin_7_items_vertical">16.9%</fraction>
+    <dimen name="ws_nav_drawer_single_page_icon_size">28dp</dimen>
+    <dimen name="ws_nav_drawer_single_page_icon_padding">6dp</dimen>
+    <dimen name="ws_nav_drawer_single_page_circle_radius">24dp</dimen>
+    <dimen name="ws_nav_drawer_text_size">12sp</dimen>
+    <fraction name="ws_nav_drawer_text_margin">13%</fraction>
+    <fraction name="ws_nav_drawer_margin_2_items">20%</fraction>
+    <fraction name="ws_nav_drawer_margin_3_items">16%</fraction>
+    <fraction name="ws_nav_drawer_margin_4_items_vertical">16.9%</fraction>
+    <fraction name="ws_nav_drawer_margin_4_items_horizontal">13%</fraction>
+    <fraction name="ws_nav_drawer_margin_5_items_horizontal_outer_rows">27.4%</fraction>
+    <fraction name="ws_nav_drawer_margin_5_items_horizontal_middle_row">13%</fraction>
+    <fraction name="ws_nav_drawer_margin_5_items_vertical">16.9%</fraction>
+    <fraction name="ws_nav_drawer_margin_6_items_horizontal">16%</fraction>
+    <fraction name="ws_nav_drawer_margin_6_items_vertical">26.8%</fraction>
+    <fraction name="ws_nav_drawer_margin_7_items_horizontal_outer_rows">27.4%</fraction>
+    <fraction name="ws_nav_drawer_margin_7_items_horizontal_middle_row">13%</fraction>
+    <fraction name="ws_nav_drawer_margin_7_items_vertical">16.9%</fraction>
 
-    <dimen name="peek_view_top_padding">8dp</dimen>
-    <dimen name="peek_view_bottom_padding">8dp</dimen>
-    <dimen name="peek_view_icon_size">22dp</dimen>
+    <dimen name="ws_peek_view_top_padding">8dp</dimen>
+    <dimen name="ws_peek_view_bottom_padding">8dp</dimen>
+    <dimen name="ws_peek_view_icon_size">22dp</dimen>
 
     <!-- Maximum distance from edge to consider an edge drag. This should be the sum of the peek
          view's top padding, bottom padding, and icon size. -->
-    <dimen name="drawer_view_edge_size">38dp</dimen>
+    <dimen name="ws_drawer_view_edge_size">38dp</dimen>
+    <!-- Dimensions for the wearable switch. -->
+    <dimen name="ws_switch_size">40dp</dimen>
 </resources>
diff --git a/wear/res/values/ids.xml b/wear/res/values/ids.xml
index 881e52a..8a7324e 100644
--- a/wear/res/values/ids.xml
+++ b/wear/res/values/ids.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <resources>
-    <item name="wearable_support_navigation_drawer_view_pager" type="id" />
+    <item name="ws_navigation_drawer_view_pager" type="id" />
 </resources>
diff --git a/wear/res/values/strings.xml b/wear/res/values/strings.xml
index 4d50ddf..3bbb78c 100644
--- a/wear/res/values/strings.xml
+++ b/wear/res/values/strings.xml
@@ -15,8 +15,8 @@
 -->
 <resources>
     <!-- String describing the navigation drawer which is read aloud when focused using accessibility gestures [CHAR LIMIT=35] -->
-    <string name="navigation_drawer_content_description">Navigation drawer</string>
+    <string name="ws_navigation_drawer_content_description">Navigation drawer</string>
 
     <!-- String describing the action drawer which is read aloud when focused using accessibility gestures [CHAR LIMIT=35] -->
-    <string name="action_drawer_content_description">Action drawer</string>
+    <string name="ws_action_drawer_content_description">Action drawer</string>
 </resources>
diff --git a/wear/res/values/styles.xml b/wear/res/values/styles.xml
index 706df13..c0036f1 100644
--- a/wear/res/values/styles.xml
+++ b/wear/res/values/styles.xml
@@ -14,27 +14,41 @@
      limitations under the License.
 -->
 <resources>
-    <style name="SinglePageNavDrawerIconStyle">
-        <item name="android:layout_height">@dimen/nav_drawer_single_page_icon_size</item>
-        <item name="android:layout_width">@dimen/nav_drawer_single_page_icon_size</item>
-        <item name="android:padding">@dimen/nav_drawer_single_page_icon_padding</item>
+    <style name="WsSinglePageNavDrawerIconStyle">
+        <item name="android:layout_height">@dimen/ws_nav_drawer_single_page_icon_size</item>
+        <item name="android:layout_width">@dimen/ws_nav_drawer_single_page_icon_size</item>
+        <item name="android:padding">@dimen/ws_nav_drawer_single_page_icon_padding</item>
         <item name="android:clipToPadding">false</item>
-        <item name="circle_radius">@dimen/nav_drawer_single_page_circle_radius</item>
-        <item name="circle_color">#33ffffff</item>
+        <item name="background_radius">@dimen/ws_nav_drawer_single_page_circle_radius</item>
+        <item name="background_color">#33ffffff</item>
     </style>
-    <style name="SinglePageNavDrawerTextStyle">
+    <style name="WsSinglePageNavDrawerTextStyle">
         <item name="android:layout_height">wrap_content</item>
         <item name="android:layout_width">match_parent</item>
         <item name="android:fontFamily">sans-serif-condensed</item>
-        <item name="android:textSize">@dimen/nav_drawer_text_size</item>
+        <item name="android:textSize">@dimen/ws_nav_drawer_text_size</item>
         <item name="android:gravity">center</item>
         <item name="android:maxLines">2</item>
-        <item name="layout_marginTopPercent">@fraction/nav_drawer_text_margin</item>
-        <item name="layout_marginStartPercent">@fraction/nav_drawer_text_margin</item>
-        <item name="layout_marginEndPercent">@fraction/nav_drawer_text_margin</item>
+        <item name="layout_marginTopPercent">@fraction/ws_nav_drawer_text_margin</item>
+        <item name="layout_marginStartPercent">@fraction/ws_nav_drawer_text_margin</item>
+        <item name="layout_marginEndPercent">@fraction/ws_nav_drawer_text_margin</item>
     </style>
-    <style name="Widget.Wearable.WearableDrawerView" parent="">
-        <item name="android:elevation">@dimen/wearable_drawer_view_elevation</item>
+    <style name="Widget.Wear.WearableDrawerView" parent="">
+        <item name="android:elevation">@dimen/ws_wearable_drawer_view_elevation</item>
         <item name="android:background">?android:attr/colorBackgroundFloating</item>
     </style>
+    <style name="Widget.Wear.RoundSwitch"
+            parent="@android:style/Widget.Material.CompoundButton.Switch">
+        <item name="android:layout_width">@dimen/ws_switch_size</item>
+        <item name="android:layout_height">@dimen/ws_switch_size</item>
+        <item name="android:switchMinWidth">@dimen/ws_switch_size</item>
+        <item name="android:layout_gravity">center</item>
+        <item name="android:thumb">@drawable/ws_switch_thumb_material_anim</item>
+        <item name="android:thumbTint">@color/ws_switch_thumb_color_material</item>
+        <item name="android:thumbTintMode">multiply</item>
+        <item name="android:track">@drawable/ws_switch_track_mtrl</item>
+        <item name="android:trackTint">@color/ws_switch_track_color_material</item>
+        <item name="android:background">@empty</item>
+        <item name="android:showText">false</item>
+    </style>
 </resources>
diff --git a/wear/src/android/support/wear/internal/widget/drawer/MultiPageUi.java b/wear/src/android/support/wear/internal/widget/drawer/MultiPageUi.java
index 8a65b57..9056845 100644
--- a/wear/src/android/support/wear/internal/widget/drawer/MultiPageUi.java
+++ b/wear/src/android/support/wear/internal/widget/drawer/MultiPageUi.java
@@ -59,16 +59,16 @@
         mPresenter = presenter;
 
         LayoutInflater inflater = LayoutInflater.from(drawer.getContext());
-        final View content =
-                inflater.inflate(R.layout.navigation_drawer_view, drawer, false /* attachToRoot */);
+        final View content = inflater.inflate(R.layout.ws_navigation_drawer_view, drawer,
+                false /* attachToRoot */);
 
         mNavigationPager =
                 (ViewPager) content
-                        .findViewById(R.id.wearable_support_navigation_drawer_view_pager);
+                        .findViewById(R.id.ws_navigation_drawer_view_pager);
         mPageIndicatorView =
                 (PageIndicatorView)
                         content.findViewById(
-                                R.id.wearable_support_navigation_drawer_page_indicator);
+                                R.id.ws_navigation_drawer_page_indicator);
 
         drawer.setDrawerContent(content);
     }
@@ -138,13 +138,13 @@
             // of this method. Attaching to root will cause view to point to container instead.
             final View view =
                     LayoutInflater.from(container.getContext())
-                            .inflate(R.layout.navigation_drawer_item_view, container, false);
+                            .inflate(R.layout.ws_navigation_drawer_item_view, container, false);
             container.addView(view);
             final ImageView iconView =
                     (ImageView) view
-                            .findViewById(R.id.wearable_support_navigation_drawer_item_icon);
+                            .findViewById(R.id.ws_navigation_drawer_item_icon);
             final TextView textView =
-                    (TextView) view.findViewById(R.id.wearable_support_navigation_drawer_item_text);
+                    (TextView) view.findViewById(R.id.ws_navigation_drawer_item_text);
             iconView.setImageDrawable(mAdapter.getItemDrawable(position));
             textView.setText(mAdapter.getItemText(position));
             return view;
diff --git a/wear/src/android/support/wear/internal/widget/drawer/SinglePageUi.java b/wear/src/android/support/wear/internal/widget/drawer/SinglePageUi.java
index 9a834eb..f3a4290 100644
--- a/wear/src/android/support/wear/internal/widget/drawer/SinglePageUi.java
+++ b/wear/src/android/support/wear/internal/widget/drawer/SinglePageUi.java
@@ -44,26 +44,26 @@
     @IdRes
     private static final int[] SINGLE_PAGE_BUTTON_IDS =
             new int[]{
-                    R.id.wearable_support_nav_drawer_icon_0,
-                    R.id.wearable_support_nav_drawer_icon_1,
-                    R.id.wearable_support_nav_drawer_icon_2,
-                    R.id.wearable_support_nav_drawer_icon_3,
-                    R.id.wearable_support_nav_drawer_icon_4,
-                    R.id.wearable_support_nav_drawer_icon_5,
-                    R.id.wearable_support_nav_drawer_icon_6,
+                    R.id.ws_nav_drawer_icon_0,
+                    R.id.ws_nav_drawer_icon_1,
+                    R.id.ws_nav_drawer_icon_2,
+                    R.id.ws_nav_drawer_icon_3,
+                    R.id.ws_nav_drawer_icon_4,
+                    R.id.ws_nav_drawer_icon_5,
+                    R.id.ws_nav_drawer_icon_6,
             };
 
     @LayoutRes
     private static final int[] SINGLE_PAGE_LAYOUT_RES =
             new int[]{
                     0,
-                    R.layout.single_page_nav_drawer_1_item,
-                    R.layout.single_page_nav_drawer_2_item,
-                    R.layout.single_page_nav_drawer_3_item,
-                    R.layout.single_page_nav_drawer_4_item,
-                    R.layout.single_page_nav_drawer_5_item,
-                    R.layout.single_page_nav_drawer_6_item,
-                    R.layout.single_page_nav_drawer_7_item,
+                    R.layout.ws_single_page_nav_drawer_1_item,
+                    R.layout.ws_single_page_nav_drawer_2_item,
+                    R.layout.ws_single_page_nav_drawer_3_item,
+                    R.layout.ws_single_page_nav_drawer_4_item,
+                    R.layout.ws_single_page_nav_drawer_5_item,
+                    R.layout.ws_single_page_nav_drawer_6_item,
+                    R.layout.ws_single_page_nav_drawer_7_item,
             };
 
     private final WearableNavigationDrawerView mDrawer;
@@ -108,10 +108,10 @@
         View content = inflater.inflate(layoutRes, mDrawer, false /* attachToRoot */);
         final View peek =
                 inflater.inflate(
-                        R.layout.single_page_nav_drawer_peek_view, mDrawer,
+                        R.layout.ws_single_page_nav_drawer_peek_view, mDrawer,
                         false /* attachToRoot */);
 
-        mTextView = (TextView) content.findViewById(R.id.wearable_support_nav_drawer_text);
+        mTextView = (TextView) content.findViewById(R.id.ws_nav_drawer_text);
         mSinglePageImageViews = new CircledImageView[count];
         for (int i = 0; i < count; i++) {
             mSinglePageImageViews[i] = (CircledImageView) content
diff --git a/wear/src/android/support/wear/widget/CircledImageView.java b/wear/src/android/support/wear/widget/CircledImageView.java
index 413b913..e21a1ab 100644
--- a/wear/src/android/support/wear/widget/CircledImageView.java
+++ b/wear/src/android/support/wear/widget/CircledImageView.java
@@ -143,54 +143,55 @@
             mDrawable = mDrawable.mutate();
         }
 
-        mCircleColor = a.getColorStateList(R.styleable.CircledImageView_circle_color);
+        mCircleColor = a.getColorStateList(R.styleable.CircledImageView_background_color);
         if (mCircleColor == null) {
             mCircleColor = ColorStateList.valueOf(android.R.color.darker_gray);
         }
 
-        mCircleRadius = a.getDimension(R.styleable.CircledImageView_circle_radius, 0);
+        mCircleRadius = a.getDimension(R.styleable.CircledImageView_background_radius, 0);
         mInitialCircleRadius = mCircleRadius;
-        mCircleRadiusPressed =
-                a.getDimension(R.styleable.CircledImageView_circle_radius_pressed, mCircleRadius);
+        mCircleRadiusPressed = a.getDimension(
+                R.styleable.CircledImageView_background_radius_pressed, mCircleRadius);
         mCircleBorderColor = a
-                .getColor(R.styleable.CircledImageView_circle_border_color, Color.BLACK);
+                .getColor(R.styleable.CircledImageView_background_border_color, Color.BLACK);
         mCircleBorderCap =
-                Paint.Cap.values()[a.getInt(R.styleable.CircledImageView_circle_border_cap, 0)];
-        mCircleBorderWidth = a.getDimension(R.styleable.CircledImageView_circle_border_width, 0);
+                Paint.Cap.values()[a.getInt(R.styleable.CircledImageView_background_border_cap, 0)];
+        mCircleBorderWidth = a.getDimension(
+                R.styleable.CircledImageView_background_border_width, 0);
 
         if (mCircleBorderWidth > 0) {
             // The border arc is drawn from the middle of the arc - take that into account.
             mRadiusInset += mCircleBorderWidth / 2;
         }
 
-        float circlePadding = a.getDimension(R.styleable.CircledImageView_circle_padding, 0);
+        float circlePadding = a.getDimension(R.styleable.CircledImageView_img_padding, 0);
         if (circlePadding > 0) {
             mRadiusInset += circlePadding;
         }
 
         mImageCirclePercentage = a
-                .getFloat(R.styleable.CircledImageView_image_circle_percentage, 0f);
+                .getFloat(R.styleable.CircledImageView_img_circle_percentage, 0f);
 
         mImageHorizontalOffcenterPercentage =
-                a.getFloat(R.styleable.CircledImageView_image_horizontal_offcenter_percentage, 0f);
+                a.getFloat(R.styleable.CircledImageView_img_horizontal_offset_percentage, 0f);
 
-        if (a.hasValue(R.styleable.CircledImageView_image_tint)) {
-            mImageTint = a.getColor(R.styleable.CircledImageView_image_tint, 0);
+        if (a.hasValue(R.styleable.CircledImageView_img_tint)) {
+            mImageTint = a.getColor(R.styleable.CircledImageView_img_tint, 0);
         }
 
-        if (a.hasValue(R.styleable.CircledImageView_square_dimen)) {
-            mSquareDimen = a.getInt(R.styleable.CircledImageView_square_dimen, SQUARE_DIMEN_NONE);
+        if (a.hasValue(R.styleable.CircledImageView_clip_dimen)) {
+            mSquareDimen = a.getInt(R.styleable.CircledImageView_clip_dimen, SQUARE_DIMEN_NONE);
         }
 
         mCircleRadiusPercent =
-                a.getFraction(R.styleable.CircledImageView_circle_radius_percent, 1, 1, 0f);
+                a.getFraction(R.styleable.CircledImageView_background_radius_percent, 1, 1, 0f);
 
         mCircleRadiusPressedPercent =
                 a.getFraction(
-                        R.styleable.CircledImageView_circle_radius_pressed_percent, 1, 1,
+                        R.styleable.CircledImageView_background_radius_pressed_percent, 1, 1,
                         mCircleRadiusPercent);
 
-        float shadowWidth = a.getDimension(R.styleable.CircledImageView_shadow_width, 0);
+        float shadowWidth = a.getDimension(R.styleable.CircledImageView_background_shadow_width, 0);
 
         a.recycle();
 
diff --git a/wear/src/android/support/wear/widget/CurvingLayoutCallback.java b/wear/src/android/support/wear/widget/CurvingLayoutCallback.java
index f6eed46..275f1f8 100644
--- a/wear/src/android/support/wear/widget/CurvingLayoutCallback.java
+++ b/wear/src/android/support/wear/widget/CurvingLayoutCallback.java
@@ -53,7 +53,7 @@
         mPathMeasure = new PathMeasure();
         mIsScreenRound = context.getResources().getConfiguration().isScreenRound();
         mXCurveOffset = context.getResources().getDimensionPixelSize(
-                R.dimen.wrv_curve_default_x_offset);
+                R.dimen.ws_wrv_curve_default_x_offset);
     }
 
     @Override
diff --git a/wear/src/android/support/wear/widget/drawer/PageIndicatorView.java b/wear/src/android/support/wear/widget/drawer/PageIndicatorView.java
index 7317c21..bd2dded 100644
--- a/wear/src/android/support/wear/widget/drawer/PageIndicatorView.java
+++ b/wear/src/android/support/wear/widget/drawer/PageIndicatorView.java
@@ -96,28 +96,28 @@
                 getContext()
                         .obtainStyledAttributes(
                                 attrs, R.styleable.PageIndicatorView, defStyleAttr,
-                                R.style.PageIndicatorViewStyle);
+                                R.style.WsPageIndicatorViewStyle);
 
         mDotSpacing =
-                a.getDimensionPixelOffset(R.styleable.PageIndicatorView_pageIndicatorDotSpacing, 0);
-        mDotRadius = a.getDimension(R.styleable.PageIndicatorView_pageIndicatorDotRadius, 0);
+                a.getDimensionPixelOffset(R.styleable.PageIndicatorView_dotSpacing, 0);
+        mDotRadius = a.getDimension(R.styleable.PageIndicatorView_dotRadius, 0);
         mDotRadiusSelected =
-                a.getDimension(R.styleable.PageIndicatorView_pageIndicatorDotRadiusSelected, 0);
-        mDotColor = a.getColor(R.styleable.PageIndicatorView_pageIndicatorDotColor, 0);
+                a.getDimension(R.styleable.PageIndicatorView_dotRadiusSelected, 0);
+        mDotColor = a.getColor(R.styleable.PageIndicatorView_dotColor, 0);
         mDotColorSelected = a
-                .getColor(R.styleable.PageIndicatorView_pageIndicatorDotColorSelected, 0);
-        mDotFadeOutDelay = a.getInt(R.styleable.PageIndicatorView_pageIndicatorDotFadeOutDelay, 0);
+                .getColor(R.styleable.PageIndicatorView_dotColorSelected, 0);
+        mDotFadeOutDelay = a.getInt(R.styleable.PageIndicatorView_dotFadeOutDelay, 0);
         mDotFadeOutDuration =
-                a.getInt(R.styleable.PageIndicatorView_pageIndicatorDotFadeOutDuration, 0);
+                a.getInt(R.styleable.PageIndicatorView_dotFadeOutDuration, 0);
         mDotFadeInDuration = a
-                .getInt(R.styleable.PageIndicatorView_pageIndicatorDotFadeInDuration, 0);
+                .getInt(R.styleable.PageIndicatorView_dotFadeInDuration, 0);
         mDotFadeWhenIdle =
-                a.getBoolean(R.styleable.PageIndicatorView_pageIndicatorDotFadeWhenIdle, false);
-        mDotShadowDx = a.getDimension(R.styleable.PageIndicatorView_pageIndicatorDotShadowDx, 0);
-        mDotShadowDy = a.getDimension(R.styleable.PageIndicatorView_pageIndicatorDotShadowDy, 0);
+                a.getBoolean(R.styleable.PageIndicatorView_dotFadeWhenIdle, false);
+        mDotShadowDx = a.getDimension(R.styleable.PageIndicatorView_dotShadowDx, 0);
+        mDotShadowDy = a.getDimension(R.styleable.PageIndicatorView_dotShadowDy, 0);
         mDotShadowRadius =
-                a.getDimension(R.styleable.PageIndicatorView_pageIndicatorDotShadowRadius, 0);
-        mDotShadowColor = a.getColor(R.styleable.PageIndicatorView_pageIndicatorDotShadowColor, 0);
+                a.getDimension(R.styleable.PageIndicatorView_dotShadowRadius, 0);
+        mDotShadowColor = a.getColor(R.styleable.PageIndicatorView_dotShadowColor, 0);
         a.recycle();
 
         mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
diff --git a/wear/src/android/support/wear/widget/drawer/WearableActionDrawerView.java b/wear/src/android/support/wear/widget/drawer/WearableActionDrawerView.java
index a663cc2..3074865 100644
--- a/wear/src/android/support/wear/widget/drawer/WearableActionDrawerView.java
+++ b/wear/src/android/support/wear/widget/drawer/WearableActionDrawerView.java
@@ -137,20 +137,20 @@
 
         if (!mShowOverflowInPeek) {
             LayoutInflater layoutInflater = LayoutInflater.from(context);
-            View peekView = layoutInflater.inflate(
-                    R.layout.action_drawer_peek_view, getPeekContainer(), false /* attachToRoot */);
+            View peekView = layoutInflater.inflate(R.layout.ws_action_drawer_peek_view,
+                    getPeekContainer(), false /* attachToRoot */);
             setPeekContent(peekView);
             mPeekActionIcon =
                     (ImageView) peekView
-                            .findViewById(R.id.wearable_support_action_drawer_peek_action_icon);
+                            .findViewById(R.id.ws_action_drawer_peek_action_icon);
             mPeekExpandIcon =
                     (ImageView) peekView
-                            .findViewById(R.id.wearable_support_action_drawer_expand_icon);
+                            .findViewById(R.id.ws_action_drawer_expand_icon);
         } else {
             mPeekActionIcon = null;
             mPeekExpandIcon = null;
             getPeekContainer().setContentDescription(
-                    context.getString(R.string.action_drawer_content_description));
+                    context.getString(R.string.ws_action_drawer_content_description));
         }
 
         if (menuRes != 0) {
@@ -164,26 +164,26 @@
         int screenHeightPx = ResourcesUtil.getScreenHeightPx(context);
 
         Resources res = getResources();
-        mTopPadding = res.getDimensionPixelOffset(R.dimen.action_drawer_item_top_padding);
-        mBottomPadding = res.getDimensionPixelOffset(R.dimen.action_drawer_item_bottom_padding);
+        mTopPadding = res.getDimensionPixelOffset(R.dimen.ws_action_drawer_item_top_padding);
+        mBottomPadding = res.getDimensionPixelOffset(R.dimen.ws_action_drawer_item_bottom_padding);
         mLeftPadding =
                 ResourcesUtil.getFractionOfScreenPx(
-                        context, screenWidthPx, R.fraction.action_drawer_item_left_padding);
+                        context, screenWidthPx, R.fraction.ws_action_drawer_item_left_padding);
         mRightPadding =
                 ResourcesUtil.getFractionOfScreenPx(
-                        context, screenWidthPx, R.fraction.action_drawer_item_right_padding);
+                        context, screenWidthPx, R.fraction.ws_action_drawer_item_right_padding);
 
         mFirstItemTopPadding =
                 ResourcesUtil.getFractionOfScreenPx(
                         context, screenHeightPx,
-                        R.fraction.action_drawer_item_first_item_top_padding);
+                        R.fraction.ws_action_drawer_item_first_item_top_padding);
         mLastItemBottomPadding =
                 ResourcesUtil.getFractionOfScreenPx(
                         context, screenHeightPx,
-                        R.fraction.action_drawer_item_last_item_bottom_padding);
+                        R.fraction.ws_action_drawer_item_last_item_bottom_padding);
 
         mIconRightMargin = res
-                .getDimensionPixelOffset(R.dimen.action_drawer_item_icon_right_margin);
+                .getDimensionPixelOffset(R.dimen.ws_action_drawer_item_icon_right_margin);
 
         mActionList = new RecyclerView(context);
         mActionList.setLayoutManager(new LinearLayoutManager(context));
@@ -359,7 +359,7 @@
         TitleViewHolder(View view) {
             super(view);
             this.view = view;
-            textView = (TextView) view.findViewById(R.id.wearable_support_action_drawer_title);
+            textView = (TextView) view.findViewById(R.id.ws_action_drawer_title);
         }
     }
 
@@ -425,14 +425,14 @@
                 case TYPE_TITLE:
                     View titleView =
                             LayoutInflater.from(parent.getContext())
-                                    .inflate(R.layout.action_drawer_title_view, parent, false);
+                                    .inflate(R.layout.ws_action_drawer_title_view, parent, false);
                     return new TitleViewHolder(titleView);
 
                 case TYPE_ACTION:
                 default:
                     View actionView =
                             LayoutInflater.from(parent.getContext())
-                                    .inflate(R.layout.action_drawer_item_view, parent, false);
+                                    .inflate(R.layout.ws_action_drawer_item_view, parent, false);
                     actionView.setOnClickListener(mItemClickListener);
                     return new ActionItemViewHolder(actionView);
             }
@@ -453,9 +453,9 @@
         ActionItemViewHolder(View view) {
             super(view);
             this.view = view;
-            iconView = (ImageView) view.findViewById(R.id.wearable_support_action_drawer_item_icon);
+            iconView = (ImageView) view.findViewById(R.id.ws_action_drawer_item_icon);
             ((LinearLayout.LayoutParams) iconView.getLayoutParams()).setMarginEnd(mIconRightMargin);
-            textView = (TextView) view.findViewById(R.id.wearable_support_action_drawer_item_text);
+            textView = (TextView) view.findViewById(R.id.ws_action_drawer_item_text);
         }
     }
 }
diff --git a/wear/src/android/support/wear/widget/drawer/WearableDrawerView.java b/wear/src/android/support/wear/widget/drawer/WearableDrawerView.java
index 97aa9f5..0203860 100644
--- a/wear/src/android/support/wear/widget/drawer/WearableDrawerView.java
+++ b/wear/src/android/support/wear/widget/drawer/WearableDrawerView.java
@@ -149,13 +149,14 @@
     public WearableDrawerView(
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        LayoutInflater.from(context).inflate(R.layout.wearable_drawer_view, this, true);
+        LayoutInflater.from(context).inflate(R.layout.ws_wearable_drawer_view, this, true);
 
         setClickable(true);
-        setElevation(context.getResources().getDimension(R.dimen.wearable_drawer_view_elevation));
+        setElevation(context.getResources()
+                .getDimension(R.dimen.ws_wearable_drawer_view_elevation));
 
-        mPeekContainer = (ViewGroup) findViewById(R.id.wearable_support_drawer_view_peek_container);
-        mPeekIcon = (ImageView) findViewById(R.id.wearable_support_drawer_view_peek_icon);
+        mPeekContainer = (ViewGroup) findViewById(R.id.ws_drawer_view_peek_container);
+        mPeekIcon = (ImageView) findViewById(R.id.ws_drawer_view_peek_icon);
 
         mPeekContainer.setOnClickListener(
                 new OnClickListener() {
@@ -215,10 +216,10 @@
                             == Gravity.TOP;
             if (isTopDrawer) {
                 peekParams.gravity = Gravity.BOTTOM;
-                mPeekIcon.setImageResource(R.drawable.ic_more_horiz_24dp_wht);
+                mPeekIcon.setImageResource(R.drawable.ws_ic_more_horiz_24dp_wht);
             } else {
                 peekParams.gravity = Gravity.TOP;
-                mPeekIcon.setImageResource(R.drawable.ic_more_vert_24dp_wht);
+                mPeekIcon.setImageResource(R.drawable.ws_ic_more_vert_24dp_wht);
             }
             mPeekContainer.setLayoutParams(peekParams);
         }
@@ -466,7 +467,7 @@
         TypedArray typedArray =
                 context.obtainStyledAttributes(
                         attrs, R.styleable.WearableDrawerView, defStyleAttr,
-                        R.style.Widget_Wearable_WearableDrawerView);
+                        R.style.Widget_Wear_WearableDrawerView);
 
         Drawable background =
                 getDrawable(context, typedArray, R.styleable.WearableDrawerView_android_background);
diff --git a/wear/src/android/support/wear/widget/drawer/WearableNavigationDrawerView.java b/wear/src/android/support/wear/widget/drawer/WearableNavigationDrawerView.java
index 5a17c5c..31cbdd0 100644
--- a/wear/src/android/support/wear/widget/drawer/WearableNavigationDrawerView.java
+++ b/wear/src/android/support/wear/widget/drawer/WearableNavigationDrawerView.java
@@ -163,7 +163,7 @@
 
         getPeekContainer()
                 .setContentDescription(
-                        context.getString(R.string.navigation_drawer_content_description));
+                        context.getString(R.string.ws_navigation_drawer_content_description));
 
         setOpenOnlyAtTopEnabled(true);
     }
diff --git a/wear/tests/src/android/support/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java b/wear/tests/src/android/support/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
index 88c89c7..07eaa87 100644
--- a/wear/tests/src/android/support/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
+++ b/wear/tests/src/android/support/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
@@ -136,7 +136,7 @@
                                 MAX_WAIT_MS));
 
         // THEN the text should display "0".
-        onView(withId(R.id.wearable_support_nav_drawer_text)).check(matches(withText("0")));
+        onView(withId(R.id.ws_nav_drawer_text)).check(matches(withText("0")));
     }
 
     @Test
@@ -151,13 +151,13 @@
                                 MAX_WAIT_MS));
 
         // WHEN the second item is selected
-        onView(withId(R.id.wearable_support_nav_drawer_icon_1)).perform(click());
+        onView(withId(R.id.ws_nav_drawer_icon_1)).perform(click());
 
         // THEN the text should display "1" and it should close.
-        onView(withId(R.id.wearable_support_nav_drawer_text))
+        onView(withId(R.id.ws_nav_drawer_text))
                 .perform(
                         waitForMatchingView(
-                                allOf(withId(R.id.wearable_support_nav_drawer_text), withText("1")),
+                                allOf(withId(R.id.ws_nav_drawer_text), withText("1")),
                                 MAX_WAIT_MS));
         onView(withId(R.id.navigation_drawer))
                 .perform(
@@ -181,7 +181,7 @@
         selectNavItem(navDrawer, 1);
 
         // THEN the text should display "1" and the listener should be notified.
-        onView(withId(R.id.wearable_support_nav_drawer_text))
+        onView(withId(R.id.ws_nav_drawer_text))
                 .check(matches(withText("1")));
         verify(mNavDrawerItemSelectedListener).onItemSelected(1);
     }
@@ -201,7 +201,7 @@
         selectNavItem(navDrawer, 1);
 
         // THEN the text should display "1" and the listener should be notified.
-        onView(allOf(withId(R.id.wearable_support_navigation_drawer_item_text), isDisplayed()))
+        onView(allOf(withId(R.id.ws_navigation_drawer_item_text), isDisplayed()))
                 .check(matches(withText("1")));
         verify(mNavDrawerItemSelectedListener).onItemSelected(1);
     }
@@ -319,7 +319,7 @@
         DrawerTestActivity activity = activityRule.getActivity();
         ImageView peekIconView =
                 (ImageView) activity
-                        .findViewById(R.id.wearable_support_action_drawer_peek_action_icon);
+                        .findViewById(R.id.ws_action_drawer_peek_action_icon);
         // THEN its peek icon should not be null
         assertNotNull(peekIconView.getDrawable());
     }
@@ -340,7 +340,7 @@
         onView(
                 allOf(
                         withParent(withId(R.id.action_drawer)),
-                        withId(R.id.wearable_support_drawer_view_peek_container)))
+                        withId(R.id.ws_drawer_view_peek_container)))
                 .perform(click());
         // THEN its click listener should be notified
         verify(mockClickListener).onMenuItemClick(any(MenuItem.class));