Merge "Improvements to GridView and size adjustments for RowView"
diff --git a/app-toolkit/core-testing/build.gradle b/app-toolkit/core-testing/build.gradle
index 3ee9584..dca8810 100644
--- a/app-toolkit/core-testing/build.gradle
+++ b/app-toolkit/core-testing/build.gradle
@@ -36,11 +36,10 @@
     api(MOCKITO_CORE, libs.exclude_bytebuddy)
 
     testImplementation(JUNIT)
-    testImplementation libs.support.annotations
 
     androidTestImplementation(JUNIT)
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
 }
 
 supportLibrary {
diff --git a/buildSrc/build_dependencies.gradle b/buildSrc/build_dependencies.gradle
index ffbc5a8..be135b9 100644
--- a/buildSrc/build_dependencies.gradle
+++ b/buildSrc/build_dependencies.gradle
@@ -29,6 +29,6 @@
 build_libs.jarjar_gradle = 'org.anarres.jarjar:jarjar-gradle:1.0.0'
 build_libs.error_prone = 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.13'
 build_libs.jacoco = 'org.jacoco:org.jacoco.core:0.7.8'
-build_libs.kotlin = [gradle_plugin: "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.51"]
+build_libs.kotlin = [gradle_plugin: "org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.0"]
 
 rootProject.ext['build_libs'] = build_libs
diff --git a/buildSrc/dependencies.gradle b/buildSrc/dependencies.gradle
index 470551b..48d3eb9 100644
--- a/buildSrc/dependencies.gradle
+++ b/buildSrc/dependencies.gradle
@@ -16,10 +16,6 @@
 // Add ext.libs for library versions
 def libs = [:]
 
-libs.exclude_annotations = {
-    exclude module: 'support-annotations'
-}
-
 libs.exclude_bytebuddy = {
     exclude group: 'net.bytebuddy'
 }
diff --git a/buildSrc/src/main/java/android/support/LibraryVersions.java b/buildSrc/src/main/java/android/support/LibraryVersions.java
index efa0cba..a96f630 100644
--- a/buildSrc/src/main/java/android/support/LibraryVersions.java
+++ b/buildSrc/src/main/java/android/support/LibraryVersions.java
@@ -43,7 +43,7 @@
     /**
      * Version code for RecyclerView & Room paging
      */
-    public static final Version PAGING = new Version("1.0.0-alpha3");
+    public static final Version PAGING = new Version("1.0.0-alpha4");
 
     private static final Version LIFECYCLES = new Version("1.0.3");
 
diff --git a/buildSrc/src/main/java/android/support/checkapi/UpdateApiTask.java b/buildSrc/src/main/java/android/support/checkapi/UpdateApiTask.java
index de2db91..15e9104 100644
--- a/buildSrc/src/main/java/android/support/checkapi/UpdateApiTask.java
+++ b/buildSrc/src/main/java/android/support/checkapi/UpdateApiTask.java
@@ -29,7 +29,6 @@
 import java.io.File;
 import java.nio.charset.Charset;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -120,11 +119,6 @@
         }
 
         if (mWhitelistErrorsFile != null && !mWhitelistErrors.isEmpty()) {
-            if (mWhitelistErrorsFile.exists()) {
-                List<String> lines =
-                        Files.readLines(mWhitelistErrorsFile, Charset.defaultCharset());
-                mWhitelistErrors.removeAll(lines);
-            }
             try (BufferedWriter writer = Files.newWriter(
                     mWhitelistErrorsFile, Charset.defaultCharset())) {
                 for (String error : mWhitelistErrors) {
diff --git a/buildSrc/src/main/kotlin/android/support/DiffAndDocs.kt b/buildSrc/src/main/kotlin/android/support/DiffAndDocs.kt
index 9f0e402..3432cbf 100644
--- a/buildSrc/src/main/kotlin/android/support/DiffAndDocs.kt
+++ b/buildSrc/src/main/kotlin/android/support/DiffAndDocs.kt
@@ -578,12 +578,12 @@
     root.subprojects { subProject ->
         subProject.afterEvaluate { project ->
             if (project.hasProperty("noDocs") && (project.properties["noDocs"] as Boolean)) {
-                project.logger.warn("Project $project.name specified noDocs, ignoring API tasks.")
+                project.logger.info("Project $project.name specified noDocs, ignoring API tasks.")
                 return@afterEvaluate
             }
             if (project.hasProperty("supportLibrary")
                     && !(project.properties["supportLibrary"] as SupportLibraryExtension).publish) {
-                project.logger.warn("Project ${project.name} is not published, ignoring API tasks.")
+                project.logger.info("Project ${project.name} is not published, ignoring API tasks.")
                 return@afterEvaluate
             }
             val library = project.extensions.findByType(LibraryExtension::class.java)
@@ -595,7 +595,7 @@
                             return@all
                         }
                         if (!hasApiFolder(project)) {
-                            project.logger.warn("Project ${project.name} doesn't have " +
+                            project.logger.info("Project ${project.name} doesn't have " +
                                     "an api folder, ignoring API tasks.")
                             return@all
                         }
@@ -609,7 +609,7 @@
                 val compileJava = project.properties["compileJava"] as JavaCompile
                 registerJavaProjectForDocsTask(generateDocsTask, compileJava)
                 if (!hasApiFolder(project)) {
-                    project.logger.warn("Project $project.name doesn't have an api folder, " +
+                    project.logger.info("Project $project.name doesn't have an api folder, " +
                             "ignoring API tasks.")
                     return@afterEvaluate
                 }
diff --git a/buildSrc/src/main/kotlin/android/support/SupportAndroidLibraryPlugin.kt b/buildSrc/src/main/kotlin/android/support/SupportAndroidLibraryPlugin.kt
index c83d740..5eaa7dd 100644
--- a/buildSrc/src/main/kotlin/android/support/SupportAndroidLibraryPlugin.kt
+++ b/buildSrc/src/main/kotlin/android/support/SupportAndroidLibraryPlugin.kt
@@ -37,6 +37,8 @@
                 SupportLibraryExtension::class.java, project)
         apply(project, supportLibraryExtension)
 
+        val isCoreSupportLibrary = project.rootProject.name == "support"
+
         project.afterEvaluate {
             val library = project.extensions.findByType(LibraryExtension::class.java)
                     ?: return@afterEvaluate
@@ -75,6 +77,21 @@
         project.apply(mapOf("plugin" to "com.android.library"))
         project.apply(mapOf("plugin" to ErrorProneBasePlugin::class.java))
 
+        project.configurations.all { configuration ->
+            if (isCoreSupportLibrary) {
+                // In projects which compile as part of the "core" support libraries (which include
+                // the annotations), replace any transitive pointer to the deployed Maven
+                // coordinate version of annotations with a reference to the local project. These
+                // usually originate from test dependencies and otherwise cause multiple copies on
+                // the classpath. We do not do this for non-"core" projects as they need to
+                // depend on the Maven coordinate variant.
+                configuration.resolutionStrategy.dependencySubstitution.apply {
+                    substitute(module("com.android.support:support-annotations"))
+                            .with(project(":support-annotations"))
+                }
+            }
+        }
+
         val library = project.extensions.findByType(LibraryExtension::class.java)
                 ?: throw Exception("Failed to find Android extension")
 
@@ -120,12 +137,15 @@
 
                         // Enforce the following checks.
                         "-Xep:RestrictTo:OFF",
+                        "-Xep:ParameterNotNullable:ERROR",
                         "-Xep:MissingOverride:ERROR",
+                        "-Xep:JdkObsolete:ERROR",
                         "-Xep:NarrowingCompoundAssignment:ERROR",
                         "-Xep:ClassNewInstance:ERROR",
                         "-Xep:ClassCanBeStatic:ERROR",
                         "-Xep:SynchronizeOnNonFinalField:ERROR",
-                        "-Xep:OperatorPrecedence:ERROR"
+                        "-Xep:OperatorPrecedence:ERROR",
+                        "-Xep:IntLongMath:ERROR"
                 )
             }
         }
diff --git a/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt
index 2502f77..e32cacd 100644
--- a/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt
@@ -29,7 +29,7 @@
 const val JAVAPOET = "com.squareup:javapoet:1.8.0"
 const val JSR250 = "javax.annotation:javax.annotation-api:1.2"
 const val JUNIT = "junit:junit:4.12"
-const val KOTLIN_STDLIB = "org.jetbrains.kotlin:kotlin-stdlib:1.1.51"
+const val KOTLIN_STDLIB = "org.jetbrains.kotlin:kotlin-stdlib:1.2.0"
 const val MOCKITO_CORE = "org.mockito:mockito-core:2.7.6"
 const val REACTIVE_STREAMS = "org.reactivestreams:reactive-streams:1.0.0"
 const val RX_JAVA = "io.reactivex.rxjava2:rxjava:2.0.6"
diff --git a/car/AndroidManifest.xml b/car/AndroidManifest.xml
index 4e6d80f..854e097 100644
--- a/car/AndroidManifest.xml
+++ b/car/AndroidManifest.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.car">
+          package="androidx.car">
 </manifest>
diff --git a/car/build.gradle b/car/build.gradle
index 487f2bc..3970df9 100644
--- a/car/build.gradle
+++ b/car/build.gradle
@@ -12,8 +12,8 @@
     api project(':support-v4')
     api project(':recyclerview-v7')
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(ESPRESSO_CONTRIB, libs.exclude_support)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
diff --git a/car/res/drawable/car_borderless_button_text_color.xml b/car/res/drawable/car_borderless_button_text_color.xml
new file mode 100644
index 0000000..ff27db5
--- /dev/null
+++ b/car/res/drawable/car_borderless_button_text_color.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<!-- Default text colors for car buttons when enabled/disabled. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/car_grey_700" android:state_enabled="false"/>
+    <item android:color="?android:attr/colorPrimary"/>
+</selector>
diff --git a/car/res/drawable/car_button_background.xml b/car/res/drawable/car_button_background.xml
index 3b139d9..1a8995c 100644
--- a/car/res/drawable/car_button_background.xml
+++ b/car/res/drawable/car_button_background.xml
@@ -18,14 +18,14 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false">
         <shape android:shape="rectangle">
-            <corners android:radius="@dimen/car_button_radius" />
+            <corners android:radius="@dimen/car_button_radius"/>
             <solid android:color="@color/car_grey_300"/>
         </shape>
     </item>
     <item>
         <shape android:shape="rectangle">
-            <corners android:radius="@dimen/car_button_radius" />
-            <solid android:color="@color/car_highlight"/>
+            <corners android:radius="@dimen/car_button_radius"/>
+            <solid android:color="?android:attr/colorPrimary"/>
         </shape>
     </item>
 </selector>
diff --git a/car/res/drawable/car_button_text_color.xml b/car/res/drawable/car_button_text_color.xml
index b14ec68..bb8c681 100644
--- a/car/res/drawable/car_button_text_color.xml
+++ b/car/res/drawable/car_button_text_color.xml
@@ -16,6 +16,6 @@
 -->
 <!-- Default text colors for car buttons when enabled/disabled. -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:color="@color/car_grey_700" />
+    <item android:color="@color/car_grey_700" android:state_enabled="false"/>
     <item android:color="@color/car_action1"/>
 </selector>
diff --git a/car/res/layout/car_drawer.xml b/car/res/layout/car_drawer.xml
index 812acb4..c4ce405 100644
--- a/car/res/layout/car_drawer.xml
+++ b/car/res/layout/car_drawer.xml
@@ -23,7 +23,7 @@
     android:background="@color/car_card"
     android:paddingTop="@dimen/car_app_bar_height" >
 
-  <android.support.car.widget.PagedListView
+  <androidx.car.widget.PagedListView
       android:id="@+id/drawer_list"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
diff --git a/car/res/layout/car_paged_list_card.xml b/car/res/layout/car_paged_list_card.xml
new file mode 100644
index 0000000..fe5de89
--- /dev/null
+++ b/car/res/layout/car_paged_list_card.xml
@@ -0,0 +1,28 @@
+<?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.v7.widget.CardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:layout_marginBottom="@dimen/car_padding_1"
+    app:cardCornerRadius="@dimen/car_radius_1"
+    app:cardBackgroundColor="@color/car_card">
+
+    <include layout="@layout/car_paged_list_item_content" />
+
+</android.support.v7.widget.CardView>
diff --git a/v14/preference/res/values-v21/styles.xml b/car/res/layout/car_paged_list_item.xml
similarity index 66%
rename from v14/preference/res/values-v21/styles.xml
rename to car/res/layout/car_paged_list_item.xml
index 9a85987..c0861d9 100644
--- a/v14/preference/res/values-v21/styles.xml
+++ b/car/res/layout/car_paged_list_item.xml
@@ -12,9 +12,14 @@
   ~ 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
+  ~ limitations under the License.
   -->
-<resources>
-    <dimen name="preference_no_icon_padding_start">72dp</dimen>
-</resources>
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:background="@color/car_card">
 
+    <include layout="@layout/car_paged_list_item_content" />
+
+</FrameLayout>
diff --git a/car/res/layout/car_paged_list_item_content.xml b/car/res/layout/car_paged_list_item_content.xml
new file mode 100644
index 0000000..0e6b809
--- /dev/null
+++ b/car/res/layout/car_paged_list_item_content.xml
@@ -0,0 +1,102 @@
+<?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.
+  -->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/container"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent">
+
+    <!-- Primary Action. -->
+    <ImageView
+        android:id="@+id/primary_icon"
+        android:layout_width="@dimen/car_single_line_list_item_height"
+        android:layout_height="@dimen/car_single_line_list_item_height"
+        android:layout_centerVertical="true"/>
+
+    <!-- Text. -->
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_toStartOf="@id/supplemental_actions"
+        android:singleLine="true"
+        android:ellipsize="end"/>
+    <TextView
+        android:id="@+id/body"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_toStartOf="@id/supplemental_actions"/>
+
+    <!-- Supplemental action(s) - supports either 1 supplemental icon or up to 2 action buttons. -->
+    <LinearLayout
+        android:id="@+id/supplemental_actions"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_alignParentEnd="true"
+        android:layout_marginEnd="@dimen/car_keyline_1"
+        android:layout_centerVertical="true"
+        android:gravity="center_vertical"
+        android:orientation="horizontal">
+        <!-- End icon with divider. -->
+        <View
+            android:id="@+id/supplemental_icon_divider"
+            android:layout_width="@dimen/car_vertical_line_divider_width"
+            android:layout_height="@dimen/car_vertical_line_divider_height"
+            android:layout_marginStart="@dimen/car_padding_4"
+            android:background="@color/car_list_divider"/>
+        <ImageView
+            android:id="@+id/supplemental_icon"
+            android:layout_width="@dimen/car_primary_icon_size"
+            android:layout_height="@dimen/car_primary_icon_size"
+            android:layout_marginStart="@dimen/car_padding_4"
+            android:scaleType="fitCenter"/>
+
+        <!-- Up to 2 action buttons with dividers. -->
+        <View
+            android:id="@+id/action2_divider"
+            android:layout_width="@dimen/car_vertical_line_divider_width"
+            android:layout_height="@dimen/car_vertical_line_divider_height"
+            android:layout_marginStart="@dimen/car_padding_4"
+            android:background="@color/car_list_divider"/>
+        <Button
+            android:id="@+id/action2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/car_padding_4"
+            android:ellipsize="end"
+            android:maxLength="@integer/car_borderless_button_text_length_limit"
+            android:maxLines="1"
+            android:background="@color/car_card"
+            style="@style/CarButton.Borderless"/>
+        <View
+            android:id="@+id/action1_divider"
+            android:layout_width="@dimen/car_vertical_line_divider_width"
+            android:layout_height="@dimen/car_vertical_line_divider_height"
+            android:layout_marginStart="@dimen/car_padding_4"
+            android:background="@color/car_list_divider"/>
+        <Button
+            android:id="@+id/action1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/car_padding_4"
+            android:ellipsize="end"
+            android:maxLength="@integer/car_borderless_button_text_length_limit"
+            android:maxLines="1"
+            android:background="@color/car_card"
+            style="@style/CarButton.Borderless"/>
+    </LinearLayout>
+</RelativeLayout>
diff --git a/car/res/layout/car_paged_recycler_view.xml b/car/res/layout/car_paged_recycler_view.xml
index 47a82ff..d3ca4a3 100644
--- a/car/res/layout/car_paged_recycler_view.xml
+++ b/car/res/layout/car_paged_recycler_view.xml
@@ -19,14 +19,14 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <android.support.car.widget.CarRecyclerView
+    <androidx.car.widget.CarRecyclerView
         android:id="@+id/recycler_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
     <!-- Putting this as the last child so that it can intercept any touch events on the
          scroll buttons. -->
-    <android.support.car.widget.PagedScrollBarView
+    <androidx.car.widget.PagedScrollBarView
         android:id="@+id/paged_scroll_view"
         android:layout_width="@dimen/car_margin"
         android:layout_height="match_parent"
diff --git a/car/res/layout/car_paged_scrollbar_buttons.xml b/car/res/layout/car_paged_scrollbar_buttons.xml
index 7dd213a..d49b532 100644
--- a/car/res/layout/car_paged_scrollbar_buttons.xml
+++ b/car/res/layout/car_paged_scrollbar_buttons.xml
@@ -37,8 +37,8 @@
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_weight="1"
-        android:layout_marginBottom="@dimen/car_paged_list_view_scrollbar_thumb_margin"
-        android:layout_marginTop="@dimen/car_paged_list_view_scrollbar_thumb_margin" >
+        android:layout_marginBottom="@dimen/car_scroll_bar_thumb_margin"
+        android:layout_marginTop="@dimen/car_scroll_bar_thumb_margin" >
 
         <ImageView
             android:id="@+id/scrollbar_thumb"
diff --git a/car/res/values-h1752dp/dimens.xml b/car/res/values-h1752dp/dimens.xml
index cd2bf46..4b6a23d 100644
--- a/car/res/values-h1752dp/dimens.xml
+++ b/car/res/values-h1752dp/dimens.xml
@@ -14,6 +14,7 @@
 limitations under the License.
 -->
 <resources>
+    <!-- Car Component Dimensions -->
     <!-- Type Sizings -->
     <dimen name="car_title2_size">40sp</dimen>
     <dimen name="car_headline1_size">56sp</dimen>
@@ -22,17 +23,31 @@
     <dimen name="car_body2_size">32sp</dimen>
     <dimen name="car_action1_size">32sp</dimen>
 
-    <!-- Car Component Dimensions -->
-    <!-- Application Bar Height -->
-    <dimen name="car_app_bar_height">112dp</dimen>
-
-    <dimen name="car_touch_target">96dp</dimen>
-
-    <!-- Icon dimensions -->
+    <!-- Icons and Buttons -->
+    <!-- Icons -->
     <dimen name="car_primary_icon_size">56dp</dimen>
     <dimen name="car_secondary_icon_size">36dp</dimen>
 
-    <!-- Line heights -->
+    <!-- Avatars -->
+    <dimen name="car_avatar_size">96dp</dimen>
+
+    <!-- Minimum touch target size. -->
+    <dimen name="car_touch_target_size">96dp</dimen>
+
+    <!-- Application Bar -->
+    <dimen name="car_app_bar_height">112dp</dimen>
+
+    <!-- List Items -->
     <dimen name="car_single_line_list_item_height">128dp</dimen>
     <dimen name="car_double_line_list_item_height">128dp</dimen>
+
+    <!-- Cards -->
+    <dimen name="car_card_header_height">96dp</dimen>
+    <dimen name="car_card_action_bar_height">96dp</dimen>
+
+    <!-- Slide Up Menu -->
+    <dimen name="car_slide_up_menu_initial_height">128dp</dimen>
+
+    <!-- Sub Header -->
+    <dimen name="car_sub_header_height">96dp</dimen>
 </resources>
diff --git a/car/res/values-h684dp/dimens.xml b/car/res/values-h684dp/dimens.xml
index a072681..039d377 100644
--- a/car/res/values-h684dp/dimens.xml
+++ b/car/res/values-h684dp/dimens.xml
@@ -15,14 +15,27 @@
 -->
 <resources>
     <!-- Car Component Dimensions -->
+    <!-- Application Bar -->
     <dimen name="car_app_bar_height">96dp</dimen>
 
-    <!-- List and Drawer Dimensions -->
+    <!-- List Items -->
+    <dimen name="car_single_line_list_item_height">116dp</dimen>
+    <dimen name="car_double_line_list_item_height">116dp</dimen>
+
+    <!-- Slide Up Menu -->
+    <dimen name="car_slide_up_menu_initial_height">116dp</dimen>
+
+    <!-- Scroll Bar -->
+    <dimen name="car_scroll_bar_padding">@dimen/car_padding_4</dimen>
+
+    <!-- Scroll Bar Thumb -->
+    <dimen name="car_scroll_bar_thumb_margin">@dimen/car_padding_2</dimen>
+
+    <!-- Scroll Bar Buttons -->
+    <dimen name="car_scroll_bar_button_size">76dp</dimen>
+
+    <!-- Drawer Dimensions -->
     <dimen name="car_drawer_list_item_icon_size">108dp</dimen>
     <dimen name="car_drawer_list_item_small_icon_size">56dp</dimen>
     <dimen name="car_drawer_list_item_end_icon_size">56dp</dimen>
-
-    <!-- Line heights -->
-    <dimen name="car_single_line_list_item_height">116dp</dimen>
-    <dimen name="car_double_line_list_item_height">116dp</dimen>
 </resources>
diff --git a/car/res/values-w1280dp/dimens.xml b/car/res/values-w1280dp/dimens.xml
index 9837355..418e51f 100644
--- a/car/res/values-w1280dp/dimens.xml
+++ b/car/res/values-w1280dp/dimens.xml
@@ -14,17 +14,11 @@
 limitations under the License.
 -->
 <resources>
-    <dimen name="car_screen_margin_size">148dp</dimen>
-    <dimen name="car_scroll_bar_button_size">76dp</dimen>
-    
-    <dimen name="car_keyline_1">32dp</dimen>
-    <dimen name="car_keyline_2">108dp</dimen>
-    <dimen name="car_keyline_3">128dp</dimen>
-    <dimen name="car_keyline_4">182dp</dimen>
-    <dimen name="car_keyline_1_neg">32dp</dimen>
-    <dimen name="car_keyline_2_neg">108dp</dimen>
-    <dimen name="car_keyline_3_neg">128dp</dimen>
-
+    <!-- Framework -->
     <!-- Margin -->
     <dimen name="car_margin">148dp</dimen>
+
+    <!-- Keylines -->
+    <dimen name="car_keyline_4">182dp</dimen>
+    <dimen name="car_keyline_4_neg">-182dp</dimen>
 </resources>
diff --git a/car/res/values-w840dp/integers.xml b/car/res/values-w1280dp/integers.xml
similarity index 84%
rename from car/res/values-w840dp/integers.xml
rename to car/res/values-w1280dp/integers.xml
index 38c0440..62fcf37 100644
--- a/car/res/values-w840dp/integers.xml
+++ b/car/res/values-w1280dp/integers.xml
@@ -14,6 +14,10 @@
 limitations under the License.
 -->
 <resources>
-    <integer name="car_screen_num_of_columns">12</integer>
+    <!-- Application Components -->
+    <!-- Cards -->
     <integer name="column_card_default_column_span">8</integer>
+
+    <!-- Dialogs -->
+    <integer name="car_dialog_column_number">8</integer>
 </resources>
diff --git a/car/res/values-w1920dp/dimens.xml b/car/res/values-w1920dp/dimens.xml
index 52962a1..b02ec00 100644
--- a/car/res/values-w1920dp/dimens.xml
+++ b/car/res/values-w1920dp/dimens.xml
@@ -14,8 +14,16 @@
 limitations under the License.
 -->
 <resources>
-    <dimen name="car_keyline_3">152dp</dimen>
-
+    <!-- Framework -->
     <!-- Margin -->
     <dimen name="car_margin">192dp</dimen>
+
+    <!-- Gutters -->
+    <dimen name="car_gutter_size">32dp</dimen>
+
+    <!-- Keylines -->
+    <dimen name="car_keyline_1">48dp</dimen>
+    <dimen name="car_keyline_3">152dp</dimen>
+    <dimen name="car_keyline_1_neg">-48dp</dimen>
+    <dimen name="car_keyline_3_neg">-152dp</dimen>
 </resources>
diff --git a/car/res/values-w1024dp/dimens.xml b/car/res/values-w1920dp/integers.xml
similarity index 86%
rename from car/res/values-w1024dp/dimens.xml
rename to car/res/values-w1920dp/integers.xml
index b1ae5ba..6519af5 100644
--- a/car/res/values-w1024dp/dimens.xml
+++ b/car/res/values-w1920dp/integers.xml
@@ -14,5 +14,7 @@
 limitations under the License.
 -->
 <resources>
-    <dimen name="car_screen_margin_size">112dp</dimen>
+    <!-- Framework -->
+    <!-- Columns -->
+    <integer name="car_column_number">16</integer>
 </resources>
diff --git a/car/res/values-w480dp/dimens.xml b/car/res/values-w480dp/dimens.xml
deleted file mode 100644
index 4077e0d..0000000
--- a/car/res/values-w480dp/dimens.xml
+++ /dev/null
@@ -1,18 +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.
--->
-<resources>
-    <dimen name="car_screen_margin_size">24dp</dimen>
-</resources>
diff --git a/car/res/values-w600dp/integers.xml b/car/res/values-w600dp/integers.xml
deleted file mode 100644
index 5dcd8df..0000000
--- a/car/res/values-w600dp/integers.xml
+++ /dev/null
@@ -1,19 +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.
--->
-<resources>
-    <integer name="car_screen_num_of_columns">8</integer>
-    <integer name="column_card_default_column_span">6</integer>
-</resources>
diff --git a/car/res/values-w690dp/dimens.xml b/car/res/values-w690dp/dimens.xml
index f797955..2d43ac8 100644
--- a/car/res/values-w690dp/dimens.xml
+++ b/car/res/values-w690dp/dimens.xml
@@ -14,14 +14,7 @@
 limitations under the License.
 -->
 <resources>
-    <dimen name="car_keyline_1">24dp</dimen>
-    <dimen name="car_keyline_2">96dp</dimen>
-    <dimen name="car_keyline_3">112dp</dimen>
-    <dimen name="car_keyline_4">148dp</dimen>
-    <dimen name="car_keyline_1_neg">-24dp</dimen>
-    <dimen name="car_keyline_2_neg">-96dp</dimen>
-    <dimen name="car_keyline_3_neg">-112dp</dimen>
-
+    <!-- Framework -->
     <!-- Margin -->
     <dimen name="car_margin">112dp</dimen>
 </resources>
diff --git a/car/res/values-w840dp/integers.xml b/car/res/values-w690dp/integers.xml
similarity index 62%
copy from car/res/values-w840dp/integers.xml
copy to car/res/values-w690dp/integers.xml
index 38c0440..0eb5837 100644
--- a/car/res/values-w840dp/integers.xml
+++ b/car/res/values-w690dp/integers.xml
@@ -14,6 +14,17 @@
 limitations under the License.
 -->
 <resources>
-    <integer name="car_screen_num_of_columns">12</integer>
-    <integer name="column_card_default_column_span">8</integer>
+    <!-- Framework -->
+    <!-- Columns -->
+    <integer name="car_column_number">12</integer>
+
+    <!-- Application Components -->
+    <!-- Cards -->
+    <integer name="column_card_default_column_span">12</integer>
+
+    <!-- Dialogs -->
+    <integer name="car_dialog_column_number">10</integer>
+
+    <!-- Slide Up Menu -->
+    <integer name="car_slide_up_menu_column_number">12</integer>
 </resources>
diff --git a/car/res/values-w720dp/dimens.xml b/car/res/values-w720dp/dimens.xml
deleted file mode 100644
index b1ae5ba..0000000
--- a/car/res/values-w720dp/dimens.xml
+++ /dev/null
@@ -1,18 +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.
--->
-<resources>
-    <dimen name="car_screen_margin_size">112dp</dimen>
-</resources>
diff --git a/car/res/values-w840dp/dimens.xml b/car/res/values-w840dp/dimens.xml
deleted file mode 100644
index 8b4d992..0000000
--- a/car/res/values-w840dp/dimens.xml
+++ /dev/null
@@ -1,21 +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.
--->
-<resources>
-    <dimen name="car_keyline_1">32dp</dimen>
-    <dimen name="car_keyline_2">108dp</dimen>
-    <dimen name="car_keyline_3">128dp</dimen>
-    <dimen name="car_screen_gutter_size">24dp</dimen>
-</resources>
diff --git a/car/res/values-w930dp/dimens.xml b/car/res/values-w930dp/dimens.xml
index 481480e..6d7714e 100644
--- a/car/res/values-w930dp/dimens.xml
+++ b/car/res/values-w930dp/dimens.xml
@@ -14,6 +14,11 @@
 limitations under the License.
 -->
 <resources>
+    <!-- Framework -->
+    <!-- Gutters -->
+    <dimen name="car_gutter_size">24dp</dimen>
+
+    <!-- Keylines -->
     <dimen name="car_keyline_1">32dp</dimen>
     <dimen name="car_keyline_2">108dp</dimen>
     <dimen name="car_keyline_3">128dp</dimen>
@@ -21,7 +26,5 @@
     <dimen name="car_keyline_1_neg">-32dp</dimen>
     <dimen name="car_keyline_2_neg">-108dp</dimen>
     <dimen name="car_keyline_3_neg">-128dp</dimen>
-
-    <!-- Margin -->
-    <dimen name="car_margin">112dp</dimen>
+    <dimen name="car_keyline_4_neg">-168dp</dimen>
 </resources>
diff --git a/car/res/values-w840dp/integers.xml b/car/res/values-w930dp/integers.xml
similarity index 76%
copy from car/res/values-w840dp/integers.xml
copy to car/res/values-w930dp/integers.xml
index 38c0440..60ddfa1 100644
--- a/car/res/values-w840dp/integers.xml
+++ b/car/res/values-w930dp/integers.xml
@@ -14,6 +14,10 @@
 limitations under the License.
 -->
 <resources>
-    <integer name="car_screen_num_of_columns">12</integer>
-    <integer name="column_card_default_column_span">8</integer>
+    <!-- Application Components -->
+    <!-- Cards -->
+    <integer name="column_card_default_column_span">10</integer>
+
+    <!-- Dialogs -->
+    <integer name="car_dialog_column_number">10</integer>
 </resources>
diff --git a/car/res/values/attrs.xml b/car/res/values/attrs.xml
index 0ba8f55..01ed95f 100644
--- a/car/res/values/attrs.xml
+++ b/car/res/values/attrs.xml
@@ -28,8 +28,24 @@
         <!-- Set to true/false to offset rows as they slide off screen. Defaults to true -->
         <attr name="offsetRows" format="boolean" />
         <!-- Whether or not to offset the list view by the width of scroll bar. Setting this to
-             true will ensure that any views within the list will not overlap the scroll bar. -->
+             true will ensure that any views within the list will not overlap the scroll bar.
+             Deprecated: use gutter instead. If gutter is specified, this value is ignored.-->
         <attr name="offsetScrollBar" format="boolean" />
+        <!-- Whether to include a gutter to the start, end or both sides of the list view items.
+             The gutter width will be the width of the scrollbar, and by default will be set to
+             both. -->
+        <attr name="gutter" format="enum">
+            <!-- No gutter on either side, the list view items will extend the full width of the
+                 PagedListView. -->
+            <enum name="none" value="0" />
+            <!-- Include a gutter on the start side only (i.e. the side with the scrollbar). -->
+            <enum name="start" value="1" />
+            <!-- Include a gutter on the end side only (i.e. the opposite side to the
+                 scrollbar). -->
+            <enum name="end" value="2" />
+            <!-- Include a gutter on both sides of the list view items. -->
+            <enum name="both" value="3" />
+        </attr>
         <!-- Whether to display the scrollbar or not. Defaults to true. -->
         <attr name="scrollBarEnabled" format="boolean" />
         <!-- Whether or not to show a diving line between each item of the list. -->
@@ -43,7 +59,8 @@
         <!-- A starting margin before the drawing of the dividing line. This margin will be an
              offset from the view specified by "alignDividerStartTo" if given. -->
         <attr name="dividerStartMargin" format="dimension" />
-        <!-- The width of the margin on the right side of the list -->
+        <!-- The width of the margin on the right side of the list.
+             Deprecated: use gutter instead. If gutter is specified, this value is ignored.-->
         <attr name="listEndMargin" format="dimension" />
         <!-- An optional spacing between items in the list -->
         <attr name="itemSpacing" format="dimension" />
diff --git a/car/res/values/dimens.xml b/car/res/values/dimens.xml
index f1761c8..8e8621c 100644
--- a/car/res/values/dimens.xml
+++ b/car/res/values/dimens.xml
@@ -14,15 +14,37 @@
 limitations under the License.
 -->
 <resources>
-    <!-- Keylines for content. -->
-    <dimen name="car_keyline_1">48dp</dimen>
-    <dimen name="car_keyline_2">108dp</dimen>
-    <dimen name="car_keyline_3">152dp</dimen>
-    <dimen name="car_keyline_4">182dp</dimen>
-    <dimen name="car_keyline_1_neg">-48dp</dimen>
-    <dimen name="car_keyline_2_neg">-108dp</dimen>
-    <dimen name="car_keyline_3_neg">-152dp</dimen>
+    <!-- Framework -->
+    <!-- Margin -->
+    <dimen name="car_margin">20dp</dimen>
 
+    <!-- Gutters -->
+    <dimen name="car_gutter_size">16dp</dimen>
+
+    <!-- Keylines -->
+    <dimen name="car_keyline_1">24dp</dimen>
+    <dimen name="car_keyline_2">96dp</dimen>
+    <dimen name="car_keyline_3">112dp</dimen>
+    <dimen name="car_keyline_4">148dp</dimen>
+    <dimen name="car_keyline_1_neg">-24dp</dimen>
+    <dimen name="car_keyline_2_neg">-96dp</dimen>
+    <dimen name="car_keyline_3_neg">-112dp</dimen>
+    <dimen name="car_keyline_4_neg">-148dp</dimen>
+
+    <!-- Paddings -->
+    <dimen name="car_padding_1">4dp</dimen>
+    <dimen name="car_padding_2">10dp</dimen>
+    <dimen name="car_padding_3">16dp</dimen>
+    <dimen name="car_padding_4">28dp</dimen>
+    <dimen name="car_padding_5">32dp</dimen>
+
+    <!-- Radii -->
+    <dimen name="car_radius_1">4dp</dimen>
+    <dimen name="car_radius_2">8dp</dimen>
+    <dimen name="car_radius_3">16dp</dimen>
+    <dimen name="car_radius_5">100dp</dimen>
+
+    <!-- Car Component Dimensions -->
     <!-- Type Sizings -->
     <dimen name="car_title_size">32sp</dimen>
     <dimen name="car_title2_size">32sp</dimen>
@@ -37,84 +59,91 @@
     <dimen name="car_body5_size">18sp</dimen>
     <dimen name="car_action1_size">26sp</dimen>
 
-    <!-- Paddings -->
-    <dimen name="car_padding_1">4dp</dimen>
-    <dimen name="car_padding_2">10dp</dimen>
-    <dimen name="car_padding_3">16dp</dimen>
-    <dimen name="car_padding_4">28dp</dimen>
-    <dimen name="car_padding_5">32dp</dimen>
-
-    <!-- Radius -->
-    <dimen name="car_radius_1">4dp</dimen>
-    <dimen name="car_radius_2">8dp</dimen>
-    <dimen name="car_radius_3">16dp</dimen>
-    <dimen name="car_radius_5">100dp</dimen>
-
-    <!-- Margin -->
-    <dimen name="car_margin">20dp</dimen>
-
-    <!-- Car Component Dimensions -->
-    <!-- Application Bar Height -->
-    <dimen name="car_app_bar_height">80dp</dimen>
-
-    <!-- The height of the bar that contains an applications action buttons. -->
-    <dimen name="car_action_bar_height">128dp</dimen>
-
-    <!-- Minimum touch target size. -->
-    <dimen name="car_touch_target">76dp</dimen>
-
-    <!-- Button Dimensions -->
-    <dimen name="car_button_height">64dp</dimen>
-    <dimen name="car_button_min_width">158dp</dimen>
-    <dimen name="car_button_horizontal_padding">@dimen/car_padding_4</dimen>
-    <dimen name="car_button_radius">@dimen/car_radius_1</dimen>
-
-    <!-- Icon dimensions -->
+    <!-- Icons and Buttons -->
+    <!-- Icons -->
     <dimen name="car_primary_icon_size">44dp</dimen>
     <dimen name="car_secondary_icon_size">24dp</dimen>
 
-    <!-- Line heights -->
+    <!-- Avatars -->
+    <dimen name="car_avatar_size">56dp</dimen>
+
+    <!-- Minimum touch target size. -->
+    <dimen name="car_touch_target_size">76dp</dimen>
+
+    <!-- Buttons -->
+    <dimen name="car_button_height">64dp</dimen>
+    <dimen name="car_button_min_width">158dp</dimen>
+    <dimen name="car_button_horizontal_padding">@dimen/car_padding_4</dimen>
+    <dimen name="car_borderless_button_horizontal_padding">0dp</dimen>
+    <dimen name="car_button_radius">@dimen/car_radius_1</dimen>
+
+    <!-- Application Bar -->
+    <dimen name="car_app_bar_height">80dp</dimen>
+
+    <!-- Action Bars -->
+    <dimen name="car_action_bar_height">128dp</dimen>
+    <dimen name="car_secondary_single_action_bar_height">@dimen/car_action_bar_height</dimen>
+    <dimen name="car_secondary_double_action_bar_height">256dp</dimen>
+
+    <!-- Lists -->
     <dimen name="car_single_line_list_item_height">76dp</dimen>
     <dimen name="car_double_line_list_item_height">96dp</dimen>
+    <dimen name="car_list_divider_height">1dp</dimen>
+    <!-- The height of a vertical line divider. -->
+    <dimen name="car_vertical_line_divider_height">60dp</dimen>
+    <dimen name="car_vertical_line_divider_width">1dp</dimen>
 
-    <!-- List and Drawer Dimensions -->
-    <!-- The margin on both sides of the screen before the contents of the PagedListView. -->
-    <dimen name="car_card_margin">96dp</dimen>
+    <!-- Cards -->
+    <dimen name="car_card_header_height">76dp</dimen>
+    <dimen name="car_card_action_bar_height">76dp</dimen>
 
-    <!-- The height of the dividers in the list. -->
-    <dimen name="car_divider_height">1dp</dimen>
+    <!-- Dialogs -->
+    <dimen name="car_dialog_header_height">@dimen/car_card_header_height</dimen>
+    <dimen name="car_dialog_action_bar_height">@dimen/car_card_action_bar_height</dimen>
 
+    <!-- Slide Up Menu -->
+    <dimen name="car_slide_up_menu_initial_height">76dp</dimen>
+
+    <!-- Slide Down Menu -->
+    <dimen name="car_slide_down_menu_initial_height">@dimen/car_slide_up_menu_initial_height</dimen>
+
+    <!-- Sub Header -->
+    <dimen name="car_sub_header_height">76dp</dimen>
+
+    <!-- Slider -->
+    <dimen name="car_slider_height">6dp</dimen>
+    <dimen name="car_slider_knob_size">20dp</dimen>
+
+    <!-- Scroll Bar -->
+    <dimen name="car_scroll_bar_padding">@dimen/car_padding_2</dimen>
+
+    <!-- Scroll Bar Thumb -->
+    <dimen name="car_scroll_bar_thumb_width">@dimen/car_slider_height</dimen>
+    <dimen name="car_min_scroll_bar_thumb_height">48dp</dimen>
+    <dimen name="car_max_scroll_bar_thumb_height">128dp</dimen>
+    <dimen name="car_scroll_bar_thumb_margin">@dimen/car_padding_1</dimen>
+
+    <!-- Scroll Bar and Alpha Jump Buttons -->
+    <dimen name="car_scroll_bar_button_size">56dp</dimen>
+    <dimen name="car_alpha_jump_button_size">@dimen/car_scroll_bar_button_size</dimen>
+
+    <!-- Progress Bar -->
+    <dimen name="car_progress_bar_height">@dimen/car_slider_height</dimen>
+
+    <!-- Text Input -->
+    <dimen name="car_text_input_line_height">2dp</dimen>
+
+    <!-- PagedListView Dimensions -->
     <!-- Sample row height used for scroll bar calculations in the off chance that a view hasn't
-         been measured. It's highly unlikely that this value will actually be used for more than
-         a frame max. The sample row is a 96dp card + 16dp margin on either side. -->
+        been measured. It's highly unlikely that this value will actually be used for more than
+        a frame max. The sample row is a 96dp card + 16dp margin on either side. -->
     <dimen name="car_sample_row_height">128dp</dimen>
 
     <!-- The amount of space the LayoutManager will make sure the last item on the screen is
          peeking before scrolling down -->
     <dimen name="car_last_card_peek_amount">16dp</dimen>
 
-    <!-- The spacing between each column that fits on the screen. The number of columns is
-         determined by integer/car_screen_num_of_columns. -->
-    <dimen name="car_screen_gutter_size">16dp</dimen>
-
-    <!-- The margin on both sizes of the scroll bar thumb. -->
-    <dimen name="car_paged_list_view_scrollbar_thumb_margin">8dp</dimen>
-
-    <!-- The size of the scroll bar up and down arrows. -->
-    <dimen name="car_scroll_bar_button_size">44dp</dimen>
-
-    <!-- The padding around the scroll bar. -->
-    <dimen name="car_scroll_bar_padding">16dp</dimen>
-
-    <!-- The width of the scroll bar thumb. -->
-    <dimen name="car_scroll_bar_thumb_width">6dp</dimen>
-
-    <!-- The minimum the scrollbar thumb can shrink to -->
-    <dimen name="min_thumb_height">48dp</dimen>
-
-    <!-- The maximum the scrollbar thumb can grow to -->
-    <dimen name="max_thumb_height">128dp</dimen>
-
+    <!-- Drawer Dimensions -->
     <!-- Size of progress-bar in Drawer -->
     <dimen name="car_drawer_progress_bar_size">48dp</dimen>
 
diff --git a/car/res/values/integers.xml b/car/res/values/integers.xml
index 575d646..6352d7c 100644
--- a/car/res/values/integers.xml
+++ b/car/res/values/integers.xml
@@ -14,10 +14,28 @@
 limitations under the License.
 -->
 <resources>
-    <!-- The number of columns that appear on-screen. -->
-    <integer name="car_screen_num_of_columns">4</integer>
+    <!-- Framework -->
+    <!-- Columns -->
+    <integer name="car_column_number">4</integer>
 
-    <!-- The default number of columns that a ColumnCardView will span if columnSpan is not
-         specified.-->
+    <!-- Application Components -->
+    <!-- Action Bar -->
+    <integer name="action_bar_column_number">@integer/car_column_number</integer>
+
+    <!-- Cards -->
     <integer name="column_card_default_column_span">4</integer>
+
+    <!-- Dialogs -->
+    <integer name="car_dialog_column_number">10</integer>
+
+    <!-- Slide Up Menu -->
+    <integer name="car_slide_up_menu_column_number">4</integer>
+
+    <!-- The length limit of body text in a paged list item. String longer than this limit should be
+         truncated. -->
+    <integer name="car_list_item_text_length_limit">120</integer>
+
+    <!-- The length limit of text in a borderless button. String longer than this limit should be
+         truncated. -->
+    <integer name="car_borderless_button_text_length_limit">20</integer>
 </resources>
diff --git a/car/res/values/strings.xml b/car/res/values/strings.xml
index 65f08b6..1fb4cf4 100644
--- a/car/res/values/strings.xml
+++ b/car/res/values/strings.xml
@@ -21,4 +21,5 @@
          -->
     <string name="car_drawer_open" translatable="false">Open drawer</string>
     <string name="car_drawer_close" translatable="false">Close drawer</string>
+    <string name="ellipsis" translatable="false">&#8230;</string>
 </resources>
diff --git a/car/res/values/styles.xml b/car/res/values/styles.xml
index 61e089b..d84f4c8 100644
--- a/car/res/values/styles.xml
+++ b/car/res/values/styles.xml
@@ -15,25 +15,25 @@
 -->
 <resources>
     <!-- The styling for title text. The color of this text changes based on day/night mode. -->
-    <style name="CarTitle" >
+    <style name="CarTitle">
         <item name="android:textStyle">normal</item>
         <item name="android:textSize">@dimen/car_title_size</item>
         <item name="android:textColor">@color/car_title</item>
     </style>
 
     <!-- Title text that is permanently a dark color. -->
-    <style name="CarTitle.Dark" >
+    <style name="CarTitle.Dark">
         <item name="android:textColor">@color/car_title_dark</item>
     </style>
 
     <!-- Title text that is permanently a light color. -->
-    <style name="CarTitle.Light" >
+    <style name="CarTitle.Light">
         <item name="android:textColor">@color/car_title_light</item>
     </style>
 
     <!-- The styling for the main headline text. The color of this text changes based on the
          day/night mode. -->
-    <style name="CarHeadline1" >
+    <style name="CarHeadline1">
         <item name="android:textStyle">normal</item>
         <item name="android:textSize">@dimen/car_headline1_size</item>
         <item name="android:textColor">@color/car_headline1</item>
@@ -41,7 +41,7 @@
 
     <!-- The styling for a sub-headline text. The color of this text changes based on the
          day/night mode. -->
-    <style name="CarHeadline2" >
+    <style name="CarHeadline2">
         <item name="android:textStyle">normal</item>
         <item name="android:textSize">@dimen/car_headline2_size</item>
         <item name="android:textColor">@color/car_headline2</item>
@@ -49,7 +49,7 @@
 
     <!-- The styling for a smaller alternate headline text. The color of this text changes based on
          the day/night mode. -->
-    <style name="CarHeadline3" >
+    <style name="CarHeadline3">
         <item name="android:textStyle">normal</item>
         <item name="android:textSize">@dimen/car_headline3_size</item>
         <item name="android:textColor">@color/car_headline3</item>
@@ -57,14 +57,14 @@
 
     <!-- The styling for the smallest headline text. The color of this text changes based on the
          day/night mode. -->
-    <style name="CarHeadline4" >
+    <style name="CarHeadline4">
         <item name="android:textStyle">normal</item>
         <item name="android:textSize">@dimen/car_headline4_size</item>
         <item name="android:textColor">@color/car_headline4</item>
     </style>
 
     <!-- The styling for body text. The color of this text changes based on the day/night mode. -->
-    <style name="CarBody1" >
+    <style name="CarBody1">
         <item name="android:textStyle">normal</item>
         <item name="android:textSize">@dimen/car_body1_size</item>
         <item name="android:textColor">@color/car_body1</item>
@@ -72,7 +72,7 @@
 
     <!-- An alternate styling for body text that is both a different color and size than
          CarBody1. -->
-    <style name="CarBody2" >
+    <style name="CarBody2">
         <item name="android:textStyle">normal</item>
         <item name="android:textSize">@dimen/car_body2_size</item>
         <item name="android:textColor">@color/car_body2</item>
@@ -80,7 +80,7 @@
 
     <!-- A smaller styling for body text. The color of this text changes based on the day/night
          mode. -->
-    <style name="CarBody3" >
+    <style name="CarBody3">
         <item name="android:textStyle">normal</item>
         <item name="android:textSize">@dimen/car_body3_size</item>
         <item name="android:textColor">@color/car_body3</item>
@@ -88,7 +88,7 @@
 
     <!-- The smallest styling for body text. The color of this text changes based on the day/night
          mode. -->
-    <style name="CarBody4" >
+    <style name="CarBody4">
         <item name="android:textStyle">normal</item>
         <item name="android:textSize">@dimen/car_body4_size</item>
         <item name="android:textColor">@color/car_body4</item>
@@ -119,14 +119,17 @@
         <item name="android:background">@drawable/car_button_background</item>
     </style>
 
-    <style name="CarBorderlessButton" parent="android:Widget.Material.Button.Borderless">
+    <style name="CarButton.Borderless" parent="android:Widget.Material.Button.Borderless">
         <item name="android:layout_height">@dimen/car_button_height</item>
-        <item name="android:minWidth">@dimen/car_button_min_width</item>
-        <item name="android:paddingStart">@dimen/car_button_horizontal_padding</item>
-        <item name="android:paddingEnd">@dimen/car_button_horizontal_padding</item>
+        <item name="android:paddingStart">@dimen/car_borderless_button_horizontal_padding</item>
+        <item name="android:paddingEnd">@dimen/car_borderless_button_horizontal_padding</item>
         <item name="android:textStyle">normal</item>
         <item name="android:textSize">@dimen/car_action1_size</item>
-        <item name="android:textColor">@drawable/car_button_text_color</item>
+        <item name="android:textColor">@drawable/car_borderless_button_text_color</item>
         <item name="android:textAllCaps">true</item>
     </style>
+
+    <!-- Style for the progress bars -->
+    <style name="CarProgressBar.Horizontal"
+           parent="android:Widget.Material.ProgressBar.Horizontal"/>
 </resources>
diff --git a/car/src/main/java/android/support/car/drawer/CarDrawerActivity.java b/car/src/main/java/androidx/car/drawer/CarDrawerActivity.java
similarity index 98%
rename from car/src/main/java/android/support/car/drawer/CarDrawerActivity.java
rename to car/src/main/java/androidx/car/drawer/CarDrawerActivity.java
index f46c652..55bb23c 100644
--- a/car/src/main/java/android/support/car/drawer/CarDrawerActivity.java
+++ b/car/src/main/java/androidx/car/drawer/CarDrawerActivity.java
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-package android.support.car.drawer;
+package androidx.car.drawer;
 
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.Nullable;
-import android.support.car.R;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.app.ActionBarDrawerToggle;
 import android.support.v7.app.AppCompatActivity;
@@ -30,6 +29,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.car.R;
+
 /**
  * Common base Activity for car apps that need to present a Drawer.
  *
diff --git a/car/src/main/java/android/support/car/drawer/CarDrawerAdapter.java b/car/src/main/java/androidx/car/drawer/CarDrawerAdapter.java
similarity index 98%
rename from car/src/main/java/android/support/car/drawer/CarDrawerAdapter.java
rename to car/src/main/java/androidx/car/drawer/CarDrawerAdapter.java
index b0fd965..ca16413 100644
--- a/car/src/main/java/android/support/car/drawer/CarDrawerAdapter.java
+++ b/car/src/main/java/androidx/car/drawer/CarDrawerAdapter.java
@@ -14,20 +14,21 @@
  * limitations under the License.
  */
 
-package android.support.car.drawer;
+package androidx.car.drawer;
 
 import android.content.Context;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.car.R;
-import android.support.car.widget.PagedListView;
 import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.car.R;
+import androidx.car.widget.PagedListView;
+
 /**
  * Base adapter for displaying items in the car navigation drawer, which uses a
  * {@link PagedListView}.
diff --git a/car/src/main/java/android/support/car/drawer/CarDrawerController.java b/car/src/main/java/androidx/car/drawer/CarDrawerController.java
similarity index 97%
rename from car/src/main/java/android/support/car/drawer/CarDrawerController.java
rename to car/src/main/java/androidx/car/drawer/CarDrawerController.java
index 7b23714..e26054f 100644
--- a/car/src/main/java/android/support/car/drawer/CarDrawerController.java
+++ b/car/src/main/java/androidx/car/drawer/CarDrawerController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.car.drawer;
+package androidx.car.drawer;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -22,8 +22,6 @@
 import android.support.annotation.AnimRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.car.R;
-import android.support.car.widget.PagedListView;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.app.ActionBarDrawerToggle;
 import android.support.v7.widget.RecyclerView;
@@ -34,7 +32,10 @@
 import android.view.animation.AnimationUtils;
 import android.widget.ProgressBar;
 
-import java.util.Stack;
+import java.util.ArrayDeque;
+
+import androidx.car.R;
+import androidx.car.widget.PagedListView;
 
 /**
  * A controller that will handle the set up of the navigation drawer. It will hook up the
@@ -58,7 +59,7 @@
      * this stack is the order that the user has visited each level. When the user navigates up,
      * the adapters are popped from this list.
      */
-    private final Stack<CarDrawerAdapter> mAdapterStack = new Stack<>();
+    private final ArrayDeque<CarDrawerAdapter> mAdapterStack = new ArrayDeque<>();
 
     private final Context mContext;
 
@@ -114,11 +115,10 @@
         }
 
         // The root adapter is always the last item in the stack.
-        if (mAdapterStack.size() > 0) {
-            mAdapterStack.set(0, rootAdapter);
-        } else {
-            mAdapterStack.push(rootAdapter);
+        if (!mAdapterStack.isEmpty()) {
+            mAdapterStack.removeLast();
         }
+        mAdapterStack.addLast(rootAdapter);
 
         setToolbarTitleFrom(rootAdapter);
         mDrawerList.setAdapter(rootAdapter);
diff --git a/car/src/main/java/android/support/car/drawer/DrawerItemClickListener.java b/car/src/main/java/androidx/car/drawer/DrawerItemClickListener.java
similarity index 95%
rename from car/src/main/java/android/support/car/drawer/DrawerItemClickListener.java
rename to car/src/main/java/androidx/car/drawer/DrawerItemClickListener.java
index d707dbd..4c0c7a2 100644
--- a/car/src/main/java/android/support/car/drawer/DrawerItemClickListener.java
+++ b/car/src/main/java/androidx/car/drawer/DrawerItemClickListener.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.car.drawer;
+package androidx.car.drawer;
 
 /**
  * Listener for handling clicks on items/views managed by {@link DrawerItemViewHolder}.
diff --git a/car/src/main/java/android/support/car/drawer/DrawerItemViewHolder.java b/car/src/main/java/androidx/car/drawer/DrawerItemViewHolder.java
similarity index 95%
rename from car/src/main/java/android/support/car/drawer/DrawerItemViewHolder.java
rename to car/src/main/java/androidx/car/drawer/DrawerItemViewHolder.java
index d016b2d..8bbded9 100644
--- a/car/src/main/java/android/support/car/drawer/DrawerItemViewHolder.java
+++ b/car/src/main/java/androidx/car/drawer/DrawerItemViewHolder.java
@@ -14,19 +14,20 @@
  * limitations under the License.
  */
 
-package android.support.car.drawer;
+package androidx.car.drawer;
 
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.car.R;
 import android.support.v7.widget.RecyclerView;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.car.R;
+
 /**
  * Re-usable {@link RecyclerView.ViewHolder} for displaying items in the
- * {@link android.support.car.drawer.CarDrawerAdapter}.
+ * {@link androidx.car.drawer.CarDrawerAdapter}.
  */
 public class DrawerItemViewHolder extends RecyclerView.ViewHolder {
     private final ImageView mIcon;
diff --git a/car/src/main/java/android/support/car/utils/ColumnCalculator.java b/car/src/main/java/androidx/car/utils/ColumnCalculator.java
similarity index 95%
rename from car/src/main/java/android/support/car/utils/ColumnCalculator.java
rename to car/src/main/java/androidx/car/utils/ColumnCalculator.java
index fa5dd43..35b1a91 100644
--- a/car/src/main/java/android/support/car/utils/ColumnCalculator.java
+++ b/car/src/main/java/androidx/car/utils/ColumnCalculator.java
@@ -14,15 +14,16 @@
  * limitations under the License.
  */
 
-package android.support.car.utils;
+package androidx.car.utils;
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.support.car.R;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.WindowManager;
 
+import androidx.car.R;
+
 /**
  * Utility class that calculates the size of the columns that will fit on the screen. A column's
  * width is determined by the size of the margins and gutters (space between the columns) that fit
@@ -66,8 +67,8 @@
     private ColumnCalculator(Context context) {
         Resources res = context.getResources();
         int marginSize = res.getDimensionPixelSize(R.dimen.car_margin);
-        mGutterSize = res.getDimensionPixelSize(R.dimen.car_screen_gutter_size);
-        mNumOfColumns = res.getInteger(R.integer.car_screen_num_of_columns);
+        mGutterSize = res.getDimensionPixelSize(R.dimen.car_gutter_size);
+        mNumOfColumns = res.getInteger(R.integer.car_column_number);
 
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, String.format("marginSize: %d; numOfColumns: %d; gutterSize: %d",
diff --git a/car/src/main/java/android/support/car/widget/CarItemAnimator.java b/car/src/main/java/androidx/car/widget/CarItemAnimator.java
similarity index 98%
rename from car/src/main/java/android/support/car/widget/CarItemAnimator.java
rename to car/src/main/java/androidx/car/widget/CarItemAnimator.java
index ef22c48..e6bfd05 100644
--- a/car/src/main/java/android/support/car/widget/CarItemAnimator.java
+++ b/car/src/main/java/androidx/car/widget/CarItemAnimator.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import android.support.v7.widget.DefaultItemAnimator;
 import android.support.v7.widget.RecyclerView;
diff --git a/car/src/main/java/android/support/car/widget/CarRecyclerView.java b/car/src/main/java/androidx/car/widget/CarRecyclerView.java
similarity index 98%
rename from car/src/main/java/android/support/car/widget/CarRecyclerView.java
rename to car/src/main/java/androidx/car/widget/CarRecyclerView.java
index bb9cb71..1d89ed1 100644
--- a/car/src/main/java/android/support/car/widget/CarRecyclerView.java
+++ b/car/src/main/java/androidx/car/widget/CarRecyclerView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import android.content.Context;
 import android.graphics.Canvas;
diff --git a/car/src/main/java/android/support/car/widget/ColumnCardView.java b/car/src/main/java/androidx/car/widget/ColumnCardView.java
similarity index 95%
rename from car/src/main/java/android/support/car/widget/ColumnCardView.java
rename to car/src/main/java/androidx/car/widget/ColumnCardView.java
index 06f8553..9ec2bb6 100644
--- a/car/src/main/java/android/support/car/widget/ColumnCardView.java
+++ b/car/src/main/java/androidx/car/widget/ColumnCardView.java
@@ -14,16 +14,17 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.support.car.R;
-import android.support.car.utils.ColumnCalculator;
 import android.support.v7.widget.CardView;
 import android.util.AttributeSet;
 import android.util.Log;
 
+import androidx.car.R;
+import androidx.car.utils.ColumnCalculator;
+
 /**
  * A {@link CardView} whose width can be specified by the number of columns that it will span.
  *
@@ -34,7 +35,7 @@
  * a default span value that it uses.
  *
  * <pre>
- * &lt;android.support.car.widget.ColumnCardView
+ * &lt;androidx.car.widget.ColumnCardView
  *     android:layout_width="wrap_content"
  *     android:layout_height="wrap_content"
  *     app:columnSpan="4" /&gt;
diff --git a/car/src/main/java/android/support/car/widget/DayNightStyle.java b/car/src/main/java/androidx/car/widget/DayNightStyle.java
similarity index 98%
rename from car/src/main/java/android/support/car/widget/DayNightStyle.java
rename to car/src/main/java/androidx/car/widget/DayNightStyle.java
index ff5a1b3..6e3ecbe 100644
--- a/car/src/main/java/android/support/car/widget/DayNightStyle.java
+++ b/car/src/main/java/androidx/car/widget/DayNightStyle.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import android.support.annotation.IntDef;
 
diff --git a/car/src/main/java/androidx/car/widget/ListItem.java b/car/src/main/java/androidx/car/widget/ListItem.java
new file mode 100644
index 0000000..c5b93d9
--- /dev/null
+++ b/car/src/main/java/androidx/car/widget/ListItem.java
@@ -0,0 +1,708 @@
+/*
+ * Copyright 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 androidx.car.widget;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.IntDef;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import java.lang.annotation.Retention;
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.car.R;
+
+/**
+ * Class to build a list item.
+ *
+ * <p>An item supports primary action and supplemental action(s).
+ *
+ * <p>An item visually composes of 3 parts; each part may contain multiple views.
+ * <ul>
+ *     <li>{@code Primary Action}: represented by an icon of following types.
+ *     <ul>
+ *         <li>Primary Icon - icon size could be large or small.
+ *         <li>No Icon
+ *         <li>Empty Icon - different from No Icon by how much margin {@code Text} offsets
+ *     </ul>
+ *     <li>{@code Text}: supports any combination of the follow text views.
+ *     <ul>
+ *         <li>Title
+ *         <li>Body
+ *     </ul>
+ *     <li>{@code Supplemental Action(s)}: represented by one of the following types; aligned toward
+ *     the end of item.
+ *     <ul>
+ *         <li>Supplemental Icon
+ *         <li>One Action Button
+ *         <li>Two Action Buttons
+ *     </ul>
+ * </ul>
+ *
+ * {@link ListItem} can be built through its {@link ListItem.Builder}. It binds data
+ * to {@link ListItemAdapter.ViewHolder} based on components selected.
+ */
+public class ListItem {
+
+    private Builder mBuilder;
+
+    private ListItem(Builder builder) {
+        mBuilder = builder;
+    }
+
+    /**
+     * Applies all {@link ViewBinder} to {@code viewHolder}.
+     */
+    void bind(ListItemAdapter.ViewHolder viewHolder) {
+        setAllSubViewsGone(viewHolder);
+        for (ViewBinder binder : mBuilder.mBinders) {
+            binder.bind(viewHolder);
+        }
+    }
+
+    void setAllSubViewsGone(ListItemAdapter.ViewHolder vh) {
+        View[] subviews = new View[] {
+                vh.getPrimaryIcon(),
+                vh.getTitle(), vh.getBody(),
+                vh.getSupplementalIcon(), vh.getSupplementalIconDivider(),
+                vh.getAction1(), vh.getAction1Divider(), vh.getAction2(), vh.getAction2Divider()};
+        for (View v : subviews) {
+            v.setVisibility(View.GONE);
+        }
+    }
+
+    /**
+     * Used by {@link ListItemAdapter} to choose layout to inflate for view holder.
+     * New view type needs support in {@link ListItemAdapter}.
+     */
+    protected int getViewType() {
+        return mBuilder.mIsCard
+                ? ListItemAdapter.CAR_PAGED_LIST_CARD
+                : ListItemAdapter.CAR_PAGED_LIST_ITEM;
+    }
+
+    /**
+     * Functional interface to provide a way to interact with views in
+     * {@link ListItemAdapter.ViewHolder}. {@code ViewBinder}s added to a
+     * {@code ListItem} will be called when {@code ListItem} {@code bind}s to
+     * {@link ListItemAdapter.ViewHolder}.
+     */
+    public interface ViewBinder {
+        /**
+         * Provides a way to interact with views in view holder.
+         */
+        void bind(ListItemAdapter.ViewHolder viewHolder);
+    }
+
+    /**
+     * Builds a {@link ListItem}.
+     *
+     * <p>With conflicting methods are called, e.g. setting primary action to both primary icon and
+     * no icon, the last called method wins.
+     */
+    public static class Builder {
+
+        @Retention(SOURCE)
+        @IntDef({
+                PRIMARY_ACTION_TYPE_NO_ICON, PRIMARY_ACTION_TYPE_EMPTY_ICON,
+                PRIMARY_ACTION_TYPE_LARGE_ICON, PRIMARY_ACTION_TYPE_SMALL_ICON})
+        private @interface PrimaryActionType {}
+
+        private static final int PRIMARY_ACTION_TYPE_NO_ICON = 0;
+        private static final int PRIMARY_ACTION_TYPE_EMPTY_ICON = 1;
+        private static final int PRIMARY_ACTION_TYPE_LARGE_ICON = 2;
+        private static final int PRIMARY_ACTION_TYPE_SMALL_ICON = 3;
+
+        @Retention(SOURCE)
+        @IntDef({SUPPLEMENTAL_ACTION_NO_ACTION, SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON,
+                SUPPLEMENTAL_ACTION_ONE_ACTION, SUPPLEMENTAL_ACTION_TWO_ACTIONS})
+        private @interface SupplementalActionType {}
+
+        private static final int SUPPLEMENTAL_ACTION_NO_ACTION = 0;
+        private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON = 1;
+        private static final int SUPPLEMENTAL_ACTION_ONE_ACTION = 2;
+        private static final int SUPPLEMENTAL_ACTION_TWO_ACTIONS = 3;
+
+        private final Context mContext;
+        private final List<ViewBinder> mBinders = new ArrayList<>();
+        // Store custom binders separately so they will bind after binders are created in build().
+        private final List<ViewBinder> mCustomBinders = new ArrayList<>();
+
+        private boolean mIsCard;
+
+        private View.OnClickListener mOnClickListener;
+
+        @PrimaryActionType private int mPrimaryActionType = PRIMARY_ACTION_TYPE_NO_ICON;
+        private int mPrimaryActionIconResId;
+        private Drawable mPrimaryActionIconDrawable;
+
+        private String mTitle;
+        private String mBody;
+        private boolean mIsBodyPrimary;
+
+        @SupplementalActionType private int mSupplementalActionType = SUPPLEMENTAL_ACTION_NO_ACTION;
+        private int mSupplementalIconResId;
+        private View.OnClickListener mSupplementalIconOnClickListener;
+        private boolean mShowSupplementalIconDivider;
+
+        private String mAction1Text;
+        private View.OnClickListener mAction1OnClickListener;
+        private boolean mShowAction1Divider;
+        private String mAction2Text;
+        private View.OnClickListener mAction2OnClickListener;
+        private boolean mShowAction2Divider;
+
+        public Builder(Context context) {
+            mContext = context;
+        }
+
+        /**
+         * Builds a {@link ListItem}. Adds {@link ViewBinder}s that will adjust layout in
+         * {@link ListItemAdapter.ViewHolder} depending on sub-views used.
+         */
+        public ListItem build() {
+            setItemLayoutHeight();
+            setPrimaryAction();
+            setText();
+            setSupplementalActions();
+            setOnClickListener();
+
+            mBinders.addAll(mCustomBinders);
+
+            return new ListItem(this);
+        }
+
+        /**
+         * Sets the height of item depending on which text field is set.
+         */
+        private void setItemLayoutHeight() {
+            if (TextUtils.isEmpty(mBody)) {
+                // If the item only has title or no text, it uses fixed-height as single line.
+                int height = (int) mContext.getResources().getDimension(
+                        R.dimen.car_single_line_list_item_height);
+                mBinders.add((vh) -> {
+                    RecyclerView.LayoutParams layoutParams =
+                            (RecyclerView.LayoutParams) vh.itemView.getLayoutParams();
+                    layoutParams.height = height;
+                    vh.itemView.setLayoutParams(layoutParams);
+                });
+            } else {
+                // If body is present, the item should be at least as tall as min height, and wraps
+                // content.
+                int minHeight = (int) mContext.getResources().getDimension(
+                        R.dimen.car_double_line_list_item_height);
+                mBinders.add((vh) -> {
+                    vh.itemView.setMinimumHeight(minHeight);
+                    vh.getContainerLayout().setMinimumHeight(minHeight);
+
+                    RecyclerView.LayoutParams layoutParams =
+                            (RecyclerView.LayoutParams) vh.itemView.getLayoutParams();
+                    layoutParams.height = RecyclerView.LayoutParams.WRAP_CONTENT;
+                    vh.itemView.setLayoutParams(layoutParams);
+                });
+            }
+        }
+
+        private void setPrimaryAction() {
+            setPrimaryIconContent();
+            setPrimaryIconLayout();
+        }
+
+        private void setText() {
+            setTextContent();
+            setTextVerticalMargin();
+            // Only setting start margin because text end is relative to the start of supplemental
+            // actions.
+            setTextStartMargin();
+        }
+
+        private void setOnClickListener() {
+            if (mOnClickListener != null) {
+                mBinders.add(vh -> vh.itemView.setOnClickListener(mOnClickListener));
+            }
+        }
+
+        private void setPrimaryIconContent() {
+            switch (mPrimaryActionType) {
+                case PRIMARY_ACTION_TYPE_SMALL_ICON:
+                case PRIMARY_ACTION_TYPE_LARGE_ICON:
+                    mBinders.add((vh) -> {
+                        vh.getPrimaryIcon().setVisibility(View.VISIBLE);
+
+                        if (mPrimaryActionIconDrawable != null) {
+                            vh.getPrimaryIcon().setImageDrawable(mPrimaryActionIconDrawable);
+                        } else if (mPrimaryActionIconResId != 0) {
+                            vh.getPrimaryIcon().setImageResource(mPrimaryActionIconResId);
+                        }
+                    });
+                    break;
+                case PRIMARY_ACTION_TYPE_EMPTY_ICON:
+                case PRIMARY_ACTION_TYPE_NO_ICON:
+                    // Do nothing.
+                    break;
+                default:
+                    throw new IllegalStateException("Unrecognizable primary action type.");
+            }
+        }
+
+        /**
+         * Sets layout params of primary icon.
+         *
+         * <p>Large icon will have no start margin, and always align center vertically.
+         *
+         * <p>Small icon will have start margin. When body text is present small icon uses a top
+         * margin otherwise align center vertically.
+         */
+        private void setPrimaryIconLayout() {
+            switch (mPrimaryActionType) {
+                case PRIMARY_ACTION_TYPE_SMALL_ICON:
+                    mBinders.add(vh -> {
+                        int iconSize = mContext.getResources().getDimensionPixelSize(
+                                R.dimen.car_primary_icon_size);
+                        // Icon size.
+                        RelativeLayout.LayoutParams layoutParams =
+                                (RelativeLayout.LayoutParams) vh.getPrimaryIcon().getLayoutParams();
+                        layoutParams.height = iconSize;
+                        layoutParams.width = iconSize;
+
+                        // Start margin.
+                        layoutParams.setMarginStart(mContext.getResources().getDimensionPixelSize(
+                                R.dimen.car_keyline_1));
+
+                        if (!TextUtils.isEmpty(mBody)) {
+                            // Set top margin.
+                            layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
+                                    R.dimen.car_padding_4);
+                        } else {
+                            // Centered vertically.
+                            layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
+                        }
+                        vh.getPrimaryIcon().setLayoutParams(layoutParams);
+                    });
+                    break;
+                case PRIMARY_ACTION_TYPE_LARGE_ICON:
+                    mBinders.add(vh -> {
+                        int iconSize = mContext.getResources().getDimensionPixelSize(
+                                R.dimen.car_single_line_list_item_height);
+                        // Icon size.
+                        RelativeLayout.LayoutParams layoutParams =
+                                (RelativeLayout.LayoutParams) vh.getPrimaryIcon().getLayoutParams();
+                        layoutParams.height = iconSize;
+                        layoutParams.width = iconSize;
+
+                        // No start margin.
+                        layoutParams.setMarginStart(0);
+
+                        // Always centered vertically.
+                        layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
+
+                        vh.getPrimaryIcon().setLayoutParams(layoutParams);
+                    });
+                    break;
+                case PRIMARY_ACTION_TYPE_EMPTY_ICON:
+                case PRIMARY_ACTION_TYPE_NO_ICON:
+                    // Do nothing.
+                    break;
+                default:
+                    throw new IllegalStateException("Unrecognizable primary action type.");
+            }
+        }
+
+        private void setTextContent() {
+            if (!TextUtils.isEmpty(mTitle)) {
+                mBinders.add(vh -> {
+                    vh.getTitle().setVisibility(View.VISIBLE);
+                    vh.getTitle().setText(mTitle);
+                });
+            }
+            if (!TextUtils.isEmpty(mBody)) {
+                mBinders.add(vh -> {
+                    vh.getBody().setVisibility(View.VISIBLE);
+                    vh.getBody().setText(mBody);
+                });
+            }
+
+            if (mIsBodyPrimary) {
+                mBinders.add((vh) -> {
+                    vh.getTitle().setTextAppearance(R.style.CarBody2);
+                    vh.getBody().setTextAppearance(R.style.CarBody1);
+                });
+            } else {
+                mBinders.add((vh) -> {
+                    vh.getTitle().setTextAppearance(R.style.CarBody1);
+                    vh.getBody().setTextAppearance(R.style.CarBody2);
+                });
+            }
+        }
+
+        /**
+         * Sets start margin of text view depending on icon type.
+         */
+        private void setTextStartMargin() {
+            final int startMarginResId;
+            switch (mPrimaryActionType) {
+                case PRIMARY_ACTION_TYPE_NO_ICON:
+                    startMarginResId = R.dimen.car_keyline_1;
+                    break;
+                case PRIMARY_ACTION_TYPE_EMPTY_ICON:
+                    startMarginResId = R.dimen.car_keyline_3;
+                    break;
+                case PRIMARY_ACTION_TYPE_SMALL_ICON:
+                    startMarginResId = R.dimen.car_keyline_3;
+                    break;
+                case PRIMARY_ACTION_TYPE_LARGE_ICON:
+                    startMarginResId = R.dimen.car_keyline_4;
+                    break;
+                default:
+                    throw new IllegalStateException("Unrecognizable primary action type.");
+            }
+            int startMargin = mContext.getResources().getDimensionPixelSize(startMarginResId);
+            mBinders.add(vh -> {
+                RelativeLayout.LayoutParams titleLayoutParams =
+                        (RelativeLayout.LayoutParams) vh.getTitle().getLayoutParams();
+                titleLayoutParams.setMarginStart(startMargin);
+                vh.getTitle().setLayoutParams(titleLayoutParams);
+
+                RelativeLayout.LayoutParams bodyLayoutParams =
+                        (RelativeLayout.LayoutParams) vh.getBody().getLayoutParams();
+                bodyLayoutParams.setMarginStart(startMargin);
+                vh.getBody().setLayoutParams(bodyLayoutParams);
+            });
+        }
+
+        /**
+         * Sets top/bottom margins of {@code Title} and {@code Body}.
+         */
+        private void setTextVerticalMargin() {
+            if (!TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mBody)) {
+                // Title only - view is aligned center vertically by itself.
+                mBinders.add(vh -> {
+                    RelativeLayout.LayoutParams layoutParams =
+                            (RelativeLayout.LayoutParams) vh.getTitle().getLayoutParams();
+                    layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
+                    vh.getTitle().setLayoutParams(layoutParams);
+                });
+            } else if (TextUtils.isEmpty(mTitle) && !TextUtils.isEmpty(mBody)) {
+                mBinders.add(vh -> {
+                    // Body uses top and bottom margin.
+                    int margin = mContext.getResources().getDimensionPixelSize(
+                            R.dimen.car_padding_3);
+                    RelativeLayout.LayoutParams layoutParams =
+                            (RelativeLayout.LayoutParams) vh.getBody().getLayoutParams();
+                    layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
+                    layoutParams.topMargin = margin;
+                    layoutParams.bottomMargin = margin;
+                    vh.getBody().setLayoutParams(layoutParams);
+                });
+            } else {
+                mBinders.add(vh -> {
+                    // Title has a top margin
+                    Resources resources = mContext.getResources();
+                    int padding1 = resources.getDimensionPixelSize(R.dimen.car_padding_1);
+                    int padding3 = resources.getDimensionPixelSize(R.dimen.car_padding_3);
+                    RelativeLayout.LayoutParams titleLayoutParams =
+                            (RelativeLayout.LayoutParams) vh.getTitle().getLayoutParams();
+                    titleLayoutParams.topMargin = padding3;
+                    vh.getTitle().setLayoutParams(titleLayoutParams);
+                    // Body is below title with a margin, and has bottom margin.
+                    RelativeLayout.LayoutParams bodyLayoutParams =
+                            (RelativeLayout.LayoutParams) vh.getBody().getLayoutParams();
+                    bodyLayoutParams.addRule(RelativeLayout.BELOW, R.id.title);
+                    bodyLayoutParams.topMargin = padding1;
+                    bodyLayoutParams.bottomMargin = padding3;
+                    vh.getBody().setLayoutParams(bodyLayoutParams);
+                });
+            }
+        }
+
+        /**
+         * Sets up view(s) for supplemental action.
+         */
+        private void setSupplementalActions() {
+            switch (mSupplementalActionType) {
+                case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON:
+                    mBinders.add((vh) -> {
+                        vh.getSupplementalIcon().setVisibility(View.VISIBLE);
+                        if (mShowSupplementalIconDivider) {
+                            vh.getSupplementalIconDivider().setVisibility(View.VISIBLE);
+                        }
+
+                        vh.getSupplementalIcon().setImageResource(mSupplementalIconResId);
+                        vh.getSupplementalIcon().setOnClickListener(
+                                mSupplementalIconOnClickListener);
+                    });
+                    break;
+                case SUPPLEMENTAL_ACTION_TWO_ACTIONS:
+                    mBinders.add((vh) -> {
+                        vh.getAction2().setVisibility(View.VISIBLE);
+                        if (mShowAction2Divider) {
+                            vh.getAction2Divider().setVisibility(View.VISIBLE);
+                        }
+
+                        vh.getAction2().setText(mAction2Text);
+                        vh.getAction2().setOnClickListener(mAction2OnClickListener);
+                    });
+                    // Fall through
+                case SUPPLEMENTAL_ACTION_ONE_ACTION:
+                    mBinders.add((vh) -> {
+                        vh.getAction1().setVisibility(View.VISIBLE);
+                        if (mShowAction1Divider) {
+                            vh.getAction1Divider().setVisibility(View.VISIBLE);
+                        }
+
+                        vh.getAction1().setText(mAction1Text);
+                        vh.getAction1().setOnClickListener(mAction1OnClickListener);
+                    });
+                    break;
+                case SUPPLEMENTAL_ACTION_NO_ACTION:
+                    // Do nothing
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unrecognized supplemental action type.");
+            }
+        }
+
+        /**
+         * Builds the item in a {@link android.support.v7.widget.CardView}.
+         *
+         * <p>Each item will have rounded corner, margin between items, and elevation.
+         *
+         * @return This Builder object to allow for chaining calls to set methods.
+         */
+        public Builder withCardLook() {
+            mIsCard = true;
+            return this;
+        }
+
+        /**
+         * Sets {@link View.OnClickListener} of {@code ListItem}.
+         *
+         * @return This Builder object to allow for chaining calls to set methods.
+         */
+        public Builder withOnClickListener(View.OnClickListener listener) {
+            mOnClickListener = listener;
+            return this;
+        }
+
+        /**
+         * Sets {@code Primary Action} to be represented by an icon.
+         *
+         * @param iconResId the resource identifier of the drawable.
+         * @param useLargeIcon the size of primary icon. Large Icon is a square as tall as an item
+         *                     with only title set; useful for album cover art.
+         * @return This Builder object to allow for chaining calls to set methods.
+         */
+        public Builder withPrimaryActionIcon(@DrawableRes int iconResId, boolean useLargeIcon) {
+            return withPrimaryActionIcon(null, iconResId, useLargeIcon);
+        }
+
+        /**
+         * Sets {@code Primary Action} to be represented by an icon.
+         *
+         * @param drawable the Drawable to set, or null to clear the content.
+         * @param useLargeIcon the size of primary icon. Large Icon is a square as tall as an item
+         *                     with only title set; useful for album cover art.
+         * @return This Builder object to allow for chaining calls to set methods.
+         */
+        public Builder withPrimaryActionIcon(Drawable drawable, boolean useLargeIcon) {
+            return withPrimaryActionIcon(drawable, 0, useLargeIcon);
+        }
+
+        private Builder withPrimaryActionIcon(Drawable drawable, @DrawableRes int iconResId,
+                boolean useLargeIcon) {
+            mPrimaryActionType = useLargeIcon
+                    ? PRIMARY_ACTION_TYPE_LARGE_ICON
+                    : PRIMARY_ACTION_TYPE_SMALL_ICON;
+            mPrimaryActionIconResId = iconResId;
+            mPrimaryActionIconDrawable = drawable;
+            return this;
+        }
+
+        /**
+         * Sets {@code Primary Action} to be empty icon.
+         *
+         * {@code Text} would have a start margin as if {@code Primary Action} were set to
+         * primary icon.
+         *
+         * @return This Builder object to allow for chaining calls to set methods.
+         */
+        public Builder withPrimaryActionEmptyIcon() {
+            mPrimaryActionType = PRIMARY_ACTION_TYPE_EMPTY_ICON;
+            return this;
+        }
+
+        /**
+         * Sets {@code Primary Action} to have no icon. Text would align to the start of item.
+         *
+         * @return This Builder object to allow for chaining calls to set methods.
+         */
+        public Builder withPrimaryActionNoIcon() {
+            mPrimaryActionType = PRIMARY_ACTION_TYPE_NO_ICON;
+            return this;
+        }
+
+        /**
+         * Sets the title of item.
+         *
+         * <p>Primary text is {@code title} by default. It can be set by
+         * {@link #withBody(String, boolean)}
+         *
+         * @param title text to display as title.
+         * @return This Builder object to allow for chaining calls to set methods.
+         */
+        public Builder withTitle(String title) {
+            mTitle = title;
+            return this;
+        }
+
+        /**
+         * Sets the body text of item.
+         *
+         * <p>Text beyond length required by regulation will be truncated. Defaults {@code Title}
+         * text as the primary.
+         *
+         * @return This Builder object to allow for chaining calls to set methods.
+         */
+        public Builder withBody(String body) {
+            return withBody(body, false);
+        }
+
+        /**
+         * Sets the body text of item.
+         *
+         * <p>Text beyond length required by regulation will be truncated.
+         *
+         * @param asPrimary sets {@code Body Text} as primary text of item.
+         * @return This Builder object to allow for chaining calls to set methods.
+         */
+        public Builder withBody(String body, boolean asPrimary) {
+            int limit = mContext.getResources().getInteger(
+                    R.integer.car_list_item_text_length_limit);
+            if (body.length() < limit) {
+                mBody = body;
+            } else {
+                mBody = body.substring(0, limit) + mContext.getString(R.string.ellipsis);
+            }
+            mIsBodyPrimary = asPrimary;
+            return this;
+        }
+
+        /**
+         * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
+         *
+         * @return This Builder object to allow for chaining calls to set methods.
+         */
+        public Builder withSupplementalIcon(int iconResId, boolean showDivider) {
+            return withSupplementalIcon(iconResId, showDivider, null);
+        }
+
+        /**
+         * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
+         *
+         * @param iconResId drawable resource id.
+         * @return This Builder object to allow for chaining calls to set methods.
+         */
+        public Builder withSupplementalIcon(int iconResId, boolean showDivider,
+                View.OnClickListener listener) {
+            mSupplementalActionType = SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON;
+
+            mSupplementalIconResId = iconResId;
+            mSupplementalIconOnClickListener = listener;
+            mShowSupplementalIconDivider = showDivider;
+            return this;
+        }
+
+        /**
+         * Sets {@code Supplemental Action} to be represented by an {@code Action Button}.
+         *
+         * @param text button text to display.
+         * @return This Builder object to allow for chaining calls to set methods.
+         */
+        public Builder withAction(String text, boolean showDivider, View.OnClickListener listener) {
+            if (TextUtils.isEmpty(text)) {
+                throw new IllegalArgumentException("Action text cannot be empty.");
+            }
+            mSupplementalActionType = SUPPLEMENTAL_ACTION_ONE_ACTION;
+
+            mAction1Text = text;
+            mAction1OnClickListener = listener;
+            mShowAction1Divider = showDivider;
+            return this;
+        }
+
+        /**
+         * Sets {@code Supplemental Action} to be represented by two {@code Action Button}s.
+         *
+         * <p>These two action buttons will be aligned towards item end.
+         *
+         * @param action1Text button text to display - this button will be closer to item end.
+         * @param action2Text button text to display.
+         */
+        public Builder withActions(String action1Text, boolean showAction1Divider,
+                View.OnClickListener action1OnClickListener,
+                String action2Text, boolean showAction2Divider,
+                View.OnClickListener action2OnClickListener) {
+            if (TextUtils.isEmpty(action1Text)) {
+                throw new IllegalArgumentException("Action1 text cannot be empty.");
+            }
+            if (TextUtils.isEmpty(action2Text)) {
+                throw new IllegalArgumentException("Action2 text cannot be empty.");
+            }
+            mSupplementalActionType = SUPPLEMENTAL_ACTION_TWO_ACTIONS;
+
+            mAction1Text = action1Text;
+            mAction1OnClickListener = action1OnClickListener;
+            mShowAction1Divider = showAction1Divider;
+            mAction2Text = action2Text;
+            mAction2OnClickListener = action2OnClickListener;
+            mShowAction2Divider = showAction2Divider;
+            return this;
+        }
+
+        /**
+         * Adds {@link ViewBinder} to interact with sub-views in
+         * {@link ListItemAdapter.ViewHolder}. These ViewBinders will always bind after
+         * other {@link Builder} methods have bond.
+         *
+         * <p>Make sure to call with...() method on the intended sub-view first.
+         *
+         * <p>Example:
+         * <pre>
+         * {@code
+         * new Builder()
+         *     .withTitle("title")
+         *     .withViewBinder((viewHolder) -> {
+         *         viewHolder.getTitle().doMoreStuff();
+         *     })
+         *     .build();
+         * }
+         * </pre>
+         */
+        public Builder withViewBinder(ViewBinder binder) {
+            mCustomBinders.add(binder);
+            return this;
+        }
+    }
+}
diff --git a/car/src/main/java/androidx/car/widget/ListItemAdapter.java b/car/src/main/java/androidx/car/widget/ListItemAdapter.java
new file mode 100644
index 0000000..2bdfae6
--- /dev/null
+++ b/car/src/main/java/androidx/car/widget/ListItemAdapter.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 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 androidx.car.widget;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.content.Context;
+import android.support.annotation.IntDef;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import java.lang.annotation.Retention;
+
+import androidx.car.R;
+
+/**
+ * Adapter for {@link PagedListView} to display {@link ListItem}.
+ *
+ * Implements {@link PagedListView.ItemCap} - defaults to unlimited item count.
+ */
+public class ListItemAdapter extends
+        RecyclerView.Adapter<ListItemAdapter.ViewHolder> implements PagedListView.ItemCap {
+    @Retention(SOURCE)
+    @IntDef({CAR_PAGED_LIST_ITEM, CAR_PAGED_LIST_CARD})
+    public @interface PagedListItemType {}
+    public static final int CAR_PAGED_LIST_ITEM = 0;
+    public static final int CAR_PAGED_LIST_CARD = 1;
+
+    private final Context mContext;
+    private final ListItemProvider mItemProvider;
+
+    private int mMaxItems = PagedListView.ItemCap.UNLIMITED;
+
+    public ListItemAdapter(Context context, ListItemProvider itemProvider) {
+        mContext = context;
+        mItemProvider = itemProvider;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return mItemProvider.get(position).getViewType();
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, @PagedListItemType int viewType) {
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        int layoutId;
+        switch (viewType) {
+            case CAR_PAGED_LIST_ITEM:
+                layoutId = R.layout.car_paged_list_item;
+                break;
+            case CAR_PAGED_LIST_CARD:
+                layoutId = R.layout.car_paged_list_card;
+                break;
+            default:
+                throw new IllegalArgumentException("Unrecognizable view type: " + viewType);
+        }
+        View itemView = inflater.inflate(layoutId, parent, false);
+        return new ViewHolder(itemView);
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        ListItem item = mItemProvider.get(position);
+        item.bind(holder);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mMaxItems == PagedListView.ItemCap.UNLIMITED
+                ? mItemProvider.size()
+                : Math.min(mItemProvider.size(), mMaxItems);
+    }
+
+    @Override
+    public void setMaxItems(int maxItems) {
+        mMaxItems = maxItems;
+    }
+
+    /**
+     * Holds views of an item in PagedListView.
+     *
+     * <p>This ViewHolder maps to views in layout car_paged_list_item_content.xml.
+     */
+    public static class ViewHolder extends RecyclerView.ViewHolder {
+
+        private RelativeLayout mContainerLayout;
+
+        private ImageView mPrimaryIcon;
+
+        private TextView mTitle;
+        private TextView mBody;
+
+        private View mSupplementalIconDivider;
+        private ImageView mSupplementalIcon;
+
+        private Button mAction1;
+        private View mAction1Divider;
+
+        private Button mAction2;
+        private View mAction2Divider;
+
+        public ViewHolder(View itemView) {
+            super(itemView);
+
+            mContainerLayout = itemView.findViewById(R.id.container);
+
+            mPrimaryIcon = itemView.findViewById(R.id.primary_icon);
+
+            mTitle = itemView.findViewById(R.id.title);
+            mBody = itemView.findViewById(R.id.body);
+
+            mSupplementalIcon = itemView.findViewById(R.id.supplemental_icon);
+            mSupplementalIconDivider = itemView.findViewById(R.id.supplemental_icon_divider);
+
+            mAction1 = itemView.findViewById(R.id.action1);
+            mAction1Divider = itemView.findViewById(R.id.action1_divider);
+            mAction2 = itemView.findViewById(R.id.action2);
+            mAction2Divider = itemView.findViewById(R.id.action2_divider);
+        }
+
+        public RelativeLayout getContainerLayout() {
+            return mContainerLayout;
+        }
+
+        public ImageView getPrimaryIcon() {
+            return mPrimaryIcon;
+        }
+
+        public TextView getTitle() {
+            return mTitle;
+        }
+
+        public TextView getBody() {
+            return mBody;
+        }
+
+        public ImageView getSupplementalIcon() {
+            return mSupplementalIcon;
+        }
+
+        public View getSupplementalIconDivider() {
+            return mSupplementalIconDivider;
+        }
+
+        public Button getAction1() {
+            return mAction1;
+        }
+
+        public View getAction1Divider() {
+            return mAction1Divider;
+        }
+
+        public Button getAction2() {
+            return mAction2;
+        }
+
+        public View getAction2Divider() {
+            return mAction2Divider;
+        }
+    }
+}
diff --git a/car/src/main/java/androidx/car/widget/ListItemProvider.java b/car/src/main/java/androidx/car/widget/ListItemProvider.java
new file mode 100644
index 0000000..5a3edbd
--- /dev/null
+++ b/car/src/main/java/androidx/car/widget/ListItemProvider.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 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 androidx.car.widget;
+
+import java.util.List;
+
+/**
+ * Supplies data as {@link ListItem}.
+ */
+public abstract class ListItemProvider {
+
+    /**
+     * Returns {@link ListItem} at requested position.
+     */
+    public abstract ListItem get(int position);
+
+    /**
+     * @return number of total items.
+     */
+    public abstract int size();
+
+    /**
+     * A simple provider that wraps around a list.
+     */
+    public static class ListProvider extends ListItemProvider {
+        private final List<ListItem> mItems;
+
+        public ListProvider(List<ListItem> items) {
+            mItems = items;
+        }
+
+        @Override
+        public ListItem get(int position) {
+            return mItems.get(position);
+        }
+
+        @Override
+        public int size() {
+            return mItems.size();
+        }
+    }
+}
diff --git a/car/src/main/java/android/support/car/widget/PagedLayoutManager.java b/car/src/main/java/androidx/car/widget/PagedLayoutManager.java
similarity index 99%
rename from car/src/main/java/android/support/car/widget/PagedLayoutManager.java
rename to car/src/main/java/androidx/car/widget/PagedLayoutManager.java
index c4f469a..cf3b75d 100644
--- a/car/src/main/java/android/support/car/widget/PagedLayoutManager.java
+++ b/car/src/main/java/androidx/car/widget/PagedLayoutManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import android.content.Context;
 import android.graphics.PointF;
@@ -23,7 +23,6 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.VisibleForTesting;
-import android.support.car.R;
 import android.support.v7.widget.LinearSmoothScroller;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.Recycler;
@@ -42,6 +41,8 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 
+import androidx.car.R;
+
 /**
  * Custom {@link RecyclerView.LayoutManager} that behaves similar to LinearLayoutManager except that
  * it has a few tricks up its sleeve.
diff --git a/car/src/main/java/android/support/car/widget/PagedListView.java b/car/src/main/java/androidx/car/widget/PagedListView.java
similarity index 93%
rename from car/src/main/java/android/support/car/widget/PagedListView.java
rename to car/src/main/java/androidx/car/widget/PagedListView.java
index 67a6247..16593b9 100644
--- a/car/src/main/java/android/support/car/widget/PagedListView.java
+++ b/car/src/main/java/androidx/car/widget/PagedListView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
@@ -29,11 +29,11 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.support.annotation.IdRes;
+import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.UiThread;
-import android.support.car.R;
 import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -43,6 +43,8 @@
 import android.view.View;
 import android.widget.FrameLayout;
 
+import androidx.car.R;
+
 /**
  * Custom {@link android.support.v7.widget.RecyclerView} that displays a list of items that
  * resembles a {@link android.widget.ListView} but also has page up and page down arrows on the
@@ -119,6 +121,39 @@
     }
 
     /**
+     * The possible values for @{link #setGutter}. The default value is actually
+     * {@link Gutter#BOTH}.
+     */
+    @IntDef({
+            Gutter.NONE,
+            Gutter.START,
+            Gutter.END,
+            Gutter.BOTH,
+    })
+    public @interface Gutter {
+        /**
+         * No gutter on either side of the list items. The items will span the full width of the
+         * {@link PagedListView}.
+         */
+        int NONE = 0;
+
+        /**
+         * Include a gutter only on the start side (that is, the same side as the scroll bar).
+         */
+        int START = 1;
+
+        /**
+         * Include a gutter only on the end side (that is, the opposite side of the scroll bar).
+         */
+        int END = 2;
+
+        /**
+         * Include a gutter on both sides of the list items. This is the default behaviour.
+         */
+        int BOTH = 3;
+    }
+
+    /**
      * Interface for a {@link android.support.v7.widget.RecyclerView.Adapter} to set the position
      * offset for the adapter to load the data.
      *
@@ -167,14 +202,17 @@
         mRecyclerView.getRecycledViewPool().setMaxRecycledViews(0, 12);
         mRecyclerView.setItemAnimator(new CarItemAnimator(mLayoutManager));
 
-        boolean offsetScrollBar = a.getBoolean(R.styleable.PagedListView_offsetScrollBar, false);
-        if (offsetScrollBar) {
-            MarginLayoutParams params = (MarginLayoutParams) mRecyclerView.getLayoutParams();
-            params.setMarginStart(getResources().getDimensionPixelSize(
-                    R.dimen.car_margin));
-            params.setMarginEnd(
-                    a.getDimensionPixelSize(R.styleable.PagedListView_listEndMargin, 0));
-            mRecyclerView.setLayoutParams(params);
+        if (a.hasValue(R.styleable.PagedListView_gutter)) {
+            int gutter = a.getInt(R.styleable.PagedListView_gutter, Gutter.BOTH);
+            setGutter(gutter);
+        } else if (a.hasValue(R.styleable.PagedListView_offsetScrollBar)) {
+            boolean offsetScrollBar =
+                    a.getBoolean(R.styleable.PagedListView_offsetScrollBar, false);
+            if (offsetScrollBar) {
+                setGutter(Gutter.START);
+            }
+        } else {
+            setGutter(Gutter.BOTH);
         }
 
         if (a.getBoolean(R.styleable.PagedListView_showPagedListViewDivider, true)) {
@@ -286,6 +324,31 @@
         return mLayoutManager.getPosition(v);
     }
 
+    /**
+     * Set the gutter to the specified value.
+     *
+     * The gutter is the space to the start/end of the list view items and will be equal in size
+     * to the scroll bars. By default, there is a gutter to both the left and right of the list
+     * view items, to account for the scroll bar.
+     *
+     * @param gutter A {@link Gutter} value that identifies which sides to apply the gutter to.
+     */
+    public void setGutter(@Gutter int gutter) {
+        int startPadding = 0;
+        int endPadding = 0;
+        if ((gutter & Gutter.START) != 0) {
+            startPadding = getResources().getDimensionPixelSize(R.dimen.car_margin);
+        }
+        if ((gutter & Gutter.END) != 0) {
+            endPadding = getResources().getDimensionPixelSize(R.dimen.car_margin);
+        }
+        mRecyclerView.setPaddingRelative(startPadding, 0, endPadding, 0);
+
+        // If there's a gutter, set ClipToPadding to false so that CardView's shadow will still
+        // appear outside of the padding.
+        mRecyclerView.setClipToPadding(startPadding == 0 && endPadding == 0);
+    }
+
     @NonNull
     public CarRecyclerView getRecyclerView() {
         return mRecyclerView;
@@ -940,7 +1003,7 @@
             Resources res = context.getResources();
             mPaint = new Paint();
             mPaint.setColor(res.getColor(R.color.car_list_divider));
-            mDividerHeight = res.getDimensionPixelSize(R.dimen.car_divider_height);
+            mDividerHeight = res.getDimensionPixelSize(R.dimen.car_list_divider_height);
         }
 
         /** Updates the list divider color which may have changed due to a day night transition. */
diff --git a/car/src/main/java/android/support/car/widget/PagedScrollBarView.java b/car/src/main/java/androidx/car/widget/PagedScrollBarView.java
similarity index 96%
rename from car/src/main/java/android/support/car/widget/PagedScrollBarView.java
rename to car/src/main/java/androidx/car/widget/PagedScrollBarView.java
index 1c46b5d..6843fb6 100644
--- a/car/src/main/java/android/support/car/widget/PagedScrollBarView.java
+++ b/car/src/main/java/androidx/car/widget/PagedScrollBarView.java
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import android.content.Context;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
-import android.support.car.R;
 import android.support.v4.content.ContextCompat;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
@@ -30,6 +29,8 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import androidx.car.R;
+
 /** A custom view to provide list scroll behaviour -- up/down buttons and scroll indicator. */
 public class PagedScrollBarView extends FrameLayout
         implements View.OnClickListener, View.OnLongClickListener {
@@ -84,8 +85,10 @@
         mScrollThumb = (ImageView) findViewById(R.id.scrollbar_thumb);
         mFiller = findViewById(R.id.filler);
 
-        mMinThumbLength = getResources().getDimensionPixelSize(R.dimen.min_thumb_height);
-        mMaxThumbLength = getResources().getDimensionPixelSize(R.dimen.max_thumb_height);
+        mMinThumbLength = getResources()
+                .getDimensionPixelSize(R.dimen.car_min_scroll_bar_thumb_height);
+        mMaxThumbLength = getResources()
+                .getDimensionPixelSize(R.dimen.car_max_scroll_bar_thumb_height);
     }
 
     @Override
diff --git a/car/tests/AndroidManifest.xml b/car/tests/AndroidManifest.xml
index 949e85a..8e5422d 100644
--- a/car/tests/AndroidManifest.xml
+++ b/car/tests/AndroidManifest.xml
@@ -15,12 +15,12 @@
   ~ limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.car.widget.test">
+          package="androidx.car.widget.test">
     <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application android:supportsRtl="true">
-        <activity android:name="android.support.car.widget.ColumnCardViewTestActivity"/>
-        <activity android:name="android.support.car.widget.PagedListViewSavedStateActivity"/>
-        <activity android:name="android.support.car.widget.PagedListViewTestActivity"/>
+        <activity android:name="androidx.car.widget.ColumnCardViewTestActivity"/>
+        <activity android:name="androidx.car.widget.PagedListViewSavedStateActivity"/>
+        <activity android:name="androidx.car.widget.PagedListViewTestActivity"/>
     </application>
 </manifest>
diff --git a/car/tests/res/layout/activity_column_card_view.xml b/car/tests/res/layout/activity_column_card_view.xml
index ad9c5e1..a6acc57 100644
--- a/car/tests/res/layout/activity_column_card_view.xml
+++ b/car/tests/res/layout/activity_column_card_view.xml
@@ -21,13 +21,13 @@
     android:layout_height="match_parent"
     android:orientation="vertical">
 
-    <android.support.car.widget.ColumnCardView
+    <androidx.car.widget.ColumnCardView
         android:id="@+id/default_width_column_card"
         android:layout_gravity="center"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
 
-    <android.support.car.widget.ColumnCardView
+    <androidx.car.widget.ColumnCardView
         car:columnSpan="2"
         android:id="@+id/span_2_column_card"
         android:layout_gravity="center"
diff --git a/car/tests/res/layout/activity_paged_list_view.xml b/car/tests/res/layout/activity_paged_list_view.xml
index d14eb96..d7c8946 100644
--- a/car/tests/res/layout/activity_paged_list_view.xml
+++ b/car/tests/res/layout/activity_paged_list_view.xml
@@ -21,7 +21,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <android.support.car.widget.PagedListView
+    <androidx.car.widget.PagedListView
         android:id="@+id/paged_list_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/car/tests/res/layout/activity_two_paged_list_view.xml b/car/tests/res/layout/activity_two_paged_list_view.xml
index 588071f..457cb95 100644
--- a/car/tests/res/layout/activity_two_paged_list_view.xml
+++ b/car/tests/res/layout/activity_two_paged_list_view.xml
@@ -21,7 +21,7 @@
     android:layout_height="match_parent"
     android:orientation="horizontal">
 
-    <android.support.car.widget.PagedListView
+    <androidx.car.widget.PagedListView
         android:id="@+id/paged_list_view_1"
         android:layout_weight="1"
         android:layout_width="0dp"
@@ -29,7 +29,7 @@
         app:showPagedListViewDivider="false"
         app:offsetScrollBar="true"/>
 
-    <android.support.car.widget.PagedListView
+    <androidx.car.widget.PagedListView
         android:id="@+id/paged_list_view_2"
         android:layout_weight="1"
         android:layout_width="0dp"
diff --git a/car/tests/res/layout/paged_list_item_column_card.xml b/car/tests/res/layout/paged_list_item_column_card.xml
index 2bc5cd0..5028abf 100644
--- a/car/tests/res/layout/paged_list_item_column_card.xml
+++ b/car/tests/res/layout/paged_list_item_column_card.xml
@@ -20,7 +20,7 @@
     android:layout_height="wrap_content"
     android:orientation="vertical">
 
-    <android.support.car.widget.ColumnCardView
+    <androidx.car.widget.ColumnCardView
         android:id="@+id/column_card"
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
@@ -30,6 +30,6 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"/>
 
-    </android.support.car.widget.ColumnCardView>
+    </androidx.car.widget.ColumnCardView>
 
 </FrameLayout>
diff --git a/car/res/values-w840dp/integers.xml b/car/tests/res/values/strings.xml
similarity index 78%
copy from car/res/values-w840dp/integers.xml
copy to car/tests/res/values/strings.xml
index 38c0440..478abc4 100644
--- a/car/res/values-w840dp/integers.xml
+++ b/car/tests/res/values/strings.xml
@@ -14,6 +14,5 @@
 limitations under the License.
 -->
 <resources>
-    <integer name="car_screen_num_of_columns">12</integer>
-    <integer name="column_card_default_column_span">8</integer>
+    <string name="over_120_chars">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</string>
 </resources>
diff --git a/car/tests/src/android/support/car/widget/ColumnCardViewTest.java b/car/tests/src/androidx/car/widget/ColumnCardViewTest.java
similarity index 96%
rename from car/tests/src/android/support/car/widget/ColumnCardViewTest.java
rename to car/tests/src/androidx/car/widget/ColumnCardViewTest.java
index cb61caf..1963629 100644
--- a/car/tests/src/android/support/car/widget/ColumnCardViewTest.java
+++ b/car/tests/src/androidx/car/widget/ColumnCardViewTest.java
@@ -14,13 +14,11 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
-import android.support.car.test.R;
-import android.support.car.utils.ColumnCalculator;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
@@ -32,6 +30,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import androidx.car.test.R;
+import androidx.car.utils.ColumnCalculator;
+
 /** Instrumentation unit tests for {@link ColumnCardView}. */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
diff --git a/car/tests/src/android/support/car/widget/ColumnCardViewTestActivity.java b/car/tests/src/androidx/car/widget/ColumnCardViewTestActivity.java
similarity index 92%
rename from car/tests/src/android/support/car/widget/ColumnCardViewTestActivity.java
rename to car/tests/src/androidx/car/widget/ColumnCardViewTestActivity.java
index 693e4a1..3f42d62 100644
--- a/car/tests/src/android/support/car/widget/ColumnCardViewTestActivity.java
+++ b/car/tests/src/androidx/car/widget/ColumnCardViewTestActivity.java
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.support.car.test.R;
+
+import androidx.car.test.R;
 
 public class ColumnCardViewTestActivity extends Activity {
     @Override
diff --git a/car/tests/src/androidx/car/widget/ListItemTest.java b/car/tests/src/androidx/car/widget/ListItemTest.java
new file mode 100644
index 0000000..2b974c3
--- /dev/null
+++ b/car/tests/src/androidx/car/widget/ListItemTest.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright 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 androidx.car.widget;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
+import static android.support.test.espresso.contrib.RecyclerViewActions.scrollToPosition;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import static junit.framework.TestCase.assertFalse;
+
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.number.IsCloseTo.closeTo;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.drawable.Drawable;
+import android.support.test.InstrumentationRegistry;
+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.v7.widget.CardView;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import androidx.car.test.R;
+
+/**
+* Tests the layout configuration in {@link ListItem}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ListItemTest {
+
+    @Rule
+    public ActivityTestRule<PagedListViewTestActivity> mActivityRule =
+            new ActivityTestRule<>(PagedListViewTestActivity.class);
+
+    private PagedListViewTestActivity mActivity;
+    private PagedListView mPagedListView;
+
+    @Before
+    public void setUp() {
+        mActivity = mActivityRule.getActivity();
+        mPagedListView = mActivity.findViewById(R.id.paged_list_view);
+    }
+
+    private void setupPagedListView(List<ListItem> items) {
+        ListItemProvider provider = new ListItemProvider.ListProvider(
+                new ArrayList<>(items));
+        try {
+            mActivityRule.runOnUiThread(() -> {
+                mPagedListView.setAdapter(new ListItemAdapter(mActivity, provider));
+            });
+        } catch (Throwable throwable) {
+            throwable.printStackTrace();
+            throw new RuntimeException(throwable);
+        }
+        // Wait for paged list view to layout by using espresso to scroll to a position.
+        onView(withId(R.id.recycler_view)).perform(scrollToPosition(0));
+    }
+
+    private static void verifyViewIsHidden(View view) {
+        if (view instanceof ViewGroup) {
+            ViewGroup viewGroup = (ViewGroup) view;
+            final int childCount = viewGroup.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                verifyViewIsHidden(viewGroup.getChildAt(i));
+            }
+        } else {
+            assertThat(view.getVisibility(), is(equalTo(View.GONE)));
+        }
+    }
+
+    private ListItemAdapter.ViewHolder getViewHolderAtPosition(int position) {
+        return (ListItemAdapter.ViewHolder) mPagedListView.getRecyclerView()
+                .findViewHolderForAdapterPosition(
+                position);
+    }
+
+    @Test
+    public void testEmptyItemHidesAllViews() {
+        ListItem item = new ListItem.Builder(mActivity).build();
+        setupPagedListView(Arrays.asList(item));
+        verifyViewIsHidden(mPagedListView.findViewByPosition(0));
+    }
+
+    @Test
+    public void testPrimaryActionVisible() {
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
+                        .build(),
+                new ListItem.Builder(mActivity)
+                        .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
+                        .build());
+        setupPagedListView(items);
+
+        assertThat(getViewHolderAtPosition(0).getPrimaryIcon().getVisibility(),
+                is(equalTo(View.VISIBLE)));
+        assertThat(getViewHolderAtPosition(1).getPrimaryIcon().getVisibility(),
+                is(equalTo(View.VISIBLE)));
+    }
+
+    @Test
+    public void testTextVisible() {
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withTitle("title")
+                        .build(),
+                new ListItem.Builder(mActivity)
+                        .withBody("body")
+                        .build());
+        setupPagedListView(items);
+
+        assertThat(getViewHolderAtPosition(0).getTitle().getVisibility(),
+                is(equalTo(View.VISIBLE)));
+        assertThat(getViewHolderAtPosition(1).getBody().getVisibility(),
+                is(equalTo(View.VISIBLE)));
+    }
+
+    @Test
+    public void testSupplementalActionVisible() {
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true)
+                        .build(),
+                new ListItem.Builder(mActivity)
+                        .withAction("text", true, null)
+                        .build(),
+                new ListItem.Builder(mActivity)
+                        .withActions("text", true, null,
+                                 "text", true, null)
+                        .build());
+        setupPagedListView(items);
+
+        ListItemAdapter.ViewHolder viewHolder = getViewHolderAtPosition(0);
+        assertThat(viewHolder.getSupplementalIcon().getVisibility(), is(equalTo(View.VISIBLE)));
+        assertThat(viewHolder.getSupplementalIconDivider().getVisibility(),
+                is(equalTo(View.VISIBLE)));
+
+        viewHolder = getViewHolderAtPosition(1);
+        assertThat(viewHolder.getAction1().getVisibility(), is(equalTo(View.VISIBLE)));
+        assertThat(viewHolder.getAction1Divider().getVisibility(), is(equalTo(View.VISIBLE)));
+
+        viewHolder = getViewHolderAtPosition(2);
+        assertThat(viewHolder.getAction1().getVisibility(), is(equalTo(View.VISIBLE)));
+        assertThat(viewHolder.getAction1Divider().getVisibility(), is(equalTo(View.VISIBLE)));
+        assertThat(viewHolder.getAction2().getVisibility(), is(equalTo(View.VISIBLE)));
+        assertThat(viewHolder.getAction2Divider().getVisibility(), is(equalTo(View.VISIBLE)));
+    }
+
+    @Test
+    public void testDividersAreOptional() {
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withSupplementalIcon(android.R.drawable.sym_def_app_icon, false)
+                        .build(),
+                new ListItem.Builder(mActivity)
+                        .withAction("text", false, null)
+                        .build(),
+                new ListItem.Builder(mActivity)
+                        .withActions("text", false, null,
+                                "text", false, null)
+                        .build());
+        setupPagedListView(items);
+
+        setupPagedListView(items);
+
+        ListItemAdapter.ViewHolder viewHolder = getViewHolderAtPosition(0);
+        assertThat(viewHolder.getSupplementalIcon().getVisibility(), is(equalTo(View.VISIBLE)));
+        assertThat(viewHolder.getSupplementalIconDivider().getVisibility(),
+                is(equalTo(View.GONE)));
+
+        viewHolder = getViewHolderAtPosition(1);
+        assertThat(viewHolder.getAction1().getVisibility(), is(equalTo(View.VISIBLE)));
+        assertThat(viewHolder.getAction1Divider().getVisibility(), is(equalTo(View.GONE)));
+
+        viewHolder = getViewHolderAtPosition(2);
+        assertThat(viewHolder.getAction1().getVisibility(), is(equalTo(View.VISIBLE)));
+        assertThat(viewHolder.getAction1Divider().getVisibility(), is(equalTo(View.GONE)));
+        assertThat(viewHolder.getAction2().getVisibility(), is(equalTo(View.VISIBLE)));
+        assertThat(viewHolder.getAction2Divider().getVisibility(), is(equalTo(View.GONE)));
+    }
+
+    @Test
+    public void testTextStartMarginMatchesPrimaryActionType() {
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
+                        .build(),
+                new ListItem.Builder(mActivity)
+                        .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
+                        .build(),
+                new ListItem.Builder(mActivity)
+                        .withPrimaryActionEmptyIcon()
+                        .build(),
+                new ListItem.Builder(mActivity)
+                        .withPrimaryActionNoIcon()
+                        .build());
+        List<Integer> expectedStartMargin = Arrays.asList(R.dimen.car_keyline_4,
+                R.dimen.car_keyline_3, R.dimen.car_keyline_3, R.dimen.car_keyline_1);
+        setupPagedListView(items);
+
+        for (int i = 0; i < items.size(); i++) {
+            ListItemAdapter.ViewHolder viewHolder = getViewHolderAtPosition(i);
+
+            int expected = InstrumentationRegistry.getContext().getResources()
+                    .getDimensionPixelSize(expectedStartMargin.get(i));
+            assertThat(((ViewGroup.MarginLayoutParams) viewHolder.getTitle().getLayoutParams())
+                    .getMarginStart(), is(equalTo(expected)));
+            assertThat(((ViewGroup.MarginLayoutParams) viewHolder.getBody().getLayoutParams())
+                    .getMarginStart(), is(equalTo(expected)));
+        }
+    }
+
+    @Test
+    public void testItemWithOnlyTitleIsSingleLine() {
+        List<ListItem> items = Arrays.asList(
+                // Only space
+                new ListItem.Builder(mActivity)
+                        .withTitle(" ")
+                        .build(),
+                // Underscore
+                new ListItem.Builder(mActivity)
+                        .withTitle("______")
+                        .build(),
+                new ListItem.Builder(mActivity)
+                        .withTitle("ALL UPPER CASE")
+                        .build(),
+                // String wouldn't fit in one line
+                new ListItem.Builder(mActivity)
+                        .withTitle(InstrumentationRegistry.getContext().getResources().getString(
+                                R.string.over_120_chars))
+                        .build());
+        setupPagedListView(items);
+
+        double singleLineHeight = InstrumentationRegistry.getContext().getResources().getDimension(
+                R.dimen.car_single_line_list_item_height);
+
+        for (int i = 0; i < items.size(); i++) {
+            assertThat((double) mPagedListView.findViewByPosition(i).getHeight(),
+                    is(closeTo(singleLineHeight, 1.0d)));
+        }
+    }
+
+    @Test
+    public void testItemWithBodyTextIsAtLeastDoubleLine() {
+        List<ListItem> items = Arrays.asList(
+                // Only space
+                new ListItem.Builder(mActivity)
+                        .withBody(" ")
+                        .build(),
+                // Underscore
+                new ListItem.Builder(mActivity)
+                        .withBody("____")
+                        .build(),
+                // String wouldn't fit in one line
+                new ListItem.Builder(mActivity)
+                        .withBody(InstrumentationRegistry.getContext().getResources().getString(
+                                R.string.over_120_chars))
+                        .build());
+        setupPagedListView(items);
+
+        final int doubleLineHeight =
+                (int) InstrumentationRegistry.getContext().getResources().getDimension(
+                        R.dimen.car_double_line_list_item_height);
+
+        for (int i = 0; i < items.size(); i++) {
+            assertThat(mPagedListView.findViewByPosition(i).getHeight(),
+                    is(greaterThanOrEqualTo(doubleLineHeight)));
+        }
+    }
+
+    @Test
+    public void testBodyTextLengthLimit() {
+        final String longText = InstrumentationRegistry.getContext().getResources().getString(
+                R.string.over_120_chars);
+        final int limit = InstrumentationRegistry.getContext().getResources().getInteger(
+                R.integer.car_list_item_text_length_limit);
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withBody(longText)
+                        .build());
+        setupPagedListView(items);
+
+        // + 1 for appended ellipsis.
+        assertThat(getViewHolderAtPosition(0).getBody().getText().length(),
+                is(equalTo(limit + 1)));
+    }
+
+    @Test
+    public void testPrimaryIconDrawable() {
+        Drawable drawable = InstrumentationRegistry.getContext().getResources().getDrawable(
+                android.R.drawable.sym_def_app_icon, null);
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withPrimaryActionIcon(drawable, true)
+                        .build());
+        setupPagedListView(items);
+
+        assertTrue(getViewHolderAtPosition(0).getPrimaryIcon().getDrawable().getConstantState()
+                .equals(drawable.getConstantState()));
+    }
+
+    @Test
+    public void testLargePrimaryIconHasNoStartMargin() {
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
+                        .build());
+        setupPagedListView(items);
+
+        ListItemAdapter.ViewHolder viewHolder = getViewHolderAtPosition(0);
+        assertThat(((ViewGroup.MarginLayoutParams) viewHolder.getPrimaryIcon().getLayoutParams())
+                .getMarginStart(), is(equalTo(0)));
+    }
+
+    @Test
+    public void testSmallPrimaryIconStartMargin() {
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
+                        .build());
+        setupPagedListView(items);
+
+        int expected = InstrumentationRegistry.getContext().getResources().getDimensionPixelSize(
+                R.dimen.car_keyline_1);
+
+        ListItemAdapter.ViewHolder viewHolder = getViewHolderAtPosition(0);
+        assertThat(((ViewGroup.MarginLayoutParams) viewHolder.getPrimaryIcon().getLayoutParams())
+                .getMarginStart(), is(equalTo(expected)));
+    }
+
+    @Test
+    public void testClickingPrimaryActionIsSeparateFromSupplementalAction() {
+        final boolean[] clicked = {false, false};
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withOnClickListener((v) -> clicked[0] = true)
+                        .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true,
+                                (v) -> clicked[1] = true)
+                        .build());
+        setupPagedListView(items);
+
+        onView(withId(R.id.recycler_view)).perform(actionOnItemAtPosition(0, click()));
+        assertTrue(clicked[0]);
+        assertFalse(clicked[1]);
+
+        onView(withId(R.id.recycler_view)).perform(
+                actionOnItemAtPosition(0, clickChildViewWithId(R.id.supplemental_icon)));
+        assertTrue(clicked[1]);
+    }
+
+    @Test
+    public void testClickingSupplementalIcon() {
+        final boolean[] clicked = {false};
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true,
+                                (v) -> clicked[0] = true)
+                        .build());
+        setupPagedListView(items);
+
+        onView(withId(R.id.recycler_view)).perform(
+                actionOnItemAtPosition(0, clickChildViewWithId(R.id.supplemental_icon)));
+        assertTrue(clicked[0]);
+    }
+
+    @Test
+    public void testClickingSupplementalAction() {
+        final boolean[] clicked = {false};
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withAction("action", true, (v) -> clicked[0] = true)
+                        .build());
+        setupPagedListView(items);
+
+        onView(withId(R.id.recycler_view)).perform(
+                actionOnItemAtPosition(0, clickChildViewWithId(R.id.action1)));
+        assertTrue(clicked[0]);
+    }
+
+    @Test
+    public void testClickingBothSupplementalActions() {
+        final boolean[] clicked = {false, false};
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withActions("action 1", true, (v) -> clicked[0] = true,
+                                "action 2", true, (v) -> clicked[1] = true)
+                        .build());
+        setupPagedListView(items);
+
+        onView(withId(R.id.recycler_view)).perform(
+                actionOnItemAtPosition(0, clickChildViewWithId(R.id.action1)));
+        assertTrue(clicked[0]);
+        assertFalse(clicked[1]);
+
+        onView(withId(R.id.recycler_view)).perform(
+                actionOnItemAtPosition(0, clickChildViewWithId(R.id.action2)));
+        assertTrue(clicked[1]);
+    }
+
+    @Test
+    public void testCustomViewBinderAreCalledLast() {
+        final String updatedTitle = "updated title";
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withTitle("original title")
+                        .withViewBinder((viewHolder) -> viewHolder.getTitle().setText(updatedTitle))
+                        .build());
+        setupPagedListView(items);
+
+        ListItemAdapter.ViewHolder viewHolder = getViewHolderAtPosition(0);
+        assertThat(viewHolder.getTitle().getText(), is(equalTo(updatedTitle)));
+    }
+
+    @Test
+    public void testCustomViewBinderOnUnusedViewsHasNoEffect() {
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withViewBinder((viewHolder) -> viewHolder.getBody().setText("text"))
+                        .build());
+        setupPagedListView(items);
+
+        ListItemAdapter.ViewHolder viewHolder = getViewHolderAtPosition(0);
+        assertThat(viewHolder.getBody().getVisibility(), is(equalTo(View.GONE)));
+        // Custom binder interacts with body but has no effect.
+        // Expect card height to remain single line.
+        assertThat((double) viewHolder.itemView.getHeight(), is(closeTo(
+                InstrumentationRegistry.getContext().getResources().getDimension(
+                        R.dimen.car_single_line_list_item_height), 1.0d)));
+    }
+
+    @Test
+    public void testCardLookUsesCardView() {
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withCardLook()
+                        .build());
+        setupPagedListView(items);
+
+        ListItemAdapter.ViewHolder viewHolder = getViewHolderAtPosition(0);
+        assertThat(viewHolder.itemView, is(instanceOf(CardView.class)));
+    }
+
+    @Test
+    public void testSettingTitleOrBodyAsPrimaryText() {
+        // Create 2 items, one with Title as primary (default) and one with Body.
+        // The primary text, regardless of view, should have consistent look (as primary).
+        List<ListItem> items = Arrays.asList(
+                new ListItem.Builder(mActivity)
+                        .withTitle("title")
+                        .withBody("body")
+                        .build(),
+                new ListItem.Builder(mActivity)
+                        .withTitle("title")
+                        .withBody("body", true)
+                        .build());
+        setupPagedListView(items);
+
+        ListItemAdapter.ViewHolder titlePrimary = getViewHolderAtPosition(0);
+        ListItemAdapter.ViewHolder bodyPrimary = getViewHolderAtPosition(1);
+        assertThat(titlePrimary.getTitle().getTextSize(),
+                is(equalTo(bodyPrimary.getBody().getTextSize())));
+        assertThat(titlePrimary.getTitle().getTextColors(),
+                is(equalTo(bodyPrimary.getBody().getTextColors())));
+    }
+
+    private static ViewAction clickChildViewWithId(final int id) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return null;
+            }
+
+            @Override
+            public String getDescription() {
+                return "Click on a child view with specific id.";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                View v = view.findViewById(id);
+                v.performClick();
+            }
+        };
+    }
+}
diff --git a/car/tests/src/android/support/car/widget/PagedListViewSavedStateActivity.java b/car/tests/src/androidx/car/widget/PagedListViewSavedStateActivity.java
similarity index 93%
rename from car/tests/src/android/support/car/widget/PagedListViewSavedStateActivity.java
rename to car/tests/src/androidx/car/widget/PagedListViewSavedStateActivity.java
index 8cb976c..d9eecd5 100644
--- a/car/tests/src/android/support/car/widget/PagedListViewSavedStateActivity.java
+++ b/car/tests/src/androidx/car/widget/PagedListViewSavedStateActivity.java
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.support.car.test.R;
+
+import androidx.car.test.R;
 
 /**
  * Test Activity for testing the saving of state for the {@link PagedListView}. It will inflate
diff --git a/car/tests/src/android/support/car/widget/PagedListViewSavedStateTest.java b/car/tests/src/androidx/car/widget/PagedListViewSavedStateTest.java
similarity index 99%
rename from car/tests/src/android/support/car/widget/PagedListViewSavedStateTest.java
rename to car/tests/src/androidx/car/widget/PagedListViewSavedStateTest.java
index 9b871b3..b134e10 100644
--- a/car/tests/src/android/support/car/widget/PagedListViewSavedStateTest.java
+++ b/car/tests/src/androidx/car/widget/PagedListViewSavedStateTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
@@ -26,7 +26,6 @@
 
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
-import android.support.car.test.R;
 import android.support.test.espresso.IdlingRegistry;
 import android.support.test.espresso.IdlingResource;
 import android.support.test.filters.SmallTest;
@@ -50,6 +49,8 @@
 import java.util.List;
 import java.util.Random;
 
+import androidx.car.test.R;
+
 /** Unit tests for the ability of the {@link PagedListView} to save state. */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
diff --git a/car/tests/src/android/support/car/widget/PagedListViewTest.java b/car/tests/src/androidx/car/widget/PagedListViewTest.java
similarity index 99%
rename from car/tests/src/android/support/car/widget/PagedListViewTest.java
rename to car/tests/src/androidx/car/widget/PagedListViewTest.java
index 0d07fbd..e924f4b 100644
--- a/car/tests/src/android/support/car/widget/PagedListViewTest.java
+++ b/car/tests/src/androidx/car/widget/PagedListViewTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
@@ -36,7 +36,6 @@
 
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
-import android.support.car.test.R;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.espresso.Espresso;
 import android.support.test.espresso.IdlingResource;
@@ -61,6 +60,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.car.test.R;
+
 /** Unit tests for {@link PagedListView}. */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
diff --git a/car/tests/src/android/support/car/widget/PagedListViewTestActivity.java b/car/tests/src/androidx/car/widget/PagedListViewTestActivity.java
similarity index 93%
rename from car/tests/src/android/support/car/widget/PagedListViewTestActivity.java
rename to car/tests/src/androidx/car/widget/PagedListViewTestActivity.java
index 6371374..741cadd 100644
--- a/car/tests/src/android/support/car/widget/PagedListViewTestActivity.java
+++ b/car/tests/src/androidx/car/widget/PagedListViewTestActivity.java
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package android.support.car.widget;
+package androidx.car.widget;
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.support.car.test.R;
+
+import androidx.car.test.R;
 
 /**
  * Simple test activity for {@link PagedListView} class.
diff --git a/compat/build.gradle b/compat/build.gradle
index b3fa298..a26eac8 100644
--- a/compat/build.gradle
+++ b/compat/build.gradle
@@ -13,8 +13,8 @@
         transitive = true
     }
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation project(':support-testutils'), {
diff --git a/compat/src/main/java/android/support/v4/app/NotificationCompat.java b/compat/src/main/java/android/support/v4/app/NotificationCompat.java
index 1077b1f..80c7757 100644
--- a/compat/src/main/java/android/support/v4/app/NotificationCompat.java
+++ b/compat/src/main/java/android/support/v4/app/NotificationCompat.java
@@ -32,6 +32,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
+import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Build;
@@ -116,7 +117,6 @@
      * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
      */
     public static final int STREAM_DEFAULT = -1;
-
     /**
      * Bit set in the Notification flags field when LEDs should be turned on
      * for this notification.
@@ -439,6 +439,14 @@
     public static final int COLOR_DEFAULT = Color.TRANSPARENT;
 
     /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    @IntDef({AudioManager.STREAM_VOICE_CALL, AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING,
+            AudioManager.STREAM_MUSIC, AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
+            AudioManager.STREAM_DTMF, AudioManager.STREAM_ACCESSIBILITY})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StreamType {}
+
+    /** @hide */
     @Retention(SOURCE)
     @IntDef({VISIBILITY_PUBLIC, VISIBILITY_PRIVATE, VISIBILITY_SECRET})
     public @interface NotificationVisibility {}
@@ -957,6 +965,12 @@
         public Builder setSound(Uri sound) {
             mNotification.sound = sound;
             mNotification.audioStreamType = Notification.STREAM_DEFAULT;
+            if (Build.VERSION.SDK_INT >= 21) {
+                mNotification.audioAttributes = new AudioAttributes.Builder()
+                        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+                        .setUsage(AudioAttributes.USAGE_NOTIFICATION)
+                        .build();
+            }
             return this;
         }
 
@@ -971,9 +985,15 @@
          * @see Notification#STREAM_DEFAULT
          * @see AudioManager for the <code>STREAM_</code> constants.
          */
-        public Builder setSound(Uri sound, int streamType) {
+        public Builder setSound(Uri sound, @StreamType int streamType) {
             mNotification.sound = sound;
             mNotification.audioStreamType = streamType;
+            if (Build.VERSION.SDK_INT >= 21) {
+                mNotification.audioAttributes = new AudioAttributes.Builder()
+                        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+                        .setLegacyStreamType(streamType)
+                        .build();
+            }
             return this;
         }
 
diff --git a/compat/src/main/java/android/support/v4/app/NotificationCompatBuilder.java b/compat/src/main/java/android/support/v4/app/NotificationCompatBuilder.java
index 71f4160..db775a5 100644
--- a/compat/src/main/java/android/support/v4/app/NotificationCompatBuilder.java
+++ b/compat/src/main/java/android/support/v4/app/NotificationCompatBuilder.java
@@ -28,6 +28,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.RestrictTo;
+import android.text.TextUtils;
 import android.util.SparseArray;
 import android.widget.RemoteViews;
 
@@ -69,7 +70,6 @@
                 .setSmallIcon(n.icon, n.iconLevel)
                 .setContent(n.contentView)
                 .setTicker(n.tickerText, b.mTickerView)
-                .setSound(n.sound, n.audioStreamType)
                 .setVibrate(n.vibrate)
                 .setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
                 .setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
@@ -86,6 +86,9 @@
                 .setLargeIcon(b.mLargeIcon)
                 .setNumber(b.mNumber)
                 .setProgress(b.mProgressMax, b.mProgress, b.mProgressIndeterminate);
+        if (Build.VERSION.SDK_INT < 21) {
+            mBuilder.setSound(n.sound, n.audioStreamType);
+        }
         if (Build.VERSION.SDK_INT >= 16) {
             mBuilder.setSubText(b.mSubText)
                     .setUsesChronometer(b.mUseChronometer)
@@ -141,7 +144,8 @@
             mBuilder.setCategory(b.mCategory)
                     .setColor(b.mColor)
                     .setVisibility(b.mVisibility)
-                    .setPublicVersion(b.mPublicVersion);
+                    .setPublicVersion(b.mPublicVersion)
+                    .setSound(n.sound, n.audioAttributes);
 
             for (String person: b.mPeople) {
                 mBuilder.addPerson(person);
@@ -169,6 +173,13 @@
             if (b.mColorizedSet) {
                 mBuilder.setColorized(b.mColorized);
             }
+
+            if (!TextUtils.isEmpty(b.mChannelId)) {
+                mBuilder.setSound(null)
+                        .setDefaults(0)
+                        .setLights(0, 0, 0)
+                        .setVibrate(null);
+            }
         }
     }
 
diff --git a/compat/src/main/java/android/support/v4/app/NotificationManagerCompat.java b/compat/src/main/java/android/support/v4/app/NotificationManagerCompat.java
index 1a0f1bc..07fcb6c 100644
--- a/compat/src/main/java/android/support/v4/app/NotificationManagerCompat.java
+++ b/compat/src/main/java/android/support/v4/app/NotificationManagerCompat.java
@@ -43,10 +43,10 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.ArrayDeque;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -560,7 +560,7 @@
             /** The service stub provided by onServiceConnected */
             public INotificationSideChannel service;
             /** Queue of pending tasks to send to this listener service */
-            public LinkedList<Task> taskQueue = new LinkedList<Task>();
+            public ArrayDeque<Task> taskQueue = new ArrayDeque<>();
             /** Number of retries attempted while connecting to this listener service */
             public int retryCount = 0;
 
diff --git a/compat/src/main/java/android/support/v4/graphics/TypefaceCompatApi26Impl.java b/compat/src/main/java/android/support/v4/graphics/TypefaceCompatApi26Impl.java
index f23ac0d..4260c55 100644
--- a/compat/src/main/java/android/support/v4/graphics/TypefaceCompatApi26Impl.java
+++ b/compat/src/main/java/android/support/v4/graphics/TypefaceCompatApi26Impl.java
@@ -233,6 +233,9 @@
             final ContentResolver resolver = context.getContentResolver();
             try (ParcelFileDescriptor pfd =
                     resolver.openFileDescriptor(bestFont.getUri(), "r", cancellationSignal)) {
+                if (pfd == null) {
+                    return null;
+                }
                 return new Typeface.Builder(pfd.getFileDescriptor())
                         .setWeight(bestFont.getWeight())
                         .setItalic(bestFont.isItalic())
diff --git a/compat/src/main/java/android/support/v4/provider/FontsContractCompat.java b/compat/src/main/java/android/support/v4/provider/FontsContractCompat.java
index 0926186..39acf68 100644
--- a/compat/src/main/java/android/support/v4/provider/FontsContractCompat.java
+++ b/compat/src/main/java/android/support/v4/provider/FontsContractCompat.java
@@ -274,7 +274,10 @@
                     : new ReplyCallback<TypefaceResult>() {
                         @Override
                         public void onReply(final TypefaceResult typeface) {
-                            if (typeface.mResult == FontFamilyResult.STATUS_OK) {
+                            if (typeface == null) {
+                                fontCallback.callbackFailAsync(
+                                        FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND, handler);
+                            } else if (typeface.mResult == FontFamilyResult.STATUS_OK) {
                                 fontCallback.callbackSuccessAsync(typeface.mTypeface, handler);
                             } else {
                                 fontCallback.callbackFailAsync(typeface.mResult, handler);
diff --git a/compat/src/main/java/android/support/v4/util/ArraySet.java b/compat/src/main/java/android/support/v4/util/ArraySet.java
index ab080fa..a9a8806 100644
--- a/compat/src/main/java/android/support/v4/util/ArraySet.java
+++ b/compat/src/main/java/android/support/v4/util/ArraySet.java
@@ -16,9 +16,6 @@
 
 package android.support.v4.util;
 
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
 import android.util.Log;
 
 import java.lang.reflect.Array;
@@ -74,7 +71,6 @@
     static Object[] sTwiceBaseCache;
     static int sTwiceBaseCacheSize;
 
-    final boolean mIdentityHashCode;
     int[] mHashes;
     Object[] mArray;
     int mSize;
@@ -238,19 +234,13 @@
      * will grow once items are added to it.
      */
     public ArraySet() {
-        this(0, false);
+        this(0);
     }
 
     /**
      * Create a new ArraySet with a given initial capacity.
      */
     public ArraySet(int capacity) {
-        this(capacity, false);
-    }
-
-    /** {@hide} */
-    public ArraySet(int capacity, boolean identityHashCode) {
-        mIdentityHashCode = identityHashCode;
         if (capacity == 0) {
             mHashes = INT;
             mArray = OBJECT;
@@ -270,14 +260,6 @@
         }
     }
 
-    /** {@hide} */
-    public ArraySet(Collection<E> set) {
-        this();
-        if (set != null) {
-            addAll(set);
-        }
-    }
-
     /**
      * Make the array map empty.  All storage is released.
      */
@@ -326,8 +308,7 @@
      * @return Returns the index of the value if it exists, else a negative integer.
      */
     public int indexOf(Object key) {
-        return key == null ? indexOfNull()
-                : indexOf(key, mIdentityHashCode ? System.identityHashCode(key) : key.hashCode());
+        return key == null ? indexOfNull() : indexOf(key, key.hashCode());
     }
 
     /**
@@ -364,7 +345,7 @@
             hash = 0;
             index = indexOfNull();
         } else {
-            hash = mIdentityHashCode ? System.identityHashCode(value) : value.hashCode();
+            hash = value.hashCode();
             index = indexOf(value, hash);
         }
         if (index >= 0) {
@@ -406,36 +387,6 @@
     }
 
     /**
-     * Special fast path for appending items to the end of the array without validation.
-     * The array must already be large enough to contain the item.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void append(E value) {
-        final int index = mSize;
-        final int hash = value == null ? 0
-                : (mIdentityHashCode ? System.identityHashCode(value) : value.hashCode());
-        if (index >= mHashes.length) {
-            throw new IllegalStateException("Array is full");
-        }
-        if (index > 0 && mHashes[index - 1] > hash) {
-            // Cannot optimize since it would break the sorted order - fallback to add()
-            if (DEBUG) {
-                RuntimeException e = new RuntimeException("here");
-                e.fillInStackTrace();
-                Log.w(TAG, "New hash " + hash
-                        + " is before end of array hash " + mHashes[index - 1]
-                        + " at index " + index, e);
-            }
-            add(value);
-            return;
-        }
-        mSize = index + 1;
-        mHashes[index] = hash;
-        mArray[index] = value;
-    }
-
-    /**
      * Perform a {@link #add(Object)} of all values in <var>array</var>
      * @param array The array whose contents are to be retrieved.
      */
diff --git a/compat/tests/java/android/support/v4/app/NotificationCompatTest.java b/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
index dd870dd..7a5a57f 100644
--- a/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
+++ b/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
@@ -34,6 +34,9 @@
 import android.annotation.TargetApi;
 import android.app.Notification;
 import android.content.Context;
+import android.graphics.Color;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -424,6 +427,71 @@
         }
     }
 
+    @Test
+    @SdkSuppress(minSdkVersion = 21)
+    public void testHasAudioAttributesFrom21() throws Throwable {
+        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setSound(Uri.EMPTY)
+                .build();
+        assertNotNull(n.audioAttributes);
+        assertEquals(-1, n.audioStreamType);
+        assertEquals(Uri.EMPTY, n.sound);
+
+        n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setSound(Uri.EMPTY, AudioManager.STREAM_RING)
+                .build();
+        assertNotNull(n.audioAttributes);
+        assertEquals(AudioAttributes.CONTENT_TYPE_SONIFICATION,
+                n.audioAttributes.getContentType());
+        assertEquals(-1, n.audioStreamType);
+        assertEquals(Uri.EMPTY, n.sound);
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = 20)
+    public void testHasStreamTypePre21() throws Throwable {
+        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setSound(Uri.EMPTY, 34)
+                .build();
+        assertEquals(34, n.audioStreamType);
+        assertEquals(Uri.EMPTY, n.sound);
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test
+    public void testClearAlertingFieldsIfUsingChannels() throws Throwable {
+        long[] vibration = new long[]{100};
+
+        // stripped if using channels
+        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity(), "test")
+                .setSound(Uri.EMPTY)
+                .setDefaults(Notification.DEFAULT_ALL)
+                .setVibrate(vibration)
+                .setLights(Color.BLUE, 100, 100)
+                .build();
+        assertNull(n.sound);
+        assertEquals(0, n.defaults);
+        assertNull(n.vibrate);
+        assertEquals(0, n.ledARGB);
+        assertEquals(0, n.ledOnMS);
+        assertEquals(0, n.ledOffMS);
+
+        // left intact if not using channels
+        n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
+                .setSound(Uri.EMPTY)
+                .setDefaults(Notification.DEFAULT_ALL)
+                .setVibrate(vibration)
+                .setLights(Color.BLUE, 100, 100)
+                .build();
+        assertEquals(Uri.EMPTY, n.sound);
+        assertNotNull(n.audioAttributes);
+        assertEquals(Notification.DEFAULT_ALL, n.defaults);
+        assertEquals(vibration, n.vibrate);
+        assertEquals(Color.BLUE, n.ledARGB);
+        assertEquals(100, n.ledOnMS);
+        assertEquals(100, n.ledOffMS);
+    }
+
     private static RemoteInput newDataOnlyRemoteInput() {
         return new RemoteInput.Builder(DATA_RESULT_KEY)
             .setAllowFreeFormInput(false)
diff --git a/compat/tests/java/android/support/v4/provider/FontsContractCompatTest.java b/compat/tests/java/android/support/v4/provider/FontsContractCompatTest.java
index 66bdd50..4edab02 100644
--- a/compat/tests/java/android/support/v4/provider/FontsContractCompatTest.java
+++ b/compat/tests/java/android/support/v4/provider/FontsContractCompatTest.java
@@ -40,9 +40,11 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.Signature;
 import android.graphics.Typeface;
+import android.support.annotation.NonNull;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.content.res.ResourcesCompat;
 import android.support.v4.provider.FontsContractCompat.FontFamilyResult;
 import android.support.v4.provider.FontsContractCompat.FontInfo;
 import android.util.Base64;
@@ -56,6 +58,8 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Unit tests for {@link FontsContractCompat}.
@@ -111,34 +115,6 @@
                 InstrumentationRegistry.getInstrumentation().getTargetContext());
     }
 
-    private static class TestCallback extends FontsContractCompat.FontRequestCallback {
-        private Typeface mTypeface;
-
-        private int mSuccessCallCount;
-        private int mFailedCallCount;
-
-        public void onTypefaceRetrieved(Typeface typeface) {
-            mTypeface = typeface;
-            mSuccessCallCount++;
-        }
-
-        public void onTypefaceRequestFailed(int reason) {
-            mFailedCallCount++;
-        }
-
-        public Typeface getTypeface() {
-            return mTypeface;
-        }
-
-        public int getSuccessCallCount() {
-            return mSuccessCallCount;
-        }
-
-        public int getFailedCallCount() {
-            return mFailedCallCount;
-        }
-    }
-
     @Test
     public void typefaceNotCacheTest() throws NameNotFoundException {
         FontRequest request = new FontRequest(
@@ -413,4 +389,43 @@
         when(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo);
         return info;
     }
+
+    @Test
+    public void testGetFontSync_invalidUri() throws InterruptedException {
+        final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+        final FontRequest request = new FontRequest(
+                AUTHORITY, PACKAGE, MockFontProvider.INVALID_URI, SIGNATURE);
+        final CountDownLatch latch = new CountDownLatch(1);
+        final FontCallback callback = new FontCallback(latch);
+
+        inst.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                FontsContractCompat.getFontSync(mContext, request, callback, null,
+                        false /* isBlockingFetch */, 300 /* timeout */, Typeface.NORMAL);
+            }
+        });
+        assertTrue(latch.await(5L, TimeUnit.SECONDS));
+        assertNull(callback.mTypeface);
+    }
+
+    public static class FontCallback extends ResourcesCompat.FontCallback {
+        private final CountDownLatch mLatch;
+        Typeface mTypeface;
+
+        FontCallback(CountDownLatch latch) {
+            mLatch = latch;
+        }
+
+        @Override
+        public void onFontRetrieved(@NonNull Typeface typeface) {
+            mTypeface = typeface;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onFontRetrievalFailed(int reason) {
+            mLatch.countDown();
+        }
+    }
 }
diff --git a/compat/tests/java/android/support/v4/provider/MockFontProvider.java b/compat/tests/java/android/support/v4/provider/MockFontProvider.java
index f07d92d..f584d68 100644
--- a/compat/tests/java/android/support/v4/provider/MockFontProvider.java
+++ b/compat/tests/java/android/support/v4/provider/MockFontProvider.java
@@ -43,6 +43,7 @@
     static final String[] FONT_FILES = {
             "samplefont.ttf", "large_a.ttf", "large_b.ttf", "large_c.ttf", "large_d.ttf"
     };
+    private static final int INVALID_FONT_FILE_ID = -1;
     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;
@@ -59,6 +60,7 @@
     static final String NEGATIVE_ERROR_CODE_QUERY = "negativeCode";
     static final String MANDATORY_FIELDS_ONLY_QUERY = "mandatoryFields";
     static final String STYLE_TEST_QUERY = "styleTest";
+    static final String INVALID_URI = "invalidURI";
 
     static class Font {
         Font(int id, int fileId, int ttcIndex, String varSettings, int weight, int italic,
@@ -176,6 +178,11 @@
                         Columns.RESULT_CODE_OK, true),
         });
 
+        map.put(INVALID_URI, new Font[] {
+                new Font(id++, INVALID_FONT_FILE_ID, 0, null, 400, 0,
+                        Columns.RESULT_CODE_OK, true),
+        });
+
         QUERY_MAP = Collections.unmodifiableMap(map);
     }
 
@@ -260,6 +267,9 @@
     @Override
     public ParcelFileDescriptor openFile(Uri uri, String mode) {
         final int id = (int) ContentUris.parseId(uri);
+        if (id < 0) {
+            return null;
+        }
         final File targetFile = getCopiedFile(getContext(), FONT_FILES[id]);
         try {
             return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY);
diff --git a/content/build.gradle b/content/build.gradle
index 15f96c4..9091053 100644
--- a/content/build.gradle
+++ b/content/build.gradle
@@ -27,8 +27,8 @@
     api(project(":support-compat"))
 
     androidTestImplementation(JUNIT)
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
 }
 
 android {
diff --git a/core-ui/build.gradle b/core-ui/build.gradle
index 8bab1cb..f7cd2d7 100644
--- a/core-ui/build.gradle
+++ b/core-ui/build.gradle
@@ -11,8 +11,8 @@
     api(project(":support-compat"))
     api project(':support-core-utils')
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(ESPRESSO_CONTRIB, libs.exclude_support)
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation project(':support-testutils'), {
diff --git a/core-ui/tests/AndroidManifest.xml b/core-ui/tests/AndroidManifest.xml
index 0185f72..904111e 100644
--- a/core-ui/tests/AndroidManifest.xml
+++ b/core-ui/tests/AndroidManifest.xml
@@ -38,6 +38,8 @@
 
         <activity android:name="android.support.v4.view.ViewPagerTest$ViewPagerActivity"/>
 
+        <activity android:name="android.support.design.widget.CoordinatorLayoutActivity"/>
+
     </application>
 
 </manifest>
diff --git a/design/tests/src/android/support/design/testutils/CoordinatorLayoutUtils.java b/core-ui/tests/java/android/support/design/testutils/CoordinatorLayoutUtils.java
similarity index 100%
rename from design/tests/src/android/support/design/testutils/CoordinatorLayoutUtils.java
rename to core-ui/tests/java/android/support/design/testutils/CoordinatorLayoutUtils.java
diff --git a/core-ui/tests/java/android/support/design/widget/CoordinatorLayoutActivity.java b/core-ui/tests/java/android/support/design/widget/CoordinatorLayoutActivity.java
new file mode 100644
index 0000000..b7fe740
--- /dev/null
+++ b/core-ui/tests/java/android/support/design/widget/CoordinatorLayoutActivity.java
@@ -0,0 +1,39 @@
+/*
+ * 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.design.widget;
+
+import android.support.coreui.test.R;
+import android.support.v4.BaseTestActivity;
+import android.widget.FrameLayout;
+
+public class CoordinatorLayoutActivity extends BaseTestActivity {
+
+    FrameLayout mContainer;
+    CoordinatorLayout mCoordinatorLayout;
+
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.activity_coordinator_layout;
+    }
+
+    @Override
+    protected void onContentViewSet() {
+        mContainer = findViewById(R.id.container);
+        mCoordinatorLayout = findViewById(R.id.coordinator);
+    }
+
+}
diff --git a/design/tests/src/android/support/design/widget/CoordinatorLayoutSortTest.java b/core-ui/tests/java/android/support/design/widget/CoordinatorLayoutSortTest.java
similarity index 91%
rename from design/tests/src/android/support/design/widget/CoordinatorLayoutSortTest.java
rename to core-ui/tests/java/android/support/design/widget/CoordinatorLayoutSortTest.java
index 0a407b7..4e0ccfc 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorLayoutSortTest.java
+++ b/core-ui/tests/java/android/support/design/widget/CoordinatorLayoutSortTest.java
@@ -22,8 +22,10 @@
 import android.support.design.testutils.CoordinatorLayoutUtils;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
 import android.view.View;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -35,8 +37,9 @@
 
 @RunWith(Parameterized.class)
 @MediumTest
-public class CoordinatorLayoutSortTest
-        extends BaseInstrumentationTestCase<CoordinatorLayoutActivity> {
+public class CoordinatorLayoutSortTest {
+    @Rule
+    public final ActivityTestRule<CoordinatorLayoutActivity> mActivityTestRule;
 
     private static final int NUMBER_VIEWS_DEPENDENCY_SORT = 4;
 
@@ -60,7 +63,7 @@
 
     public CoordinatorLayoutSortTest(int firstIndex, int secondIndex, int thirdIndex,
             int fourthIndex) {
-        super(CoordinatorLayoutActivity.class);
+        mActivityTestRule = new ActivityTestRule<>(CoordinatorLayoutActivity.class);
         mFirstAddIndex = firstIndex;
         mSecondAddIndex = secondIndex;
         mThirdAddIndex = thirdIndex;
@@ -86,8 +89,8 @@
 
             // Create a Behavior which depends on the previously added view
             View dependency = i > 0 ? views.get(i - 1) : null;
-            final CoordinatorLayout.Behavior<View> behavior
-                    = new CoordinatorLayoutUtils.DependentBehavior(dependency);
+            final CoordinatorLayout.Behavior<View> behavior =
+                    new CoordinatorLayoutUtils.DependentBehavior(dependency);
 
             // And set its LayoutParams to use the Behavior
             CoordinatorLayout.LayoutParams lp = col.generateDefaultLayoutParams();
diff --git a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java b/core-ui/tests/java/android/support/design/widget/CoordinatorLayoutTest.java
similarity index 97%
rename from design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
rename to core-ui/tests/java/android/support/design/widget/CoordinatorLayoutTest.java
index 3588043..4062efb 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
+++ b/core-ui/tests/java/android/support/design/widget/CoordinatorLayoutTest.java
@@ -42,13 +42,15 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.support.annotation.NonNull;
-import android.support.design.test.R;
+import android.support.coreui.test.R;
 import android.support.design.testutils.CoordinatorLayoutUtils;
 import android.support.design.testutils.CoordinatorLayoutUtils.DependentBehavior;
 import android.support.design.widget.CoordinatorLayout.Behavior;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.WindowInsetsCompat;
@@ -59,19 +61,24 @@
 import android.widget.ImageView;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
 @MediumTest
-public class CoordinatorLayoutTest extends BaseInstrumentationTestCase<CoordinatorLayoutActivity> {
+@RunWith(AndroidJUnit4.class)
+public class CoordinatorLayoutTest {
+    @Rule
+    public final ActivityTestRule<CoordinatorLayoutActivity> mActivityTestRule;
 
     private Instrumentation mInstrumentation;
 
     public CoordinatorLayoutTest() {
-        super(CoordinatorLayoutActivity.class);
+        mActivityTestRule = new ActivityTestRule<>(CoordinatorLayoutActivity.class);
     }
 
     @Before
@@ -383,14 +390,14 @@
         final View viewA = new View(col.getContext());
         final View viewB = new View(col.getContext());
         final CoordinatorLayout.LayoutParams lpB = col.generateDefaultLayoutParams();
-        final CoordinatorLayout.Behavior behavior
-                = new CoordinatorLayoutUtils.DependentBehavior(viewA) {
-            @Override
-            public void onDependentViewRemoved(CoordinatorLayout parent, View child,
-                    View dependency) {
-                parent.getDependencies(child);
-            }
-        };
+        final CoordinatorLayout.Behavior behavior =
+                new CoordinatorLayoutUtils.DependentBehavior(viewA) {
+                    @Override
+                    public void onDependentViewRemoved(
+                            CoordinatorLayout parent, View child, View dependency) {
+                        parent.getDependencies(child);
+                    }
+                };
         lpB.setBehavior(behavior);
 
         // Now add views
diff --git a/core-ui/tests/res/layout/activity_coordinator_layout.xml b/core-ui/tests/res/layout/activity_coordinator_layout.xml
new file mode 100644
index 0000000..93bf947
--- /dev/null
+++ b/core-ui/tests/res/layout/activity_coordinator_layout.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:id="@+id/container"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent">
+
+    <android.support.design.widget.CoordinatorLayout
+        android:id="@+id/coordinator"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/design/tests/res/layout/include_nestedscrollview.xml b/core-ui/tests/res/layout/include_nestedscrollview.xml
similarity index 100%
rename from design/tests/res/layout/include_nestedscrollview.xml
rename to core-ui/tests/res/layout/include_nestedscrollview.xml
diff --git a/core-ui/tests/res/values/ids.xml b/core-ui/tests/res/values/ids.xml
index e5fcf63..f3ac9b4 100644
--- a/core-ui/tests/res/values/ids.xml
+++ b/core-ui/tests/res/values/ids.xml
@@ -24,4 +24,5 @@
     <item name="page_7" type="id"/>
     <item name="page_8" type="id"/>
     <item name="page_9" type="id"/>
+    <item name="anchor" type="id"/>
 </resources>
\ No newline at end of file
diff --git a/core-utils/build.gradle b/core-utils/build.gradle
index de4f67c..3f1efa1 100644
--- a/core-utils/build.gradle
+++ b/core-utils/build.gradle
@@ -10,8 +10,8 @@
     api(project(":support-annotations"))
     api(project(":support-compat"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
 }
diff --git a/customtabs/build.gradle b/customtabs/build.gradle
index abcca93..75e28f7 100644
--- a/customtabs/build.gradle
+++ b/customtabs/build.gradle
@@ -10,8 +10,8 @@
     api(project(":support-compat"))
     api(project(":support-annotations"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(project(":support-testutils"))
 }
 
diff --git a/design/build.gradle b/design/build.gradle
index 804ed16..e7ebc91 100644
--- a/design/build.gradle
+++ b/design/build.gradle
@@ -12,8 +12,8 @@
     api(project(":recyclerview-v7"))
     api(project(":transition"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(ESPRESSO_CONTRIB, libs.exclude_support)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
diff --git a/design/tests/res/values/ids.xml b/design/tests/res/values/ids.xml
index 73540b7..52b8356 100644
--- a/design/tests/res/values/ids.xml
+++ b/design/tests/res/values/ids.xml
@@ -26,5 +26,4 @@
     <item name="page_9" type="id"/>
     <item name="textinputlayout" type="id"/>
     <item name="textinputedittext" type="id"/>
-    <item name="anchor" type="id"/>
 </resources>
\ No newline at end of file
diff --git a/dynamic-animation/build.gradle b/dynamic-animation/build.gradle
index 21dbfb3..ac5623d 100644
--- a/dynamic-animation/build.gradle
+++ b/dynamic-animation/build.gradle
@@ -9,8 +9,8 @@
 dependencies {
     api(project(":support-core-utils"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
 }
diff --git a/emoji/core/api/current.txt b/emoji/core/api/current.txt
index d5ce666..40bd7d8 100644
--- a/emoji/core/api/current.txt
+++ b/emoji/core/api/current.txt
@@ -32,6 +32,8 @@
     method public android.support.text.emoji.EmojiCompat.Config setEmojiSpanIndicatorColor(int);
     method public android.support.text.emoji.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
     method public android.support.text.emoji.EmojiCompat.Config setReplaceAll(boolean);
+    method public android.support.text.emoji.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+    method public android.support.text.emoji.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer>);
     method public android.support.text.emoji.EmojiCompat.Config unregisterInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
   }
 
diff --git a/emoji/core/build.gradle b/emoji/core/build.gradle
index 42bbf52..a311f25 100644
--- a/emoji/core/build.gradle
+++ b/emoji/core/build.gradle
@@ -24,8 +24,8 @@
 
     api(project(":support-compat"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation project(':support-testutils')
@@ -97,12 +97,12 @@
                         if (i > 0) {
                             // If we get here, we know this is some timing issues.  The jar file is
                             // properly created, but only if we wait.
-                            log.error("Succeeded in opening jar file '$f' after $i retries.  Failing build.")
+                            logger.error("Succeeded in opening jar file '$f' after $i retries.  Failing build.")
                             throw new RuntimeException("Failed opening zip file earlier.")
                         }
                         break
                     } catch (ZipException e) {
-                        log.error("Error opening jar file '$f' (attempt: $i): $e.message")
+                        logger.error("Error opening jar file '$f' (attempt: $i): $e.message")
                         sleep(1000)
                     } finally {
                         if (zip != null) {
diff --git a/emoji/core/src/main/java/android/support/text/emoji/EmojiCompat.java b/emoji/core/src/main/java/android/support/text/emoji/EmojiCompat.java
index 5436aa2..413a9dd 100644
--- a/emoji/core/src/main/java/android/support/text/emoji/EmojiCompat.java
+++ b/emoji/core/src/main/java/android/support/text/emoji/EmojiCompat.java
@@ -184,6 +184,16 @@
     private final boolean mReplaceAll;
 
     /**
+     * @see Config#setUseEmojiAsDefaultStyle(boolean)
+     */
+    private final boolean mUseEmojiAsDefaultStyle;
+
+    /**
+     * @see Config#setUseEmojiAsDefaultStyle(boolean, List)
+     */
+    private final int[] mEmojiAsDefaultStyleExceptions;
+
+    /**
      * @see Config#setEmojiSpanIndicatorEnabled(boolean)
      */
     private final boolean mEmojiSpanIndicatorEnabled;
@@ -201,6 +211,8 @@
     private EmojiCompat(@NonNull final Config config) {
         mInitLock = new ReentrantReadWriteLock();
         mReplaceAll = config.mReplaceAll;
+        mUseEmojiAsDefaultStyle = config.mUseEmojiAsDefaultStyle;
+        mEmojiAsDefaultStyleExceptions = config.mEmojiAsDefaultStyleExceptions;
         mEmojiSpanIndicatorEnabled = config.mEmojiSpanIndicatorEnabled;
         mEmojiSpanIndicatorColor = config.mEmojiSpanIndicatorColor;
         mMetadataLoader = config.mMetadataLoader;
@@ -787,6 +799,8 @@
     public abstract static class Config {
         private final MetadataRepoLoader mMetadataLoader;
         private boolean mReplaceAll;
+        private boolean mUseEmojiAsDefaultStyle;
+        private int[] mEmojiAsDefaultStyleExceptions;
         private Set<InitCallback> mInitCallbacks;
         private boolean mEmojiSpanIndicatorEnabled;
         private int mEmojiSpanIndicatorColor = Color.GREEN;
@@ -849,6 +863,56 @@
         }
 
         /**
+         * Determines whether EmojiCompat should use the emoji presentation style for emojis
+         * that have text style as default. By default, the text style would be used, unless these
+         * are followed by the U+FE0F variation selector.
+         * Details about emoji presentation and text presentation styles can be found here:
+         * http://unicode.org/reports/tr51/#Presentation_Style
+         * If useEmojiAsDefaultStyle is true, the emoji presentation style will be used for all
+         * emojis, including potentially unexpected ones (such as digits or other keycap emojis). If
+         * this is not the expected behaviour, method
+         * {@link #setUseEmojiAsDefaultStyle(boolean, List)} can be used to specify the
+         * exception emojis that should be still presented as text style.
+         *
+         * @param useEmojiAsDefaultStyle whether to use the emoji style presentation for all emojis
+         *                               that would be presented as text style by default
+         */
+        public Config setUseEmojiAsDefaultStyle(final boolean useEmojiAsDefaultStyle) {
+            return setUseEmojiAsDefaultStyle(useEmojiAsDefaultStyle, null);
+        }
+
+        /**
+         * @see #setUseEmojiAsDefaultStyle(boolean)
+         *
+         * @param emojiAsDefaultStyleExceptions Contains the exception emojis which will be still
+         *                                      presented as text style even if the
+         *                                      useEmojiAsDefaultStyle flag is set to {@code true}.
+         *                                      This list will be ignored if useEmojiAsDefaultStyle
+         *                                      is {@code false}. Note that emojis with default
+         *                                      emoji style presentation will remain emoji style
+         *                                      regardless the value of useEmojiAsDefaultStyle or
+         *                                      whether they are included in the exceptions list or
+         *                                      not. When no exception is wanted, the method
+         *                                      {@link #setUseEmojiAsDefaultStyle(boolean)} should
+         *                                      be used instead.
+         */
+        public Config setUseEmojiAsDefaultStyle(final boolean useEmojiAsDefaultStyle,
+                @Nullable final List<Integer> emojiAsDefaultStyleExceptions) {
+            mUseEmojiAsDefaultStyle = useEmojiAsDefaultStyle;
+            if (mUseEmojiAsDefaultStyle && emojiAsDefaultStyleExceptions != null) {
+                mEmojiAsDefaultStyleExceptions = new int[emojiAsDefaultStyleExceptions.size()];
+                int i = 0;
+                for (Integer exception : emojiAsDefaultStyleExceptions) {
+                    mEmojiAsDefaultStyleExceptions[i++] = exception;
+                }
+                Arrays.sort(mEmojiAsDefaultStyleExceptions);
+            } else {
+                mEmojiAsDefaultStyleExceptions = null;
+            }
+            return this;
+        }
+
+        /**
          * Determines whether a background will be drawn for the emojis that are found and
          * replaced by EmojiCompat. Should be used only for debugging purposes. The indicator color
          * can be set using {@link #setEmojiSpanIndicatorColor(int)}.
@@ -1020,7 +1084,9 @@
             }
 
             mMetadataRepo = metadataRepo;
-            mProcessor = new EmojiProcessor(mMetadataRepo, new SpanFactory());
+            mProcessor = new EmojiProcessor(mMetadataRepo, new SpanFactory(),
+                    mEmojiCompat.mUseEmojiAsDefaultStyle,
+                    mEmojiCompat.mEmojiAsDefaultStyleExceptions);
 
             mEmojiCompat.onMetadataLoadSuccess();
         }
diff --git a/emoji/core/src/main/java/android/support/text/emoji/EmojiMetadata.java b/emoji/core/src/main/java/android/support/text/emoji/EmojiMetadata.java
index 488dcf9..7d495ed 100644
--- a/emoji/core/src/main/java/android/support/text/emoji/EmojiMetadata.java
+++ b/emoji/core/src/main/java/android/support/text/emoji/EmojiMetadata.java
@@ -26,12 +26,13 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
-import android.support.text.emoji.flatbuffer.MetadataItem;
-import android.support.text.emoji.flatbuffer.MetadataList;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
+import androidx.text.emoji.flatbuffer.MetadataItem;
+import androidx.text.emoji.flatbuffer.MetadataList;
+
 /**
  * Information about a single emoji.
  *
diff --git a/emoji/core/src/main/java/android/support/text/emoji/EmojiProcessor.java b/emoji/core/src/main/java/android/support/text/emoji/EmojiProcessor.java
index 3feb36d..f711704 100644
--- a/emoji/core/src/main/java/android/support/text/emoji/EmojiProcessor.java
+++ b/emoji/core/src/main/java/android/support/text/emoji/EmojiProcessor.java
@@ -22,6 +22,7 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.IntRange;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.text.emoji.widget.SpannableBuilder;
@@ -40,6 +41,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * Processes the CharSequence and adds the emojis.
@@ -90,14 +93,29 @@
      */
     private GlyphChecker mGlyphChecker = new GlyphChecker();
 
+    /**
+     * @see EmojiCompat.Config#setUseEmojiAsDefaultStyle(boolean)
+     */
+    private final boolean mUseEmojiAsDefaultStyle;
+
+    /**
+     * @see EmojiCompat.Config#setUseEmojiAsDefaultStyle(boolean, List)
+     */
+    private final int[] mEmojiAsDefaultStyleExceptions;
+
     EmojiProcessor(@NonNull final MetadataRepo metadataRepo,
-            @NonNull final EmojiCompat.SpanFactory spanFactory) {
+            @NonNull final EmojiCompat.SpanFactory spanFactory,
+            final boolean useEmojiAsDefaultStyle,
+            @Nullable final int[] emojiAsDefaultStyleExceptions) {
         mSpanFactory = spanFactory;
         mMetadataRepo = metadataRepo;
+        mUseEmojiAsDefaultStyle = useEmojiAsDefaultStyle;
+        mEmojiAsDefaultStyleExceptions = emojiAsDefaultStyleExceptions;
     }
 
     EmojiMetadata getEmojiMetadata(@NonNull final CharSequence charSequence) {
-        final ProcessorSm sm = new ProcessorSm(mMetadataRepo.getRootNode());
+        final ProcessorSm sm = new ProcessorSm(mMetadataRepo.getRootNode(),
+                mUseEmojiAsDefaultStyle, mEmojiAsDefaultStyleExceptions);
         final int end = charSequence.length();
         int currentOffset = 0;
 
@@ -189,7 +207,8 @@
             }
             // add new ones
             int addedCount = 0;
-            final ProcessorSm sm = new ProcessorSm(mMetadataRepo.getRootNode());
+            final ProcessorSm sm = new ProcessorSm(mMetadataRepo.getRootNode(),
+                    mUseEmojiAsDefaultStyle, mEmojiAsDefaultStyleExceptions);
 
             int currentOffset = start;
             int codePoint = Character.codePointAt(charSequence, currentOffset);
@@ -483,9 +502,22 @@
          */
         private int mCurrentDepth;
 
-        ProcessorSm(MetadataRepo.Node rootNode) {
+        /**
+         * @see EmojiCompat.Config#setUseEmojiAsDefaultStyle(boolean)
+         */
+        private final boolean mUseEmojiAsDefaultStyle;
+
+        /**
+         * @see EmojiCompat.Config#setUseEmojiAsDefaultStyle(boolean, List)
+         */
+        private final int[] mEmojiAsDefaultStyleExceptions;
+
+        ProcessorSm(MetadataRepo.Node rootNode, boolean useEmojiAsDefaultStyle,
+                int[] emojiAsDefaultStyleExceptions) {
             mRootNode = rootNode;
             mCurrentNode = rootNode;
+            mUseEmojiAsDefaultStyle = useEmojiAsDefaultStyle;
+            mEmojiAsDefaultStyleExceptions = emojiAsDefaultStyleExceptions;
         }
 
         @Action
@@ -505,8 +537,7 @@
                             action = ACTION_ADVANCE_END;
                         } else if (mCurrentNode.getData() != null) {
                             if (mCurrentDepth == 1) {
-                                if (mCurrentNode.getData().isDefaultEmoji()
-                                        || isEmojiStyle(mLastCodepoint)) {
+                                if (shouldUseEmojiPresentationStyleForSingleCodepoint()) {
                                     mFlushNode = mCurrentNode;
                                     action = ACTION_FLUSH;
                                     reset();
@@ -571,9 +602,32 @@
          */
         boolean isInFlushableState() {
             return mState == STATE_WALKING && mCurrentNode.getData() != null
-                    && (mCurrentNode.getData().isDefaultEmoji()
-                    || isEmojiStyle(mLastCodepoint)
-                    || mCurrentDepth > 1);
+                    && (mCurrentDepth > 1 || shouldUseEmojiPresentationStyleForSingleCodepoint());
+        }
+
+        private boolean shouldUseEmojiPresentationStyleForSingleCodepoint() {
+            if (mCurrentNode.getData().isDefaultEmoji()) {
+                // The codepoint is emoji style by default.
+                return true;
+            }
+            if (isEmojiStyle(mLastCodepoint)) {
+                // The codepoint was followed by the emoji style variation selector.
+                return true;
+            }
+            if (mUseEmojiAsDefaultStyle) {
+                // Emoji presentation style for text style default emojis is enabled. We have
+                // to check that the current codepoint is not an exception.
+                if (mEmojiAsDefaultStyleExceptions == null) {
+                    return true;
+                }
+                final int codepoint = mCurrentNode.getData().getCodepointAt(0);
+                final int index = Arrays.binarySearch(mEmojiAsDefaultStyleExceptions, codepoint);
+                if (index < 0) {
+                    // Index is negative, so the codepoint was not found in the array of exceptions.
+                    return true;
+                }
+            }
+            return false;
         }
 
         /**
diff --git a/emoji/core/src/main/java/android/support/text/emoji/MetadataListReader.java b/emoji/core/src/main/java/android/support/text/emoji/MetadataListReader.java
index 1008c17..02856cb 100644
--- a/emoji/core/src/main/java/android/support/text/emoji/MetadataListReader.java
+++ b/emoji/core/src/main/java/android/support/text/emoji/MetadataListReader.java
@@ -22,13 +22,14 @@
 import android.support.annotation.IntRange;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
-import android.support.text.emoji.flatbuffer.MetadataList;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
+import androidx.text.emoji.flatbuffer.MetadataList;
+
 /**
  * Reads the emoji metadata from a given InputStream or ByteBuffer.
  *
diff --git a/emoji/core/src/main/java/android/support/text/emoji/MetadataRepo.java b/emoji/core/src/main/java/android/support/text/emoji/MetadataRepo.java
index e86277e..f5afec8 100644
--- a/emoji/core/src/main/java/android/support/text/emoji/MetadataRepo.java
+++ b/emoji/core/src/main/java/android/support/text/emoji/MetadataRepo.java
@@ -24,7 +24,6 @@
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
-import android.support.text.emoji.flatbuffer.MetadataList;
 import android.support.v4.util.Preconditions;
 import android.util.SparseArray;
 
@@ -32,6 +31,8 @@
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 
+import androidx.text.emoji.flatbuffer.MetadataList;
+
 /**
  * Class to hold the emoji metadata required to process and draw emojis.
  */
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 0ce26e4..724f8a0 100644
--- a/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
@@ -87,8 +87,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 @SmallTest
@@ -729,6 +731,58 @@
         assertFalse(EmojiCompat.handleOnKeyDown(editable, event.getKeyCode(), event));
     }
 
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testUseEmojiAsDefaultStyle_whenEmojiInTheMiddle() {
+        final Config config = new TestConfig().setReplaceAll(true);
+        EmojiCompat.reset(config);
+        String s = new TestString(0x0061, CHAR_DEFAULT_TEXT_STYLE, 0x0062).toString();
+        // no span should be added as the emoji is text style presented by default
+        assertThat(EmojiCompat.get().process(s), not(hasEmoji()));
+        // a span should be added when we use the emoji style presentation as default
+        EmojiCompat.reset(config.setUseEmojiAsDefaultStyle(true));
+        assertThat(EmojiCompat.get().process(s), hasEmojiAt(DEFAULT_TEXT_STYLE, 1, 2));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testUseEmojiAsDefaultStyle_whenEmojiAtTheEnd() {
+        final Config config = new TestConfig().setReplaceAll(true);
+        EmojiCompat.reset(config);
+        String s = new TestString(0x0061, CHAR_DEFAULT_TEXT_STYLE).toString();
+        // no span should be added as the emoji is text style presented by default
+        assertThat(EmojiCompat.get().process(s), not(hasEmoji()));
+        // a span should be added when we use the emoji style presentation as default
+        EmojiCompat.reset(config.setUseEmojiAsDefaultStyle(true));
+        assertThat(EmojiCompat.get().process(s), hasEmojiAt(DEFAULT_TEXT_STYLE, 1, 2));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testUseEmojiAsDefaultStyle_noEmojisAdded_whenMarkedAsException() {
+        final String s = new TestString(CHAR_DEFAULT_TEXT_STYLE).toString();
+        final List<Integer> exceptions =
+                Arrays.asList(CHAR_DEFAULT_TEXT_STYLE + 1, CHAR_DEFAULT_TEXT_STYLE);
+        final Config config = new TestConfig().setReplaceAll(true)
+                .setUseEmojiAsDefaultStyle(true, exceptions);
+        EmojiCompat.reset(config);
+        // no span should be added as the text style codepoint is marked as exception
+        assertThat(EmojiCompat.get().process(s), not(hasEmoji()));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    public void testUseEmojiAsDefaultStyle_emojisAdded_whenNotMarkedAsException() {
+        final String s = new TestString(CHAR_DEFAULT_TEXT_STYLE).toString();
+        final List<Integer> exceptions =
+                Arrays.asList(CHAR_DEFAULT_TEXT_STYLE - 1, CHAR_DEFAULT_TEXT_STYLE + 1);
+        final Config config = new TestConfig().setReplaceAll(true)
+                .setUseEmojiAsDefaultStyle(true, exceptions);
+        EmojiCompat.reset(config);
+        // a span should be added as the codepoint is not included in the set of exceptions
+        assertThat(EmojiCompat.get().process(s), hasEmojiAt(DEFAULT_TEXT_STYLE, 0, 1));
+    }
+
     private void assertCodePointMatch(EmojiMapping emoji) {
         assertCodePointMatch(emoji.id(), emoji.codepoints());
     }
diff --git a/exifinterface/build.gradle b/exifinterface/build.gradle
index f5e63f6..fa4d7b4 100644
--- a/exifinterface/build.gradle
+++ b/exifinterface/build.gradle
@@ -9,7 +9,7 @@
 dependencies {
     api(project(":support-annotations"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
 }
 
 android {
diff --git a/exifinterface/src/main/java/android/support/media/ExifInterface.java b/exifinterface/src/main/java/android/support/media/ExifInterface.java
index 72b61cb..eea69ab 100644
--- a/exifinterface/src/main/java/android/support/media/ExifInterface.java
+++ b/exifinterface/src/main/java/android/support/media/ExifInterface.java
@@ -4678,9 +4678,7 @@
     private int getMimeType(BufferedInputStream in) throws IOException {
         in.mark(SIGNATURE_CHECK_SIZE);
         byte[] signatureCheckBytes = new byte[SIGNATURE_CHECK_SIZE];
-        if (in.read(signatureCheckBytes) != SIGNATURE_CHECK_SIZE) {
-            throw new EOFException();
-        }
+        in.read(signatureCheckBytes);
         in.reset();
         if (isJpegFormat(signatureCheckBytes)) {
             return IMAGE_TYPE_JPEG;
@@ -5333,7 +5331,7 @@
             int dataFormat = dataInputStream.readUnsignedShort();
             int numberOfComponents = dataInputStream.readInt();
             // Next four bytes is for data offset or value.
-            long nextEntryOffset = dataInputStream.peek() + 4;
+            long nextEntryOffset = dataInputStream.peek() + 4L;
 
             // Look up a corresponding tag from tag number
             ExifTag tag = (ExifTag) sExifTagMapsForReading[ifdType].get(tagNumber);
diff --git a/fragment/build.gradle b/fragment/build.gradle
index 720a68e..b1cc47e 100644
--- a/fragment/build.gradle
+++ b/fragment/build.gradle
@@ -12,8 +12,8 @@
     api(project(":support-core-utils"))
     api(project(":support-annotations"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation project(':support-testutils'), {
diff --git a/graphics/drawable/animated/build.gradle b/graphics/drawable/animated/build.gradle
index ffa850f..e76f846 100644
--- a/graphics/drawable/animated/build.gradle
+++ b/graphics/drawable/animated/build.gradle
@@ -10,8 +10,8 @@
     api(project(":support-vector-drawable"))
     api(project(":support-core-ui"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
 }
 
 android {
diff --git a/graphics/drawable/static/build.gradle b/graphics/drawable/static/build.gradle
index 8e498fe..8575d6a 100644
--- a/graphics/drawable/static/build.gradle
+++ b/graphics/drawable/static/build.gradle
@@ -10,7 +10,7 @@
     api(project(":support-annotations"))
     api(project(":support-compat"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
 }
 
 android {
diff --git a/graphics/drawable/static/src/main/java/android/support/graphics/drawable/VectorDrawableCompat.java b/graphics/drawable/static/src/main/java/android/support/graphics/drawable/VectorDrawableCompat.java
index a34fe2b..943f1aa 100644
--- a/graphics/drawable/static/src/main/java/android/support/graphics/drawable/VectorDrawableCompat.java
+++ b/graphics/drawable/static/src/main/java/android/support/graphics/drawable/VectorDrawableCompat.java
@@ -56,8 +56,8 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.Stack;
 
 /**
  * For API 24 and above, this class is delegating to the framework's {@link VectorDrawable}.
@@ -730,7 +730,7 @@
 
         // Use a stack to help to build the group tree.
         // The top of the stack is always the current group.
-        final Stack<VGroup> groupStack = new Stack<VGroup>();
+        final ArrayDeque<VGroup> groupStack = new ArrayDeque<>();
         groupStack.push(pathRenderer.mRootGroup);
 
         int eventType = parser.getEventType();
@@ -785,14 +785,7 @@
         }
 
         if (noPathTag) {
-            final StringBuffer tag = new StringBuffer();
-
-            if (tag.length() > 0) {
-                tag.append(" or ");
-            }
-            tag.append(SHAPE_PATH);
-
-            throw new XmlPullParserException("no " + tag + " defined");
+            throw new XmlPullParserException("no " + SHAPE_PATH + " defined");
         }
     }
 
diff --git a/jetifier/jetifier/build.gradle b/jetifier/jetifier/build.gradle
index e873ec3..c817220 100644
--- a/jetifier/jetifier/build.gradle
+++ b/jetifier/jetifier/build.gradle
@@ -18,7 +18,7 @@
     ext.supportRootFolder = "${project.projectDir}/../../"
     apply from: "$supportRootFolder/buildSrc/repos.gradle"
 
-    ext.kotlin_version = '1.1.3'
+    ext.kotlin_version = '1.2.0'
 
     repos.addMavenRepositories(repositories)
 
diff --git a/leanback/build.gradle b/leanback/build.gradle
index c07bc76..036f08f 100644
--- a/leanback/build.gradle
+++ b/leanback/build.gradle
@@ -13,8 +13,8 @@
     api(project(":support-fragment"))
     api(project(":recyclerview-v7"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
 }
diff --git a/leanback/src/android/support/v17/leanback/widget/WindowAlignment.java b/leanback/src/android/support/v17/leanback/widget/WindowAlignment.java
index 55fa758..c6589d4 100644
--- a/leanback/src/android/support/v17/leanback/widget/WindowAlignment.java
+++ b/leanback/src/android/support/v17/leanback/widget/WindowAlignment.java
@@ -390,11 +390,7 @@
 
     @Override
     public String toString() {
-        return new StringBuffer().append("horizontal=")
-                .append(horizontal.toString())
-                .append("; vertical=")
-                .append(vertical.toString())
-                .toString();
+        return "horizontal=" + horizontal + "; vertical=" + vertical;
     }
 
 }
diff --git a/lifecycle/common/src/main/java/android/arch/lifecycle/Lifecycle.java b/lifecycle/common/src/main/java/android/arch/lifecycle/Lifecycle.java
index c0a2090..e04cdb4 100644
--- a/lifecycle/common/src/main/java/android/arch/lifecycle/Lifecycle.java
+++ b/lifecycle/common/src/main/java/android/arch/lifecycle/Lifecycle.java
@@ -108,6 +108,7 @@
      * @return The current state of the Lifecycle.
      */
     @MainThread
+    @NonNull
     public abstract State getCurrentState();
 
     @SuppressWarnings("WeakerAccess")
diff --git a/lifecycle/compiler/build.gradle b/lifecycle/compiler/build.gradle
index 211c65f..534d1ec 100644
--- a/lifecycle/compiler/build.gradle
+++ b/lifecycle/compiler/build.gradle
@@ -41,7 +41,7 @@
 supportLibrary {
     name = "Android Lifecycles Compiler"
     publish = true
-    mavenVersion = LibraryVersions.LIFECYCLES_CORE
+    mavenVersion = LibraryVersions.LIFECYCLES_EXT
     mavenGroup = LibraryGroups.LIFECYCLE
     inceptionYear = "2017"
     description = "Android Lifecycles annotation processor"
diff --git a/lifecycle/extensions/build.gradle b/lifecycle/extensions/build.gradle
index b7166ea..38640b6 100644
--- a/lifecycle/extensions/build.gradle
+++ b/lifecycle/extensions/build.gradle
@@ -44,8 +44,8 @@
     testImplementation(JUNIT)
     testImplementation(MOCKITO_CORE)
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation libs.support.app_compat, libs.support_exclude_config
 }
 
diff --git a/lifecycle/integration-tests/testapp/build.gradle b/lifecycle/integration-tests/testapp/build.gradle
index c640eeb..19d556b 100644
--- a/lifecycle/integration-tests/testapp/build.gradle
+++ b/lifecycle/integration-tests/testapp/build.gradle
@@ -68,8 +68,8 @@
     annotationProcessor(project(":lifecycle:compiler"))
 
     androidTestAnnotationProcessor(project(":lifecycle:compiler"))
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
 
     testImplementation(JUNIT)
     testImplementation(MOCKITO_CORE)
diff --git a/lifecycle/reactivestreams/build.gradle b/lifecycle/reactivestreams/build.gradle
index 76e9bc8..511ad7a 100644
--- a/lifecycle/reactivestreams/build.gradle
+++ b/lifecycle/reactivestreams/build.gradle
@@ -29,21 +29,19 @@
     }
 }
 
-allprojects {
-    dependencies {
-        api(project(":arch:common"))
-        api(project(":lifecycle:common"))
-        api(project(":lifecycle:extensions"))
-        api(project(":lifecycle:runtime"))
-        api libs.support.annotations
-        api(REACTIVE_STREAMS)
+dependencies {
+    api(project(":arch:common"))
+    api(project(":lifecycle:common"))
+    api(project(":lifecycle:extensions"))
+    api(project(":lifecycle:runtime"))
+    api libs.support.annotations
+    api(REACTIVE_STREAMS)
 
-        testImplementation(JUNIT)
-        testImplementation(RX_JAVA)
-        testImplementation(TEST_RUNNER, libs.exclude_annotations)
+    testImplementation(JUNIT)
+    testImplementation(RX_JAVA)
+    testImplementation(TEST_RUNNER)
 
-        androidTestImplementation libs.support.app_compat, libs.support_exclude_config
-    }
+    androidTestImplementation libs.support.app_compat, libs.support_exclude_config
 }
 
 supportLibrary {
diff --git a/lifecycle/reactivestreams/src/main/java/android/arch/lifecycle/LiveDataReactiveStreams.java b/lifecycle/reactivestreams/src/main/java/android/arch/lifecycle/LiveDataReactiveStreams.java
index ba76f8e..ed3c57c 100644
--- a/lifecycle/reactivestreams/src/main/java/android/arch/lifecycle/LiveDataReactiveStreams.java
+++ b/lifecycle/reactivestreams/src/main/java/android/arch/lifecycle/LiveDataReactiveStreams.java
@@ -50,8 +50,9 @@
      * will buffer the latest item and emit it to the subscriber when data is again requested. Any
      * other items emitted during the time there was no backpressure requested will be dropped.
      */
+    @NonNull
     public static <T> Publisher<T> toPublisher(
-            final LifecycleOwner lifecycle, final LiveData<T> liveData) {
+            @NonNull LifecycleOwner lifecycle, @NonNull LiveData<T> liveData) {
 
         return new LiveDataPublisher<>(lifecycle, liveData);
     }
@@ -60,7 +61,7 @@
         final LifecycleOwner mLifecycle;
         final LiveData<T> mLiveData;
 
-        LiveDataPublisher(final LifecycleOwner lifecycle, final LiveData<T> liveData) {
+        LiveDataPublisher(LifecycleOwner lifecycle, LiveData<T> liveData) {
             this.mLifecycle = lifecycle;
             this.mLiveData = liveData;
         }
@@ -91,7 +92,7 @@
             }
 
             @Override
-            public void onChanged(T t) {
+            public void onChanged(@Nullable T t) {
                 if (mCanceled) {
                     return;
                 }
@@ -183,7 +184,8 @@
      *
      * @param <T> The type of data hold by this instance.
      */
-    public static <T> LiveData<T> fromPublisher(final Publisher<T> publisher) {
+    @NonNull
+    public static <T> LiveData<T> fromPublisher(@NonNull Publisher<T> publisher) {
         return new PublisherLiveData<>(publisher);
     }
 
@@ -209,10 +211,10 @@
      * @param <T> The type of data hold by this instance.
      */
     private static class PublisherLiveData<T> extends LiveData<T> {
-        private final Publisher mPublisher;
+        private final Publisher<T> mPublisher;
         final AtomicReference<LiveDataSubscriber> mSubscriber;
 
-        PublisherLiveData(@NonNull final Publisher publisher) {
+        PublisherLiveData(@NonNull Publisher<T> publisher) {
             mPublisher = publisher;
             mSubscriber = new AtomicReference<>();
         }
diff --git a/lifecycle/runtime/build.gradle b/lifecycle/runtime/build.gradle
index 96f9a6f..44e992b 100644
--- a/lifecycle/runtime/build.gradle
+++ b/lifecycle/runtime/build.gradle
@@ -27,7 +27,7 @@
     testImplementation(MOCKITO_CORE)
 
     androidTestImplementation(JUNIT)
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
 }
 
 supportLibrary {
diff --git a/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistry.java b/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistry.java
index bf8aff7..eff946b 100644
--- a/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistry.java
+++ b/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistry.java
@@ -225,6 +225,7 @@
         return mObserverMap.size();
     }
 
+    @NonNull
     @Override
     public State getCurrentState() {
         return mState;
diff --git a/media-compat/build.gradle b/media-compat/build.gradle
index b832de6..18ff5a3 100644
--- a/media-compat/build.gradle
+++ b/media-compat/build.gradle
@@ -10,8 +10,8 @@
     api(project(":support-annotations"))
     api(project(":support-compat"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation project(':support-testutils')
diff --git a/media-compat/version-compat-tests/current/client/build.gradle b/media-compat/version-compat-tests/current/client/build.gradle
index 24e46d4..aeb82c1 100644
--- a/media-compat/version-compat-tests/current/client/build.gradle
+++ b/media-compat/version-compat-tests/current/client/build.gradle
@@ -24,7 +24,7 @@
     androidTestImplementation(project(":support-media-compat"))
     androidTestImplementation(project(":support-media-compat-test-lib"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
 }
 
 android {
diff --git a/media-compat/version-compat-tests/current/service/build.gradle b/media-compat/version-compat-tests/current/service/build.gradle
index 1318d33..cf82918 100644
--- a/media-compat/version-compat-tests/current/service/build.gradle
+++ b/media-compat/version-compat-tests/current/service/build.gradle
@@ -24,7 +24,7 @@
     androidTestImplementation(project(":support-media-compat"))
     androidTestImplementation(project(":support-media-compat-test-lib"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
 }
 
 android {
diff --git a/media-compat/version-compat-tests/runtest.sh b/media-compat/version-compat-tests/runtest.sh
index d1a3c3a..817cd33 100755
--- a/media-compat/version-compat-tests/runtest.sh
+++ b/media-compat/version-compat-tests/runtest.sh
@@ -19,17 +19,28 @@
 #  - Exactly one test device should be connected.
 #
 # TODO:
-#  - The test result should be easily seen. (Can we report the results to the Sponge?)
-#  - Run specific combination of the test (e.g. Only want to test ToT-ToT)
-#  - Run specific test class / method by using argument.
 #  - Support simultaneous multiple device connection
 
-# Usage './runtest.sh'
+# Usage './runtest.sh <version_combination_number> [option]'
 
 CLIENT_MODULE_NAME_BASE="support-media-compat-test-client"
 SERVICE_MODULE_NAME_BASE="support-media-compat-test-service"
 CLIENT_VERSION=""
 SERVICE_VERSION=""
+OPTION_TEST_TARGET=""
+
+function printRunTestUsage() {
+  echo "Usage: ./runtest.sh <version_combination_number> [option]"
+  echo ""
+  echo "Version combination number:"
+  echo "    1. Client-ToT             / Service-ToT"
+  echo "    2. Client-ToT             / Service-Latest release"
+  echo "    3. Client-Latest release  / Service-ToT"
+  echo "    4. Run all of the above"
+  echo ""
+  echo "Option:"
+  echo "    -t <class/method>: Only run the specific test class/method."
+}
 
 function runTest() {
   echo "Running test: Client-$CLIENT_VERSION / Service-$SERVICE_VERSION"
@@ -38,42 +49,59 @@
   local SERVICE_MODULE_NAME="$SERVICE_MODULE_NAME_BASE$([ "$SERVICE_VERSION" = "tot" ] || echo "-previous")"
 
   # Build test apks
-  ./gradlew $CLIENT_MODULE_NAME:assembleDebugAndroidTest || (echo "Build failed. Aborting."; return 1)
-  ./gradlew $SERVICE_MODULE_NAME:assembleDebugAndroidTest || (echo "Build failed. Aborting."; return 1)
+  ./gradlew $CLIENT_MODULE_NAME:assembleDebugAndroidTest || { echo "Build failed. Aborting."; exit 1; }
+  ./gradlew $SERVICE_MODULE_NAME:assembleDebugAndroidTest || { echo "Build failed. Aborting."; exit 1; }
 
   # Install the apks
-  adb install -r -d "../../out/dist/$CLIENT_MODULE_NAME.apk" || (echo "Apk installation failed. Aborting."; return 1)
-  adb install -r -d "../../out/dist/$SERVICE_MODULE_NAME.apk" || (echo "Apk installation failed. Aborting."; return 1)
+  adb install -r -d "../../out/dist/$CLIENT_MODULE_NAME.apk" || { echo "Apk installation failed. Aborting."; exit 1; }
+  adb install -r -d "../../out/dist/$SERVICE_MODULE_NAME.apk" || { echo "Apk installation failed. Aborting."; exit 1; }
 
   # Run the tests
+  local test_command="adb shell am instrument -w -e debug false -e client_version $CLIENT_VERSION -e service_version $SERVICE_VERSION"
+  local client_test_runner="android.support.mediacompat.client.test/android.support.test.runner.AndroidJUnitRunner"
+  local service_test_runner="android.support.mediacompat.service.test/android.support.test.runner.AndroidJUnitRunner"
+
   echo ">>>>>>>>>>>>>>>>>>>>>>>> Test Started: Client-$CLIENT_VERSION & Service-$SERVICE_VERSION <<<<<<<<<<<<<<<<<<<<<<<<"
-  adb shell am instrument -w -r -e package android.support.mediacompat.client -e debug false -e client_version $CLIENT_VERSION \
-     -e service_version $SERVICE_VERSION android.support.mediacompat.client.test/android.support.test.runner.AndroidJUnitRunner
-  adb shell am instrument -w -r -e package android.support.mediacompat.service -e debug false -e client_version $CLIENT_VERSION \
-     -e service_version $SERVICE_VERSION android.support.mediacompat.service.test/android.support.test.runner.AndroidJUnitRunner
+
+  if [[ $OPTION_TEST_TARGET == *"client"* ]]; then
+    ${test_command} $OPTION_TEST_TARGET ${client_test_runner}
+  elif [[ $OPTION_TEST_TARGET == *"service"* ]]; then
+    ${test_command} $OPTION_TEST_TARGET ${service_test_runner}
+  else
+    ${test_command} ${client_test_runner}
+    ${test_command} ${service_test_runner}
+  fi
+
   echo ">>>>>>>>>>>>>>>>>>>>>>>> Test Ended: Client-$CLIENT_VERSION & Service-$SERVICE_VERSION <<<<<<<<<<<<<<<<<<<<<<<<<<"
 }
 
 
 OLD_PWD=$(pwd)
-if [[ $OLD_PWD != *"frameworks/support"* ]]; then
-  echo "Current working directory is" $OLD_PWD.
+
+if ! cd "$(echo $OLD_PWD | awk -F'frameworks/support' '{print $1}')"/frameworks/support &> /dev/null
+then
+  echo "Current working directory is $OLD_PWD"
   echo "Please re-run this script in any folder under frameworks/support."
   exit 1;
-else
-  # Change working directory to frameworks/support
-  cd "$(echo $OLD_PWD | awk -F'frameworks/support' '{print $1}')"/frameworks/support
 fi
 
-echo "Choose the support library versions of the test you want to run:"
-echo "    1. Client-ToT             / Service-ToT"
-echo "    2. Client-ToT             / Service-Latest release"
-echo "    3. Client-Latest release  / Service-ToT"
-echo "    4. Run all of the above"
-printf "Pick one of them: "
+if [[ $# -eq 0 || $1 -le 0 || $1 -gt 4 ]]
+then
+  printRunTestUsage
+  exit 1;
+fi
 
-read ANSWER
-case $ANSWER in
+if [[ ${2} == "-t" ]]; then
+  if [[ ${3} == *"client"* || ${3} == *"service"* ]]; then
+    OPTION_TEST_TARGET="-e class ${3}"
+  else
+    echo "Wrong test class/method name. Aborting."
+    echo "It should be in the form of \"<FULL_CLASS_NAME>[#METHOD_NAME]\"."
+    exit 1;
+  fi
+fi
+
+case ${1} in
   1)
      CLIENT_VERSION="tot"
      SERVICE_VERSION="tot"
diff --git a/paging/common/src/main/java/android/arch/paging/DataSource.java b/paging/common/src/main/java/android/arch/paging/DataSource.java
index 3cf35c4..23f8154 100644
--- a/paging/common/src/main/java/android/arch/paging/DataSource.java
+++ b/paging/common/src/main/java/android/arch/paging/DataSource.java
@@ -30,11 +30,8 @@
  * Base class for loading pages of snapshot data into a {@link PagedList}.
  * <p>
  * DataSource is queried to load pages of content into a {@link PagedList}. A PagedList can grow as
- * it loads more data, but the data loaded cannot be updated.
- * <p>
- * A PagedList / DataSource pair serve as a snapshot of the data set being loaded. If the
- * underlying data set is modified, a new PagedList / DataSource pair must be created to represent
- * the new data.
+ * it loads more data, but the data loaded cannot be updated. If the underlying data set is
+ * modified, a new PagedList / DataSource pair must be created to represent the new data.
  * <h4>Loading Pages</h4>
  * PagedList queries data from its DataSource in response to loading hints. {@link PagedListAdapter}
  * calls {@link PagedList#loadAround(int)} to load content as the user scrolls in a RecyclerView.
@@ -68,18 +65,23 @@
  * copy changes, invalidate the previous DataSource, and a new one wrapping the new state of the
  * snapshot can be created.
  * <h4>Implementing a DataSource</h4>
- * To implement, extend either the {@link KeyedDataSource}, or {@link PositionalDataSource}
- * subclass. Choose based on whether each load operation is based on the position of the data in the
- * list.
+ * To implement, extend one of the subclasses: {@link PageKeyedDataSource},
+ * {@link ItemKeyedDataSource}, or {@link PositionalDataSource}.
  * <p>
- * Use {@link KeyedDataSource} if you need to use data from item {@code N-1} to load item
+ * Use {@link PageKeyedDataSource} if pages you load embed keys for loading adjacent pages. For
+ * example a network response that returns some items, and a next/previous page links.
+ * <p>
+ * Use {@link ItemKeyedDataSource} if you need to use data from item {@code N-1} to load item
  * {@code N}. For example, if requesting the backend for the next comments in the list
  * requires the ID or timestamp of the most recent loaded comment, or if querying the next users
  * from a name-sorted database query requires the name and unique ID of the previous.
  * <p>
- * Use {@link PositionalDataSource} if you can load arbitrary pages based solely on position
- * information, and can provide a fixed item count. PositionalDataSource supports querying pages at
- * arbitrary positions, so can provide data to PagedLists in arbitrary order.
+ * Use {@link PositionalDataSource} if you can load pages of a requested size at arbitrary
+ * positions, and provide a fixed item count. PositionalDataSource supports querying pages at
+ * arbitrary positions, so can provide data to PagedLists in arbitrary order. Note that
+ * PositionalDataSource is required to respect page size for efficient tiling. If you want to
+ * override page size (e.g. when network page size constraints are only known at runtime), use one
+ * of the other DataSource classes.
  * <p>
  * Because a {@code null} item indicates a placeholder in {@link PagedList}, DataSource may not
  * return {@code null} items in lists that it loads. This is so that users of the PagedList
@@ -115,8 +117,13 @@
         /**
          * Create a DataSource.
          * <p>
-         * The DataSource should invalidate itself if the snapshot is no longer valid, and a new
-         * DataSource should be queried from the Factory.
+         * The DataSource should invalidate itself if the snapshot is no longer valid. If a
+         * DataSource becomes invalid, the only way to query more data is to create a new DataSource
+         * from the Factory.
+         * <p>
+         * {@link LivePagedListBuilder} for example will construct a new PagedList and DataSource
+         * when the current DataSource is invalidated, and pass the new PagedList through the
+         * {@code LiveData<PagedList>} to observers.
          *
          * @return the new DataSource.
          */
@@ -159,11 +166,11 @@
         private Executor mPostExecutor = null;
         private boolean mHasSignalled = false;
 
-        BaseLoadCallback(@PageResult.ResultType int resultType, @NonNull DataSource dataSource,
+        BaseLoadCallback(@NonNull DataSource dataSource, @PageResult.ResultType int resultType,
                 @Nullable Executor mainThreadExecutor, @NonNull PageResult.Receiver<T> receiver) {
+            mDataSource = dataSource;
             mResultType = resultType;
             mPostExecutor = mainThreadExecutor;
-            mDataSource = dataSource;
             mReceiver = receiver;
         }
 
diff --git a/paging/common/src/main/java/android/arch/paging/KeyedDataSource.java b/paging/common/src/main/java/android/arch/paging/ItemKeyedDataSource.java
similarity index 88%
rename from paging/common/src/main/java/android/arch/paging/KeyedDataSource.java
rename to paging/common/src/main/java/android/arch/paging/ItemKeyedDataSource.java
index ff4b1d4..b113af4 100644
--- a/paging/common/src/main/java/android/arch/paging/KeyedDataSource.java
+++ b/paging/common/src/main/java/android/arch/paging/ItemKeyedDataSource.java
@@ -26,14 +26,20 @@
  * Incremental data loader for paging keyed content, where loaded content uses previously loaded
  * items as input to future loads.
  * <p>
- * Implement a DataSource using KeyedDataSource if you need to use data from item {@code N - 1}
+ * Implement a DataSource using ItemKeyedDataSource if you need to use data from item {@code N - 1}
  * to load item {@code N}. This is common, for example, in sorted database queries where
  * attributes of the item such just before the next query define how to execute it.
+ * <p>
+ * The {@code InMemoryByItemRepository} in the
+ * <a href="https://github.com/googlesamples/android-architecture-components/blob/master/PagingWithNetworkSample/README.md">PagingWithNetworkSample</a>
+ * shows how to implement a network ItemKeyedDataSource using
+ * <a href="https://square.github.io/retrofit/">Retrofit</a>, while
+ * handling swipe-to-refresh, network errors, and retry.
  *
  * @param <Key> Type of data used to query Value types out of the DataSource.
  * @param <Value> Type of items being loaded by the DataSource.
  */
-public abstract class KeyedDataSource<Key, Value> extends ContiguousDataSource<Key, Value> {
+public abstract class ItemKeyedDataSource<Key, Value> extends ContiguousDataSource<Key, Value> {
 
     /**
      * Holder object for inputs to {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}.
@@ -46,7 +52,8 @@
          * Load items around this key, or at the beginning of the data set if {@code null} is
          * passed.
          * <p>
-         * Note that this key is generally a hint
+         * Note that this key is generally a hint, and may be ignored if you want to always load
+         * from the beginning.
          */
         @Nullable
         public final Key requestedInitialKey;
@@ -107,6 +114,12 @@
      * <p>
      * A callback can be called only once, and will throw if called again.
      * <p>
+     * If you can compute the number of items in the data set before and after the loaded range,
+     * call the three parameter {@link #onResult(List, int, int)} to pass that information. You
+     * can skip passing this information by calling the single parameter {@link #onResult(List)},
+     * either if it's difficult to compute, or if {@link LoadInitialParams#placeholdersEnabled} is
+     * {@code false}, so the positioning information will be ignored.
+     * <p>
      * It is always valid for a DataSource loading method that takes a callback to stash the
      * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
      * temporary, recoverable error states (such as a network error that can be retried).
@@ -115,7 +128,7 @@
      */
     public static class LoadInitialCallback<Value> extends LoadCallback<Value> {
         private final boolean mCountingEnabled;
-        LoadInitialCallback(@NonNull KeyedDataSource dataSource, boolean countingEnabled,
+        LoadInitialCallback(@NonNull ItemKeyedDataSource dataSource, boolean countingEnabled,
                 @NonNull PageResult.Receiver<Value> receiver) {
             super(dataSource, PageResult.INIT, null, receiver);
             mCountingEnabled = countingEnabled;
@@ -156,7 +169,7 @@
     }
 
     /**
-     * Callback for KeyedDataSource {@link #loadBefore(LoadParams, LoadCallback)}
+     * Callback for ItemKeyedDataSource {@link #loadBefore(LoadParams, LoadCallback)}
      * and {@link #loadAfter(LoadParams, LoadCallback)} to return data.
      * <p>
      * A callback can be called only once, and will throw if called again.
@@ -168,16 +181,16 @@
      * @param <Value> Type of items being loaded.
      */
     public static class LoadCallback<Value> extends BaseLoadCallback<Value> {
-        LoadCallback(@NonNull KeyedDataSource dataSource, @PageResult.ResultType int type,
+        LoadCallback(@NonNull ItemKeyedDataSource dataSource, @PageResult.ResultType int type,
                 @Nullable Executor mainThreadExecutor,
                 @NonNull PageResult.Receiver<Value> receiver) {
-            super(type, dataSource, mainThreadExecutor, receiver);
+            super(dataSource, type, mainThreadExecutor, receiver);
         }
 
         /**
          * Called to pass loaded data from a DataSource.
          * <p>
-         * Call this method from your KeyedDataSource's
+         * Call this method from your ItemKeyedDataSource's
          * {@link #loadBefore(LoadParams, LoadCallback)} and
          * {@link #loadAfter(LoadParams, LoadCallback)} methods to return data.
          * <p>
@@ -187,7 +200,7 @@
          * It is always valid to pass a different amount of data than what is requested. Pass an
          * empty list if there is no more data to load.
          *
-         * @param data List of items loaded from the KeyedDataSource.
+         * @param data List of items loaded from the ItemKeyedDataSource.
          */
         public void onResult(@NonNull List<Value> data) {
             dispatchResultToReceiver(new PageResult<>(data, 0, 0, 0));
@@ -301,7 +314,7 @@
     /**
      * Return a key associated with the given item.
      * <p>
-     * If your KeyedDataSource is loading from a source that is sorted and loaded by a unique
+     * If your ItemKeyedDataSource is loading from a source that is sorted and loaded by a unique
      * integer ID, you would return {@code item.getID()} here. This key can then be passed to
      * {@link #loadBefore(LoadParams, LoadCallback)} or
      * {@link #loadAfter(LoadParams, LoadCallback)} to load additional items adjacent to the item
diff --git a/paging/common/src/main/java/android/arch/paging/PageKeyedDataSource.java b/paging/common/src/main/java/android/arch/paging/PageKeyedDataSource.java
new file mode 100644
index 0000000..c12df3d
--- /dev/null
+++ b/paging/common/src/main/java/android/arch/paging/PageKeyedDataSource.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright 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.arch.paging;
+
+import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Incremental data loader for page-keyed content, where requests return keys for next/previous
+ * pages.
+ * <p>
+ * Implement a DataSource using PageKeyedDataSource if you need to use data from page {@code N - 1}
+ * to load page {@code N}. This is common, for example, in network APIs that include a next/previous
+ * link or key with each page load.
+ * <p>
+ * The {@code InMemoryByPageRepository} in the
+ * <a href="https://github.com/googlesamples/android-architecture-components/blob/master/PagingWithNetworkSample/README.md">PagingWithNetworkSample</a>
+ * shows how to implement a network PageKeyedDataSource using
+ * <a href="https://square.github.io/retrofit/">Retrofit</a>, while
+ * handling swipe-to-refresh, network errors, and retry.
+ *
+ * @param <Key> Type of data used to query Value types out of the DataSource.
+ * @param <Value> Type of items being loaded by the DataSource.
+ */
+public abstract class PageKeyedDataSource<Key, Value> extends ContiguousDataSource<Key, Value> {
+    private final Object mKeyLock = new Object();
+
+    @Nullable
+    @GuardedBy("mKeyLock")
+    private Key mNextKey = null;
+
+    @Nullable
+    @GuardedBy("mKeyLock")
+    private Key mPreviousKey = null;
+
+    private void initKeys(@Nullable Key previousKey, @Nullable Key nextKey) {
+        synchronized (mKeyLock) {
+            mPreviousKey = previousKey;
+            mNextKey = nextKey;
+        }
+    }
+
+    private void setPreviousKey(@Nullable Key previousKey) {
+        synchronized (mKeyLock) {
+            mPreviousKey = previousKey;
+        }
+    }
+
+    private void setNextKey(@Nullable Key nextKey) {
+        synchronized (mKeyLock) {
+            mNextKey = nextKey;
+        }
+    }
+
+    private @Nullable Key getPreviousKey() {
+        synchronized (mKeyLock) {
+            return mPreviousKey;
+        }
+    }
+
+    private @Nullable Key getNextKey() {
+        synchronized (mKeyLock) {
+            return mNextKey;
+        }
+    }
+
+    /**
+     * Holder object for inputs to {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}.
+     *
+     * @param <Key> Type of data used to query pages.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public static class LoadInitialParams<Key> {
+        /**
+         * Requested number of items to load.
+         * <p>
+         * Note that this may be larger than available data.
+         */
+        public final int requestedLoadSize;
+
+        /**
+         * Defines whether placeholders are enabled, and whether the total count passed to
+         * {@link LoadInitialCallback#onResult(List, int, int, Key, Key)} will be ignored.
+         */
+        public final boolean placeholdersEnabled;
+
+
+        LoadInitialParams(int requestedLoadSize,
+                boolean placeholdersEnabled) {
+            this.requestedLoadSize = requestedLoadSize;
+            this.placeholdersEnabled = placeholdersEnabled;
+        }
+    }
+
+    /**
+     * Holder object for inputs to {@link #loadBefore(LoadParams, LoadCallback)} and
+     * {@link #loadAfter(LoadParams, LoadCallback)}.
+     *
+     * @param <Key> Type of data used to query pages.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public static class LoadParams<Key> {
+        /**
+         * Load items before/after this key.
+         * <p>
+         * Returned data must begin directly adjacent to this position.
+         */
+        public final Key key;
+
+        /**
+         * Requested number of items to load.
+         * <p>
+         * Returned page can be of this size, but it may be altered if that is easier, e.g. a
+         * network data source where the backend defines page size.
+         */
+        public final int requestedLoadSize;
+
+        LoadParams(Key key, int requestedLoadSize) {
+            this.key = key;
+            this.requestedLoadSize = requestedLoadSize;
+        }
+    }
+
+    /**
+     * Callback for {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}
+     * to return data and, optionally, position/count information.
+     * <p>
+     * A callback can be called only once, and will throw if called again.
+     * <p>
+     * If you can compute the number of items in the data set before and after the loaded range,
+     * call the five parameter {@link #onResult(List, int, int, Object, Object)} to pass that
+     * information. You can skip passing this information by calling the three parameter
+     * {@link #onResult(List, Object, Object)}, either if it's difficult to compute, or if
+     * {@link LoadInitialParams#placeholdersEnabled} is {@code false}, so the positioning
+     * information will be ignored.
+     * <p>
+     * It is always valid for a DataSource loading method that takes a callback to stash the
+     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
+     * temporary, recoverable error states (such as a network error that can be retried).
+     *
+     * @param <Key> Type of data used to query pages.
+     * @param <Value> Type of items being loaded.
+     */
+    public static class LoadInitialCallback<Key, Value> extends BaseLoadCallback<Value> {
+        private final PageKeyedDataSource<Key, Value> mDataSource;
+        private final boolean mCountingEnabled;
+        LoadInitialCallback(@NonNull PageKeyedDataSource<Key, Value> dataSource,
+                boolean countingEnabled, @NonNull PageResult.Receiver<Value> receiver) {
+            super(dataSource, PageResult.INIT, null, receiver);
+            mDataSource = dataSource;
+            mCountingEnabled = countingEnabled;
+        }
+
+        /**
+         * Called to pass initial load state from a DataSource.
+         * <p>
+         * Call this method from your DataSource's {@code loadInitial} function to return data,
+         * and inform how many placeholders should be shown before and after. If counting is cheap
+         * to compute (for example, if a network load returns the information regardless), it's
+         * recommended to pass data back through this method.
+         * <p>
+         * It is always valid to pass a different amount of data than what is requested. Pass an
+         * empty list if there is no more data to load.
+         *
+         * @param data List of items loaded from the DataSource. If this is empty, the DataSource
+         *             is treated as empty, and no further loads will occur.
+         * @param position Position of the item at the front of the list. If there are {@code N}
+         *                 items before the items in data that can be loaded from this DataSource,
+         *                 pass {@code N}.
+         * @param totalCount Total number of items that may be returned from this DataSource.
+         *                   Includes the number in the initial {@code data} parameter
+         *                   as well as any items that can be loaded in front or behind of
+         *                   {@code data}.
+         */
+        public void onResult(@NonNull List<Value> data, int position, int totalCount,
+                @Nullable Key previousPageKey, @Nullable Key nextPageKey) {
+            validateInitialLoadParams(data, position, totalCount);
+
+            // setup keys before dispatching data, so guaranteed to be ready
+            mDataSource.initKeys(previousPageKey, nextPageKey);
+
+            int trailingUnloadedCount = totalCount - position - data.size();
+            if (mCountingEnabled) {
+                dispatchResultToReceiver(new PageResult<>(
+                        data, position, trailingUnloadedCount, 0));
+            } else {
+                dispatchResultToReceiver(new PageResult<>(data, position));
+            }
+        }
+
+        /**
+         * Called to pass loaded data from a DataSource.
+         * <p>
+         * Call this from {@link #loadInitial(LoadInitialParams, LoadInitialCallback)} to
+         * initialize without counting available data, or supporting placeholders.
+         * <p>
+         * It is always valid to pass a different amount of data than what is requested. Pass an
+         * empty list if there is no more data to load.
+         *
+         * @param data List of items loaded from the PageKeyedDataSource.
+         * @param previousPageKey Key for page before the initial load result, or {@code null} if no
+         *                        more data can be loaded before.
+         * @param nextPageKey Key for page after the initial load result, or {@code null} if no
+         *                        more data can be loaded after.
+         */
+        public void onResult(@NonNull List<Value> data, @Nullable Key previousPageKey,
+                @Nullable Key nextPageKey) {
+            mDataSource.initKeys(previousPageKey, nextPageKey);
+            dispatchResultToReceiver(new PageResult<>(data, 0, 0, 0));
+        }
+    }
+
+    /**
+     * Callback for PageKeyedDataSource {@link #loadBefore(LoadParams, LoadCallback)} and
+     * {@link #loadAfter(LoadParams, LoadCallback)} to return data.
+     * <p>
+     * A callback can be called only once, and will throw if called again.
+     * <p>
+     * It is always valid for a DataSource loading method that takes a callback to stash the
+     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
+     * temporary, recoverable error states (such as a network error that can be retried).
+     *
+     * @param <Key> Type of data used to query pages.
+     * @param <Value> Type of items being loaded.
+     */
+    public static class LoadCallback<Key, Value> extends BaseLoadCallback<Value> {
+        private final PageKeyedDataSource<Key, Value> mDataSource;
+        LoadCallback(@NonNull PageKeyedDataSource<Key, Value> dataSource,
+                @PageResult.ResultType int type, @Nullable Executor mainThreadExecutor,
+                @NonNull PageResult.Receiver<Value> receiver) {
+            super(dataSource, type, mainThreadExecutor, receiver);
+            mDataSource = dataSource;
+        }
+
+        /**
+         * Called to pass loaded data from a DataSource.
+         * <p>
+         * Call this method from your PageKeyedDataSource's
+         * {@link #loadBefore(LoadParams, LoadCallback)} and
+         * {@link #loadAfter(LoadParams, LoadCallback)} methods to return data.
+         * <p>
+         * It is always valid to pass a different amount of data than what is requested. Pass an
+         * empty list if there is no more data to load.
+         * <p>
+         * Pass the key for the subsequent page to load to adjacentPageKey. For example, if you've
+         * loaded a page in {@link #loadBefore(LoadParams, LoadCallback)}, pass the key for the
+         * previous page, or {@code null} if the loaded page is the first. If in
+         * {@link #loadAfter(LoadParams, LoadCallback)}, pass the key for the next page, or
+         * {@code null} if the loaded page is the last.
+         *
+         * @param data List of items loaded from the PageKeyedDataSource.
+         * @param adjacentPageKey Key for subsequent page load (previous page in {@link #loadBefore}
+         *                        / next page in {@link #loadAfter}), or {@code null} if there are
+         *                        no more pages to load in the current load direction.
+         */
+        public void onResult(@NonNull List<Value> data, @Nullable Key adjacentPageKey) {
+            if (mResultType == PageResult.APPEND) {
+                mDataSource.setNextKey(adjacentPageKey);
+            } else {
+                mDataSource.setPreviousKey(adjacentPageKey);
+            }
+            dispatchResultToReceiver(new PageResult<>(data, 0, 0, 0));
+        }
+    }
+
+    @Nullable
+    @Override
+    final Key getKey(int position, Value item) {
+        // don't attempt to persist keys, since we currently don't pass them to initial load
+        return null;
+    }
+
+    @Override
+    final void dispatchLoadInitial(@Nullable Key key, int initialLoadSize, int pageSize,
+            boolean enablePlaceholders, @NonNull Executor mainThreadExecutor,
+            @NonNull PageResult.Receiver<Value> receiver) {
+        LoadInitialCallback<Key, Value> callback =
+                new LoadInitialCallback<>(this, enablePlaceholders, receiver);
+        loadInitial(new LoadInitialParams<Key>(initialLoadSize, enablePlaceholders), callback);
+
+        // If initialLoad's callback is not called within the body, we force any following calls
+        // to post to the UI thread. This constructor may be run on a background thread, but
+        // after constructor, mutation must happen on UI thread.
+        callback.setPostExecutor(mainThreadExecutor);
+    }
+
+
+    @Override
+    final void dispatchLoadAfter(int currentEndIndex, @NonNull Value currentEndItem,
+            int pageSize, @NonNull Executor mainThreadExecutor,
+            @NonNull PageResult.Receiver<Value> receiver) {
+        @Nullable Key key = getNextKey();
+        if (key != null) {
+            loadAfter(new LoadParams<>(key, pageSize),
+                    new LoadCallback<>(this, PageResult.APPEND, mainThreadExecutor, receiver));
+        }
+    }
+
+    @Override
+    final void dispatchLoadBefore(int currentBeginIndex, @NonNull Value currentBeginItem,
+            int pageSize, @NonNull Executor mainThreadExecutor,
+            @NonNull PageResult.Receiver<Value> receiver) {
+        @Nullable Key key = getPreviousKey();
+        if (key != null) {
+            loadBefore(new LoadParams<>(key, pageSize),
+                    new LoadCallback<>(this, PageResult.PREPEND, mainThreadExecutor, receiver));
+        }
+    }
+
+    /**
+     * Load initial data.
+     * <p>
+     * This method is called first to initialize a PagedList with data. If it's possible to count
+     * the items that can be loaded by the DataSource, it's recommended to pass the loaded data to
+     * the callback via the three-parameter
+     * {@link LoadInitialCallback#onResult(List, int, int, Object, Object)}. This enables PagedLists
+     * presenting data from this source to display placeholders to represent unloaded items.
+     * <p>
+     * {@link LoadInitialParams#requestedLoadSize} is a hint, not a requirement, so it may be may be
+     * altered or ignored.
+     *
+     * @param params Parameters for initial load, including requested load size.
+     * @param callback Callback that receives initial load data.
+     */
+    public abstract void loadInitial(@NonNull LoadInitialParams<Key> params,
+            @NonNull LoadInitialCallback<Key, Value> callback);
+
+    /**
+     * Prepend page with the key specified by {@link LoadParams#key LoadParams.key}.
+     * <p>
+     * It's valid to return a different list size than the page size if it's easier, e.g. if your
+     * backend defines page sizes. It is generally safer to increase the number loaded than reduce.
+     * <p>
+     * Data may be passed synchronously during the load method, or deferred and called at a
+     * later time. Further loads going down will be blocked until the callback is called.
+     * <p>
+     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
+     * and inconsistent, it is valid to call {@link #invalidate()} to invalidate the data source,
+     * and prevent further loading.
+     *
+     * @param params Parameters for the load, including the key for the new page, and requested load
+     *               size.
+     * @param callback Callback that receives loaded data.
+     */
+    public abstract void loadBefore(@NonNull LoadParams<Key> params,
+            @NonNull LoadCallback<Key, Value> callback);
+
+    /**
+     * Append page with the key specified by {@link LoadParams#key LoadParams.key}.
+     * <p>
+     * It's valid to return a different list size than the page size if it's easier, e.g. if your
+     * backend defines page sizes. It is generally safer to increase the number loaded than reduce.
+     * <p>
+     * Data may be passed synchronously during the load method, or deferred and called at a
+     * later time. Further loads going down will be blocked until the callback is called.
+     * <p>
+     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
+     * and inconsistent, it is valid to call {@link #invalidate()} to invalidate the data source,
+     * and prevent further loading.
+     *
+     * @param params Parameters for the load, including the key for the new page, and requested load
+     *               size.
+     * @param callback Callback that receives loaded data.
+     */
+    public abstract void loadAfter(@NonNull LoadParams<Key> params,
+            @NonNull LoadCallback<Key, Value> callback);
+}
diff --git a/paging/common/src/main/java/android/arch/paging/PagedList.java b/paging/common/src/main/java/android/arch/paging/PagedList.java
index a0ba388..c6de5c5 100644
--- a/paging/common/src/main/java/android/arch/paging/PagedList.java
+++ b/paging/common/src/main/java/android/arch/paging/PagedList.java
@@ -578,7 +578,8 @@
      * If data is supplied by a {@link PositionalDataSource}, the item returned from
      * <code>get(i)</code> has a position of <code>i + getPositionOffset()</code>.
      * <p>
-     * If the DataSource is a {@link KeyedDataSource}, and thus doesn't use positions, returns 0.
+     * If the DataSource is a {@link ItemKeyedDataSource} or {@link PageKeyedDataSource}, it
+     * doesn't use positions, returns 0.
      */
     public int getPositionOffset() {
         return mStorage.getPositionOffset();
@@ -858,12 +859,14 @@
             }
 
             /**
-             * Defines how many items to load when first load occurs, if you are using a
-             * {@link KeyedDataSource}.
+             * Defines how many items to load when first load occurs.
              * <p>
              * This value is typically larger than page size, so on first load data there's a large
              * enough range of content loaded to cover small scrolls.
              * <p>
+             * When using a {@link PositionalDataSource}, the initial load size will be coerced to
+             * an integer multiple of pageSize, to enable efficient tiling.
+             * <p>
              * If not set, defaults to three times page size.
              *
              * @param initialLoadSizeHint Number of items to load while initializing the PagedList.
@@ -875,7 +878,6 @@
                 return this;
             }
 
-
             /**
              * Creates a {@link Config} with the given parameters.
              *
@@ -906,13 +908,32 @@
     /**
      * Signals when a PagedList has reached the end of available data.
      * <p>
-     * This can be used to implement paging from the network into a local database - when the
-     * database has no more data to present, a BoundaryCallback can be used to fetch more data.
+     * When local storage is a cache of network data, it's common to set up a streaming pipeline:
+     * Network data is paged into the database, database is paged into UI. Paging from the database
+     * to UI can be done with a {@code LiveData<PagedList>}, but it's still necessary to know when
+     * to trigger network loads.
      * <p>
-     * If an instance is shared across multiple PagedLists (e.g. when passed to
+     * BoundaryCallback does this signaling - when a DataSource runs out of data at the end of
+     * the list, {@link #onItemAtEndLoaded(Object)} is called, and you can start an async network
+     * load that will write the result directly to the database. Because the database is being
+     * observed, the UI bound to the {@code LiveData<PagedList>} will update automatically to
+     * account for the new items.
+     * <p>
+     * Note that a BoundaryCallback instance shared across multiple PagedLists (e.g. when passed to
      * {@link LivePagedListBuilder#setBoundaryCallback}), the callbacks may be issued multiple
      * times. If for example {@link #onItemAtEndLoaded(Object)} triggers a network load, it should
      * avoid triggering it again while the load is ongoing.
+     * <p>
+     * BoundaryCallback only passes the item at front or end of the list. Number of items is not
+     * passed, since it may not be fully computed by the DataSource if placeholders are not
+     * supplied. Keys are not known because the BoundaryCallback is independent of the
+     * DataSource-specific keys, which may be different for local vs remote storage.
+     * <p>
+     * The database + network Repository in the
+     * <a href="https://github.com/googlesamples/android-architecture-components/blob/master/PagingWithNetworkSample/README.md">PagingWithNetworkSample</a>
+     * shows how to implement a network BoundaryCallback using
+     * <a href="https://square.github.io/retrofit/">Retrofit</a>, while
+     * handling swipe-to-refresh, network errors, and retry.
      *
      * @param <T> Type loaded by the PagedList.
      */
diff --git a/paging/common/src/main/java/android/arch/paging/PositionalDataSource.java b/paging/common/src/main/java/android/arch/paging/PositionalDataSource.java
index c8345d4..4b9f1c0 100644
--- a/paging/common/src/main/java/android/arch/paging/PositionalDataSource.java
+++ b/paging/common/src/main/java/android/arch/paging/PositionalDataSource.java
@@ -25,15 +25,19 @@
 import java.util.concurrent.Executor;
 
 /**
- * Position-based data loader for a fixed-size, countable data set, supporting loads at arbitrary
- * positions.
+ * Position-based data loader for a fixed-size, countable data set, supporting fixed-size loads at
+ * arbitrary page positions.
  * <p>
- * Extend PositionalDataSource if you can support counting your data set, and loading based on
- * position information.
+ * Extend PositionalDataSource if you can load pages of a requested size at arbitrary
+ * positions, and provide a fixed item count. If your data source can't support loading arbitrary
+ * requested page sizes (e.g. when network page size constraints are only known at runtime), use
+ * either {@link PageKeyedDataSource} or {@link ItemKeyedDataSource} instead.
  * <p>
  * Note that unless {@link PagedList.Config#enablePlaceholders placeholders are disabled}
- * PositionalDataSource requires counting the size of the dataset. This allows pages to be tiled in
+ * PositionalDataSource requires counting the size of the data set. This allows pages to be tiled in
  * at arbitrary, non-contiguous locations based upon what the user observes in a {@link PagedList}.
+ * If placeholders are disabled, initialize with the two parameter
+ * {@link LoadInitialCallback#onResult(List, int)}.
  * <p>
  * Room can generate a Factory of PositionalDataSources for you:
  * <pre>
@@ -134,7 +138,7 @@
 
         LoadInitialCallback(@NonNull PositionalDataSource dataSource, boolean countingEnabled,
                 int pageSize, PageResult.Receiver<T> receiver) {
-            super(PageResult.INIT, dataSource, null, receiver);
+            super(dataSource, PageResult.INIT, null, receiver);
             mCountingEnabled = countingEnabled;
             mPageSize = pageSize;
             if (mPageSize < 1) {
@@ -148,7 +152,9 @@
          * Call this method from your DataSource's {@code loadInitial} function to return data,
          * and inform how many placeholders should be shown before and after. If counting is cheap
          * to compute (for example, if a network load returns the information regardless), it's
-         * recommended to pass data back through this method.
+         * recommended to pass the total size to the totalCount parameter. If placeholders are not
+         * requested (when {@link LoadInitialParams#placeholdersEnabled} is false), you can instead
+         * call {@link #onResult(List, int)}.
          *
          * @param data List of items loaded from the DataSource. If this is empty, the DataSource
          *             is treated as empty, and no further loads will occur.
@@ -179,11 +185,14 @@
         }
 
         /**
-         * Called to pass initial load state from a DataSource without supporting placeholders.
+         * Called to pass initial load state from a DataSource without total count,
+         * when placeholders aren't requested.
+         * <p class="note"><strong>Note:</strong> This method can only be called when placeholders
+         * are disabled ({@link LoadInitialParams#placeholdersEnabled} is false).
          * <p>
          * Call this method from your DataSource's {@code loadInitial} function to return data,
-         * if position is known but total size is not. If counting is not expensive, consider
-         * calling the three parameter variant: {@link #onResult(List, int, int)}.
+         * if position is known but total size is not. If placeholders are requested, call the three
+         * parameter variant: {@link #onResult(List, int, int)}.
          *
          * @param data List of items loaded from the DataSource. If this is empty, the DataSource
          *             is treated as empty, and no further loads will occur.
@@ -191,10 +200,21 @@
          *                 items before the items in data that can be provided by this DataSource,
          *                 pass {@code N}.
          */
-        void onResult(@NonNull List<T> data, int position) {
-            // not counting, don't need to check mAcceptCount
-            dispatchResultToReceiver(new PageResult<>(
-                    data, 0, 0, position));
+        @SuppressWarnings("WeakerAccess")
+        public void onResult(@NonNull List<T> data, int position) {
+            if (position < 0) {
+                throw new IllegalArgumentException("Position must be non-negative");
+            }
+            if (data.isEmpty() && position != 0) {
+                throw new IllegalArgumentException(
+                        "Initial result cannot be empty if items are present in data set.");
+            }
+            if (mCountingEnabled) {
+                throw new IllegalStateException("Placeholders requested, but totalCount not"
+                        + " provided. Please call the three-parameter onResult method, or disable"
+                        + " placeholders in the PagedList.Config");
+            }
+            dispatchResultToReceiver(new PageResult<>(data, position));
         }
     }
 
@@ -214,7 +234,7 @@
         private final int mPositionOffset;
         LoadRangeCallback(@NonNull PositionalDataSource dataSource, int positionOffset,
                 Executor mainThreadExecutor, PageResult.Receiver<T> receiver) {
-            super(PageResult.TILE, dataSource, mainThreadExecutor, receiver);
+            super(dataSource, PageResult.TILE, mainThreadExecutor, receiver);
             mPositionOffset = positionOffset;
         }
 
@@ -237,7 +257,7 @@
                 new LoadInitialCallback<>(this, acceptCount, pageSize, receiver);
 
         LoadInitialParams params = new LoadInitialParams(
-                requestedStartPosition, requestedLoadSize, pageSize, true);
+                requestedStartPosition, requestedLoadSize, pageSize, acceptCount);
         loadInitial(params, callback);
 
         // If initialLoad's callback is not called within the body, we force any following calls
diff --git a/paging/common/src/test/java/android/arch/paging/Executors.kt b/paging/common/src/test/java/android/arch/paging/Executors.kt
index b472eed..e1c45a7 100644
--- a/paging/common/src/test/java/android/arch/paging/Executors.kt
+++ b/paging/common/src/test/java/android/arch/paging/Executors.kt
@@ -23,8 +23,8 @@
 class TestExecutor : Executor {
     private val mTasks = LinkedList<Runnable>()
 
-    override fun execute(command: Runnable) {
-        mTasks.add(command)
+    override fun execute(runnable: Runnable) {
+        mTasks.add(runnable)
     }
 
     internal fun executeAll(): Boolean {
@@ -40,7 +40,7 @@
 }
 
 class FailExecutor(val string: String = "Executor expected to be unused") : Executor {
-    override fun execute(p0: Runnable?) {
+    override fun execute(runnable: Runnable?) {
         fail(string)
     }
 }
diff --git a/paging/common/src/test/java/android/arch/paging/KeyedDataSourceTest.kt b/paging/common/src/test/java/android/arch/paging/ItemKeyedDataSourceTest.kt
similarity index 92%
rename from paging/common/src/test/java/android/arch/paging/KeyedDataSourceTest.kt
rename to paging/common/src/test/java/android/arch/paging/ItemKeyedDataSourceTest.kt
index b36b433..f687b88 100644
--- a/paging/common/src/test/java/android/arch/paging/KeyedDataSourceTest.kt
+++ b/paging/common/src/test/java/android/arch/paging/ItemKeyedDataSourceTest.kt
@@ -30,7 +30,7 @@
 import org.mockito.Mockito.verifyNoMoreInteractions
 
 @RunWith(JUnit4::class)
-class KeyedDataSourceTest {
+class ItemKeyedDataSourceTest {
 
     // ----- STANDARD -----
 
@@ -185,11 +185,11 @@
     fun loadBefore() {
         val dataSource = ItemDataSource()
         @Suppress("UNCHECKED_CAST")
-        val callback = mock(KeyedDataSource.LoadCallback::class.java)
-                as KeyedDataSource.LoadCallback<Item>
+        val callback = mock(ItemKeyedDataSource.LoadCallback::class.java)
+                as ItemKeyedDataSource.LoadCallback<Item>
 
         dataSource.loadBefore(
-                KeyedDataSource.LoadParams(dataSource.getKey(ITEMS_BY_NAME_ID[5]), 5), callback)
+                ItemKeyedDataSource.LoadParams(dataSource.getKey(ITEMS_BY_NAME_ID[5]), 5), callback)
 
         @Suppress("UNCHECKED_CAST")
         val argument = ArgumentCaptor.forClass(List::class.java) as ArgumentCaptor<List<Item>>
@@ -208,7 +208,7 @@
 
     internal class ItemDataSource(private val counted: Boolean = true,
                                   private val items: List<Item> = ITEMS_BY_NAME_ID)
-            : KeyedDataSource<Key, Item>() {
+            : ItemKeyedDataSource<Key, Item>() {
 
         override fun loadInitial(
                 params: LoadInitialParams<Key>,
@@ -256,9 +256,9 @@
         }
     }
 
-    private fun performInitialLoad(
-            callbackInvoker: (callback: KeyedDataSource.LoadInitialCallback<String>) -> Unit) {
-        val dataSource = object : KeyedDataSource<String, String>() {
+    private fun performLoadInitial(
+            callbackInvoker: (callback: ItemKeyedDataSource.LoadInitialCallback<String>) -> Unit) {
+        val dataSource = object : ItemKeyedDataSource<String, String>() {
             override fun getKey(item: String): String {
                 return ""
             }
@@ -287,38 +287,38 @@
     }
 
     @Test
-    fun initialLoadCallbackSuccess() = performInitialLoad {
+    fun loadInitialCallbackSuccess() = performLoadInitial {
         // LoadInitialCallback correct usage
         it.onResult(listOf("a", "b"), 0, 2)
     }
 
     @Test
-    fun initialLoadCallbackNotPageSizeMultiple() = performInitialLoad {
+    fun loadInitialCallbackNotPageSizeMultiple() = performLoadInitial {
         // Keyed LoadInitialCallback *can* accept result that's not a multiple of page size
         val elevenLetterList = List(11) { "" + 'a' + it }
         it.onResult(elevenLetterList, 0, 12)
     }
 
     @Test(expected = IllegalArgumentException::class)
-    fun initialLoadCallbackListTooBig() = performInitialLoad {
+    fun loadInitialCallbackListTooBig() = performLoadInitial {
         // LoadInitialCallback can't accept pos + list > totalCount
         it.onResult(listOf("a", "b", "c"), 0, 2)
     }
 
     @Test(expected = IllegalArgumentException::class)
-    fun initialLoadCallbackPositionTooLarge() = performInitialLoad {
+    fun loadInitialCallbackPositionTooLarge() = performLoadInitial {
         // LoadInitialCallback can't accept pos + list > totalCount
         it.onResult(listOf("a", "b"), 1, 2)
     }
 
     @Test(expected = IllegalArgumentException::class)
-    fun initialLoadCallbackPositionNegative() = performInitialLoad {
+    fun loadInitialCallbackPositionNegative() = performLoadInitial {
         // LoadInitialCallback can't accept negative position
         it.onResult(listOf("a", "b", "c"), -1, 2)
     }
 
     @Test(expected = IllegalArgumentException::class)
-    fun initialLoadCallbackEmptyCannotHavePlaceholders() = performInitialLoad {
+    fun loadInitialCallbackEmptyCannotHavePlaceholders() = performLoadInitial {
         // LoadInitialCallback can't accept empty result unless data set is empty
         it.onResult(emptyList(), 0, 2)
     }
diff --git a/paging/common/src/test/java/android/arch/paging/PageKeyedDataSourceTest.kt b/paging/common/src/test/java/android/arch/paging/PageKeyedDataSourceTest.kt
new file mode 100644
index 0000000..bcc2535
--- /dev/null
+++ b/paging/common/src/test/java/android/arch/paging/PageKeyedDataSourceTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright 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.arch.paging
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class PageKeyedDataSourceTest {
+    private val mMainThread = TestExecutor()
+    private val mBackgroundThread = TestExecutor()
+
+    internal data class Item(val name: String)
+
+    internal data class Page(val prev: String?, val data: List<Item>, val next: String?)
+
+    internal class ItemDataSource(val data: Map<String, Page> = PAGE_MAP)
+            : PageKeyedDataSource<String, Item>() {
+
+        private fun getPage(key: String): Page = data[key]!!
+
+        override fun loadInitial(
+                params: LoadInitialParams<String>,
+                callback: LoadInitialCallback<String, Item>) {
+            val page = getPage(INIT_KEY)
+            callback.onResult(page.data, page.prev, page.next)
+        }
+
+        override fun loadBefore(params: LoadParams<String>, callback: LoadCallback<String, Item>) {
+            val page = getPage(params.key)
+            callback.onResult(page.data, page.prev)
+        }
+
+        override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<String, Item>) {
+            val page = getPage(params.key)
+            callback.onResult(page.data, page.next)
+        }
+    }
+
+    @Test
+    fun loadFullVerify() {
+        // validate paging entire ItemDataSource results in full, correctly ordered data
+        val pagedList = ContiguousPagedList<String, Item>(ItemDataSource(),
+                mMainThread, mBackgroundThread,
+                null, PagedList.Config.Builder().setPageSize(100).build(), null)
+
+        // validate initial load
+        assertEquals(PAGE_MAP[INIT_KEY]!!.data, pagedList)
+
+        // flush the remaining loads
+        for (i in 0..PAGE_MAP.keys.size) {
+            pagedList.loadAround(0)
+            pagedList.loadAround(pagedList.size - 1)
+            drain()
+        }
+
+        // validate full load
+        assertEquals(ITEM_LIST, pagedList)
+    }
+
+    private fun performLoadInitial(callbackInvoker:
+            (callback: PageKeyedDataSource.LoadInitialCallback<String, String>) -> Unit) {
+        val dataSource = object : PageKeyedDataSource<String, String>() {
+            override fun loadInitial(
+                    params: LoadInitialParams<String>,
+                    callback: LoadInitialCallback<String, String>) {
+                callbackInvoker(callback)
+            }
+
+            override fun loadBefore(
+                    params: LoadParams<String>,
+                    callback: LoadCallback<String, String>) {
+                fail("loadBefore not expected")
+            }
+
+            override fun loadAfter(
+                    params: LoadParams<String>,
+                    callback: LoadCallback<String, String>) {
+                fail("loadAfter not expected")
+            }
+        }
+
+        ContiguousPagedList<String, String>(
+                dataSource, FailExecutor(), FailExecutor(), null,
+                PagedList.Config.Builder()
+                        .setPageSize(10)
+                        .build(),
+                "")
+    }
+
+    @Test
+    fun loadInitialCallbackSuccess() = performLoadInitial {
+        // LoadInitialCallback correct usage
+        it.onResult(listOf("a", "b"), 0, 2, null, null)
+    }
+
+    @Test
+    fun loadInitialCallbackNotPageSizeMultiple() = performLoadInitial {
+        // Keyed LoadInitialCallback *can* accept result that's not a multiple of page size
+        val elevenLetterList = List(11) { "" + 'a' + it }
+        it.onResult(elevenLetterList, 0, 12, null, null)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun loadInitialCallbackListTooBig() = performLoadInitial {
+        // LoadInitialCallback can't accept pos + list > totalCount
+        it.onResult(listOf("a", "b", "c"), 0, 2, null, null)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun loadInitialCallbackPositionTooLarge() = performLoadInitial {
+        // LoadInitialCallback can't accept pos + list > totalCount
+        it.onResult(listOf("a", "b"), 1, 2, null, null)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun loadInitialCallbackPositionNegative() = performLoadInitial {
+        // LoadInitialCallback can't accept negative position
+        it.onResult(listOf("a", "b", "c"), -1, 2, null, null)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun loadInitialCallbackEmptyCannotHavePlaceholders() = performLoadInitial {
+        // LoadInitialCallback can't accept empty result unless data set is empty
+        it.onResult(emptyList(), 0, 2, null, null)
+    }
+
+    companion object {
+        // first load is 2nd page to ensure we test prepend as well as append behavior
+        private val INIT_KEY: String = "key 2"
+        private val PAGE_MAP: Map<String, Page>
+        private val ITEM_LIST: List<Item>
+
+        init {
+            val map = HashMap<String, Page>()
+            val list = ArrayList<Item>()
+            val pageCount = 5
+            for (i in 1..pageCount) {
+                val data = List(4) { Item("name $i $it") }
+                list.addAll(data)
+
+                val key = "key $i"
+                val prev = if (i > 1) ("key " + (i - 1)) else null
+                val next = if (i < pageCount) ("key " + (i + 1)) else null
+                map.put(key, Page(prev, data, next))
+            }
+            PAGE_MAP = map
+            ITEM_LIST = list
+        }
+    }
+
+    private fun drain() {
+        var executed: Boolean
+        do {
+            executed = mBackgroundThread.executeAll()
+            executed = mMainThread.executeAll() || executed
+        } while (executed)
+    }
+}
diff --git a/paging/common/src/test/java/android/arch/paging/PositionalDataSourceTest.kt b/paging/common/src/test/java/android/arch/paging/PositionalDataSourceTest.kt
index da4b265..280a64d 100644
--- a/paging/common/src/test/java/android/arch/paging/PositionalDataSourceTest.kt
+++ b/paging/common/src/test/java/android/arch/paging/PositionalDataSourceTest.kt
@@ -80,6 +80,7 @@
     }
 
     private fun performInitialLoad(
+            enablePlaceholders: Boolean = true,
             callbackInvoker: (callback: PositionalDataSource.LoadInitialCallback<String>) -> Unit) {
         val dataSource = object : PositionalDataSource<String>() {
             override fun loadInitial(
@@ -93,12 +94,16 @@
             }
         }
 
-        TiledPagedList(
-                dataSource, FailExecutor(), FailExecutor(), null,
-                PagedList.Config.Builder()
-                        .setPageSize(10)
-                        .build(),
-                0)
+        val config = PagedList.Config.Builder()
+                .setPageSize(10)
+                .setEnablePlaceholders(enablePlaceholders)
+                .build()
+        if (enablePlaceholders) {
+            TiledPagedList(dataSource, FailExecutor(), FailExecutor(), null, config, 0)
+        } else {
+            ContiguousPagedList(dataSource.wrapAsContiguousWithoutPlaceholders(),
+                    FailExecutor(), FailExecutor(), null, config, null)
+        }
     }
 
     @Test
@@ -137,4 +142,28 @@
         // LoadInitialCallback can't accept empty result unless data set is empty
         it.onResult(emptyList(), 0, 2)
     }
+
+    @Test(expected = IllegalStateException::class)
+    fun initialLoadCallbackRequireTotalCount() = performInitialLoad(enablePlaceholders = true) {
+        // LoadInitialCallback requires 3 args when placeholders enabled
+        it.onResult(listOf("a", "b"), 0)
+    }
+
+    @Test
+    fun initialLoadCallbackSuccessTwoArg() = performInitialLoad(enablePlaceholders = false) {
+        // LoadInitialCallback correct 2 arg usage
+        it.onResult(listOf("a", "b"), 0)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun initialLoadCallbackPosNegativeTwoArg() = performInitialLoad(enablePlaceholders = false) {
+        // LoadInitialCallback can't accept negative position
+        it.onResult(listOf("a", "b"), -1)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun initialLoadCallbackEmptyWithOffset() = performInitialLoad(enablePlaceholders = false) {
+        // LoadInitialCallback can't accept empty result unless pos is 0
+        it.onResult(emptyList(), 1)
+    }
 }
diff --git a/paging/runtime/build.gradle b/paging/runtime/build.gradle
index 6bda233..1a2bd2d 100644
--- a/paging/runtime/build.gradle
+++ b/paging/runtime/build.gradle
@@ -41,8 +41,8 @@
     androidTestImplementation(JUNIT)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(KOTLIN_STDLIB)
 }
 
diff --git a/percent/build.gradle b/percent/build.gradle
index da5ccc9..7d5a651 100644
--- a/percent/build.gradle
+++ b/percent/build.gradle
@@ -9,8 +9,8 @@
 dependencies {
     api(project(":support-compat"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
 }
 
 android {
diff --git a/persistence/db/api/1.0.0.txt b/persistence/db/api/1.0.0.txt
index 23d3bb5..f96f17a 100644
--- a/persistence/db/api/1.0.0.txt
+++ b/persistence/db/api/1.0.0.txt
@@ -85,7 +85,7 @@
     method public abstract android.arch.persistence.db.SupportSQLiteOpenHelper create(android.arch.persistence.db.SupportSQLiteOpenHelper.Configuration);
   }
 
-  public abstract interface SupportSQLiteProgram implements java.lang.AutoCloseable {
+  public abstract interface SupportSQLiteProgram implements java.io.Closeable {
     method public abstract void bindBlob(int, byte[]);
     method public abstract void bindDouble(int, double);
     method public abstract void bindLong(int, long);
diff --git a/recyclerview-selection/build.gradle b/recyclerview-selection/build.gradle
index 9234fd8..06dc730 100644
--- a/recyclerview-selection/build.gradle
+++ b/recyclerview-selection/build.gradle
@@ -25,8 +25,8 @@
     api project(':support-annotations')
     api project(':support-compat')
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(JUNIT)
diff --git a/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionStorage.java b/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionStorage.java
index 81db30f..454a76b 100644
--- a/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionStorage.java
+++ b/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionStorage.java
@@ -139,7 +139,7 @@
         }
     }
 
-    private @Nullable Selection<String> readStringSelection(@Nullable Bundle state) {
+    private @Nullable Selection<String> readStringSelection(Bundle state) {
         @Nullable ArrayList<String> stored =
                 state.getStringArrayList(EXTRA_SAVED_SELECTION_ENTRIES);
         if (stored == null) {
@@ -151,7 +151,7 @@
         return selection;
     }
 
-    private @Nullable Selection<Long> readLongSelection(@Nullable Bundle state) {
+    private @Nullable Selection<Long> readLongSelection(Bundle state) {
         @Nullable long[] stored = state.getLongArray(EXTRA_SAVED_SELECTION_ENTRIES);
         if (stored == null) {
             return null;
diff --git a/room/common/src/main/java/android/arch/persistence/room/Database.java b/room/common/src/main/java/android/arch/persistence/room/Database.java
index f12d1b9..14e722f 100644
--- a/room/common/src/main/java/android/arch/persistence/room/Database.java
+++ b/room/common/src/main/java/android/arch/persistence/room/Database.java
@@ -34,7 +34,7 @@
  * <pre>
  * // User and Book are classes annotated with {@literal @}Entity.
  * {@literal @}Database(version = 1, entities = {User.class, Book.class})
- * abstract class AppDatabase extends RoomDatabase() {
+ * abstract class AppDatabase extends RoomDatabase {
  *     // BookDao is a class annotated with {@literal @}Dao.
  *     abstract public BookDao bookDao();
  *     // UserDao is a class annotated with {@literal @}Dao.
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
index 6458e2a..d4d4893 100644
--- a/room/integration-tests/testapp/build.gradle
+++ b/room/integration-tests/testapp/build.gradle
@@ -84,8 +84,8 @@
     androidTestImplementation(project(":room:rxjava2"))
     androidTestImplementation(project(":arch:core-testing"))
     androidTestImplementation(RX_JAVA)
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
 
diff --git a/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/LastNameAscCustomerDataSource.java b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/LastNameAscCustomerDataSource.java
index b259643..6278bc2 100644
--- a/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/LastNameAscCustomerDataSource.java
+++ b/room/integration-tests/testapp/src/main/java/android/arch/persistence/room/integration/testapp/database/LastNameAscCustomerDataSource.java
@@ -16,7 +16,7 @@
 package android.arch.persistence.room.integration.testapp.database;
 
 import android.arch.paging.DataSource;
-import android.arch.paging.KeyedDataSource;
+import android.arch.paging.ItemKeyedDataSource;
 import android.arch.persistence.room.InvalidationTracker;
 import android.support.annotation.NonNull;
 
@@ -27,7 +27,7 @@
 /**
  * Sample Room keyed data source.
  */
-public class LastNameAscCustomerDataSource extends KeyedDataSource<String, Customer> {
+public class LastNameAscCustomerDataSource extends ItemKeyedDataSource<String, Customer> {
     private final CustomerDao mCustomerDao;
     @SuppressWarnings("FieldCanBeLocal")
     private final InvalidationTracker.Observer mObserver;
diff --git a/room/runtime/build.gradle b/room/runtime/build.gradle
index ecbd0b3..8a91ac7 100644
--- a/room/runtime/build.gradle
+++ b/room/runtime/build.gradle
@@ -46,11 +46,10 @@
     testImplementation(project(":arch:core-testing"))
     testImplementation(JUNIT)
     testImplementation(MOCKITO_CORE)
-    testImplementation libs.support.annotations
 
     androidTestImplementation(JUNIT)
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
 }
 
 // Used by testCompile in room-compiler
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/RoomDatabase.java b/room/runtime/src/main/java/android/arch/persistence/room/RoomDatabase.java
index 8c94024..2a108f9 100644
--- a/room/runtime/src/main/java/android/arch/persistence/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/android/arch/persistence/room/RoomDatabase.java
@@ -90,7 +90,7 @@
      * @param configuration The database configuration.
      */
     @CallSuper
-    public void init(DatabaseConfiguration configuration) {
+    public void init(@NonNull DatabaseConfiguration configuration) {
         mOpenHelper = createOpenHelper(configuration);
         mCallbacks = configuration.callbacks;
         mAllowMainThreadQueries = configuration.allowMainThreadQueries;
@@ -101,6 +101,7 @@
      *
      * @return The SQLite open helper used by this database.
      */
+    @NonNull
     public SupportSQLiteOpenHelper getOpenHelper() {
         return mOpenHelper;
     }
@@ -113,6 +114,7 @@
      * @param config The configuration of the Room database.
      * @return A new SupportSQLiteOpenHelper to be used while connecting to the database.
      */
+    @NonNull
     protected abstract SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config);
 
     /**
@@ -122,6 +124,7 @@
      *
      * @return Creates a new InvalidationTracker.
      */
+    @NonNull
     protected abstract InvalidationTracker createInvalidationTracker();
 
     /**
@@ -199,7 +202,7 @@
      * @param sql The query to compile.
      * @return The compiled query.
      */
-    public SupportSQLiteStatement compileStatement(String sql) {
+    public SupportSQLiteStatement compileStatement(@NonNull String sql) {
         assertNotMainThread();
         return mOpenHelper.getWritableDatabase().compileStatement(sql);
     }
@@ -238,7 +241,7 @@
      *
      * @param body The piece of code to execute.
      */
-    public void runInTransaction(Runnable body) {
+    public void runInTransaction(@NonNull Runnable body) {
         beginTransaction();
         try {
             body.run();
@@ -256,7 +259,7 @@
      * @param <V>  The type of the return value.
      * @return The value returned from the {@link Callable}.
      */
-    public <V> V runInTransaction(Callable<V> body) {
+    public <V> V runInTransaction(@NonNull Callable<V> body) {
         beginTransaction();
         try {
             V result = body.call();
@@ -278,7 +281,7 @@
      *
      * @param db The database instance.
      */
-    protected void internalInitInvalidationTracker(SupportSQLiteDatabase db) {
+    protected void internalInitInvalidationTracker(@NonNull SupportSQLiteDatabase db) {
         mInvalidationTracker.internalInit(db);
     }
 
@@ -290,6 +293,7 @@
      *
      * @return The invalidation tracker for the database.
      */
+    @NonNull
     public InvalidationTracker getInvalidationTracker() {
         return mInvalidationTracker;
     }
@@ -365,7 +369,7 @@
          * @return this
          */
         @NonNull
-        public Builder<T> addMigrations(Migration... migrations) {
+        public Builder<T> addMigrations(@NonNull Migration... migrations) {
             mMigrationContainer.addMigrations(migrations);
             return this;
         }
@@ -471,7 +475,7 @@
          *
          * @param migrations List of available migrations.
          */
-        public void addMigrations(Migration... migrations) {
+        public void addMigrations(@NonNull Migration... migrations) {
             for (Migration migration : migrations) {
                 addMigration(migration);
             }
diff --git a/samples/SupportCarDemos/OWNERS b/samples/SupportCarDemos/OWNERS
new file mode 100644
index 0000000..42ef8d4
--- /dev/null
+++ b/samples/SupportCarDemos/OWNERS
@@ -0,0 +1,2 @@
+ajchen@google.com
+yaoyx@google.com
\ No newline at end of file
diff --git a/samples/SupportCarDemos/src/main/AndroidManifest.xml b/samples/SupportCarDemos/src/main/AndroidManifest.xml
index fd46652..c32fcf4 100644
--- a/samples/SupportCarDemos/src/main/AndroidManifest.xml
+++ b/samples/SupportCarDemos/src/main/AndroidManifest.xml
@@ -15,12 +15,12 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.support.car">
+    package="com.example.androidx.car">
 
     <application android:label="@string/activity_sample_code"
             android:supportsRtl="true"
             android:icon="@drawable/app_sample_code"
-            android:theme="@style/android:Theme.Holo.Light">
+            android:theme="@style/CarTheme">
 
         <activity android:name=".SupportCarDemoActivity">
             <intent-filter>
@@ -39,6 +39,17 @@
             <meta-data android:name="android.support.PARENT_ACTIVITY"
                        android:value=".SupportCarDemoActivity" />
         </activity>
+
+        <activity android:name=".ListItemActivity"
+                  android:label="ListItem"
+                  android:parentActivityName=".SupportCarDemoActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+            <meta-data android:name="android.support.PARENT_ACTIVITY"
+                       android:value=".SupportCarDemoActivity" />
+        </activity>
     </application>
 </manifest>
 
diff --git a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/ListItemActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/ListItemActivity.java
new file mode 100644
index 0000000..6aa5ba6
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/ListItemActivity.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 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.androidx.car;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import androidx.car.widget.ListItem;
+import androidx.car.widget.ListItemAdapter;
+import androidx.car.widget.ListItemProvider;
+import androidx.car.widget.PagedListView;
+
+/**
+ * Demo activity for {@link ListItem}.
+ */
+public class ListItemActivity extends Activity {
+
+    private static int pixelToDip(Context context, int pixels) {
+        return (int) (pixels / context.getResources().getDisplayMetrics().density);
+    }
+
+    PagedListView mPagedListView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_paged_list_view);
+
+        mPagedListView = findViewById(R.id.paged_list_view);
+
+        ListItemAdapter adapter = new ListItemAdapter(this,
+                new SampleProvider(this));
+        mPagedListView.setAdapter(adapter);
+        mPagedListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
+    }
+
+    private static class SampleProvider extends ListItemProvider {
+        private Context mContext;
+        private List<ListItem> mItems;
+
+        private View.OnClickListener mGetParentHeight = (v) -> {
+            int parentHeight = ((FrameLayout) v.getParent().getParent().getParent()).getHeight();
+            Toast.makeText(v.getContext(),
+                    "card height is " + pixelToDip(mContext, parentHeight) + " dp",
+                    Toast.LENGTH_SHORT).show();
+        };
+
+        private ListItemProvider.ListProvider mListProvider;
+
+        SampleProvider(Context context) {
+            mContext = context;
+            mItems = new ArrayList<>();
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
+                    .withTitle("single line with full icon and one action")
+                    .withAction("card height", true, mGetParentHeight)
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withTitle("primary action set by drawable")
+                    .withPrimaryActionIcon(mContext.getDrawable(R.drawable.pressed_icon), true)
+                    .withViewBinder(vh -> vh.getPrimaryIcon().setClickable(true))
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
+                    .withTitle("single line with small icon and clickable end icon")
+                    .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true,
+                            mGetParentHeight)
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withPrimaryActionEmptyIcon()
+                    .withTitle("single line with empty icon and end icon no divider")
+                    .withSupplementalIcon(android.R.drawable.sym_def_app_icon, false)
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withTitle("title is single line and ellipsizes. "
+                            + mContext.getString(R.string.long_text))
+                    .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true)
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withPrimaryActionNoIcon()
+                    .withTitle("single line with two actions and no divider")
+                    .withActions("action 1", false,
+                            (v) -> Toast.makeText(
+                                    v.getContext(), "action 1", Toast.LENGTH_SHORT).show(),
+                            "action 2", false,
+                            (v) -> Toast.makeText(
+                                    v.getContext(), "action 2", Toast.LENGTH_SHORT).show())
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withPrimaryActionNoIcon()
+                    .withTitle("single line with two actions and action 2 divider")
+                    .withActions("action 1", false,
+                            (v) -> Toast.makeText(
+                                    v.getContext(), "action 1", Toast.LENGTH_SHORT).show(),
+                            "action 2", true,
+                            (v) -> Toast.makeText(
+                                    v.getContext(), "action 2", Toast.LENGTH_SHORT).show())
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withPrimaryActionNoIcon()
+                    .withTitle("single line with divider between actions. "
+                            + mContext.getString(R.string.long_text))
+                    .withActions("action 1", true,
+                            (v) -> Toast.makeText(
+                                    v.getContext(), "action 1", Toast.LENGTH_SHORT).show(),
+                            "action 2", false,
+                            (v) -> Toast.makeText(
+                                    v.getContext(), "action 2", Toast.LENGTH_SHORT).show())
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
+                    .withTitle("double line with full icon and no end icon divider")
+                    .withBody("one line text")
+                    .withSupplementalIcon(android.R.drawable.sym_def_app_icon, false,
+                            mGetParentHeight)
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
+                    .withTitle("double line with small icon and one action")
+                    .withBody("one line text")
+                    .withAction("card height", true, mGetParentHeight)
+                    .build());
+
+            String tenChars = "Ten Chars.";
+            mItems.add(new ListItem.Builder(mContext)
+                    .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
+                    .withTitle("Card with small icon and text longer than limit")
+                    .withBody("some chars")
+                    .withBody(TextUtils.join("", Collections.nCopies(20, tenChars)))
+                    .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true,
+                            mGetParentHeight)
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withPrimaryActionEmptyIcon()
+                    .withTitle("double line with empty primary icon."
+                            + mContext.getString(R.string.long_text))
+                    .withBody("one line text as primary", true)
+                    .withActions("screen size", false, (v) -> {
+                        Context c = v.getContext();
+                        Point size = new Point();
+                        c.getSystemService(WindowManager.class).getDefaultDisplay().getSize(size);
+
+                        Toast.makeText(v.getContext(),
+                                String.format("%s x %s dp", pixelToDip(c, size.x),
+                                        pixelToDip(c, size.y)), Toast.LENGTH_SHORT).show();
+                    }, "card height", true, mGetParentHeight)
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withTitle("double line with no primary action and one divider")
+                    .withBody("one line text as primary", true)
+                    .withActions("screen size", false, (v) -> {
+                        Context c = v.getContext();
+                        Point size = new Point();
+                        c.getSystemService(WindowManager.class).getDefaultDisplay().getSize(size);
+
+                        Toast.makeText(v.getContext(),
+                                String.format("%s x %s dp", pixelToDip(c, size.x),
+                                        pixelToDip(c, size.y)), Toast.LENGTH_SHORT).show();
+                    }, "card height", true, mGetParentHeight)
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
+                    .withBody("Only body - no title is set")
+                    .withAction("card height", true, mGetParentHeight)
+                    .build());
+
+            mItems.add(new ListItem.Builder(mContext)
+                    .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
+                    .withBody("Only body - no title. " + mContext.getString(R.string.long_text))
+                    .build());
+
+            mListProvider = new ListItemProvider.ListProvider(mItems);
+        }
+
+        @Override
+        public ListItem get(int position) {
+            return mListProvider.get(position);
+        }
+
+        @Override
+        public int size() {
+            return mListProvider.size();
+        }
+    }
+}
diff --git a/samples/SupportCarDemos/src/main/java/com/example/android/support/car/PagedListViewActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/PagedListViewActivity.java
similarity index 96%
rename from samples/SupportCarDemos/src/main/java/com/example/android/support/car/PagedListViewActivity.java
rename to samples/SupportCarDemos/src/main/java/com/example/androidx/car/PagedListViewActivity.java
index 0fb643f..2aa4e0c 100644
--- a/samples/SupportCarDemos/src/main/java/com/example/android/support/car/PagedListViewActivity.java
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/PagedListViewActivity.java
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-package com.example.android.support.car;
+package com.example.androidx.car;
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.support.car.widget.PagedListView;
 import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -28,6 +27,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.car.widget.PagedListView;
+
 /**
  * Demo activity for PagedListView.
  */
diff --git a/samples/SupportCarDemos/src/main/java/com/example/android/support/car/SupportCarDemoActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SupportCarDemoActivity.java
similarity index 98%
rename from samples/SupportCarDemos/src/main/java/com/example/android/support/car/SupportCarDemoActivity.java
rename to samples/SupportCarDemos/src/main/java/com/example/androidx/car/SupportCarDemoActivity.java
index 2b4f894..049c5c6 100644
--- a/samples/SupportCarDemos/src/main/java/com/example/android/support/car/SupportCarDemoActivity.java
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SupportCarDemoActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.example.android.support.car;
+package com.example.androidx.car;
 
 import android.app.ListActivity;
 import android.content.Intent;
diff --git a/samples/SupportCarDemos/src/main/res/drawable/pressed_icon.xml b/samples/SupportCarDemos/src/main/res/drawable/pressed_icon.xml
new file mode 100644
index 0000000..32a497f
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/res/drawable/pressed_icon.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@android:drawable/btn_star_big_on"/>
+    <item android:drawable="@android:drawable/btn_star_big_off"/>
+</selector>
diff --git a/samples/SupportCarDemos/src/main/res/layout/activity_paged_list_view.xml b/samples/SupportCarDemos/src/main/res/layout/activity_paged_list_view.xml
index aa8d527..5b9a1a5 100644
--- a/samples/SupportCarDemos/src/main/res/layout/activity_paged_list_view.xml
+++ b/samples/SupportCarDemos/src/main/res/layout/activity_paged_list_view.xml
@@ -19,7 +19,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
-    <android.support.car.widget.PagedListView
+    <androidx.car.widget.PagedListView
         android:id="@+id/paged_list_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/samples/SupportCarDemos/src/main/res/layout/paged_list_item.xml b/samples/SupportCarDemos/src/main/res/layout/paged_list_item.xml
index 387e2c9..26f9c5a 100644
--- a/samples/SupportCarDemos/src/main/res/layout/paged_list_item.xml
+++ b/samples/SupportCarDemos/src/main/res/layout/paged_list_item.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<android.support.car.widget.ColumnCardView
+<androidx.car.widget.ColumnCardView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="0dp"
@@ -27,4 +27,4 @@
         android:singleLine="true"
         android:ellipsize="end"
         android:textAppearance="@style/CarBody1"/>
-</android.support.car.widget.ColumnCardView>
\ No newline at end of file
+</androidx.car.widget.ColumnCardView>
\ No newline at end of file
diff --git a/samples/SupportCarDemos/src/main/res/values/strings.xml b/samples/SupportCarDemos/src/main/res/values/strings.xml
index 0a49857..adffe89 100644
--- a/samples/SupportCarDemos/src/main/res/values/strings.xml
+++ b/samples/SupportCarDemos/src/main/res/values/strings.xml
@@ -16,5 +16,6 @@
 -->
 <resources>
     <string name="activity_sample_code">Support Car Demos</string>
+    <string name="long_text">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</string>
 </resources>
 
diff --git a/samples/SupportCarDemos/src/main/res/values/themes.xml b/samples/SupportCarDemos/src/main/res/values/themes.xml
new file mode 100644
index 0000000..4b82ecd
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/res/values/themes.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- The main theme for all activities within the Car Demo. -->
+    <style name="CarTheme" parent="android:Theme.Material.Light">
+        <item name="android:windowBackground">@color/car_grey_50</item>
+        <item name="android:colorAccent">@color/car_yellow_500</item>
+        <item name="android:colorPrimary">@color/car_highlight</item>
+        <item name="android:colorPrimaryDark">@color/car_grey_300</item>
+        <item name="android:buttonStyle">@style/CarButton</item>
+        <item name="android:borderlessButtonStyle">@style/CarButton.Borderless</item>
+        <item name="android:progressBarStyleHorizontal">
+            @style/CarProgressBar.Horizontal
+        </item>
+        <item name="android:windowLightStatusBar">true</item>
+    </style>
+</resources>
diff --git a/slices/core/src/main/java/androidx/app/slice/core/SliceQuery.java b/slices/core/src/main/java/androidx/app/slice/core/SliceQuery.java
index 6ed9885..d799352 100644
--- a/slices/core/src/main/java/androidx/app/slice/core/SliceQuery.java
+++ b/slices/core/src/main/java/androidx/app/slice/core/SliceQuery.java
@@ -26,8 +26,8 @@
 import android.support.annotation.RestrictTo;
 import android.text.TextUtils;
 
+import java.util.ArrayDeque;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Queue;
 import java.util.Spliterators;
@@ -285,7 +285,7 @@
     /**
      */
     public static Stream<SliceItem> stream(SliceItem slice) {
-        Queue<SliceItem> items = new LinkedList();
+        Queue<SliceItem> items = new ArrayDeque<>();
         items.add(slice);
         return getSliceItemStream(items);
     }
@@ -293,7 +293,7 @@
     /**
      */
     public static Stream<SliceItem> stream(Slice slice) {
-        Queue<SliceItem> items = new LinkedList();
+        Queue<SliceItem> items = new ArrayDeque<>();
         items.addAll(slice.getItems());
         return getSliceItemStream(items);
     }
diff --git a/testutils/build.gradle b/testutils/build.gradle
index d99826d..074ab34 100644
--- a/testutils/build.gradle
+++ b/testutils/build.gradle
@@ -24,8 +24,8 @@
     api(project(":support-fragment"))
     api(project(":appcompat-v7"))
 
-    implementation(TEST_RUNNER, libs.exclude_annotations)
-    implementation(ESPRESSO_CORE, libs.exclude_annotations)
+    implementation(TEST_RUNNER)
+    implementation(ESPRESSO_CORE)
     implementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     implementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     implementation(JUNIT)
diff --git a/transition/build.gradle b/transition/build.gradle
index 3e78cfe..dcf3a76 100644
--- a/transition/build.gradle
+++ b/transition/build.gradle
@@ -11,8 +11,8 @@
     api(project(":support-compat"))
     compileOnly project(':support-fragment')
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(project(":support-v4"))
diff --git a/tv-provider/build.gradle b/tv-provider/build.gradle
index eb5d297..7090108 100644
--- a/tv-provider/build.gradle
+++ b/tv-provider/build.gradle
@@ -10,7 +10,7 @@
     api(project(":support-annotations"))
     api(project(":support-compat"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
 }
 
 android {
diff --git a/v13/api/current.txt b/v13/api/current.txt
index 73871f6..1d9fdbf 100644
--- a/v13/api/current.txt
+++ b/v13/api/current.txt
@@ -5,44 +5,63 @@
     method public static android.support.v13.view.DragAndDropPermissionsCompat requestDragAndDropPermissions(android.app.Activity, android.view.DragEvent);
   }
 
-  public class FragmentCompat {
-    ctor public FragmentCompat();
-    method public static void requestPermissions(android.app.Fragment, java.lang.String[], int);
+  public deprecated class FragmentCompat {
+    ctor public deprecated FragmentCompat();
+    method public static deprecated void requestPermissions(android.app.Fragment, java.lang.String[], int);
     method public static deprecated void setMenuVisibility(android.app.Fragment, boolean);
-    method public static void setPermissionCompatDelegate(android.support.v13.app.FragmentCompat.PermissionCompatDelegate);
-    method public static void setUserVisibleHint(android.app.Fragment, boolean);
-    method public static boolean shouldShowRequestPermissionRationale(android.app.Fragment, java.lang.String);
+    method public static deprecated void setPermissionCompatDelegate(android.support.v13.app.FragmentCompat.PermissionCompatDelegate);
+    method public static deprecated void setUserVisibleHint(android.app.Fragment, boolean);
+    method public static deprecated boolean shouldShowRequestPermissionRationale(android.app.Fragment, java.lang.String);
   }
 
-  public static abstract interface FragmentCompat.OnRequestPermissionsResultCallback {
-    method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]);
+  public static abstract deprecated interface FragmentCompat.OnRequestPermissionsResultCallback {
+    method public abstract deprecated void onRequestPermissionsResult(int, java.lang.String[], int[]);
   }
 
-  public static abstract interface FragmentCompat.PermissionCompatDelegate {
-    method public abstract boolean requestPermissions(android.app.Fragment, java.lang.String[], int);
+  public static abstract deprecated interface FragmentCompat.PermissionCompatDelegate {
+    method public abstract deprecated boolean requestPermissions(android.app.Fragment, java.lang.String[], int);
   }
 
-  public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
-    ctor public FragmentPagerAdapter(android.app.FragmentManager);
-    method public abstract android.app.Fragment getItem(int);
-    method public long getItemId(int);
-    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  public abstract deprecated class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public deprecated FragmentPagerAdapter(android.app.FragmentManager);
+    method public deprecated void destroyItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void finishUpdate(android.view.ViewGroup);
+    method public abstract deprecated android.app.Fragment getItem(int);
+    method public deprecated long getItemId(int);
+    method public deprecated java.lang.Object instantiateItem(android.view.ViewGroup, int);
+    method public deprecated boolean isViewFromObject(android.view.View, java.lang.Object);
+    method public deprecated void restoreState(android.os.Parcelable, java.lang.ClassLoader);
+    method public deprecated android.os.Parcelable saveState();
+    method public deprecated void setPrimaryItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void startUpdate(android.view.ViewGroup);
   }
 
-  public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
-    ctor public FragmentStatePagerAdapter(android.app.FragmentManager);
-    method public abstract android.app.Fragment getItem(int);
-    method public boolean isViewFromObject(android.view.View, java.lang.Object);
+  public abstract deprecated class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter {
+    ctor public deprecated FragmentStatePagerAdapter(android.app.FragmentManager);
+    method public deprecated void destroyItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void finishUpdate(android.view.ViewGroup);
+    method public abstract deprecated android.app.Fragment getItem(int);
+    method public deprecated java.lang.Object instantiateItem(android.view.ViewGroup, int);
+    method public deprecated boolean isViewFromObject(android.view.View, java.lang.Object);
+    method public deprecated void restoreState(android.os.Parcelable, java.lang.ClassLoader);
+    method public deprecated android.os.Parcelable saveState();
+    method public deprecated void setPrimaryItem(android.view.ViewGroup, int, java.lang.Object);
+    method public deprecated void startUpdate(android.view.ViewGroup);
   }
 
-  public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
-    ctor public FragmentTabHost(android.content.Context);
-    ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet);
-    method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
-    method public void onTabChanged(java.lang.String);
+  public deprecated class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+    ctor public deprecated FragmentTabHost(android.content.Context);
+    ctor public deprecated FragmentTabHost(android.content.Context, android.util.AttributeSet);
+    method public deprecated void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle);
+    method protected deprecated void onAttachedToWindow();
+    method protected deprecated void onDetachedFromWindow();
+    method protected deprecated void onRestoreInstanceState(android.os.Parcelable);
+    method protected deprecated android.os.Parcelable onSaveInstanceState();
+    method public deprecated void onTabChanged(java.lang.String);
+    method public deprecated void setOnTabChangedListener(android.widget.TabHost.OnTabChangeListener);
     method public deprecated void setup();
-    method public void setup(android.content.Context, android.app.FragmentManager);
-    method public void setup(android.content.Context, android.app.FragmentManager, int);
+    method public deprecated void setup(android.content.Context, android.app.FragmentManager);
+    method public deprecated void setup(android.content.Context, android.app.FragmentManager, int);
   }
 
 }
diff --git a/v13/build.gradle b/v13/build.gradle
index 0f5c9b6..425a31f 100644
--- a/v13/build.gradle
+++ b/v13/build.gradle
@@ -10,8 +10,8 @@
     api(project(":support-annotations"))
     api(project(":support-v4"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
 }
diff --git a/v13/java/android/support/v13/app/FragmentCompat.java b/v13/java/android/support/v13/app/FragmentCompat.java
index 31c2343..e8915fb 100644
--- a/v13/java/android/support/v13/app/FragmentCompat.java
+++ b/v13/java/android/support/v13/app/FragmentCompat.java
@@ -30,8 +30,19 @@
 
 /**
  * Helper for accessing features in {@link Fragment} in a backwards compatible fashion.
+ *
+ * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework fragment.
  */
+@Deprecated
 public class FragmentCompat {
+
+    /**
+     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework fragment.
+     */
+    @Deprecated
+    public FragmentCompat() {
+    }
+
     interface FragmentCompatImpl {
         void setUserVisibleHint(Fragment f, boolean deferStart);
         void requestPermissions(Fragment fragment, String[] permissions, int requestCode);
@@ -48,7 +59,11 @@
      *     to the compatibility methods in this class will first check whether the delegate can
      *     handle the method call, and invoke the corresponding method if it can.
      * </p>
+     *
+     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
+     * {@link Fragment}.
      */
+    @Deprecated
     public interface PermissionCompatDelegate {
 
         /**
@@ -66,7 +81,11 @@
          *
          * @return Whether the delegate has handled the permission request.
          * @see FragmentCompat#requestPermissions(Fragment, String[], int)
+         *
+         * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
+         * {@link Fragment}.
          */
+        @Deprecated
         boolean requestPermissions(Fragment fragment, String[] permissions, int requestCode);
     }
 
@@ -157,22 +176,34 @@
      * delegate.
      *
      * @param delegate The delegate to be set. {@code null} to clear the set delegate.
+     *
+     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
+     * {@link Fragment}.
      */
+    @Deprecated
     public static void setPermissionCompatDelegate(PermissionCompatDelegate delegate) {
         sDelegate = delegate;
     }
 
     /**
      * @hide
+     *
+     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
+     * {@link Fragment}.
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Deprecated
     public static PermissionCompatDelegate getPermissionCompatDelegate() {
         return sDelegate;
     }
 
     /**
      * This interface is the contract for receiving the results for permission requests.
+     *
+     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
+     * {@link Fragment}.
      */
+    @Deprecated
     public interface OnRequestPermissionsResultCallback {
 
         /**
@@ -188,7 +219,11 @@
          *     or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
          *
          * @see #requestPermissions(android.app.Fragment, String[], int)
+         *
+         * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
+         * {@link Fragment}.
          */
+        @Deprecated
         public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                 @NonNull int[] grantResults);
     }
@@ -197,7 +232,8 @@
      * Call {@link Fragment#setMenuVisibility(boolean) Fragment.setMenuVisibility(boolean)}
      * if running on an appropriate version of the platform.
      *
-     * @deprecated Use {@link Fragment#setMenuVisibility(boolean)} directly.
+     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
+     * {@link Fragment}.
      */
     @Deprecated
     public static void setMenuVisibility(Fragment f, boolean visible) {
@@ -207,7 +243,11 @@
     /**
      * Call {@link Fragment#setUserVisibleHint(boolean) setUserVisibleHint(boolean)}
      * if running on an appropriate version of the platform.
+     *
+     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
+     * {@link Fragment}.
      */
+    @Deprecated
     public static void setUserVisibleHint(Fragment f, boolean deferStart) {
         IMPL.setUserVisibleHint(f, deferStart);
     }
@@ -262,7 +302,11 @@
      * @see android.support.v4.content.ContextCompat#checkSelfPermission(
      *     android.content.Context, String)
      * @see #shouldShowRequestPermissionRationale(android.app.Fragment, String)
+     *
+     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
+     * {@link Fragment}.
      */
+    @Deprecated
     public static void requestPermissions(@NonNull Fragment fragment,
             @NonNull String[] permissions, int requestCode) {
         if (sDelegate != null && sDelegate.requestPermissions(fragment, permissions, requestCode)) {
@@ -293,7 +337,11 @@
      * @see android.support.v4.content.ContextCompat#checkSelfPermission(
      *     android.content.Context, String)
      * @see #requestPermissions(android.app.Fragment, String[], int)
+     *
+     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
+     * {@link Fragment}.
      */
+    @Deprecated
     public static boolean shouldShowRequestPermissionRationale(@NonNull Fragment fragment,
             @NonNull String permission) {
         return IMPL.shouldShowRequestPermissionRationale(fragment, permission);
diff --git a/v13/java/android/support/v13/app/FragmentPagerAdapter.java b/v13/java/android/support/v13/app/FragmentPagerAdapter.java
index e0b788a..112ed02 100644
--- a/v13/java/android/support/v13/app/FragmentPagerAdapter.java
+++ b/v13/java/android/support/v13/app/FragmentPagerAdapter.java
@@ -61,7 +61,10 @@
  *
  * {@sample frameworks/support/samples/Support13Demos/src/main/res/layout/fragment_pager_list.xml
  *      complete}
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
  */
+@Deprecated
 public abstract class FragmentPagerAdapter extends PagerAdapter {
     private static final String TAG = "FragmentPagerAdapter";
     private static final boolean DEBUG = false;
@@ -70,15 +73,26 @@
     private FragmentTransaction mCurTransaction = null;
     private Fragment mCurrentPrimaryItem = null;
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
+     */
+    @Deprecated
     public FragmentPagerAdapter(FragmentManager fm) {
         mFragmentManager = fm;
     }
 
     /**
      * Return the Fragment associated with a specified position.
+     *
+     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
      */
+    @Deprecated
     public abstract Fragment getItem(int position);
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public void startUpdate(ViewGroup container) {
         if (container.getId() == View.NO_ID) {
@@ -87,6 +101,10 @@
         }
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
+     */
+    @Deprecated
     @SuppressWarnings("ReferenceEquality")
     @Override
     public Object instantiateItem(ViewGroup container, int position) {
@@ -116,6 +134,10 @@
         return fragment;
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public void destroyItem(ViewGroup container, int position, Object object) {
         if (mCurTransaction == null) {
@@ -126,6 +148,10 @@
         mCurTransaction.detach((Fragment)object);
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
+     */
+    @Deprecated
     @SuppressWarnings("ReferenceEquality")
     @Override
     public void setPrimaryItem(ViewGroup container, int position, Object object) {
@@ -143,6 +169,10 @@
         }
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public void finishUpdate(ViewGroup container) {
         if (mCurTransaction != null) {
@@ -152,16 +182,28 @@
         }
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public boolean isViewFromObject(View view, Object object) {
         return ((Fragment)object).getView() == view;
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public Parcelable saveState() {
         return null;
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public void restoreState(Parcelable state, ClassLoader loader) {
     }
@@ -174,7 +216,10 @@
      *
      * @param position Position within this adapter
      * @return Unique identifier for the item at position
+     *
+     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
      */
+    @Deprecated
     public long getItemId(int position) {
         return position;
     }
diff --git a/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java b/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
index 45a6bf5..76a3224 100644
--- a/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
+++ b/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
@@ -64,7 +64,10 @@
  *
  * {@sample frameworks/support/samples/Support4Demos/src/main/res/layout/fragment_pager_list.xml
  *      complete}
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
  */
+@Deprecated
 public abstract class FragmentStatePagerAdapter extends PagerAdapter {
     private static final String TAG = "FragStatePagerAdapter";
     private static final boolean DEBUG = false;
@@ -76,15 +79,26 @@
     private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
     private Fragment mCurrentPrimaryItem = null;
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
+     */
+    @Deprecated
     public FragmentStatePagerAdapter(FragmentManager fm) {
         mFragmentManager = fm;
     }
 
     /**
      * Return the Fragment associated with a specified position.
+     *
+     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
      */
+    @Deprecated
     public abstract Fragment getItem(int position);
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public void startUpdate(ViewGroup container) {
         if (container.getId() == View.NO_ID) {
@@ -93,6 +107,10 @@
         }
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public Object instantiateItem(ViewGroup container, int position) {
         // If we already have this item instantiated, there is nothing
@@ -129,6 +147,10 @@
         return fragment;
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public void destroyItem(ViewGroup container, int position, Object object) {
         Fragment fragment = (Fragment) object;
@@ -148,6 +170,10 @@
         mCurTransaction.remove(fragment);
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
+     */
+    @Deprecated
     @SuppressWarnings("ReferenceEquality")
     @Override
     public void setPrimaryItem(ViewGroup container, int position, Object object) {
@@ -165,6 +191,10 @@
         }
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public void finishUpdate(ViewGroup container) {
         if (mCurTransaction != null) {
@@ -174,11 +204,19 @@
         }
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public boolean isViewFromObject(View view, Object object) {
         return ((Fragment)object).getView() == view;
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public Parcelable saveState() {
         Bundle state = null;
@@ -201,6 +239,10 @@
         return state;
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
+     */
+    @Deprecated
     @Override
     public void restoreState(Parcelable state, ClassLoader loader) {
         if (state != null) {
diff --git a/v13/java/android/support/v13/app/FragmentTabHost.java b/v13/java/android/support/v13/app/FragmentTabHost.java
index 2326ccb..5c34ab5 100644
--- a/v13/java/android/support/v13/app/FragmentTabHost.java
+++ b/v13/java/android/support/v13/app/FragmentTabHost.java
@@ -38,7 +38,10 @@
  * Version of {@link android.support.v4.app.FragmentTabHost} that can be
  * used with the platform {@link android.app.Fragment} APIs.  You will not
  * normally use this, instead using action bar tabs.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
  */
+@Deprecated
 public class FragmentTabHost extends TabHost implements TabHost.OnTabChangeListener {
     private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
     private FrameLayout mRealTabContent;
@@ -117,6 +120,10 @@
         };
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
+     */
+    @Deprecated
     public FragmentTabHost(Context context) {
         // Note that we call through to the version that takes an AttributeSet,
         // because the simple Context construct can result in a broken object!
@@ -124,6 +131,10 @@
         initFragmentTabHost(context, null);
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
+     */
+    @Deprecated
     public FragmentTabHost(Context context, AttributeSet attrs) {
         super(context, attrs);
         initFragmentTabHost(context, attrs);
@@ -167,9 +178,7 @@
     }
 
     /**
-     * @deprecated Don't call the original TabHost setup, you must instead
-     * call {@link #setup(Context, FragmentManager)} or
-     * {@link #setup(Context, FragmentManager, int)}.
+     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
      */
     @Override
     @Deprecated
@@ -178,6 +187,10 @@
                 "Must call setup() that takes a Context and FragmentManager");
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
+     */
+    @Deprecated
     public void setup(Context context, FragmentManager manager) {
         ensureHierarchy(context);  // Ensure views required by super.setup()
         super.setup();
@@ -186,6 +199,10 @@
         ensureContent();
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
+     */
+    @Deprecated
     public void setup(Context context, FragmentManager manager, int containerId) {
         ensureHierarchy(context);  // Ensure views required by super.setup()
         super.setup();
@@ -212,11 +229,19 @@
         }
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
+     */
+    @Deprecated
     @Override
     public void setOnTabChangedListener(OnTabChangeListener l) {
         mOnTabChangeListener = l;
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
+     */
+    @Deprecated
     public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
         tabSpec.setContent(new DummyTabFactory(mContext));
         String tag = tabSpec.getTag();
@@ -239,6 +264,10 @@
         addTab(tabSpec);
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
+     */
+    @Deprecated
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -278,12 +307,20 @@
         }
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
+     */
+    @Deprecated
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mAttached = false;
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
+     */
+    @Deprecated
     @Override
     protected Parcelable onSaveInstanceState() {
         Parcelable superState = super.onSaveInstanceState();
@@ -292,6 +329,10 @@
         return ss;
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
+     */
+    @Deprecated
     @Override
     protected void onRestoreInstanceState(Parcelable state) {
         if (!(state instanceof SavedState)) {
@@ -303,6 +344,10 @@
         setCurrentTabByTag(ss.curTab);
     }
 
+    /**
+     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
+     */
+    @Deprecated
     @Override
     public void onTabChanged(String tabId) {
         if (mAttached) {
diff --git a/v14/preference/res/layout-v17/preference_category_material.xml b/v14/preference/res/layout-v17/preference_category_material.xml
index 804da6a..db3abfe 100644
--- a/v14/preference/res/layout-v17/preference_category_material.xml
+++ b/v14/preference/res/layout-v17/preference_category_material.xml
@@ -15,49 +15,13 @@
   ~ limitations under the License
   -->
 
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/title"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginBottom="8dp"
-    android:layout_marginTop="8dp"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart">
-
-    <LinearLayout
-        android:id="@+id/icon_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:gravity="start|center_vertical"
-        android:orientation="horizontal">
-        <android.support.v7.internal.widget.PreferenceImageView
-            android:id="@android:id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            app:maxHeight="18dp"
-            app:maxWidth="18dp"/>
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:paddingStart="56dp">
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="16dp"
-            android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-            android:textAlignment="viewStart"
-            android:textColor="@color/preference_fallback_accent_color"/>
-        <TextView
-            android:id="@android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:ellipsize="end"
-            android:singleLine="true"
-            android:textColor="?android:attr/textColorSecondary"/>
-    </LinearLayout>
-
-</FrameLayout>
+    android:layout_marginBottom="16dip"
+    android:textAppearance="@style/Preference_TextAppearanceMaterialBody2"
+    android:textColor="@color/preference_fallback_accent_color"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingTop="16dip" />
diff --git a/v14/preference/res/layout-v21/preference_category_material.xml b/v14/preference/res/layout-v21/preference_category_material.xml
index 1331268..dad9a5c 100644
--- a/v14/preference/res/layout-v21/preference_category_material.xml
+++ b/v14/preference/res/layout-v21/preference_category_material.xml
@@ -15,52 +15,13 @@
   ~ limitations under the License
   -->
 
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/title"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginBottom="8dp"
-    android:layout_marginTop="8dp"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart">
-
-    <LinearLayout
-        android:id="@+id/icon_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:gravity="start|center_vertical"
-        android:orientation="horizontal">
-        <android.support.v7.internal.widget.PreferenceImageView
-            android:id="@android:id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            app:maxHeight="18dp"
-            app:maxWidth="18dp"
-            android:tint="?android:attr/textColorPrimary"/>
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:paddingStart="56dp">
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="16dp"
-            android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-            android:textAlignment="viewStart"
-            android:textAppearance="@android:style/TextAppearance.Material.Body2"
-            android:textColor="?android:attr/colorAccent"/>
-        <TextView
-            android:id="@android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:ellipsize="end"
-            android:singleLine="true"
-            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
-            android:textColor="?android:attr/textColorSecondary"/>
-    </LinearLayout>
-
-</FrameLayout>
+    android:layout_marginBottom="16dip"
+    android:textAppearance="@android:style/TextAppearance.Material.Body2"
+    android:textColor="?android:attr/colorAccent"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingTop="16dip" />
diff --git a/v14/preference/res/layout-v21/preference_dropdown_material.xml b/v14/preference/res/layout-v21/preference_dropdown_material.xml
index f886d88..a92095e 100644
--- a/v14/preference/res/layout-v21/preference_dropdown_material.xml
+++ b/v14/preference/res/layout-v21/preference_dropdown_material.xml
@@ -15,18 +15,74 @@
   ~ limitations under the License
   -->
 
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false"
+    android:focusable="true" >
 
     <Spinner
         android:id="@+id/spinner"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/preference_no_icon_padding_start"
         android:visibility="invisible" />
 
-    <include layout="@layout/preference_material"/>
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="-4dp"
+        android:minWidth="60dp"
+        android:gravity="start|center_vertical"
+        android:orientation="horizontal"
+        android:paddingRight="12dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <android.support.v7.internal.widget.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:maxWidth="48dp"
+            app:maxHeight="48dp" />
+    </LinearLayout>
 
-</FrameLayout>
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
+            android:ellipsize="marquee" />
+
+        <TextView android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLines="10" />
+
+    </RelativeLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="end|center_vertical"
+        android:paddingLeft="16dp"
+        android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/v14/preference/res/layout/preference_category_material.xml b/v14/preference/res/layout/preference_category_material.xml
index 8eb2137..e366e7a 100644
--- a/v14/preference/res/layout/preference_category_material.xml
+++ b/v14/preference/res/layout/preference_category_material.xml
@@ -15,49 +15,13 @@
   ~ limitations under the License
   -->
 
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/title"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginBottom="8dp"
-    android:layout_marginTop="8dp"
-    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft">
-
-    <LinearLayout
-        android:id="@+id/icon_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:gravity="start|center_vertical"
-        android:orientation="horizontal">
-        <android.support.v7.internal.widget.PreferenceImageView
-            android:id="@android:id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            app:maxHeight="18dp"
-            app:maxWidth="18dp"/>
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:paddingLeft="56dp">
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="16dp"
-            android:paddingRight="?android:attr/listPreferredItemPaddingRight"
-            android:textAlignment="viewStart"
-            android:textColor="@color/preference_fallback_accent_color"/>
-        <TextView
-            android:id="@android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:ellipsize="end"
-            android:singleLine="true"
-            android:textColor="?android:attr/textColorSecondary"/>
-    </LinearLayout>
-
-</FrameLayout>
+    android:layout_marginBottom="16dip"
+    android:textAppearance="@style/Preference_TextAppearanceMaterialBody2"
+    android:textColor="@color/preference_fallback_accent_color"
+    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+    android:paddingTop="16dip" />
diff --git a/v14/preference/res/values/styles.xml b/v14/preference/res/values/styles.xml
index edd5285..26b1544 100644
--- a/v14/preference/res/values/styles.xml
+++ b/v14/preference/res/values/styles.xml
@@ -24,10 +24,6 @@
 
     <style name="Preference.Material">
         <item name="android:layout">@layout/preference_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="singleLineTitle">false</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.Information.Material">
@@ -38,16 +34,10 @@
 
     <style name="Preference.Category.Material">
         <item name="android:layout">@layout/preference_category_material</item>
-        <item name="allowDividerAbove">true</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.CheckBoxPreference.Material">
         <item name="android:layout">@layout/preference_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.SwitchPreferenceCompat.Material">
@@ -56,10 +46,6 @@
 
     <style name="Preference.SwitchPreference.Material">
         <item name="android:layout">@layout/preference_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="singleLineTitle">false</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.SeekBarPreference.Material">
@@ -70,31 +56,18 @@
 
     <style name="Preference.PreferenceScreen.Material">
         <item name="android:layout">@layout/preference_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.DialogPreference.Material">
         <item name="android:layout">@layout/preference_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.DialogPreference.EditTextPreference.Material">
         <item name="android:layout">@layout/preference_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="singleLineTitle">false</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.DropDown.Material">
         <item name="android:layout">@layout/preference_dropdown_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference_TextAppearanceMaterialBody2">
@@ -113,7 +86,6 @@
 
     <style name="PreferenceFragment.Material">
         <item name="android:divider">@drawable/preference_list_divider_material</item>
-        <item name="allowDividerAfterLastItem">false</item>
     </style>
 
     <style name="PreferenceFragmentList.Material">
diff --git a/v14/preference/res/values/themes.xml b/v14/preference/res/values/themes.xml
index 919873e..a69126f 100644
--- a/v14/preference/res/values/themes.xml
+++ b/v14/preference/res/values/themes.xml
@@ -36,6 +36,5 @@
         <item name="editTextPreferenceStyle">@style/Preference.DialogPreference.EditTextPreference.Material</item>
         <item name="dropdownPreferenceStyle">@style/Preference.DropDown.Material</item>
         <item name="preferenceFragmentListStyle">@style/PreferenceFragmentList.Material</item>
-        <item name="android:scrollbars">vertical</item>
     </style>
 </resources>
diff --git a/v7/appcompat/build.gradle b/v7/appcompat/build.gradle
index 214c6db..a3b80a8 100644
--- a/v7/appcompat/build.gradle
+++ b/v7/appcompat/build.gradle
@@ -13,8 +13,8 @@
     api(project(":support-vector-drawable"))
     api(project(":animated-vector-drawable"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation project(':support-testutils'), {
diff --git a/v7/appcompat/res/values-bs/strings.xml b/v7/appcompat/res/values-bs/strings.xml
index 3687875..07d1411 100644
--- a/v7/appcompat/res/values-bs/strings.xml
+++ b/v7/appcompat/res/values-bs/strings.xml
@@ -29,8 +29,8 @@
     <string name="abc_searchview_description_voice" msgid="893419373245838918">"Glasovno pretraživanje"</string>
     <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Odaberite aplikaciju"</string>
     <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Prikaži sve"</string>
-    <string name="abc_shareactionprovider_share_with_application" msgid="3300176832234831527">"Podijeli koristeći aplikaciju <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Podijeli sa"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="3300176832234831527">"Dijeli koristeći aplikaciju <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Dijeli sa"</string>
     <string name="abc_capital_on" msgid="3405795526292276155">"UKLJUÄŒI"</string>
     <string name="abc_capital_off" msgid="121134116657445385">"ISKLJUÄŒI"</string>
     <string name="search_menu_title" msgid="146198913615257606">"Pretraži"</string>
diff --git a/v7/appcompat/src/main/java/android/support/v7/view/menu/CascadingMenuPopup.java b/v7/appcompat/src/main/java/android/support/v7/view/menu/CascadingMenuPopup.java
index 564bbfc..834f854 100644
--- a/v7/appcompat/src/main/java/android/support/v7/view/menu/CascadingMenuPopup.java
+++ b/v7/appcompat/src/main/java/android/support/v7/view/menu/CascadingMenuPopup.java
@@ -54,7 +54,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -85,7 +84,7 @@
     final Handler mSubMenuHoverHandler;
 
     /** List of menus that were added before this popup was shown. */
-    private final List<MenuBuilder> mPendingMenus = new LinkedList<>();
+    private final List<MenuBuilder> mPendingMenus = new ArrayList<>();
 
     /**
      * List of open menus. The first item is the root menu and each
diff --git a/v7/appcompat/src/main/java/android/support/v7/widget/ActionMenuView.java b/v7/appcompat/src/main/java/android/support/v7/widget/ActionMenuView.java
index 76e06da..14723a0 100644
--- a/v7/appcompat/src/main/java/android/support/v7/widget/ActionMenuView.java
+++ b/v7/appcompat/src/main/java/android/support/v7/widget/ActionMenuView.java
@@ -268,10 +268,10 @@
                 // Mark indices of children that can receive an extra cell.
                 if (lp.cellsUsed < minCells) {
                     minCells = lp.cellsUsed;
-                    minCellsAt = 1 << i;
+                    minCellsAt = 1L << i;
                     minCellsItemCount = 1;
                 } else if (lp.cellsUsed == minCells) {
-                    minCellsAt |= 1 << i;
+                    minCellsAt |= 1L << i;
                     minCellsItemCount++;
                 }
             }
diff --git a/v7/appcompat/src/main/java/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java b/v7/appcompat/src/main/java/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java
index e82e469..7e98494 100644
--- a/v7/appcompat/src/main/java/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java
+++ b/v7/appcompat/src/main/java/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java
@@ -45,8 +45,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Hashtable;
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Utility class which encapsulates the logic for the TextView auto-size text feature added to
@@ -66,7 +66,8 @@
     private static final int DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX = 1;
     // Cache of TextView methods used via reflection; the key is the method name and the value is
     // the method itself or null if it can not be found.
-    private static Hashtable<String, Method> sTextViewMethodByNameCache = new Hashtable<>();
+    private static ConcurrentHashMap<String, Method> sTextViewMethodByNameCache =
+            new ConcurrentHashMap<>();
     // Use this to specify that any of the auto-size configuration int values have not been set.
     static final float UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE = -1f;
     // Ported from TextView#VERY_WIDE. Represents a maximum width in pixels the TextView takes when
diff --git a/v7/appcompat/src/main/java/android/support/v7/widget/ListPopupWindow.java b/v7/appcompat/src/main/java/android/support/v7/widget/ListPopupWindow.java
index edc9781..b98197c 100644
--- a/v7/appcompat/src/main/java/android/support/v7/widget/ListPopupWindow.java
+++ b/v7/appcompat/src/main/java/android/support/v7/widget/ListPopupWindow.java
@@ -283,7 +283,7 @@
             mAdapter.unregisterDataSetObserver(mObserver);
         }
         mAdapter = adapter;
-        if (mAdapter != null) {
+        if (adapter != null) {
             adapter.registerDataSetObserver(mObserver);
         }
 
diff --git a/v7/cardview/res/values/attrs.xml b/v7/cardview/res/values/attrs.xml
index deed51b..8bac9cc 100644
--- a/v7/cardview/res/values/attrs.xml
+++ b/v7/cardview/res/values/attrs.xml
@@ -15,6 +15,9 @@
 -->
 
 <resources>
+    <!-- Default CardView style -->
+    <attr name="cardViewStyle" format="reference" />
+
     <declare-styleable name="CardView">
         <!-- Background color for CardView. -->
         <attr name="cardBackgroundColor" format="color" />
diff --git a/v7/cardview/src/main/java/android/support/v7/widget/CardView.java b/v7/cardview/src/main/java/android/support/v7/widget/CardView.java
index 58a04f0..a45ee98 100644
--- a/v7/cardview/src/main/java/android/support/v7/widget/CardView.java
+++ b/v7/cardview/src/main/java/android/support/v7/widget/CardView.java
@@ -108,18 +108,57 @@
     final Rect mShadowBounds = new Rect();
 
     public CardView(@NonNull Context context) {
-        super(context);
-        initialize(context, null, 0);
+        this(context, null);
     }
 
     public CardView(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-        initialize(context, attrs, 0);
+        this(context, attrs, R.attr.cardViewStyle);
     }
 
     public CardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        initialize(context, attrs, defStyleAttr);
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CardView, defStyleAttr,
+                R.style.CardView);
+        ColorStateList backgroundColor;
+        if (a.hasValue(R.styleable.CardView_cardBackgroundColor)) {
+            backgroundColor = a.getColorStateList(R.styleable.CardView_cardBackgroundColor);
+        } else {
+            // There isn't one set, so we'll compute one based on the theme
+            final TypedArray aa = getContext().obtainStyledAttributes(COLOR_BACKGROUND_ATTR);
+            final int themeColorBackground = aa.getColor(0, 0);
+            aa.recycle();
+
+            // If the theme colorBackground is light, use our own light color, otherwise dark
+            final float[] hsv = new float[3];
+            Color.colorToHSV(themeColorBackground, hsv);
+            backgroundColor = ColorStateList.valueOf(hsv[2] > 0.5f
+                    ? getResources().getColor(R.color.cardview_light_background)
+                    : getResources().getColor(R.color.cardview_dark_background));
+        }
+        float radius = a.getDimension(R.styleable.CardView_cardCornerRadius, 0);
+        float elevation = a.getDimension(R.styleable.CardView_cardElevation, 0);
+        float maxElevation = a.getDimension(R.styleable.CardView_cardMaxElevation, 0);
+        mCompatPadding = a.getBoolean(R.styleable.CardView_cardUseCompatPadding, false);
+        mPreventCornerOverlap = a.getBoolean(R.styleable.CardView_cardPreventCornerOverlap, true);
+        int defaultPadding = a.getDimensionPixelSize(R.styleable.CardView_contentPadding, 0);
+        mContentPadding.left = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingLeft,
+                defaultPadding);
+        mContentPadding.top = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingTop,
+                defaultPadding);
+        mContentPadding.right = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingRight,
+                defaultPadding);
+        mContentPadding.bottom = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingBottom,
+                defaultPadding);
+        if (elevation > maxElevation) {
+            maxElevation = elevation;
+        }
+        mUserSetMinWidth = a.getDimensionPixelSize(R.styleable.CardView_android_minWidth, 0);
+        mUserSetMinHeight = a.getDimensionPixelSize(R.styleable.CardView_android_minHeight, 0);
+        a.recycle();
+
+        IMPL.initialize(mCardViewDelegate, context, backgroundColor, radius,
+                elevation, maxElevation);
     }
 
     @Override
@@ -220,50 +259,6 @@
         }
     }
 
-    private void initialize(Context context, AttributeSet attrs, int defStyleAttr) {
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CardView, defStyleAttr,
-                R.style.CardView);
-        ColorStateList backgroundColor;
-        if (a.hasValue(R.styleable.CardView_cardBackgroundColor)) {
-            backgroundColor = a.getColorStateList(R.styleable.CardView_cardBackgroundColor);
-        } else {
-            // There isn't one set, so we'll compute one based on the theme
-            final TypedArray aa = getContext().obtainStyledAttributes(COLOR_BACKGROUND_ATTR);
-            final int themeColorBackground = aa.getColor(0, 0);
-            aa.recycle();
-
-            // If the theme colorBackground is light, use our own light color, otherwise dark
-            final float[] hsv = new float[3];
-            Color.colorToHSV(themeColorBackground, hsv);
-            backgroundColor = ColorStateList.valueOf(hsv[2] > 0.5f
-                    ? getResources().getColor(R.color.cardview_light_background)
-                    : getResources().getColor(R.color.cardview_dark_background));
-        }
-        float radius = a.getDimension(R.styleable.CardView_cardCornerRadius, 0);
-        float elevation = a.getDimension(R.styleable.CardView_cardElevation, 0);
-        float maxElevation = a.getDimension(R.styleable.CardView_cardMaxElevation, 0);
-        mCompatPadding = a.getBoolean(R.styleable.CardView_cardUseCompatPadding, false);
-        mPreventCornerOverlap = a.getBoolean(R.styleable.CardView_cardPreventCornerOverlap, true);
-        int defaultPadding = a.getDimensionPixelSize(R.styleable.CardView_contentPadding, 0);
-        mContentPadding.left = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingLeft,
-                defaultPadding);
-        mContentPadding.top = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingTop,
-                defaultPadding);
-        mContentPadding.right = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingRight,
-                defaultPadding);
-        mContentPadding.bottom = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingBottom,
-                defaultPadding);
-        if (elevation > maxElevation) {
-            maxElevation = elevation;
-        }
-        mUserSetMinWidth = a.getDimensionPixelSize(R.styleable.CardView_android_minWidth, 0);
-        mUserSetMinHeight = a.getDimensionPixelSize(R.styleable.CardView_android_minHeight, 0);
-        a.recycle();
-
-        IMPL.initialize(mCardViewDelegate, context, backgroundColor, radius,
-                elevation, maxElevation);
-    }
-
     @Override
     public void setMinimumWidth(int minWidth) {
         mUserSetMinWidth = minWidth;
diff --git a/v7/gridlayout/build.gradle b/v7/gridlayout/build.gradle
index dc3a494..7df5397 100644
--- a/v7/gridlayout/build.gradle
+++ b/v7/gridlayout/build.gradle
@@ -10,8 +10,8 @@
     api(project(":support-compat"))
     api(project(":support-core-ui"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
 }
 
 android {
diff --git a/v7/mediarouter/build.gradle b/v7/mediarouter/build.gradle
index 0c94194..dbf3da5 100644
--- a/v7/mediarouter/build.gradle
+++ b/v7/mediarouter/build.gradle
@@ -11,8 +11,8 @@
     api(project(":appcompat-v7"))
     api(project(":palette-v7"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(TEST_RULES)
 }
 
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
index cf6fc1f..cc372ec 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
@@ -2560,12 +2560,16 @@
             // TODO: Remove the following logging when no longer needed.
             if (sGlobal == null || (mBluetoothRoute != null && route.isDefault())) {
                 final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
-                StringBuffer sb = new StringBuffer();
+                StringBuilder sb = new StringBuilder();
                 // 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("  ");
+                    sb.append(caller.getClassName())
+                            .append(".")
+                            .append(caller.getMethodName())
+                            .append(":")
+                            .append(caller.getLineNumber())
+                            .append("  ");
                 }
                 if (sGlobal == null) {
                     Log.w(TAG, "setSelectedRouteInternal is called while sGlobal is null: pkgName="
diff --git a/v7/palette/build.gradle b/v7/palette/build.gradle
index 95c0799..a1b1fc9 100644
--- a/v7/palette/build.gradle
+++ b/v7/palette/build.gradle
@@ -10,7 +10,7 @@
     api(project(":support-compat"))
     api(project(":support-core-utils"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
 }
 
 android {
diff --git a/v7/preference/build.gradle b/v7/preference/build.gradle
index 16af11e..698afb6 100644
--- a/v7/preference/build.gradle
+++ b/v7/preference/build.gradle
@@ -27,8 +27,8 @@
     api(project(":appcompat-v7"))
     api(project(":recyclerview-v7"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
 }
diff --git a/v7/recyclerview/build.gradle b/v7/recyclerview/build.gradle
index b98e7f7..0a83989 100644
--- a/v7/recyclerview/build.gradle
+++ b/v7/recyclerview/build.gradle
@@ -11,8 +11,8 @@
     api(project(":support-compat"))
     api(project(":support-core-ui"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(JUNIT)
@@ -20,7 +20,7 @@
 
     testImplementation(JUNIT)
     testImplementation(MOCKITO_CORE)
-    testImplementation(TEST_RUNNER, libs.exclude_annotations)
+    testImplementation(TEST_RUNNER)
 }
 
 android {
diff --git a/v7/recyclerview/src/main/java/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/main/java/android/support/v7/widget/RecyclerView.java
index 84c28b1..afadfc9 100644
--- a/v7/recyclerview/src/main/java/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/main/java/android/support/v7/widget/RecyclerView.java
@@ -6544,7 +6544,8 @@
          * @see #getItemViewType(int)
          * @see #onBindViewHolder(ViewHolder, int)
          */
-        public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
+        @NonNull
+        public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
 
         /**
          * Called by RecyclerView to display the data at the specified position. This method should
@@ -6566,7 +6567,7 @@
          *        item at the given position in the data set.
          * @param position The position of the item within the adapter's data set.
          */
-        public abstract void onBindViewHolder(VH holder, int position);
+        public abstract void onBindViewHolder(@NonNull VH holder, int position);
 
         /**
          * Called by RecyclerView to display the data at the specified position. This method
@@ -6597,7 +6598,8 @@
          * @param payloads A non-null list of merged payloads. Can be empty list if requires full
          *                 update.
          */
-        public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
+        public void onBindViewHolder(@NonNull VH holder, int position,
+                @NonNull List<Object> payloads) {
             onBindViewHolder(holder, position);
         }
 
@@ -6607,7 +6609,7 @@
          *
          * @see #onCreateViewHolder(ViewGroup, int)
          */
-        public final VH createViewHolder(ViewGroup parent, int viewType) {
+        public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
             TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
             final VH holder = onCreateViewHolder(parent, viewType);
             holder.mItemViewType = viewType;
@@ -6622,7 +6624,7 @@
          *
          * @see #onBindViewHolder(ViewHolder, int)
          */
-        public final void bindViewHolder(VH holder, int position) {
+        public final void bindViewHolder(@NonNull VH holder, int position) {
             holder.mPosition = position;
             if (hasStableIds()) {
                 holder.mItemId = getItemId(position);
@@ -6719,7 +6721,7 @@
          *
          * @param holder The ViewHolder for the view being recycled
          */
-        public void onViewRecycled(VH holder) {
+        public void onViewRecycled(@NonNull VH holder) {
         }
 
         /**
@@ -6756,7 +6758,7 @@
          * RecyclerView will check the View's transient state again before giving a final decision.
          * Default implementation returns false.
          */
-        public boolean onFailedToRecycleView(VH holder) {
+        public boolean onFailedToRecycleView(@NonNull VH holder) {
             return false;
         }
 
@@ -6770,7 +6772,7 @@
          *
          * @param holder Holder of the view being attached
          */
-        public void onViewAttachedToWindow(VH holder) {
+        public void onViewAttachedToWindow(@NonNull VH holder) {
         }
 
         /**
@@ -6782,7 +6784,7 @@
          *
          * @param holder Holder of the view being detached
          */
-        public void onViewDetachedFromWindow(VH holder) {
+        public void onViewDetachedFromWindow(@NonNull VH holder) {
         }
 
         /**
@@ -6810,7 +6812,7 @@
          *
          * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
          */
-        public void registerAdapterDataObserver(AdapterDataObserver observer) {
+        public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
             mObservable.registerObserver(observer);
         }
 
@@ -6824,7 +6826,7 @@
          *
          * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
          */
-        public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
+        public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
             mObservable.unregisterObserver(observer);
         }
 
@@ -6836,7 +6838,7 @@
          * @param recyclerView The RecyclerView instance which started observing this adapter.
          * @see #onDetachedFromRecyclerView(RecyclerView)
          */
-        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
+        public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
         }
 
         /**
@@ -6845,7 +6847,7 @@
          * @param recyclerView The RecyclerView instance which stopped observing this adapter.
          * @see #onAttachedToRecyclerView(RecyclerView)
          */
-        public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
+        public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
         }
 
         /**
@@ -6921,7 +6923,7 @@
          *
          * @see #notifyItemRangeChanged(int, int)
          */
-        public final void notifyItemChanged(int position, Object payload) {
+        public final void notifyItemChanged(int position, @Nullable Object payload) {
             mObservable.notifyItemRangeChanged(position, 1, payload);
         }
 
@@ -6969,7 +6971,8 @@
          *
          * @see #notifyItemChanged(int)
          */
-        public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
+        public final void notifyItemRangeChanged(int positionStart, int itemCount,
+                @Nullable Object payload) {
             mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
         }
 
@@ -10072,7 +10075,7 @@
             if (vScroll == 0 && hScroll == 0) {
                 return false;
             }
-            mRecyclerView.smoothScrollBy(hScroll, vScroll);
+            mRecyclerView.scrollBy(hScroll, vScroll);
             return true;
         }
 
@@ -10833,8 +10836,12 @@
          */
         private void onEnteredHiddenState(RecyclerView parent) {
             // While the view item is in hidden state, make it invisible for the accessibility.
-            mWasImportantForAccessibilityBeforeHidden =
-                    ViewCompat.getImportantForAccessibility(itemView);
+            if (mPendingAccessibilityState != PENDING_ACCESSIBILITY_STATE_NOT_SET) {
+                mWasImportantForAccessibilityBeforeHidden = mPendingAccessibilityState;
+            } else {
+                mWasImportantForAccessibilityBeforeHidden =
+                        ViewCompat.getImportantForAccessibility(itemView);
+            }
             parent.setChildImportantForAccessibilityInternal(this,
                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
         }
@@ -11193,7 +11200,7 @@
             // do nothing
         }
 
-        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
+        public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
             // fallback to onItemRangeChanged(positionStart, itemCount) if app
             // does not override this method.
             onItemRangeChanged(positionStart, itemCount);
@@ -11670,7 +11677,8 @@
             notifyItemRangeChanged(positionStart, itemCount, null);
         }
 
-        public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
+        public void notifyItemRangeChanged(int positionStart, int itemCount,
+                @Nullable Object payload) {
             // since onItemRangeChanged() is implemented by the app, it could do anything, including
             // removing itself from {@link mObservers} - and that could cause problems if
             // an iterator is used on the ArrayList {@link mObservers}.
@@ -12063,6 +12071,7 @@
                     + "mTargetPosition=" + mTargetPosition
                     + ", mData=" + mData
                     + ", mItemCount=" + mItemCount
+                    + ", mIsMeasuring=" + mIsMeasuring
                     + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
                     + ", mDeletedInvisibleItemCountSincePreviousLayout="
                     + mDeletedInvisibleItemCountSincePreviousLayout
diff --git a/v7/recyclerview/src/main/java/android/support/v7/widget/helper/ItemTouchHelper.java b/v7/recyclerview/src/main/java/android/support/v7/widget/helper/ItemTouchHelper.java
index aee48df..d2b6a20 100644
--- a/v7/recyclerview/src/main/java/android/support/v7/widget/helper/ItemTouchHelper.java
+++ b/v7/recyclerview/src/main/java/android/support/v7/widget/helper/ItemTouchHelper.java
@@ -457,7 +457,7 @@
             destroyCallbacks();
         }
         mRecyclerView = recyclerView;
-        if (mRecyclerView != null) {
+        if (recyclerView != null) {
             final Resources resources = recyclerView.getResources();
             mSwipeEscapeVelocity = resources
                     .getDimension(R.dimen.item_touch_helper_swipe_escape_velocity);
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/ImeCleanUpTestRule.java b/v7/recyclerview/tests/src/android/support/v7/util/ImeCleanUpTestRule.java
deleted file mode 100644
index 32e1295..0000000
--- a/v7/recyclerview/tests/src/android/support/v7/util/ImeCleanUpTestRule.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v7.util;
-
-import android.app.Instrumentation;
-import android.graphics.Rect;
-import android.support.annotation.NonNull;
-import android.support.testutils.PollingCheck;
-import android.support.v7.widget.TestActivity;
-import android.view.KeyEvent;
-import android.view.View;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * A JUnit rule that ensures that IME is closed after a test is finished by determining if the
- * keyboard is open, and if it is closing it.  If the rules determines the keyboard is open and is
- * unable to close it within a timeout (see source), an exception will be thrown.
- *
- * A test that wants to benefit from this functionality must call
- * {@link #setup(TestActivity, Instrumentation)} with the {@link TestActivity} under test and the
- * test's {@link Instrumentation}, or this rule does nothing.
- */
-public class ImeCleanUpTestRule implements TestRule {
-
-    // We consider the keyboard open if its height is at least this percentage of the available
-    // screen height.
-    private static final float KEYBOARD_HEIGHT_TO_SCREEN_RATIO = .15f;
-
-    private View mContainerView;
-    private Instrumentation mInstrumentation;
-
-    @Override
-    public Statement apply(final Statement base, Description description) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                try {
-                    base.evaluate();
-                } finally {
-                    closeImeIfOpen();
-                }
-            }
-        };
-    }
-
-    /**
-     * Call to enable the functionality of this TestRule.
-     * @param testActivity The {@link TestActivity} under test.
-     * @param instrumentation The test's {@link Instrumentation}.
-     */
-    public void setup(@NonNull TestActivity testActivity,
-            @NonNull Instrumentation instrumentation) {
-        mContainerView = testActivity.getContainer();
-        mInstrumentation = instrumentation;
-    }
-
-    private void closeImeIfOpen() {
-        if (mContainerView == null || mInstrumentation == null) {
-            return;
-        }
-
-        final Rect r = new Rect();
-        mContainerView.getWindowVisibleDisplayFrame(r);
-
-        // This is the entire height of the screen available to both the view and IME
-        final int screenHeight = mContainerView.getHeight();
-
-        // r.bottom is the position above IME if it's open or device button.
-        // if IME is shown, r.bottom is smaller than screenHeight.
-        int imeHeight = screenHeight - r.bottom;
-
-        if (imeHeight > screenHeight * KEYBOARD_HEIGHT_TO_SCREEN_RATIO) {
-            // Soft keyboard is shown, therefore we click the back button to close it and wait for
-            // it to be closed.
-            mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-            PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
-                @Override
-                public boolean canProceed() {
-                    mContainerView.getWindowVisibleDisplayFrame(r);
-                    int imeHeight = screenHeight - r.bottom;
-                    return imeHeight < screenHeight * KEYBOARD_HEIGHT_TO_SCREEN_RATIO;
-                }
-            });
-        }
-    }
-}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
index 13dd1e4..f4a8e37 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
@@ -325,16 +325,16 @@
         }
     }
 
-    class GridEditTextAdapter extends EditTextAdapter {
+    class GridFocusableAdapter extends FocusableAdapter {
 
         Set<Integer> mFullSpanItems = new HashSet<Integer>();
         int mSpanPerItem = 1;
 
-        GridEditTextAdapter(int count) {
+        GridFocusableAdapter(int count) {
             this(count, 1);
         }
 
-        GridEditTextAdapter(int count, int spanPerItem) {
+        GridFocusableAdapter(int count, int spanPerItem) {
             super(count);
             mSpanPerItem = spanPerItem;
         }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
index 157fb12..eed7d20 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
@@ -35,14 +35,13 @@
 import android.support.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
+import android.support.testutils.PollingCheck;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.recyclerview.test.R;
-import android.text.Editable;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.EditText;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
@@ -325,6 +324,12 @@
                 result[0] = view.requestFocus();
             }
         });
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return view.hasFocus();
+            }
+        });
         if (waitForScroll && result[0]) {
             waitForIdleScroll(mRecyclerView);
         }
@@ -789,34 +794,32 @@
         }
     }
 
-    public class EditTextAdapter extends RecyclerView.Adapter<TestViewHolder> {
+    public class FocusableAdapter extends RecyclerView.Adapter<TestViewHolder> {
 
-        final ArrayList<Editable> mEditables;
-        public EditTextAdapter(int count) {
-            mEditables = new ArrayList<>();
-            for (int i = 0; i < count; ++i) {
-                mEditables.add(Editable.Factory.getInstance().newEditable("Sample Text " + i));
-            }
+        private int mCount;
+
+        FocusableAdapter(int count) {
+            mCount = count;
         }
 
         @Override
         public TestViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-            final EditText editText = new EditText(parent.getContext());
-            editText.setLayoutParams(new ViewGroup.LayoutParams(
+            final TextView textView = new TextView(parent.getContext());
+            textView.setLayoutParams(new ViewGroup.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-            final TestViewHolder viewHolder = new TestViewHolder(editText);
-            return viewHolder;
+            textView.setFocusable(true);
+            textView.setBackgroundResource(R.drawable.item_bg);
+            return new TestViewHolder(textView);
         }
 
         @Override
         public void onBindViewHolder(TestViewHolder holder, int position) {
-            ((EditText) holder.itemView).setText(Editable.Factory.getInstance().newEditable(
-                    mEditables.get(position)));
+            ((TextView) holder.itemView).setText("Item " + position);
         }
 
         @Override
         public int getItemCount() {
-            return mEditables.size();
+            return mCount;
         }
     }
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/DummyItemAnimator.java b/v7/recyclerview/tests/src/android/support/v7/widget/DummyItemAnimator.java
new file mode 100644
index 0000000..a1491fa
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/DummyItemAnimator.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This is a dummy ItemAnimator class that does not depends on Duration, Tests would use this class
+ * to control whenever they want the Animator to finish.
+ * 1. Test MUST call endAnimation(ViewHolder) on UI thread to finish animation of a given ViewHolder
+ *    Or Test calls endAnimations() on UI thread to end animations for all.
+ * 2. Test can call getAddAnimations() etc. to get ViewHolders that currently running animation.
+ * 3. Test can call {@link #expect(int, int)} and {@link #waitFor(int)} to wait given
+ *    Events are fired.
+ */
+public class DummyItemAnimator extends SimpleItemAnimator {
+
+    static final long TIMEOUT_SECOND = 10;
+
+    ArrayList<RecyclerView.ViewHolder> mAdds = new ArrayList();
+    ArrayList<RecyclerView.ViewHolder> mRemoves = new ArrayList();
+    ArrayList<RecyclerView.ViewHolder> mMoves = new ArrayList();
+    ArrayList<RecyclerView.ViewHolder> mChangesOld = new ArrayList();
+    ArrayList<RecyclerView.ViewHolder> mChangesNew = new ArrayList();
+
+    @Retention(CLASS)
+    @Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD})
+    public @interface CountDownLatchIndex {
+    }
+
+    @CountDownLatchIndex
+    public static final int ADD_START = 0;
+
+    @CountDownLatchIndex
+    public static final int ADD_FINISHED = 1;
+
+    @CountDownLatchIndex
+    public static final int REMOVE_START = 2;
+
+    @CountDownLatchIndex
+    public static final int REMOVE_FINISHED = 3;
+
+    @CountDownLatchIndex
+    public static final int MOVE_START = 4;
+
+    @CountDownLatchIndex
+    public static final int MOVE_FINISHED = 5;
+
+    @CountDownLatchIndex
+    public static final int CHANGE_OLD_START = 6;
+
+    @CountDownLatchIndex
+    public static final int CHANGE_OLD_FINISHED = 7;
+
+    @CountDownLatchIndex
+    public static final int CHANGE_NEW_START = 8;
+
+    @CountDownLatchIndex
+    public static final int CHANGE_NEW_FINISHED = 9;
+
+    static final int NUM_COUNT_DOWN_LATCH = 10;
+
+    CountDownLatch[] mCountDownLatches = new CountDownLatch[NUM_COUNT_DOWN_LATCH];
+
+
+    public List<RecyclerView.ViewHolder> getAddAnimations() {
+        return mAdds;
+    }
+
+    public List<RecyclerView.ViewHolder> getRemoveAnimations() {
+        return mRemoves;
+    }
+
+    public List<RecyclerView.ViewHolder> getMovesAnimations() {
+        return mMoves;
+    }
+
+    public List<RecyclerView.ViewHolder> getChangesOldAnimations() {
+        return mChangesOld;
+    }
+
+    public List<RecyclerView.ViewHolder> getChangesNewAnimations() {
+        return mChangesNew;
+    }
+
+    @Override
+    public boolean animateRemove(RecyclerView.ViewHolder holder) {
+        mRemoves.add(holder);
+        dispatchRemoveStarting(holder);
+        return false;
+    }
+
+    @Override
+    public boolean animateAdd(RecyclerView.ViewHolder holder) {
+        mAdds.add(holder);
+        dispatchAddStarting(holder);
+        return false;
+    }
+
+    @Override
+    public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX,
+            int toY) {
+        mMoves.add(holder);
+        dispatchMoveStarting(holder);
+        return false;
+    }
+
+    @Override
+    public boolean animateChange(RecyclerView.ViewHolder oldHolder,
+            RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop) {
+        mChangesOld.add(oldHolder);
+        mChangesNew.add(newHolder);
+        dispatchChangeStarting(oldHolder, true);
+        dispatchChangeStarting(newHolder, false);
+        return false;
+    }
+
+    public void expect(@CountDownLatchIndex int index, int count) {
+        mCountDownLatches[index] = new CountDownLatch(count);
+    }
+
+    public void waitFor(@CountDownLatchIndex int index)
+            throws InterruptedException {
+        mCountDownLatches[index].await(TIMEOUT_SECOND, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public void onChangeStarting(RecyclerView.ViewHolder item, boolean oldItem) {
+        CountDownLatch latch = mCountDownLatches[oldItem ? CHANGE_OLD_START : CHANGE_NEW_START];
+        if (latch != null) {
+            latch.countDown();
+        }
+    }
+
+    @Override
+    public void onMoveStarting(RecyclerView.ViewHolder item) {
+        CountDownLatch latch = mCountDownLatches[MOVE_START];
+        if (latch != null) {
+            latch.countDown();
+        }
+    }
+
+    @Override
+    public void onAddStarting(RecyclerView.ViewHolder item) {
+        CountDownLatch latch = mCountDownLatches[ADD_START];
+        if (latch != null) {
+            latch.countDown();
+        }
+    }
+
+    @Override
+    public void onRemoveStarting(RecyclerView.ViewHolder item) {
+        CountDownLatch latch = mCountDownLatches[REMOVE_START];
+        if (latch != null) {
+            latch.countDown();
+        }
+    }
+
+    @Override
+    public void onChangeFinished(RecyclerView.ViewHolder item, boolean oldItem) {
+        CountDownLatch latch = mCountDownLatches[oldItem
+                ? CHANGE_OLD_FINISHED : CHANGE_NEW_FINISHED];
+        if (latch != null) {
+            latch.countDown();
+        }
+    }
+
+    @Override
+    public void onMoveFinished(RecyclerView.ViewHolder item) {
+        CountDownLatch latch = mCountDownLatches[MOVE_FINISHED];
+        if (latch != null) {
+            latch.countDown();
+        }
+    }
+
+    @Override
+    public void onAddFinished(RecyclerView.ViewHolder item) {
+        CountDownLatch latch = mCountDownLatches[ADD_FINISHED];
+        if (latch != null) {
+            latch.countDown();
+        }
+    }
+
+    @Override
+    public void onRemoveFinished(RecyclerView.ViewHolder item) {
+        CountDownLatch latch = mCountDownLatches[REMOVE_FINISHED];
+        if (latch != null) {
+            latch.countDown();
+        }
+    }
+
+    @Override
+    public void runPendingAnimations() {
+    }
+
+    @Override
+    public void endAnimation(RecyclerView.ViewHolder item) {
+        if (mAdds.remove(item)) {
+            dispatchAddFinished(item);
+        } else if (mRemoves.remove(item)) {
+            dispatchRemoveFinished(item);
+        } else if (mMoves.remove(item)) {
+            dispatchMoveFinished(item);
+        } else if (mChangesOld.remove(item)) {
+            dispatchChangeFinished(item, true);
+        } else if (mChangesNew.remove(item)) {
+            dispatchChangeFinished(item, false);
+        }
+    }
+
+    @Override
+    public void endAnimations() {
+        for (int i = mAdds.size() - 1; i >= 0; i--) {
+            endAnimation(mAdds.get(i));
+        }
+        for (int i = mRemoves.size() - 1; i >= 0; i--) {
+            endAnimation(mRemoves.get(i));
+        }
+        for (int i = mMoves.size() - 1; i >= 0; i--) {
+            endAnimation(mMoves.get(i));
+        }
+        for (int i = mChangesOld.size() - 1; i >= 0; i--) {
+            endAnimation(mChangesOld.get(i));
+        }
+        for (int i = mChangesNew.size() - 1; i >= 0; i--) {
+            endAnimation(mChangesNew.get(i));
+        }
+    }
+
+    @Override
+    public boolean isRunning() {
+        return mAdds.size() != 0
+                || mRemoves.size() != 0
+                || mMoves.size() != 0
+                || mChangesOld.size() != 0
+                || mChangesNew.size() != 0;
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
index 5d378ff..1a5892d 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
@@ -20,7 +20,6 @@
 import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
@@ -37,20 +36,15 @@
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.PollingCheck;
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v7.util.ImeCleanUpTestRule;
-import android.support.v7.util.TouchUtils;
 import android.test.UiThreadTest;
 import android.util.SparseIntArray;
 import android.util.StateSet;
-import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
 import org.hamcrest.CoreMatchers;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -64,9 +58,6 @@
 @RunWith(AndroidJUnit4.class)
 public class GridLayoutManagerTest extends BaseGridLayoutManagerTest {
 
-    @Rule
-    public final ImeCleanUpTestRule imeCleanUp = new ImeCleanUpTestRule();
-
     @Test
     public void focusSearchFailureUp() throws Throwable {
         focusSearchFailure(false);
@@ -179,100 +170,140 @@
         }
     }
 
+    /**
+     * Tests that the GridLayoutManager retains the focused element after multiple measure
+     * calls to the RecyclerView.  There was a bug where the focused view was lost when the soft
+     * keyboard opened.  This test simulates the measure/layout events triggered by the opening
+     * of the soft keyboard by making two calls to measure.  A simulation was done because using
+     * the soft keyboard in the test caused many issues on API levels 15, 17 and 19.
+     */
     @Test
-    public void editTextVisibility() throws Throwable {
+    public void focusedChildStaysInViewWhenRecyclerViewShrinks() throws Throwable {
+
+        // Arrange.
+
         final int spanCount = 3;
         final int itemCount = 100;
 
-        imeCleanUp.setup(getActivity(), getInstrumentation());
-        RecyclerView recyclerView = new WrappedRecyclerView(getActivity());
-        GridEditTextAdapter editTextAdapter = new GridEditTextAdapter(itemCount) {
-            @Override
-            public TestViewHolder onCreateViewHolder(ViewGroup parent,
-                    int viewType) {
-                TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType);
-                // Good to have colors for debugging
-                StateListDrawable stl = new StateListDrawable();
-                stl.addState(new int[]{android.R.attr.state_focused},
-                        new ColorDrawable(Color.RED));
-                stl.addState(StateSet.WILD_CARD, new ColorDrawable(Color.BLUE));
-                //noinspection deprecation using this for kitkat tests
-                testViewHolder.itemView.setBackgroundDrawable(stl);
-                return testViewHolder;
-            }
-        };
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mActivityRule.getActivity().getWindow().setSoftInputMode(SOFT_INPUT_ADJUST_RESIZE);
-            }
-        });
-
-        recyclerView.setLayoutParams(
-                new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+        final RecyclerView recyclerView = inflateWrappedRV();
+        ViewGroup.LayoutParams lp = recyclerView.getLayoutParams();
+        lp.height = WRAP_CONTENT;
+        lp.width = MATCH_PARENT;
 
         Config config = new Config(spanCount, itemCount);
         mGlm = new WrappedGridLayoutManager(getActivity(), config.mSpanCount, config.mOrientation,
                 config.mReverseLayout);
-        editTextAdapter.assignSpanSizeLookup(mGlm);
-        recyclerView.setAdapter(editTextAdapter);
         recyclerView.setLayoutManager(mGlm);
-        waitForFirstLayout(recyclerView);
 
-        // First focus on the last fully visible EditText located at span index #1.
-        View toFocus = findLastFullyVisibleChild(mRecyclerView);
-        int focusIndex = mRecyclerView.getChildAdapterPosition(toFocus);
-        focusIndex = (focusIndex / spanCount) * spanCount + 1;
-        toFocus = mRecyclerView.findViewHolderForAdapterPosition(focusIndex).itemView;
-        assertTrue(focusIndex >= 1 && focusIndex < itemCount);
+        GridFocusableAdapter gridFocusableAdapter = new GridFocusableAdapter(itemCount);
+        gridFocusableAdapter.assignSpanSizeLookup(mGlm);
+        recyclerView.setAdapter(gridFocusableAdapter);
 
-        final int heightBeforeImeOpen = mRecyclerView.getHeight();
-        TouchUtils.tapView(getInstrumentation(), mRecyclerView, toFocus);
-        getInstrumentation().waitForIdleSync();
-        // Wait for IME to pop up.
-        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+        mGlm.expectLayout(1);
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
-            public boolean canProceed() {
-                return mRecyclerView.getHeight() < heightBeforeImeOpen;
+            public void run() {
+                getActivity().getContainer().addView(recyclerView);
             }
         });
+        mGlm.waitForLayout(3);
+
+        int width = recyclerView.getWidth();
+        int height = recyclerView.getHeight();
+        final int widthMeasureSpec =
+                View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
+        final int fullHeightMeasureSpec =
+                View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);
+        // "MinusOne" so that a measure call will appropriately trigger onMeasure after RecyclerView
+        // was previously laid out with the full height version.
+        final int fullHeightMinusOneMeasureSpec =
+                View.MeasureSpec.makeMeasureSpec(height - 1, View.MeasureSpec.AT_MOST);
+        final int halfHeightMeasureSpec =
+                View.MeasureSpec.makeMeasureSpec(height / 2, View.MeasureSpec.AT_MOST);
+
+        // Act 1.
+
+        // First focus on the last fully visible child located at span index #1.
+        View toFocus = findLastFullyVisibleChild(recyclerView);
+        int focusIndex = recyclerView.getChildAdapterPosition(toFocus);
+        focusIndex = (focusIndex / spanCount) * spanCount + 1;
+        toFocus = recyclerView.findViewHolderForAdapterPosition(focusIndex).itemView;
+        assertTrue(focusIndex >= 1 && focusIndex < itemCount);
+
+        requestFocus(toFocus, false);
+
+        mGlm.expectLayout(1);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                recyclerView.measure(widthMeasureSpec, fullHeightMinusOneMeasureSpec);
+                recyclerView.measure(widthMeasureSpec, halfHeightMeasureSpec);
+                recyclerView.layout(
+                        0,
+                        0,
+                        recyclerView.getMeasuredWidth(),
+                        recyclerView.getMeasuredHeight());
+            }
+        });
+        mGlm.waitForLayout(3);
+
+        // Assert 1.
 
         assertThat("Child at position " + focusIndex + " should be focused",
                 toFocus.hasFocus(), is(true));
         assertTrue("Child view at adapter pos " + focusIndex + " should be fully visible.",
-                isViewPartiallyInBound(mRecyclerView, toFocus));
+                isViewPartiallyInBound(recyclerView, toFocus));
 
-        // Close IME
-        final int heightBeforeImeClose = mRecyclerView.getHeight();
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-        getInstrumentation().waitForIdleSync();
-        // Wait for IME to close
-        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+        // Act 2.
+
+        mGlm.expectLayout(1);
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
-            public boolean canProceed() {
-                return mRecyclerView.getHeight() > heightBeforeImeClose;
+            public void run() {
+                recyclerView.measure(widthMeasureSpec, fullHeightMeasureSpec);
+                recyclerView.layout(
+                        0,
+                        0,
+                        recyclerView.getMeasuredWidth(),
+                        recyclerView.getMeasuredHeight());
             }
         });
+        mGlm.waitForLayout(3);
+
+        // Assert 2.
+
         assertTrue("Child view at adapter pos " + focusIndex + " should be fully visible.",
-                isViewPartiallyInBound(mRecyclerView, toFocus));
+                isViewPartiallyInBound(recyclerView, toFocus));
+
+        // Act 3.
 
         // Now focus on the first fully visible EditText located at the last span index.
-        toFocus = findFirstFullyVisibleChild(mRecyclerView);
-        focusIndex = mRecyclerView.getChildAdapterPosition(toFocus);
+        toFocus = findFirstFullyVisibleChild(recyclerView);
+        focusIndex = recyclerView.getChildAdapterPosition(toFocus);
         focusIndex = (focusIndex / spanCount) * spanCount + (spanCount - 1);
-        toFocus = mRecyclerView.findViewHolderForAdapterPosition(focusIndex).itemView;
-        final int heightBeforeImeOpen2 = mRecyclerView.getHeight();
-        TouchUtils.tapView(getInstrumentation(), mRecyclerView, toFocus);
-        getInstrumentation().waitForIdleSync();
-        // Wait for IME to pop up
-        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+        toFocus = recyclerView.findViewHolderForAdapterPosition(focusIndex).itemView;
+
+        requestFocus(toFocus, false);
+
+        mGlm.expectLayout(1);
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
-            public boolean canProceed() {
-                return mRecyclerView.getHeight() < heightBeforeImeOpen2;
+            public void run() {
+                recyclerView.measure(widthMeasureSpec, fullHeightMinusOneMeasureSpec);
+                recyclerView.measure(widthMeasureSpec, halfHeightMeasureSpec);
+                recyclerView.layout(
+                        0,
+                        0,
+                        recyclerView.getMeasuredWidth(),
+                        recyclerView.getMeasuredHeight());
             }
         });
+        mGlm.waitForLayout(3);
+
+        // Assert 3.
+
         assertTrue("Child view at adapter pos " + focusIndex + " should be fully visible.",
-                isViewPartiallyInBound(mRecyclerView, toFocus));
+                isViewPartiallyInBound(recyclerView, toFocus));
     }
 
     @Test
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
index 4dd0d8f..77585b5 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
@@ -20,7 +20,6 @@
 import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
@@ -37,19 +36,14 @@
 import android.os.Build;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.SdkSuppress;
-import android.support.testutils.PollingCheck;
 import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v7.util.ImeCleanUpTestRule;
-import android.support.v7.util.TouchUtils;
+import android.support.v4.view.ViewCompat;
 import android.util.Log;
 import android.util.StateSet;
-import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
-import android.widget.LinearLayout;
 
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -69,68 +63,76 @@
 @LargeTest
 public class LinearLayoutManagerTest extends BaseLinearLayoutManagerTest {
 
-    @Rule
-    public final ImeCleanUpTestRule imeCleanUp = new ImeCleanUpTestRule();
-
+    /**
+     * Tests that the LinearLayoutManager retains the focused element after multiple measure
+     * calls to the RecyclerView.  There was a bug where the focused view was lost when the soft
+     * keyboard opened.  This test simulates the measure/layout events triggered by the opening
+     * of the soft keyboard by making two calls to measure.  A simulation was done because using
+     * the soft keyboard in the test caused many issues on API levels 15, 17 and 19.
+     */
     @Test
-    public void editTextVisibility() throws Throwable {
+    public void focusedChildStaysInViewWhenRecyclerViewShrinks() throws Throwable {
 
-        // Simulating a scenario where an EditText is tapped (which will receive focus).
-        // The soft keyboard that's opened overlaps the focused EditText which will shrink RV's
-        // padded bounded area. LLM should still lay out the focused EditText so that it becomes
-        // visible above the soft keyboard.
-        // The condition for this test is setting RV's height to a non-exact height, so that measure
-        // is called twice (once with the larger height and another time with smaller height when
-        // the keyboard shows up). To ensure this resizing of RV, SOFT_INPUT_ADJUST_RESIZE is set.
-        imeCleanUp.setup(getActivity(), getInstrumentation());
-        final LinearLayout container = new LinearLayout(getActivity());
-        container.setOrientation(LinearLayout.VERTICAL);
-        container.setLayoutParams(
-                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup
-                        .LayoutParams.MATCH_PARENT));
+        // Arrange.
 
-        final EditTextAdapter editTextAdapter = new EditTextAdapter(50);
-
-        mRecyclerView = inflateWrappedRV();
-        ViewGroup.LayoutParams lp = mRecyclerView.getLayoutParams();
+        final RecyclerView recyclerView = inflateWrappedRV();
+        ViewGroup.LayoutParams lp = recyclerView.getLayoutParams();
         lp.height = WRAP_CONTENT;
         lp.width = MATCH_PARENT;
+        recyclerView.setHasFixedSize(true);
 
-        mRecyclerView.setHasFixedSize(true);
-        mRecyclerView.setAdapter(editTextAdapter);
+        final FocusableAdapter focusableAdapter =
+                new FocusableAdapter(50);
+        recyclerView.setAdapter(focusableAdapter);
+
         mLayoutManager = new WrappedLinearLayoutManager(getActivity(), VERTICAL, false);
-        mRecyclerView.setLayoutManager(mLayoutManager);
-
-        container.addView(mRecyclerView);
+        recyclerView.setLayoutManager(mLayoutManager);
 
         mLayoutManager.expectLayouts(1);
         mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                getActivity().getContainer().addView(container);
+                getActivity().getContainer().addView(recyclerView);
             }
         });
+        mLayoutManager.waitForLayout(3);
+
+        int width = recyclerView.getWidth();
+        int height = recyclerView.getHeight();
+        final int widthMeasureSpec =
+                View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
+        final int fullHeightMeasureSpec =
+                View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);
+        // "MinusOne" so that a measure call will appropriately trigger onMeasure after RecyclerView
+        // was previously laid out with the full height version.
+        final int fullHeightMinusOneMeasureSpec =
+                View.MeasureSpec.makeMeasureSpec(height - 1, View.MeasureSpec.AT_MOST);
+        final int halfHeightMeasureSpec =
+                View.MeasureSpec.makeMeasureSpec(height / 2, View.MeasureSpec.AT_MOST);
+
+        // Act 1.
+
+        View toFocus = findLastFullyVisibleChild(recyclerView);
+        int focusIndex = recyclerView.getChildAdapterPosition(toFocus);
+
+        requestFocus(toFocus, false);
+
+        mLayoutManager.expectLayouts(1);
         mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                mActivityRule.getActivity().getWindow().setSoftInputMode(SOFT_INPUT_ADJUST_RESIZE);
+                recyclerView.measure(widthMeasureSpec, fullHeightMinusOneMeasureSpec);
+                recyclerView.measure(widthMeasureSpec, halfHeightMeasureSpec);
+                recyclerView.layout(
+                        0,
+                        0,
+                        recyclerView.getMeasuredWidth(),
+                        recyclerView.getMeasuredHeight());
             }
         });
+        mLayoutManager.waitForLayout(3);
 
-        // First focus on the last fully visible EditText.
-        View toFocus = findLastFullyVisibleChild(mRecyclerView);
-        int focusIndex = mRecyclerView.getChildAdapterPosition(toFocus);
-
-        final int heightBeforeImeOpen = mRecyclerView.getHeight();
-        TouchUtils.tapView(getInstrumentation(), mRecyclerView, toFocus);
-        getInstrumentation().waitForIdleSync();
-       // Wait for IME to pop up.
-        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
-            @Override
-            public boolean canProceed() {
-                return mRecyclerView.getHeight() < heightBeforeImeOpen;
-            }
-        });
+        // Verify 1.
 
         assertThat("Child at position " + focusIndex + " should be focused",
                 toFocus.hasFocus(), is(true));
@@ -138,40 +140,59 @@
         // requestRectangleOnScreen (inside bringPointIntoView) for the focused view with a rect
         // containing the content area. This rect is guaranteed to be fully visible whereas a
         // portion of TextView could be out of bounds.
-        assertTrue("Child view at adapter pos " + focusIndex + " should be fully visible.",
-                isViewPartiallyInBound(mRecyclerView, toFocus));
+        assertThat("Child view at adapter pos " + focusIndex + " should be fully visible.",
+                isViewPartiallyInBound(recyclerView, toFocus), is(true));
 
-        // Close IME
-        final int heightBeforeImeClose = mRecyclerView.getHeight();
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
-        getInstrumentation().waitForIdleSync();
-        // Wait for IME to close
-        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+        // Act 2.
+
+        mLayoutManager.expectLayouts(1);
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
-            public boolean canProceed() {
-                return mRecyclerView.getHeight() > heightBeforeImeClose;
+            public void run() {
+                recyclerView.measure(widthMeasureSpec, fullHeightMeasureSpec);
+                recyclerView.layout(
+                        0,
+                        0,
+                        recyclerView.getMeasuredWidth(),
+                        recyclerView.getMeasuredHeight());
             }
         });
+        mLayoutManager.waitForLayout(3);
+
+        // Verify 2.
+
         assertThat("Child at position " + focusIndex + " should be focused",
                 toFocus.hasFocus(), is(true));
         assertTrue("Child view at adapter pos " + focusIndex + " should be fully visible.",
-                isViewPartiallyInBound(mRecyclerView, toFocus));
+                isViewPartiallyInBound(recyclerView, toFocus));
+
+        // Act 3.
 
         // Now focus on the first fully visible EditText.
-        toFocus = findFirstFullyVisibleChild(mRecyclerView);
-        focusIndex = mRecyclerView.getChildAdapterPosition(toFocus);
-        final int heightBeforeImeOpen2 = mRecyclerView.getHeight();
-        TouchUtils.tapView(getInstrumentation(), mRecyclerView, toFocus);
-        getInstrumentation().waitForIdleSync();
-        // Wait for IME to pop up
-        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+        toFocus = findFirstFullyVisibleChild(recyclerView);
+        focusIndex = recyclerView.getChildAdapterPosition(toFocus);
+
+        requestFocus(toFocus, false);
+
+        mLayoutManager.expectLayouts(1);
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
-            public boolean canProceed() {
-                return mRecyclerView.getHeight() < heightBeforeImeOpen2;
+            public void run() {
+                recyclerView.measure(widthMeasureSpec, fullHeightMinusOneMeasureSpec);
+                recyclerView.measure(widthMeasureSpec, halfHeightMeasureSpec);
+                recyclerView.layout(
+                        0,
+                        0,
+                        recyclerView.getMeasuredWidth(),
+                        recyclerView.getMeasuredHeight());
             }
         });
+        mLayoutManager.waitForLayout(3);
+
+        // Assert 3.
+
         assertTrue("Child view at adapter pos " + focusIndex + " should be fully visible.",
-                isViewPartiallyInBound(mRecyclerView, toFocus));
+                isViewPartiallyInBound(recyclerView, toFocus));
     }
 
     @Test
@@ -592,7 +613,7 @@
                     @Override
                     public void onFocusChange(View v, boolean hasFocus) {
                         assertNull("Focus just got cleared and no children should be holding"
-                                        + " focus now.", mRecyclerView.getFocusedChild());
+                                + " focus now.", mRecyclerView.getFocusedChild());
                         try {
                             // Calling focusSearch should be a no-op here since even though there
                             // are unfocusable views down to scroll to, none of RV's children hold
@@ -771,6 +792,118 @@
                 mRecyclerView.getChildCount() <= childCount + 3 /*1 for removed view, 2 for its size*/);
     }
 
+    void waitOneCycle() throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+            }
+        });
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
+    @Test
+    public void hiddenNoneRemoveViewAccessibility() throws Throwable {
+        final Config config = new Config();
+        int adapterSize = 1000;
+        final boolean[] firstItemSpecialSize = new boolean[] {false};
+        TestAdapter adapter = new TestAdapter(adapterSize) {
+            @Override
+            public void onBindViewHolder(TestViewHolder holder,
+                    int position) {
+                super.onBindViewHolder(holder, position);
+                ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
+                if (!(lp instanceof ViewGroup.MarginLayoutParams)) {
+                    lp = new ViewGroup.MarginLayoutParams(0, 0);
+                    holder.itemView.setLayoutParams(lp);
+                }
+                ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+                final int maxSize;
+                if (config.mOrientation == HORIZONTAL) {
+                    maxSize = mRecyclerView.getWidth();
+                    mlp.height = ViewGroup.MarginLayoutParams.MATCH_PARENT;
+                } else {
+                    maxSize = mRecyclerView.getHeight();
+                    mlp.width = ViewGroup.MarginLayoutParams.MATCH_PARENT;
+                }
+
+                final int desiredSize;
+                if (position == 0 && firstItemSpecialSize[0]) {
+                    desiredSize = maxSize / 3;
+                } else {
+                    desiredSize = maxSize / 8;
+                }
+                if (config.mOrientation == HORIZONTAL) {
+                    mlp.width = desiredSize;
+                } else {
+                    mlp.height = desiredSize;
+                }
+            }
+
+            @Override
+            public void onBindViewHolder(TestViewHolder holder,
+                    int position, List<Object> payloads) {
+                onBindViewHolder(holder, position);
+            }
+        };
+        adapter.setHasStableIds(false);
+        config.adapter(adapter);
+        setupByConfig(config, true);
+        final DummyItemAnimator itemAnimator = new DummyItemAnimator();
+        mRecyclerView.setItemAnimator(itemAnimator);
+
+        // push last item out by increasing first item's size
+        final int childBeingPushOut = mLayoutManager.getChildCount() - 1;
+        RecyclerView.ViewHolder itemViewHolder = mRecyclerView
+                .findViewHolderForAdapterPosition(childBeingPushOut);
+        final int originalAccessibility = ViewCompat.getImportantForAccessibility(
+                itemViewHolder.itemView);
+        assertTrue(ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO == originalAccessibility
+                || ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES == originalAccessibility);
+
+        itemAnimator.expect(DummyItemAnimator.MOVE_START, 1);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                firstItemSpecialSize[0] = true;
+                mTestAdapter.notifyItemChanged(0, "XXX");
+            }
+        });
+        // wait till itemAnimator starts which will block itemView's accessibility
+        itemAnimator.waitFor(DummyItemAnimator.MOVE_START);
+        // RV Changes accessiblity after onMoveStart, so wait one more cycle.
+        waitOneCycle();
+        assertTrue(itemAnimator.getMovesAnimations().contains(itemViewHolder));
+        assertEquals(ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS,
+                ViewCompat.getImportantForAccessibility(itemViewHolder.itemView));
+
+        // notify Change again to run predictive animation.
+        mLayoutManager.expectLayouts(2);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mTestAdapter.notifyItemChanged(0, "XXX");
+            }
+        });
+        mLayoutManager.waitForLayout(1);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                itemAnimator.endAnimations();
+            }
+        });
+        // scroll to the view being pushed out, it should get same view from cache as the item
+        // in adapter does not change.
+        smoothScrollToPosition(childBeingPushOut);
+        RecyclerView.ViewHolder itemViewHolder2 = mRecyclerView
+                .findViewHolderForAdapterPosition(childBeingPushOut);
+        assertSame(itemViewHolder, itemViewHolder2);
+        // the important for accessibility should be reset to YES/AUTO:
+        final int newAccessibility = ViewCompat.getImportantForAccessibility(
+                itemViewHolder.itemView);
+        assertTrue(ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO == newAccessibility
+                || ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES == newAccessibility);
+    }
+
     @Test
     public void keepFocusOnRelayout() throws Throwable {
         setupByConfig(new Config(VERTICAL, false, false).itemCount(500), true);
diff --git a/wear/Android.mk b/wear/Android.mk
index 0ea7890..2be9bfa 100644
--- a/wear/Android.mk
+++ b/wear/Android.mk
@@ -42,7 +42,8 @@
     android-support-core-ui \
     android-support-percent \
     android-support-v7-recyclerview \
-    android-support-v4
+    android-support-v4 \
+    android-support-constraint-layout
 LOCAL_STATIC_JAVA_LIBRARIES := \
     prebuilt-com.google.android.wearable-stubs
 LOCAL_JAR_EXCLUDE_FILES := none
diff --git a/wear/build.gradle b/wear/build.gradle
index 55320b9..4937619 100644
--- a/wear/build.gradle
+++ b/wear/build.gradle
@@ -13,12 +13,12 @@
     api(project(":percent"))
     api(project(":recyclerview-v7"))
 
-    androidTestImplementation(TEST_RUNNER, libs.exclude_annotations)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_annotations)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
 
-    provided fileTree(dir: 'wear_stubs', include: ['com.google.android.wearable-stubs.jar'])
+    compileOnly fileTree(dir: 'wear_stubs', include: ['com.google.android.wearable-stubs.jar'])
 }
 
 android {