Merge "[WebView Support Library] Add initial WebViewClientCompat" into pi-dev
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index c42446b..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,77 +0,0 @@
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Disable the frameworks support for PDK builds instead
-# use prebuilts/sdk/current/
-ifneq ($(TARGET_BUILD_PDK),true)
-# Don't include in unbundled build.
-ifeq ($(TARGET_BUILD_APPS),)
-
-SUPPORT_CURRENT_SDK_VERSION := current
-
-###########################################################
-# Find all of the files in the given subdirs that match the
-# specified pattern but do not match another pattern. This
-# function uses $(1) instead of LOCAL_PATH as the base.
-# $(1): the base dir, relative to the root of the source tree.
-# $(2): the file name pattern to match.
-# $(3): the file name pattern to exclude.
-# $(4): a list of subdirs of the base dir.
-# Returns: a list of paths relative to the base dir.
-###########################################################
-
-define find-files-in-subdirs-exclude
-$(sort $(patsubst ./%,%, \
-  $(shell cd $(1) ; \
-          find -L $(4) -name $(2) -and -not -name $(3) -and -not -name ".*") \
- ))
-endef
-
-###########################################################
-## Find all of the files under the named directories where
-## the file name matches the specified pattern but does not
-## match another pattern. Meant to be used like:
-##    SRC_FILES := $(call all-named-files-under,.*\.h,src tests)
-###########################################################
-
-define all-named-files-under-exclude
-$(call find-files-in-subdirs-exclude,$(LOCAL_PATH),"$(1)","$(2)",$(3))
-endef
-
-###########################################################
-## Find all of the files under the current directory where
-## the file name matches the specified pattern but does not
-## match another pattern.
-###########################################################
-
-define all-subdir-named-files-exclude
-$(call all-named-files-under-exclude,$(1),$(2),.)
-endef
-
-# Pre-process support library AIDLs
-aidl_files := $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files-exclude,*.aidl,I*.aidl))
-support-aidl := $(TARGET_OUT_COMMON_INTERMEDIATES)/support.aidl
-$(support-aidl): $(aidl_files) | $(AIDL)
-	$(AIDL) --preprocess $@ $(aidl_files)
-
-# Build all support libraries
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
-# Clear out variables
-SUPPORT_CURRENT_SDK_VERSION :=
-
-endif
-endif
diff --git a/CleanSpec.mk b/CleanSpec.mk
deleted file mode 100644
index 7dc4050..0000000
--- a/CleanSpec.mk
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# If you don't need to do a full clean build but would like to touch
-# a file or delete some intermediate files, add a clean step to the end
-# of the list.  These steps will only be run once, if they haven't been
-# run before.
-#
-# E.g.:
-#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
-#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
-#
-# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
-# files that are missing or have been moved.
-#
-# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
-# Use $(OUT_DIR) to refer to the "out" directory.
-#
-# If you need to re-do something that's already mentioned, just copy
-# the command and add it to the bottom of the list.  E.g., if a change
-# that you made last week required touching a file and a change you
-# made today requires touching the same file, just copy the old
-# touch step and add it to the end of the list.
-#
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
-
-# For example:
-#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
-#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
-#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
-#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
-
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android-support-v*)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/support.aidl)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android-support-customtabs_intermediates/src/)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android-support-annotations_intermediates)
-
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
diff --git a/adding-support-library-as-included-build.md b/adding-support-library-as-included-build.md
index 136929f..b77233e 100644
--- a/adding-support-library-as-included-build.md
+++ b/adding-support-library-as-included-build.md
@@ -1,5 +1,8 @@
 # Adding the Support Library Build Within Another Build
 
+Sorry, this doesn't seem to be working at the moment.
+For now, run `./gradlew createArchive` and copy the output to where your project can use it, as described fuller in go/support-dev
+
 Would you like to make a change in Support Library and have it be propagated to
 your downstream Gradle build (generally an app) without having to separately
 build Support Library and then build your application?
diff --git a/app-toolkit/init.gradle b/app-toolkit/init.gradle
index 3c58368..6a91a4e 100644
--- a/app-toolkit/init.gradle
+++ b/app-toolkit/init.gradle
@@ -44,7 +44,8 @@
 buildServerAnchorTask.dependsOn createDiffArchive
 buildServerAnchorTask.dependsOn createArchive
 rootProject.tasks.whenTaskAdded { task ->
-    if (CheckExternalDependencyLicensesTask.ROOT_TASK_NAME.equals(task.name)) {
+    if (CheckExternalDependencyLicensesTask.ROOT_TASK_NAME.equals(task.name)
+            || "dejetifyArchive".equals(task.name)) {
         buildServerAnchorTask.dependsOn task
     }
 }
diff --git a/app-toolkit/settings.gradle b/app-toolkit/settings.gradle
index 3cd11cc..4a9a91c 100644
--- a/app-toolkit/settings.gradle
+++ b/app-toolkit/settings.gradle
@@ -79,6 +79,7 @@
 includeProject(":sqlite:sqlite-ktx", new File(supportRoot, "persistence/db/ktx"))
 
 includeProject(":jetifier-core", new File(supportRoot, "jetifier/jetifier/core"))
+includeProject(":jetifier-processor", new File(supportRoot, "jetifier/jetifier/processor"))
 includeProject(":jetifier-gradle-plugin", new File(supportRoot, "jetifier/jetifier/gradle-plugin"))
 includeProject(":jetifier-standalone", new File(supportRoot, "jetifier/jetifier/standalone"))
 includeProject(":jetifier-preprocessor", new File(supportRoot, "jetifier/jetifier/preprocessor"))
diff --git a/build.gradle b/build.gradle
index c731c92..88257c7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -34,7 +34,6 @@
 
     dependencies {
         classpath build_libs.gradle
-        classpath build_libs.lint
         classpath build_libs.jacoco
         classpath build_libs.kotlin.gradle_plugin
     }
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 537f042..88a8aa8 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -17,7 +17,6 @@
 
     dependencies {
         classpath build_libs.kotlin.gradle_plugin
-        classpath build_libs.lint
     }
 
     configurations.classpath.resolutionStrategy {
@@ -43,7 +42,6 @@
 
 dependencies {
     compile build_libs.gradle
-    compile build_libs.lint
     compile build_libs.jacoco
     compile build_libs.error_prone_gradle
     compile build_libs.jarjar_gradle
diff --git a/buildSrc/build_dependencies.gradle b/buildSrc/build_dependencies.gradle
index 4e23e62..0b292f3 100644
--- a/buildSrc/build_dependencies.gradle
+++ b/buildSrc/build_dependencies.gradle
@@ -29,15 +29,7 @@
     build_libs.gradle = 'com.android.tools.build:gradle:' + androidPluginVersionOverride
 } else {
     // Keep gradle plugin version in sync with ub_supportlib-master manifest.
-    build_libs.gradle = 'com.android.tools.build:gradle:3.0.0'
-}
-
-// lint
-def lintPluginVersionOverride = System.getenv("LINT_PLUGIN_VERSION")
-if (lintPluginVersionOverride != null) {
-  build_libs.lint = 'com.android.tools.lint:lint:' + lintPluginVersionOverride
-} else {
-  build_libs.lint = 'com.android.tools.lint:lint:26.0.0'
+    build_libs.gradle = 'com.android.tools.build:gradle:3.2.0-alpha09'
 }
 
 // jarjar plugin
diff --git a/buildSrc/init.gradle b/buildSrc/init.gradle
index df9fc85..046376f 100644
--- a/buildSrc/init.gradle
+++ b/buildSrc/init.gradle
@@ -161,6 +161,7 @@
     rootProject.tasks.whenTaskAdded { task ->
         if ("createArchive".equals(task.name)
                 || "distDocs".equals(task.name)
+                || "dejetifyArchive".equals(task.name)
                 || CheckExternalDependencyLicensesTask.ROOT_TASK_NAME.equals(task.name)) {
             buildOnServerTask.dependsOn task
         }
diff --git a/buildSrc/jetify.gradle b/buildSrc/jetify.gradle
index e81d325..c991385 100644
--- a/buildSrc/jetify.gradle
+++ b/buildSrc/jetify.gradle
@@ -25,7 +25,7 @@
     dependsOn ':jetifier-standalone:installDist'
     inputs.file project.tasks['createArchive'].archivePath
 
-    outputs.file "${rootProject.ext.distDir}/top-of-tree-m2-repository-dejetified-${project.ext.buildNumber}.zip"
+    outputs.file "${rootProject.ext.distDir}/top-of-tree-m2repository-dejetified-${project.ext.buildNumber}.zip"
 
     commandLine ("${jetifierBin}", "-s", "-r", "-outputfile", "${outputs.files.singleFile}",  "-i", "${inputs.files.singleFile}", "-l", "error", "-rebuildTopOfTree")
 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt b/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
index 10c2566..f9c82cf 100644
--- a/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
@@ -27,6 +27,7 @@
     val compilerArgs = this.options.compilerArgs
     compilerArgs += listOf(
             "-XDcompilePolicy=simple", // Workaround for b/36098770
+            "-XepExcludedPaths:.*/(build/generated|external)/.*",
 
             // Enforce the following checks.
             "-Xep:RestrictTo:OFF",
@@ -41,6 +42,7 @@
             "-Xep:IntLongMath:ERROR",
             "-Xep:MissingFail:ERROR",
             "-Xep:JavaLangClash:ERROR",
+            "-Xep:PrivateConstructorForUtilityClass:ERROR",
 
             // Nullaway
             "-XepIgnoreUnknownCheckNames", // https://github.com/uber/NullAway/issues/25
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
index 4a791cc..9ba3c38 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
@@ -70,5 +70,5 @@
     const val ARCH_CORE = "androidx.arch.core"
     const val PAGING = "androidx.paging"
     const val NAVIGATION = "androidx.navigation"
-    const val JETIFIER = "com.android.support.jetifier"
+    const val JETIFIER = "com.android.tools.build.jetifier"
 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index e2993e9..37869e4 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -23,17 +23,17 @@
     /**
      * Version code of the support library components.
      */
-    val SUPPORT_LIBRARY = Version("1.0.0-SNAPSHOT")
+    val SUPPORT_LIBRARY = Version("1.0.0-alpha1")
 
     /**
      * Version code for Room
      */
-    val ROOM = Version("2.0.0-SNAPSHOT")
+    val ROOM = Version("2.0.0-alpha1")
 
     /**
      * Version code for Lifecycle extensions (ProcessLifecycleOwner, Fragment support)
      */
-    val LIFECYCLES_EXT = Version("2.0.0-SNAPSHOT")
+    val LIFECYCLES_EXT = Version("2.0.0-alpha1")
 
     /**
      * Version code for Lifecycle LiveData
@@ -48,9 +48,9 @@
     /**
      * Version code for Paging
      */
-    val PAGING = Version("2.0.0-SNAPSHOT")
+    val PAGING = Version("2.0.0-alpha1")
 
-    private val LIFECYCLES = Version("2.0.0-SNAPSHOT")
+    private val LIFECYCLES = Version("2.0.0-alpha1")
 
     /**
      * Version code for Lifecycle libs that are required by the support library
@@ -65,7 +65,7 @@
     /**
      * Version code for shared code of flatfoot
      */
-    val ARCH_CORE = Version("2.0.0-SNAPSHOT")
+    val ARCH_CORE = Version("2.0.0-alpha1")
 
     /**
      * Version code for shared code of flatfoot runtime
@@ -80,5 +80,5 @@
     /**
      * Version code for Jetifier
      */
-    val JETIFIER = Version("0.2.0")
+    val JETIFIER = Version("0.0.1")
 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/SupportAndroidLibraryPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/SupportAndroidLibraryPlugin.kt
index d65dd5e..dd2e6da 100644
--- a/buildSrc/src/main/kotlin/androidx/build/SupportAndroidLibraryPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SupportAndroidLibraryPlugin.kt
@@ -160,11 +160,8 @@
     lintOptions.isNoLines = false
     lintOptions.isQuiet = true
 
-    //lintOptions.fatal("NewApi")
+    lintOptions.fatal("NewApi")
     lintOptions.fatal("ObsoleteSdkInt")
-    lintOptions.disable("NewApi")
-    lintOptions.disable("MissingPermission")
-    lintOptions.disable("ResourceType")
 
     if (verifyTranslations) {
         lintOptions.fatal("MissingTranslation")
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 9cd1a93..606ac30 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -55,7 +55,7 @@
 const val TEST_RULES_TMP = "com.android.temp.support.test:rules:1.0.1"
 
 // AndroidX libraries
-private const val SUPPORT_VERSION = "1.0.0-SNAPSHOT"
+private const val SUPPORT_VERSION = "1.0.0-alpha1"
 const val SUPPORT_ANNOTATIONS = "androidx.annotation:annotation:$SUPPORT_VERSION"
 const val SUPPORT_APPCOMPAT = "androidx.appcompat:appcompat:$SUPPORT_VERSION"
 const val SUPPORT_CARDVIEW = "androidx.cardview:cardview:$SUPPORT_VERSION"
@@ -66,7 +66,8 @@
 const val SUPPORT_V4 = "androidx.legacy:legacy-support-v4:$SUPPORT_VERSION"
 
 // Arch libraries
-const val ARCH_LIFECYCLE_RUNTIME = "androidx.lifecycle:lifecycle-runtime:2.0.0-SNAPSHOT@aar"
-const val ARCH_LIFECYCLE_LIVEDATA_CORE = "androidx.lifecycle:lifecycle-livedata-core:2.0.0-SNAPSHOT@aar"
-const val ARCH_LIFECYCLE_VIEWMODEL = "androidx.lifecycle:lifecycle-viewmodel:2.0.0-SNAPSHOT@aar"
-const val ARCH_LIFECYCLE_EXTENSIONS = "androidx.lifecycle:lifecycle-extensions:2.0.0-SNAPSHOT@aar"
+const val ARCH_LIFECYCLE_RUNTIME = "androidx.lifecycle:lifecycle-runtime:2.0.0-alpha1@aar"
+const val ARCH_LIFECYCLE_LIVEDATA_CORE =
+        "androidx.lifecycle:lifecycle-livedata-core:2.0.0-alpha1@aar"
+const val ARCH_LIFECYCLE_VIEWMODEL = "androidx.lifecycle:lifecycle-viewmodel:2.0.0-alpha1@aar"
+const val ARCH_LIFECYCLE_EXTENSIONS = "androidx.lifecycle:lifecycle-extensions:2.0.0-alpha1@aar"
diff --git a/car/api/current.txt b/car/api/current.txt
new file mode 100644
index 0000000..0aee549
--- /dev/null
+++ b/car/api/current.txt
@@ -0,0 +1,462 @@
+package androidx.car.app {
+
+  public class CarAlertDialog extends android.app.Dialog {
+  }
+
+  public static final class CarAlertDialog.Builder {
+    ctor public CarAlertDialog.Builder(android.content.Context);
+    method public androidx.car.app.CarAlertDialog create();
+    method public androidx.car.app.CarAlertDialog.Builder setBody(int);
+    method public androidx.car.app.CarAlertDialog.Builder setBody(java.lang.CharSequence);
+    method public androidx.car.app.CarAlertDialog.Builder setCancelable(boolean);
+    method public androidx.car.app.CarAlertDialog.Builder setNegativeButton(int, android.content.DialogInterface.OnClickListener);
+    method public androidx.car.app.CarAlertDialog.Builder setNegativeButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public androidx.car.app.CarAlertDialog.Builder setOnCancelListener(android.content.DialogInterface.OnCancelListener);
+    method public androidx.car.app.CarAlertDialog.Builder setOnDismissListener(android.content.DialogInterface.OnDismissListener);
+    method public androidx.car.app.CarAlertDialog.Builder setPositiveButton(int, android.content.DialogInterface.OnClickListener);
+    method public androidx.car.app.CarAlertDialog.Builder setPositiveButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+    method public androidx.car.app.CarAlertDialog.Builder setTitle(int);
+    method public androidx.car.app.CarAlertDialog.Builder setTitle(java.lang.CharSequence);
+  }
+
+  public class CarListDialog extends android.app.Dialog {
+  }
+
+  public static final class CarListDialog.Builder {
+    ctor public CarListDialog.Builder(android.content.Context);
+    method public androidx.car.app.CarListDialog create();
+    method public androidx.car.app.CarListDialog.Builder setCancelable(boolean);
+    method public androidx.car.app.CarListDialog.Builder setInitialPosition(int);
+    method public androidx.car.app.CarListDialog.Builder setItems(java.lang.String[], android.content.DialogInterface.OnClickListener);
+    method public androidx.car.app.CarListDialog.Builder setOnCancelListener(android.content.DialogInterface.OnCancelListener);
+    method public androidx.car.app.CarListDialog.Builder setOnDismissListener(android.content.DialogInterface.OnDismissListener);
+  }
+
+}
+
+package androidx.car.drawer {
+
+  public class CarDrawerActivity extends androidx.appcompat.app.AppCompatActivity {
+    ctor public CarDrawerActivity();
+    method protected int getContentContainerId();
+    method protected androidx.car.drawer.CarDrawerController getDrawerController();
+    method protected deprecated androidx.car.drawer.CarDrawerAdapter getRootAdapter();
+    method public void setMainContent(android.view.View);
+    method public void setMainContent(int);
+    method public void setToolbarAlwaysShow();
+    method public void setToolbarClickThrough(boolean);
+    method public void setToolbarCollapsible();
+    method public void setToolbarElevation(float);
+    method public void setToolbarElevationWithAnimation(float);
+  }
+
+  public abstract class CarDrawerAdapter extends androidx.recyclerview.widget.RecyclerView.Adapter implements androidx.car.drawer.DrawerItemClickListener androidx.car.widget.PagedListView.ItemCap {
+    ctor protected CarDrawerAdapter(android.content.Context, boolean);
+    method public void cleanup();
+    method protected abstract int getActualItemCount();
+    method public final int getItemCount();
+    method public final int getItemViewType(int);
+    method public final void onBindViewHolder(androidx.car.drawer.DrawerItemViewHolder, int);
+    method public final androidx.car.drawer.DrawerItemViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method protected abstract void populateViewHolder(androidx.car.drawer.DrawerItemViewHolder, int);
+    method public final void setMaxItems(int);
+    method public final void setTitle(java.lang.CharSequence);
+    method public void start();
+    method public void stop();
+    method protected boolean usesSmallLayout(int);
+  }
+
+  public class CarDrawerController {
+    ctor public CarDrawerController(androidx.appcompat.widget.Toolbar, androidx.drawerlayout.widget.DrawerLayout, androidx.appcompat.app.ActionBarDrawerToggle);
+    method public void addDrawerListener(androidx.drawerlayout.widget.DrawerLayout.DrawerListener);
+    method public void closeDrawer();
+    method public void onConfigurationChanged(android.content.res.Configuration);
+    method public boolean onOptionsItemSelected(android.view.MenuItem);
+    method public void openDrawer();
+    method public final void pushAdapter(androidx.car.drawer.CarDrawerAdapter);
+    method public void removeDrawerListener(androidx.drawerlayout.widget.DrawerLayout.DrawerListener);
+    method public void scrollToPosition(int);
+    method public void setRootAdapter(androidx.car.drawer.CarDrawerAdapter);
+    method public void showLoadingProgressBar(boolean);
+    method public void syncState();
+  }
+
+  public abstract interface DrawerItemClickListener {
+    method public abstract void onItemClick(int);
+  }
+
+  public class DrawerItemViewHolder extends androidx.recyclerview.widget.RecyclerView.ViewHolder {
+    method public android.widget.ImageView getEndIcon();
+    method public android.widget.ImageView getIcon();
+    method public android.widget.TextView getText();
+    method public android.widget.TextView getTitle();
+  }
+
+}
+
+package androidx.car.moderator {
+
+  public class ContentRateLimiter {
+    ctor public ContentRateLimiter(float, float, long);
+    method public float getAvailablePermits();
+    method public float getMaxStoredPermits();
+    method public void setAvailablePermits(float);
+    method public void setUnlimitedMode(boolean);
+    method public boolean tryAcquire();
+    method public boolean tryAcquire(int);
+  }
+
+  public class SpeedBumpView extends android.widget.FrameLayout {
+    ctor public SpeedBumpView(android.content.Context);
+    ctor public SpeedBumpView(android.content.Context, android.util.AttributeSet);
+    ctor public SpeedBumpView(android.content.Context, android.util.AttributeSet, int);
+    ctor public SpeedBumpView(android.content.Context, android.util.AttributeSet, int, int);
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+  }
+
+}
+
+package androidx.car.utils {
+
+  public class ColumnCalculator {
+    method public int getColumnWidth();
+    method public int getGutterSize();
+    method public static androidx.car.utils.ColumnCalculator getInstance(android.content.Context);
+    method public int getNumOfColumns();
+    method public int getNumOfGutters();
+    method public int getSizeForColumnSpan(int);
+  }
+
+  public class ListItemBackgroundResolver {
+    method public static void setBackground(android.view.View, int, int);
+  }
+
+}
+
+package androidx.car.widget {
+
+  public class ActionBar extends android.widget.RelativeLayout {
+    ctor public ActionBar(android.content.Context);
+    ctor public ActionBar(android.content.Context, android.util.AttributeSet);
+    ctor public ActionBar(android.content.Context, android.util.AttributeSet, int);
+    ctor public ActionBar(android.content.Context, android.util.AttributeSet, int, int);
+    method public void setExpandCollapseView(android.view.View);
+    method public void setView(android.view.View, int);
+    method public void setViews(android.view.View[]);
+    field public static final int SLOT_EXPAND_COLLAPSE = 3; // 0x3
+    field public static final int SLOT_LEFT = 1; // 0x1
+    field public static final int SLOT_MAIN = 0; // 0x0
+    field public static final int SLOT_RIGHT = 2; // 0x2
+  }
+
+  public static abstract class ActionBar.SlotPosition implements java.lang.annotation.Annotation {
+  }
+
+  public class AlphaJumpBucketer {
+    ctor public AlphaJumpBucketer();
+    ctor public AlphaJumpBucketer(androidx.car.widget.AlphaJumpBucketer.Bucket[]);
+    method public java.util.Collection<androidx.car.widget.IAlphaJumpAdapter.Bucket> createBuckets(java.lang.String[]);
+    method public java.util.Collection<androidx.car.widget.IAlphaJumpAdapter.Bucket> createBuckets(java.lang.Iterable<java.lang.String>);
+    method public java.util.Collection<androidx.car.widget.IAlphaJumpAdapter.Bucket> createBuckets(java.util.Iterator<java.lang.String>);
+  }
+
+  public static class AlphaJumpBucketer.Bucket implements androidx.car.widget.IAlphaJumpAdapter.Bucket {
+    method public int getIndex();
+    method public java.lang.CharSequence getLabel();
+    method public boolean isEmpty();
+  }
+
+  public class AlphaJumpOverlayView extends androidx.gridlayout.widget.GridLayout {
+    ctor public AlphaJumpOverlayView(android.content.Context);
+  }
+
+  public class ClickThroughToolbar extends androidx.appcompat.widget.Toolbar {
+    ctor public ClickThroughToolbar(android.content.Context);
+    ctor public ClickThroughToolbar(android.content.Context, android.util.AttributeSet);
+    ctor public ClickThroughToolbar(android.content.Context, android.util.AttributeSet, int);
+    method public void setClickPassThrough(boolean);
+  }
+
+  public final class ColumnCardView extends androidx.cardview.widget.CardView {
+    ctor public ColumnCardView(android.content.Context);
+    ctor public ColumnCardView(android.content.Context, android.util.AttributeSet);
+    ctor public ColumnCardView(android.content.Context, android.util.AttributeSet, int);
+    method public int getColumnSpan();
+    method public void onMeasure(int, int);
+    method public void setColumnSpan(int);
+  }
+
+  public abstract class DayNightStyle implements java.lang.annotation.Annotation {
+    field public static final int AUTO = 0; // 0x0
+    field public static final int AUTO_INVERSE = 1; // 0x1
+    field public static final int FORCE_DAY = 3; // 0x3
+    field public static final int FORCE_NIGHT = 2; // 0x2
+  }
+
+  public abstract interface IAlphaJumpAdapter {
+    method public abstract java.util.Collection<androidx.car.widget.IAlphaJumpAdapter.Bucket> getAlphaJumpBuckets();
+    method public abstract void onAlphaJumpEnter();
+    method public abstract void onAlphaJumpLeave(androidx.car.widget.IAlphaJumpAdapter.Bucket);
+  }
+
+  public static abstract interface IAlphaJumpAdapter.Bucket {
+    method public abstract int getIndex();
+    method public abstract java.lang.CharSequence getLabel();
+    method public abstract boolean isEmpty();
+  }
+
+  public abstract class ListItem<VH extends androidx.car.widget.ListItem.ViewHolder> {
+    ctor public ListItem();
+    method public final void addViewBinder(androidx.car.widget.ListItem.ViewBinder<VH>);
+    method public final void addViewBinder(androidx.car.widget.ListItem.ViewBinder<VH>, androidx.car.widget.ListItem.ViewBinder<VH>);
+    method public abstract int getViewType();
+    method protected boolean isDirty();
+    method protected void markClean();
+    method protected void markDirty();
+    method protected abstract void onBind(VH);
+    method public boolean removeViewBinder(androidx.car.widget.ListItem.ViewBinder<VH>);
+    method protected abstract void resolveDirtyState();
+    method public void setHideDivider(boolean);
+    method public boolean shouldHideDivider();
+  }
+
+  public static abstract interface ListItem.ViewBinder<VH> {
+    method public abstract void bind(VH);
+  }
+
+  public static abstract class ListItem.ViewHolder extends androidx.recyclerview.widget.RecyclerView.ViewHolder {
+    ctor public ListItem.ViewHolder(android.view.View);
+    method public final void addCleanUp(androidx.car.widget.ListItem.ViewBinder<androidx.car.widget.ListItem.ViewHolder>);
+    method public final void cleanUp();
+  }
+
+  public class ListItemAdapter extends androidx.recyclerview.widget.RecyclerView.Adapter implements androidx.car.widget.PagedListView.DividerVisibilityManager androidx.car.widget.PagedListView.ItemCap {
+    ctor public ListItemAdapter(android.content.Context, androidx.car.widget.ListItemProvider);
+    ctor public ListItemAdapter(android.content.Context, androidx.car.widget.ListItemProvider, int);
+    method public int getItemCount();
+    method public void onBindViewHolder(androidx.car.widget.ListItem.ViewHolder, int);
+    method public androidx.car.widget.ListItem.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public void registerListItemViewType(int, int, java.util.function.Function<android.view.View, androidx.car.widget.ListItem.ViewHolder>);
+    method public void setMaxItems(int);
+    method public boolean shouldHideDivider(int);
+    method public void start();
+    method public void stop();
+  }
+
+  public static final class ListItemAdapter.BackgroundStyle {
+    field public static final int CARD = 2; // 0x2
+    field public static final int NONE = 1; // 0x1
+    field public static final int PANEL = 3; // 0x3
+    field public static final int SOLID = 0; // 0x0
+  }
+
+  public abstract class ListItemProvider {
+    ctor public ListItemProvider();
+    method public abstract androidx.car.widget.ListItem get(int);
+    method public abstract int size();
+  }
+
+  public static class ListItemProvider.ListProvider extends androidx.car.widget.ListItemProvider {
+    ctor public ListItemProvider.ListProvider(java.util.List<androidx.car.widget.ListItem>);
+    method public androidx.car.widget.ListItem get(int);
+    method public int size();
+  }
+
+  public class PagedListView extends android.widget.FrameLayout {
+    ctor public PagedListView(android.content.Context);
+    ctor public PagedListView(android.content.Context, android.util.AttributeSet);
+    ctor public PagedListView(android.content.Context, android.util.AttributeSet, int);
+    ctor public PagedListView(android.content.Context, android.util.AttributeSet, int, int);
+    method public void addItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration);
+    method public void addOnItemTouchListener(androidx.recyclerview.widget.RecyclerView.OnItemTouchListener);
+    method public androidx.recyclerview.widget.RecyclerView.Adapter<? extends androidx.recyclerview.widget.RecyclerView.ViewHolder> getAdapter();
+    method public int getMaxPages();
+    method public int getPage(int);
+    method public androidx.recyclerview.widget.RecyclerView getRecyclerView();
+    method public int getRowsPerPage();
+    method public boolean isAtEnd();
+    method public boolean isAtStart();
+    method public void onLayout(boolean, int, int, int, int);
+    method public void onRestoreInstanceState(android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState();
+    method public int positionOf(android.view.View);
+    method public void removeItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration);
+    method public void removeOnItemTouchListener(androidx.recyclerview.widget.RecyclerView.OnItemTouchListener);
+    method public void resetMaxPages();
+    method public void resetScrollbarColor();
+    method public void scrollToPosition(int);
+    method public void setAdapter(androidx.recyclerview.widget.RecyclerView.Adapter<? extends androidx.recyclerview.widget.RecyclerView.ViewHolder>);
+    method public void setDayNightStyle(int);
+    method public void setDefaultMaxPages(int);
+    method public void setDividerVisibilityManager(androidx.car.widget.PagedListView.DividerVisibilityManager);
+    method public void setDownButtonIcon(android.graphics.drawable.Drawable);
+    method public void setGutter(int);
+    method public void setGutterSize(int);
+    method public void setItemSpacing(int);
+    method public void setListContentTopOffset(int);
+    method public void setMaxPages(int);
+    method public void setOnScrollListener(androidx.car.widget.PagedListView.OnScrollListener);
+    method public void setScrollBarContainerWidth(int);
+    method public void setScrollBarTopMargin(int);
+    method public void setScrollbarColor(int);
+    method public void setUpButtonIcon(android.graphics.drawable.Drawable);
+    method public void snapToPosition(int);
+    field public static final int DEFAULT_MAX_CLICKS = 6; // 0x6
+    field public static final int UNLIMITED_PAGES = -1; // 0xffffffff
+  }
+
+  public static abstract interface PagedListView.DividerVisibilityManager {
+    method public abstract boolean shouldHideDivider(int);
+  }
+
+  public static abstract class PagedListView.Gutter implements java.lang.annotation.Annotation {
+    field public static final int BOTH = 3; // 0x3
+    field public static final int END = 2; // 0x2
+    field public static final int NONE = 0; // 0x0
+    field public static final int START = 1; // 0x1
+  }
+
+  public static abstract interface PagedListView.ItemCap {
+    method public abstract void setMaxItems(int);
+    field public static final int UNLIMITED = -1; // 0xffffffff
+  }
+
+  public static abstract interface PagedListView.ItemPositionOffset {
+    method public abstract void setPositionOffset(int);
+  }
+
+  public static abstract class PagedListView.OnScrollListener {
+    ctor public PagedListView.OnScrollListener();
+    method public void onAlphaJumpButtonClicked();
+    method public void onGestureDown();
+    method public void onGestureUp();
+    method public void onReachBottom();
+    method public void onScrollDownButtonClicked();
+    method public void onScrollStateChanged(androidx.recyclerview.widget.RecyclerView, int);
+    method public void onScrollUpButtonClicked();
+    method public void onScrolled(androidx.recyclerview.widget.RecyclerView, int, int);
+  }
+
+  public class PagedScrollBarView extends android.widget.FrameLayout {
+    ctor public PagedScrollBarView(android.content.Context, android.util.AttributeSet);
+    ctor public PagedScrollBarView(android.content.Context, android.util.AttributeSet, int);
+    ctor public PagedScrollBarView(android.content.Context, android.util.AttributeSet, int, int);
+    method public boolean isDownEnabled();
+    method public boolean isDownPressed();
+    method public boolean isUpPressed();
+    method public void resetThumbColor();
+    method public void setDayNightStyle(int);
+    method public void setDownButtonIcon(android.graphics.drawable.Drawable);
+    method public void setDownEnabled(boolean);
+    method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener);
+    method public void setParameters(int, int, int, boolean);
+    method public void setThumbColor(int);
+    method public void setUpButtonIcon(android.graphics.drawable.Drawable);
+    method public void setUpEnabled(boolean);
+  }
+
+  public static abstract interface PagedScrollBarView.PaginationListener {
+    method public abstract void onAlphaJump();
+    method public abstract void onPaginate(int);
+    field public static final int PAGE_DOWN = 1; // 0x1
+    field public static final int PAGE_UP = 0; // 0x0
+  }
+
+  public final class PagedSmoothScroller extends androidx.recyclerview.widget.LinearSmoothScroller {
+    ctor public PagedSmoothScroller(android.content.Context);
+  }
+
+  public class PagedSnapHelper extends androidx.recyclerview.widget.LinearSnapHelper {
+    ctor public PagedSnapHelper(android.content.Context);
+    method public void attachToRecyclerView(androidx.recyclerview.widget.RecyclerView);
+    method public boolean isAtEnd(androidx.recyclerview.widget.RecyclerView.LayoutManager);
+    method public boolean isAtStart(androidx.recyclerview.widget.RecyclerView.LayoutManager);
+  }
+
+  public class SeekbarListItem extends androidx.car.widget.ListItem {
+    ctor public SeekbarListItem(android.content.Context, int, int, android.widget.SeekBar.OnSeekBarChangeListener, java.lang.String);
+    method public static androidx.car.widget.SeekbarListItem.ViewHolder createViewHolder(android.view.View);
+    method public int getViewType();
+    method protected void onBind(androidx.car.widget.SeekbarListItem.ViewHolder);
+    method protected void resolveDirtyState();
+    method public void setPrimaryActionEmptyIcon();
+    method public void setPrimaryActionIcon(int);
+    method public void setPrimaryActionIcon(android.graphics.drawable.Drawable);
+    method public void setSupplementalEmptyIcon(boolean);
+    method public void setSupplementalIcon(int, boolean);
+    method public void setSupplementalIcon(int, boolean, android.view.View.OnClickListener);
+  }
+
+  public static class SeekbarListItem.ViewHolder extends androidx.car.widget.ListItem.ViewHolder {
+    ctor public SeekbarListItem.ViewHolder(android.view.View);
+    method public android.widget.RelativeLayout getContainerLayout();
+    method public android.widget.ImageView getPrimaryIcon();
+    method public android.widget.SeekBar getSeekBar();
+    method public android.widget.LinearLayout getSeekBarContainer();
+    method public android.widget.ImageView getSupplementalIcon();
+    method public android.view.View getSupplementalIconDivider();
+    method public android.widget.TextView getText();
+  }
+
+  public class SubheaderListItem extends androidx.car.widget.ListItem {
+    ctor public SubheaderListItem(android.content.Context, java.lang.String);
+    method public static androidx.car.widget.SubheaderListItem.ViewHolder createViewHolder(android.view.View);
+    method public int getViewType();
+    method protected void onBind(androidx.car.widget.SubheaderListItem.ViewHolder);
+    method protected void resolveDirtyState();
+    method public void setText(java.lang.String);
+    method public void setTextStartMarginType(int);
+    field public static final int TEXT_START_MARGIN_TYPE_LARGE = 2; // 0x2
+    field public static final int TEXT_START_MARGIN_TYPE_NONE = 0; // 0x0
+    field public static final int TEXT_START_MARGIN_TYPE_SMALL = 1; // 0x1
+  }
+
+  public static abstract class SubheaderListItem.TextStartMarginType implements java.lang.annotation.Annotation {
+  }
+
+  public static class SubheaderListItem.ViewHolder extends androidx.car.widget.ListItem.ViewHolder {
+    ctor public SubheaderListItem.ViewHolder(android.view.View);
+    method public android.widget.TextView getText();
+  }
+
+  public class TextListItem extends androidx.car.widget.ListItem {
+    ctor public TextListItem(android.content.Context);
+    method public static androidx.car.widget.TextListItem.ViewHolder createViewHolder(android.view.View);
+    method public int getViewType();
+    method public void onBind(androidx.car.widget.TextListItem.ViewHolder);
+    method protected void resolveDirtyState();
+    method public void setAction(java.lang.String, boolean, android.view.View.OnClickListener);
+    method public void setActions(java.lang.String, boolean, android.view.View.OnClickListener, java.lang.String, boolean, android.view.View.OnClickListener);
+    method public void setBody(java.lang.String);
+    method public void setBody(java.lang.String, boolean);
+    method public void setOnClickListener(android.view.View.OnClickListener);
+    method public void setPrimaryActionEmptyIcon();
+    method public void setPrimaryActionIcon(int, boolean);
+    method public void setPrimaryActionIcon(android.graphics.drawable.Drawable, boolean);
+    method public void setPrimaryActionNoIcon();
+    method public void setSupplementalIcon(int, boolean);
+    method public void setSupplementalIcon(int, boolean, android.view.View.OnClickListener);
+    method public void setSwitch(boolean, boolean, android.widget.CompoundButton.OnCheckedChangeListener);
+    method public void setSwitchState(boolean);
+    method public void setTitle(java.lang.String);
+  }
+
+  public static class TextListItem.ViewHolder extends androidx.car.widget.ListItem.ViewHolder {
+    ctor public TextListItem.ViewHolder(android.view.View);
+    method public android.widget.Button getAction1();
+    method public android.view.View getAction1Divider();
+    method public android.widget.Button getAction2();
+    method public android.view.View getAction2Divider();
+    method public android.widget.TextView getBody();
+    method public android.widget.RelativeLayout getContainerLayout();
+    method public android.widget.ImageView getPrimaryIcon();
+    method public android.widget.ImageView getSupplementalIcon();
+    method public android.view.View getSupplementalIconDivider();
+    method public android.widget.Switch getSwitch();
+    method public android.view.View getSwitchDivider();
+    method public android.widget.TextView getTitle();
+  }
+
+}
+
diff --git a/car/build.gradle b/car/build.gradle
index d971e78..52d2a40 100644
--- a/car/build.gradle
+++ b/car/build.gradle
@@ -22,6 +22,7 @@
     api project(':annotation')
     api project(':legacy-support-v4')
     api project(':recyclerview')
+    api project(':gridlayout')
 
     androidTestImplementation(TEST_RUNNER_TMP, libs.exclude_for_espresso)
     androidTestImplementation(ESPRESSO_CORE_TMP, libs.exclude_for_espresso)
@@ -46,7 +47,7 @@
 
 supportLibrary {
     name = "Android Car Support UI"
-    publish = false
+    publish = true
     mavenVersion = LibraryVersions.SUPPORT_LIBRARY
     mavenGroup = LibraryGroups.CAR
     inceptionYear = "2017"
diff --git a/car/car-stubs/android.car.jar b/car/car-stubs/android.car.jar
index b7ee974..01f5cd9 100644
--- a/car/car-stubs/android.car.jar
+++ b/car/car-stubs/android.car.jar
Binary files differ
diff --git a/car/res/drawable/car_borderless_button_background.xml b/car/res/drawable/car_borderless_button_background.xml
new file mode 100644
index 0000000..0ef4bea
--- /dev/null
+++ b/car/res/drawable/car_borderless_button_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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.
+  -->
+<ripple
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?attr/listItemBackgroundColor">
+    <item android:id="@android:id/mask">
+        <color android:color="#42ffffff "/>
+    </item>
+</ripple>
diff --git a/car/res/layout/car_alpha_jump_button.xml b/car/res/layout/car_alpha_jump_button.xml
new file mode 100644
index 0000000..c5e90fd
--- /dev/null
+++ b/car/res/layout/car_alpha_jump_button.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 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.
+  -->
+
+<!-- This is sized in code, so we'll set it to 0dp for now. -->
+<android.support.v7.widget.CardView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:grid="http://schemas.android.com/apk/res-auto"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        grid:layout_gravity="fill"
+        grid:layout_columnWeight="1"
+        grid:layout_rowWeight="1"
+        android:layout_margin="@dimen/car_padding_2">
+    <TextView
+        android:textAppearance="@style/TextAppearance.Car.Title"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@drawable/car_card_ripple_background"
+        android:id="@+id/button"
+        android:gravity="center"/>
+</android.support.v7.widget.CardView>
diff --git a/car/res/layout/car_list_item_seekbar_content.xml b/car/res/layout/car_list_item_seekbar_content.xml
index 2e3b165..eedbe73 100644
--- a/car/res/layout/car_list_item_seekbar_content.xml
+++ b/car/res/layout/car_list_item_seekbar_content.xml
@@ -14,15 +14,14 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
+<!-- This layout should only be used by class SeekbarListItem, as it requires layout params
+     being set programmatically depending on item data/view configuration. -->
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/container"
     android:layout_width="wrap_content"
     android:layout_height="match_parent">
 
-    <!-- This layout should only be used by class SeekbarListItem, as it requires layout params
-         being set programmatically depending on item data/view configuration. -->
-
     <!-- Primary Action. -->
     <ImageView
         android:id="@+id/primary_icon"
diff --git a/car/res/layout/car_list_item_subheader_content.xml b/car/res/layout/car_list_item_subheader_content.xml
new file mode 100644
index 0000000..7bd8580
--- /dev/null
+++ b/car/res/layout/car_list_item_subheader_content.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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.
+  -->
+<!-- This layout should only be used by class SubheaderListItem, as it requires layout params
+     being set programmatically depending on item data/view configuration. -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:maxLines="1"
+        android:textAppearance="@style/TextAppearance.Car.Subheader"/>
+</FrameLayout>
diff --git a/car/res/layout/car_list_item_text_content.xml b/car/res/layout/car_list_item_text_content.xml
index a85a31e..0e5d4b9 100644
--- a/car/res/layout/car_list_item_text_content.xml
+++ b/car/res/layout/car_list_item_text_content.xml
@@ -88,8 +88,6 @@
         android:ellipsize="end"
         android:maxLength="@integer/car_borderless_button_text_length_limit"
         android:maxLines="1"
-        android:background="@color/car_card"
-        android:foreground="@drawable/car_card_ripple_background"
         style="?android:attr/borderlessButtonStyle"/>
     <View
         android:id="@+id/action1_divider"
@@ -107,7 +105,5 @@
         android:ellipsize="end"
         android:maxLength="@integer/car_borderless_button_text_length_limit"
         android:maxLines="1"
-        android:background="@color/car_card"
-        android:foreground="@drawable/car_card_ripple_background"
         style="?android:attr/borderlessButtonStyle"/>
 </RelativeLayout>
diff --git a/car/res/layout/car_paged_scrollbar_buttons.xml b/car/res/layout/car_paged_scrollbar_buttons.xml
index e982b66..cd62329 100644
--- a/car/res/layout/car_paged_scrollbar_buttons.xml
+++ b/car/res/layout/car_paged_scrollbar_buttons.xml
@@ -26,10 +26,26 @@
         android:id="@+id/page_up"
         android:layout_width="@dimen/car_scroll_bar_button_size"
         android:layout_height="@dimen/car_scroll_bar_button_size"
+        android:background="@drawable/car_button_ripple_background"
+        android:focusable="false"
+        android:hapticFeedbackEnabled="false"
+        android:src="@drawable/ic_up"
+        android:scaleType="centerInside" />
+
+    <!-- TODO(deanh): Replace this with a proper asset when we have done. -->
+    <TextView
+        android:textAppearance="@style/TextAppearance.Car.Title"
+        android:id="@+id/alpha_jump"
+        android:layout_width="@dimen/car_scroll_bar_button_size"
+        android:layout_height="@dimen/car_scroll_bar_button_size"
         android:background="@drawable/car_card_ripple_background"
         android:focusable="false"
         android:hapticFeedbackEnabled="false"
-        android:src="@drawable/ic_up" />
+        android:gravity="center"
+        android:layout_marginBottom="@dimen/car_padding_3"
+        android:layout_marginTop="@dimen/car_padding_3"
+        android:visibility="gone"
+        android:text="A" />
 
     <FrameLayout
         android:id="@+id/filler"
@@ -51,8 +67,9 @@
         android:id="@+id/page_down"
         android:layout_width="@dimen/car_scroll_bar_button_size"
         android:layout_height="@dimen/car_scroll_bar_button_size"
-        android:background="@drawable/car_card_ripple_background"
+        android:background="@drawable/car_button_ripple_background"
         android:focusable="false"
         android:hapticFeedbackEnabled="false"
-        android:src="@drawable/ic_down" />
+        android:src="@drawable/ic_down"
+        android:scaleType="centerInside" />
 </LinearLayout>
diff --git a/car/res/values-as/strings.xml b/car/res/values-as/strings.xml
new file mode 100644
index 0000000..e5a0015
--- /dev/null
+++ b/car/res/values-as/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="speed_bump_lockout_message" msgid="5405697774899378511">"ৰাষ্টাত মনোযোগ দিয়ক"</string>
+    <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"সম্প্ৰসাৰণ/সংকোচন বুটাম"</string>
+</resources>
diff --git a/car/res/values-or/strings.xml b/car/res/values-or/strings.xml
new file mode 100644
index 0000000..3a003b8
--- /dev/null
+++ b/car/res/values-or/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="speed_bump_lockout_message" msgid="5405697774899378511">"ରାସ୍ତା ଉପରେ ଧ୍ୟାନରଖନ୍ତୁ"</string>
+    <string name="action_bar_expand_collapse_button" msgid="196909968432559564">"ବିସ୍ତାର/ସଂକୋଚନ ବଟନ୍"</string>
+</resources>
diff --git a/car/res/values/attrs.xml b/car/res/values/attrs.xml
index 0548f79..7108c05 100644
--- a/car/res/values/attrs.xml
+++ b/car/res/values/attrs.xml
@@ -111,6 +111,8 @@
         <attr name="listItemTitleTextAppearance" format="reference"/>
         <!-- The TextAppearance of the body text in the ListItem. -->
         <attr name="listItemBodyTextAppearance" format="reference"/>
+        <!-- The TextAppearance of the text in the Subheader ListItem. -->
+        <attr name="listItemSubheaderTextAppearance" format="reference"/>
     </declare-styleable>
 
     <!-- The attributes for customizing the appearance of the hamburger and back arrow in the
diff --git a/car/res/values/integers.xml b/car/res/values/integers.xml
index 6c737af..a9f6284 100644
--- a/car/res/values/integers.xml
+++ b/car/res/values/integers.xml
@@ -58,4 +58,8 @@
     <integer name="car_action_bar_expand_anim_duration">333</integer>
     <integer name="car_action_bar_collapse_anim_duration">233</integer>
 
+    <!-- The number of columns of buttons to display in the alpha jump overview. -->
+    <!-- TODO(deanh): this could be calculated dynamically, or something else to better support
+        adaptive/responsive. -->
+    <integer name="alpha_jump_button_columns">6</integer>
 </resources>
diff --git a/car/res/values/styles.xml b/car/res/values/styles.xml
index ccd661a..7e32c39 100644
--- a/car/res/values/styles.xml
+++ b/car/res/values/styles.xml
@@ -58,6 +58,21 @@
         <item name="android:textColor">@color/car_title2_light</item>
     </style>
 
+    <!-- The styling for subheader text. This text uses color accent. -->
+    <style name="TextAppearance.Car.Subheader" parent="TextAppearance.Car.Title2" >
+        <item name="android:textColor">@color/car_accent</item>
+    </style>
+
+    <!-- Subheader text that is permanently a dark color. -->
+    <style name="TextAppearance.Car.Subheader.Dark" parent="TextAppearance.Car.Subheader" >
+        <item name="android:textColor">@color/car_accent_dark</item>
+    </style>
+
+    <!-- Subheader text that is permanently a light color. -->
+    <style name="TextAppearance.Car.Subheader.Light" parent="TextAppearance.Car.Subheader" >
+        <item name="android:textColor">@color/car_accent_light</item>
+    </style>
+
     <!-- The styling for the main headline text. The color of this text changes based on the
          day/night mode. -->
     <style name="TextAppearance.Car.Headline1">
@@ -205,6 +220,7 @@
         <item name="android:paddingEnd">@dimen/car_borderless_button_horizontal_padding</item>
         <item name="android:textSize">@dimen/car_action1_size</item>
         <item name="android:textColor">@drawable/car_borderless_button_text_color</item>
+        <item name="android:background">@drawable/car_borderless_button_background</item>
     </style>
 
     <style name="Widget.Car.Button.Borderless.Colored.Light">
diff --git a/car/res/values/themes.xml b/car/res/values/themes.xml
index bc3880d..60949e5 100644
--- a/car/res/values/themes.xml
+++ b/car/res/values/themes.xml
@@ -36,6 +36,7 @@
         <item name="listItemBackgroundColor">@color/car_card</item>
         <item name="listItemTitleTextAppearance">@style/TextAppearance.Car.Body1</item>
         <item name="listItemBodyTextAppearance">@style/TextAppearance.Car.Body2</item>
+        <item name="listItemSubheaderTextAppearance">@style/TextAppearance.Car.Subheader</item>
     </style>
 
     <!-- Theme for the Car that is a passthrough for the default theme. -->
@@ -51,6 +52,7 @@
         <item name="listItemBackgroundColor">@color/car_card_dark</item>
         <item name="listItemTitleTextAppearance">@style/TextAppearance.Car.Body1.Light</item>
         <item name="listItemBodyTextAppearance">@style/TextAppearance.Car.Body2.Light</item>
+        <item name="listItemSubheaderTextAppearance">@style/TextAppearance.Car.Subheader.Light</item>
     </style>
 
     <!-- A Theme for activities that have a drawer affordance. This theme will automatically switch
diff --git a/car/src/androidTest/java/androidx/car/utils/CarUxRestrictionsTestUtils.java b/car/src/androidTest/java/androidx/car/utils/CarUxRestrictionsTestUtils.java
index 15ff3f1..7100488 100644
--- a/car/src/androidTest/java/androidx/car/utils/CarUxRestrictionsTestUtils.java
+++ b/car/src/androidTest/java/androidx/car/utils/CarUxRestrictionsTestUtils.java
@@ -30,7 +30,7 @@
         return new CarUxRestrictions(true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED, 0);
     }
 
-    public static CarUxRestrictions getUnrestricted() {
-        return new CarUxRestrictions(false, CarUxRestrictions.UX_RESTRICTIONS_UNRESTRICTED, 0);
+    public static CarUxRestrictions getBaseline() {
+        return new CarUxRestrictions(false, CarUxRestrictions.UX_RESTRICTIONS_BASELINE, 0);
     }
 }
diff --git a/car/src/androidTest/java/androidx/car/widget/SubheaderListItemTest.java b/car/src/androidTest/java/androidx/car/widget/SubheaderListItemTest.java
new file mode 100644
index 0000000..1c5bc39
--- /dev/null
+++ b/car/src/androidTest/java/androidx/car/widget/SubheaderListItemTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2018 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.contrib.RecyclerViewActions.scrollToPosition;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThat;
+
+import android.content.pm.PackageManager;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assume;
+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 SubheaderListItem}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SubheaderListItemTest {
+
+    @Rule
+    public ActivityTestRule<PagedListViewTestActivity> mActivityRule =
+            new ActivityTestRule<>(PagedListViewTestActivity.class);
+
+    private PagedListViewTestActivity mActivity;
+    private PagedListView mPagedListView;
+
+    private boolean isAutoDevice() {
+        PackageManager packageManager = mActivityRule.getActivity().getPackageManager();
+        return packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+    }
+
+    @Before
+    public void setUp() {
+        Assume.assumeTrue(isAutoDevice());
+        mActivity = mActivityRule.getActivity();
+        mPagedListView = mActivity.findViewById(R.id.paged_list_view);
+    }
+
+    private void setupPagedListView(List<? extends 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 SubheaderListItem.ViewHolder getViewHolderAtPosition(int position) {
+        return (SubheaderListItem.ViewHolder) mPagedListView.getRecyclerView()
+                .findViewHolderForAdapterPosition(position);
+    }
+
+    private TextListItem.ViewHolder getTextViewHolderAtPosition(int position) {
+        return (TextListItem.ViewHolder) mPagedListView.getRecyclerView()
+                .findViewHolderForAdapterPosition(position);
+    }
+
+    @Test
+    public void testEmptyStartMargin() {
+        SubheaderListItem subheader = new SubheaderListItem(mActivity, "text");
+        subheader.setTextStartMarginType(SubheaderListItem.TEXT_START_MARGIN_TYPE_NONE);
+
+        TextListItem item = new TextListItem(mActivity);
+        item.setTitle("title");
+        item.setPrimaryActionNoIcon();
+
+        setupPagedListView(Arrays.asList(subheader, item));
+
+        assertThat(getViewHolderAtPosition(0).getText().getLeft(),
+                is(equalTo(getTextViewHolderAtPosition(1).getTitle().getLeft())));
+    }
+
+    @Test
+    public void testStartMarginMatchesSmallIcon() {
+        SubheaderListItem subheader = new SubheaderListItem(mActivity, "text");
+        subheader.setTextStartMarginType(SubheaderListItem.TEXT_START_MARGIN_TYPE_SMALL);
+
+        TextListItem item = new TextListItem(mActivity);
+        item.setTitle("title");
+        item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, /* useLargeIcon= */ false);
+
+        setupPagedListView(Arrays.asList(subheader, item));
+
+        assertThat(getViewHolderAtPosition(0).getText().getLeft(),
+                is(equalTo(getTextViewHolderAtPosition(1).getTitle().getLeft())));
+    }
+
+    @Test
+    public void testStartMarginMatchesLargeIcon() {
+        SubheaderListItem subheader = new SubheaderListItem(mActivity, "text");
+        subheader.setTextStartMarginType(SubheaderListItem.TEXT_START_MARGIN_TYPE_LARGE);
+
+        TextListItem item = new TextListItem(mActivity);
+        item.setTitle("title");
+        item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, /* useLargeIcon= */ true);
+
+        setupPagedListView(Arrays.asList(subheader, item));
+
+        assertThat(getViewHolderAtPosition(0).getText().getLeft(),
+                is(equalTo(getTextViewHolderAtPosition(1).getTitle().getLeft())));
+    }
+}
diff --git a/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java b/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
index d413b5c..9a9e46d 100644
--- a/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
+++ b/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
@@ -28,6 +28,7 @@
 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.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
@@ -40,7 +41,6 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import androidx.recyclerview.widget.LinearLayoutManager;
 import android.text.InputFilter;
 import android.text.TextUtils;
 import android.view.View;
@@ -61,6 +61,7 @@
 
 import androidx.car.test.R;
 import androidx.car.utils.CarUxRestrictionsTestUtils;
+import androidx.recyclerview.widget.LinearLayoutManager;
 
 /**
 * Tests the layout configuration in {@link TextListItem}.
@@ -803,7 +804,7 @@
         refreshUi();
         assertTrue(Arrays.asList(viewHolder.getBody().getFilters()).contains(filter));
 
-        viewHolder.complyWithUxRestrictions(CarUxRestrictionsTestUtils.getUnrestricted());
+        viewHolder.complyWithUxRestrictions(CarUxRestrictionsTestUtils.getBaseline());
         refreshUi();
         assertTrue(Arrays.asList(viewHolder.getBody().getFilters()).contains(filter));
     }
diff --git a/car/src/main/java/androidx/car/app/CarAlertDialog.java b/car/src/main/java/androidx/car/app/CarAlertDialog.java
index ee67cf7..453ad4e 100644
--- a/car/src/main/java/androidx/car/app/CarAlertDialog.java
+++ b/car/src/main/java/androidx/car/app/CarAlertDialog.java
@@ -21,8 +21,6 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Bundle;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
 import android.text.TextUtils;
 import android.util.TypedValue;
 import android.view.MotionEvent;
@@ -33,6 +31,8 @@
 import android.widget.Button;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
 import androidx.car.R;
 
 /**
@@ -338,7 +338,7 @@
      * Builder class that can be used to create a {@link CarAlertDialog} by configuring the options
      * for what shows up in the resulting dialog.
      */
-    public static class Builder {
+    public static final class Builder {
         private final Context mContext;
         private final DialogData mDialogData;
 
@@ -409,8 +409,8 @@
          * an "OK" action).
          *
          * @param textId The resource id of the string to be used for the positive button text.
-         * @param listener A {@link OnClickListener} to be invoked when the button is clicked. Can
-         *                 be {@code null} to represent no listener.
+         * @param listener A {@link android.content.DialogInterface.OnClickListener} to be invoked
+         *                 when the button is clicked. Can be {@code null} to represent no listener.
          * @return This {@code Builder} object to allow for chaining of calls.
          */
         public Builder setPositiveButton(@StringRes int textId,
@@ -429,8 +429,8 @@
          * an "OK" action).
          *
          * @param text The string to be used for the positive button text.
-         * @param listener A {@link OnClickListener} to be invoked when the button is clicked. Can
-         *                 be {@code null} to represent no listener.
+         * @param listener A {@link android.content.DialogInterface.OnClickListener} to be invoked
+         *                 when the button is clicked. Can be {@code null} to represent no listener.
          * @return This {@code Builder} object to allow for chaining of calls.
          */
         public Builder setPositiveButton(CharSequence text, @Nullable OnClickListener listener) {
@@ -447,8 +447,8 @@
          * <p>The negative button should be used to cancel any actions the dialog represents.
          *
          * @param textId The resource id of the string to be used for the negative button text.
-         * @param listener A {@link OnClickListener} to be invoked when the button is clicked. Can
-         *                 be {@code null} to represent no listener.
+         * @param listener A {@link android.content.DialogInterface.OnClickListener} to be invoked
+         *                 when the button is clicked. Can be {@code null} to represent no listener.
          * @return This {@code Builder} object to allow for chaining of calls.
          */
         public Builder setNegativeButton(@StringRes int textId,
@@ -466,8 +466,8 @@
          * <p>The negative button should be used to cancel any actions the dialog represents.
          *
          * @param text The string to be used for the negative button text.
-         * @param listener A {@link OnClickListener} to be invoked when the button is clicked. Can
-         *                 be {@code null} to represent no listener.
+         * @param listener A {@link android.content.DialogInterface.OnClickListener} to be invoked
+         *                 when the button is clicked. Can be {@code null} to represent no listener.
          * @return This {@code Builder} object to allow for chaining of calls.
          */
         public Builder setNegativeButton(CharSequence text, @Nullable OnClickListener listener) {
@@ -518,8 +518,8 @@
         /**
          * Creates an {@link CarAlertDialog} with the arguments supplied to this {@code Builder}.
          *
-         * <p>Calling this method does not display the dialog. If no additional processing is
-         * needed, {@link #show()} may be called instead to both create and display the dialog.
+         * <p>Calling this method does not display the dialog. Utilize this dialog within a
+         * {@link androidx.fragment.app.DialogFragment} to show the dialog.
          */
         public CarAlertDialog create() {
             CarAlertDialog dialog = new CarAlertDialog(mContext, mDialogData);
@@ -531,21 +531,5 @@
 
             return dialog;
         }
-
-        /**
-         * Creates an {@link CarAlertDialog} with the arguments supplied to this {@code Builder}
-         * and immediately displays the dialog.
-         *
-         * <p>Calling this method is functionally identical to:
-         * <pre>
-         *     CarAlertDialog dialog = new CarAlertDialog.Builder().create();
-         *     dialog.show();
-         * </pre>
-         */
-        public CarAlertDialog show() {
-            CarAlertDialog dialog = create();
-            dialog.show();
-            return dialog;
-        }
     }
 }
diff --git a/car/src/main/java/androidx/car/app/CarListDialog.java b/car/src/main/java/androidx/car/app/CarListDialog.java
index 6261745..4e02d03 100644
--- a/car/src/main/java/androidx/car/app/CarListDialog.java
+++ b/car/src/main/java/androidx/car/app/CarListDialog.java
@@ -20,9 +20,6 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.RecyclerView;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.View;
@@ -31,9 +28,8 @@
 import android.view.Window;
 import android.view.WindowManager;
 
-import java.util.ArrayList;
-import java.util.List;
-
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.car.R;
 import androidx.car.widget.DayNightStyle;
 import androidx.car.widget.ListItem;
@@ -42,6 +38,10 @@
 import androidx.car.widget.PagedListView;
 import androidx.car.widget.PagedScrollBarView;
 import androidx.car.widget.TextListItem;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A subclass of {@link Dialog} that is tailored for the car environment. This dialog can display a
@@ -167,16 +167,23 @@
         mScrollBarView = getWindow().findViewById(R.id.scrollbar);
         mScrollBarView.setDayNightStyle(DayNightStyle.FORCE_NIGHT);
 
-        mScrollBarView.setPaginationListener(direction -> {
-            switch (direction) {
-                case PagedScrollBarView.PaginationListener.PAGE_UP:
-                    mList.pageUp();
-                    break;
-                case PagedScrollBarView.PaginationListener.PAGE_DOWN:
-                    mList.pageDown();
-                    break;
-                default:
-                    Log.e(TAG, "Unknown pagination direction (" + direction + ")");
+        mScrollBarView.setPaginationListener(new PagedScrollBarView.PaginationListener() {
+            @Override
+            public void onPaginate(int direction) {
+                switch (direction) {
+                    case PagedScrollBarView.PaginationListener.PAGE_UP:
+                        mList.pageUp();
+                        break;
+                    case PagedScrollBarView.PaginationListener.PAGE_DOWN:
+                        mList.pageDown();
+                        break;
+                    default:
+                        Log.e(TAG, "Unknown pagination direction (" + direction + ")");
+                }
+            }
+
+            @Override
+            public void onAlphaJump() {
             }
         });
     }
@@ -276,7 +283,7 @@
      * Builder class that can be used to create a {@link CarListDialog} by configuring the
      * options for the list and behavior of the dialog.
      */
-    public static class Builder {
+    public static final class Builder {
         private final Context mContext;
         private int mInitialPosition;
         private String[] mItems;
@@ -383,8 +390,8 @@
          * <p>If {@link #setItems(String[],DialogInterface.OnClickListener)} is never called, then
          * calling this method will throw an exception.
          *
-         * <p>Calling this method does not display the dialog. If no additional processing is
-         * needed, {@link #show()} may be called instead to both create and display the dialog.
+         * <p>Calling this method does not display the dialog. Utilize this dialog within a
+         * {@link androidx.fragment.app.DialogFragment} to show the dialog.
          */
         public CarListDialog create() {
             if (mItems == null || mItems.length == 0) {
@@ -410,21 +417,5 @@
 
             return dialog;
         }
-
-        /**
-         * Creates an {@link CarAlertDialog} with the arguments supplied to this {@code Builder}
-         * and immediately displays the dialog.
-         *
-         * <p>Calling this method is functionally identical to:
-         * <pre>{@code
-         * CarAlertDialog dialog = new CarAlertDialog.Builder().create();
-         * dialog.show();
-         * }</pre>
-         */
-        public CarListDialog show() {
-            CarListDialog dialog = create();
-            dialog.show();
-            return dialog;
-        }
     }
 }
diff --git a/car/src/main/java/androidx/car/drawer/CarDrawerAdapter.java b/car/src/main/java/androidx/car/drawer/CarDrawerAdapter.java
index 3410486..7991cfa 100644
--- a/car/src/main/java/androidx/car/drawer/CarDrawerAdapter.java
+++ b/car/src/main/java/androidx/car/drawer/CarDrawerAdapter.java
@@ -26,7 +26,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.car.R;
 import androidx.car.utils.CarUxRestrictionsHelper;
@@ -37,10 +36,11 @@
  * Base adapter for displaying items in the car navigation drawer, which uses a
  * {@link PagedListView}.
  *
- * <p>Subclasses must set the title that will be displayed when displaying the contents of the
- * drawer via {@link #setTitle(CharSequence)}. The title can be updated at any point later on. The
- * title of the root adapter will also be the main title showed in the toolbar when the drawer is
- * closed. See {@link CarDrawerController#setRootAdapter(CarDrawerAdapter)} for more information.
+ * <p>Subclasses can optionally set the title that will be displayed when displaying the contents
+ * of the drawer via {@link #setTitle(CharSequence)}. The title can be updated at any point later
+ * on. The title of the root adapter will also be the main title showed in the toolbar when the
+ * drawer is closed. See {@link CarDrawerController#setRootAdapter(CarDrawerAdapter)} for more
+ * information.
  *
  * <p>This class also takes care of implementing the PageListView.ItemCamp contract and subclasses
  * should implement {@link #getActualItemCount()}.
@@ -90,7 +90,7 @@
          * Called when {@link #setTitle(CharSequence)} has been called and the title has been
          * changed.
          */
-        void onTitleChanged(CharSequence newTitle);
+        void onTitleChanged(@Nullable CharSequence newTitle);
     }
 
     protected CarDrawerAdapter(Context context, boolean showDisabledListOnEmpty) {
@@ -113,11 +113,7 @@
     }
 
     /** Updates the title to display in the toolbar for this Adapter. */
-    public final void setTitle(@NonNull CharSequence title) {
-        if (title == null) {
-            throw new IllegalArgumentException("setTitle() cannot be passed a null title!");
-        }
-
+    public final void setTitle(@Nullable CharSequence title) {
         mTitle = title;
 
         if (mTitleChangeListener != null) {
diff --git a/car/src/main/java/androidx/car/drawer/CarDrawerController.java b/car/src/main/java/androidx/car/drawer/CarDrawerController.java
index c25ac80..ad98e78 100644
--- a/car/src/main/java/androidx/car/drawer/CarDrawerController.java
+++ b/car/src/main/java/androidx/car/drawer/CarDrawerController.java
@@ -19,14 +19,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.os.Bundle;
-import androidx.annotation.AnimRes;
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.drawerlayout.widget.DrawerLayout;
-import androidx.appcompat.app.ActionBarDrawerToggle;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.appcompat.widget.Toolbar;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.MenuItem;
@@ -34,10 +26,18 @@
 import android.view.animation.AnimationUtils;
 import android.widget.ProgressBar;
 
-import java.util.ArrayDeque;
-
+import androidx.annotation.AnimRes;
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.ActionBarDrawerToggle;
+import androidx.appcompat.widget.Toolbar;
 import androidx.car.R;
 import androidx.car.widget.PagedListView;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayDeque;
 
 /**
  * A controller that will handle the set up of the navigation drawer. It will hook up the
@@ -105,6 +105,10 @@
 
         resolveThemeColors();
         setupDrawerToggling();
+
+        // Update the colors of the drawer title and arrow. Since this is called when the controller
+        // is first created, the drawer will be closed.
+        updateTitleAndArrowColor(/* drawerOpen= */ false);
     }
 
     private void resolveThemeColors() {
@@ -134,9 +138,7 @@
             mAdapterStack.removeLast();
         }
         mAdapterStack.addLast(rootAdapter);
-
-        setToolbarTitleFrom(rootAdapter);
-        mDrawerList.setAdapter(rootAdapter);
+        setDisplayAdapter(rootAdapter);
     }
 
     /**
@@ -200,10 +202,6 @@
      * controller's internal Toolbar.
      */
     private void setToolbarTitleFrom(CarDrawerAdapter adapter) {
-        if (adapter.getTitle() == null) {
-            throw new RuntimeException("CarDrawerAdapter must supply a title via setTitle()");
-        }
-
         mToolbar.setTitle(adapter.getTitle());
         adapter.setTitleChangeListener(mToolbar::setTitle);
     }
diff --git a/car/src/main/java/androidx/car/widget/AlphaJumpBucketer.java b/car/src/main/java/androidx/car/widget/AlphaJumpBucketer.java
new file mode 100644
index 0000000..1bf50d7
--- /dev/null
+++ b/car/src/main/java/androidx/car/widget/AlphaJumpBucketer.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2018 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * A helper class for building the list of buckets for alpha jump.
+ */
+public class AlphaJumpBucketer {
+    private static final Character[] DEFAULT_INITIAL_CHARS = {
+            '0', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
+
+    private static final String[] PREFIX_WORDS = {"a", "the"};
+
+    private final List<Bucket> mBuckets;
+
+    public AlphaJumpBucketer() {
+        mBuckets = new ArrayList<>();
+        for (Character ch : DEFAULT_INITIAL_CHARS) {
+            if (ch == '0') {
+                mBuckets.add(new Bucket("123", (String s) -> s.matches("^[0-9]")));
+            } else {
+                String prefix = new String(new char[] {ch});
+                mBuckets.add(new Bucket(prefix, (String s) -> s.startsWith(prefix.toLowerCase())));
+            }
+        }
+    }
+
+    public AlphaJumpBucketer(Bucket[] buckets) {
+        mBuckets = Arrays.asList(buckets);
+    }
+
+    /**
+     * Creates a collection of {@link IAlphaJumpAdapter.Bucket}s from the given list of strings.
+     */
+    public Collection<IAlphaJumpAdapter.Bucket> createBuckets(String[] values) {
+        return createBuckets(Arrays.asList(values));
+    }
+
+    /**
+     * Creates a collection of {@link IAlphaJumpAdapter.Bucket}s from the given iterable collection
+     * of strings.
+    */
+    public Collection<IAlphaJumpAdapter.Bucket> createBuckets(Iterable<String> values) {
+        return createBuckets(values.iterator());
+    }
+
+    /**
+     * Creates the collection of {@link IAlphaJumpAdapter.Bucket}s from the given enumeration of
+     * values.
+     */
+    public Collection<IAlphaJumpAdapter.Bucket> createBuckets(Iterator<String> values) {
+        int index = 0;
+        while (values.hasNext()) {
+            String value = values.next();
+            for (Bucket bucket : mBuckets) {
+                if (bucket.matchString(value, index)) {
+                    break;
+                }
+            }
+            index++;
+        }
+        ArrayList<IAlphaJumpAdapter.Bucket> buckets = new ArrayList<>();
+        buckets.addAll(mBuckets);
+        return buckets;
+    }
+
+    /**
+     * "Preprocess" a string so that we remove some common prefixes and so on when performing the
+     * bucketing.
+     *
+     * @param s The string to pre-process.
+     * @return The input string with whitespace trimmed, and also words like "the", "a" and so on
+     *    removed.
+     */
+    private static String preprocess(String s) {
+        s = s.trim().toLowerCase();
+
+        for (String word : PREFIX_WORDS) {
+            if (s.startsWith(word + " ")) {
+                s = s.substring(0, word.length() + 1).trim();
+                break;
+            }
+        }
+
+        return s;
+    }
+
+    /**
+     * A basic implementation of {@link IAlphaJumpAdapter.Bucket}.
+     */
+    public static class Bucket implements IAlphaJumpAdapter.Bucket {
+        private CharSequence mLabel;
+        private int mIndex;
+        private Predicate<String> mStringMatcher;
+        private boolean mIsEmpty;
+
+        Bucket(CharSequence label, Predicate<String> stringMatcher) {
+            mLabel = label;
+            mIndex = -1;
+            mIsEmpty = true;
+            mStringMatcher = stringMatcher;
+        }
+
+        boolean matchString(String s, int index) {
+            boolean match = mStringMatcher.test(preprocess(s));
+            if (match) {
+                if (mIndex < 0) {
+                    mIndex = index;
+                }
+                mIsEmpty = false;
+            }
+            return match;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return mIsEmpty;
+        }
+
+        @Override
+        public CharSequence getLabel() {
+            return mLabel;
+        }
+
+        @Override
+        public int getIndex() {
+            return mIndex;
+        }
+    }
+}
diff --git a/car/src/main/java/androidx/car/widget/AlphaJumpOverlayView.java b/car/src/main/java/androidx/car/widget/AlphaJumpOverlayView.java
new file mode 100644
index 0000000..5672880
--- /dev/null
+++ b/car/src/main/java/androidx/car/widget/AlphaJumpOverlayView.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2018 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 android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import java.util.Collection;
+
+import androidx.annotation.NonNull;
+import androidx.car.R;
+import androidx.gridlayout.widget.GridLayout;
+
+/**
+ * This view shows a grid of alphabetic letters that you can tap on to advance a list to the
+ * beginning of that list.
+ */
+public class AlphaJumpOverlayView extends GridLayout {
+    private IAlphaJumpAdapter mAdapter;
+    private PagedListView mPagedListView;
+    private Collection<IAlphaJumpAdapter.Bucket> mBuckets;
+
+    public AlphaJumpOverlayView(@NonNull Context context) {
+        super(context);
+        setBackgroundResource(R.color.car_card);
+        setColumnCount(context.getResources().getInteger(R.integer.alpha_jump_button_columns));
+        setUseDefaultMargins(false);
+    }
+
+    void init(PagedListView plv, IAlphaJumpAdapter adapter) {
+        mPagedListView = plv;
+        mAdapter = adapter;
+        mBuckets = adapter.getAlphaJumpBuckets();
+
+        createButtons();
+    }
+
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        // TODO: change the hamburger button into a back button...
+        if (visibility == VISIBLE && changedView == this) {
+            mAdapter.onAlphaJumpEnter();
+        }
+    }
+
+    private void createButtons() {
+        LayoutInflater inflater = LayoutInflater.from(getContext());
+        removeAllViews();
+        for (IAlphaJumpAdapter.Bucket bucket : mBuckets) {
+            View container = inflater.inflate(R.layout.car_alpha_jump_button, this, false);
+            TextView btn = container.findViewById(R.id.button);
+            btn.setText(bucket.getLabel());
+            btn.setOnClickListener(this::onButtonClick);
+            btn.setTag(bucket);
+            if (bucket.isEmpty()) {
+                btn.setEnabled(false);
+            }
+            addView(container);
+        }
+    }
+
+    private void onButtonClick(View v) {
+        setVisibility(View.GONE);
+        IAlphaJumpAdapter.Bucket bucket = (IAlphaJumpAdapter.Bucket) v.getTag();
+        if (bucket != null) {
+            mAdapter.onAlphaJumpLeave(bucket);
+
+            mPagedListView.snapToPosition(bucket.getIndex());
+        }
+    }
+}
diff --git a/car/src/main/java/androidx/car/widget/DayNightStyle.java b/car/src/main/java/androidx/car/widget/DayNightStyle.java
index 37d0d51..73f9ce4 100644
--- a/car/src/main/java/androidx/car/widget/DayNightStyle.java
+++ b/car/src/main/java/androidx/car/widget/DayNightStyle.java
@@ -16,8 +16,12 @@
 
 package androidx.car.widget;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import androidx.annotation.IntDef;
 
+import java.lang.annotation.Retention;
+
 /**
  * Specifies how the system UI should respond to day/night mode events.
  *
@@ -37,6 +41,7 @@
         DayNightStyle.FORCE_NIGHT,
         DayNightStyle.FORCE_DAY,
 })
+@Retention(SOURCE)
 public @interface DayNightStyle {
     /**
      * Sets the foreground color to be automatically changed based on day/night mode, assuming the
diff --git a/car/src/main/java/androidx/car/widget/IAlphaJumpAdapter.java b/car/src/main/java/androidx/car/widget/IAlphaJumpAdapter.java
new file mode 100644
index 0000000..cc84a8c
--- /dev/null
+++ b/car/src/main/java/androidx/car/widget/IAlphaJumpAdapter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2018 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.Collection;
+
+/**
+ * An interface that you can implement on your Adapter to enable support for Alpha-Jump.
+ */
+public interface IAlphaJumpAdapter {
+    /**
+     * A bucket represents a "button" in the alpha-jump menu.
+     */
+    interface Bucket {
+        /**
+         * Return true if the bucket is empty (and therefore the button should be displayed
+         * disabled).
+         */
+        boolean isEmpty();
+
+        /**
+         * Return the label for this bucket, that is displayed at the text to the user.
+         */
+        CharSequence getLabel();
+
+        /**
+         * Return the index of the first item that this bucket matches in the list.
+         */
+        int getIndex();
+    }
+
+    /**
+     * Generate and populate the buckets used to populate the alpha-jump menu.
+     */
+    Collection<Bucket> getAlphaJumpBuckets();
+
+    /**
+     * Called every time the alpha-jump menu is entered.
+     */
+    void onAlphaJumpEnter();
+
+    /**
+     * Called every time the alpha-jump menu is left.
+     *
+     * @param bucket The {@link Bucket} that the user clicked on, and should be jumped to.
+     */
+    void onAlphaJumpLeave(Bucket bucket);
+}
diff --git a/car/src/main/java/androidx/car/widget/ListItem.java b/car/src/main/java/androidx/car/widget/ListItem.java
index fb8b2f4..8103dd5 100644
--- a/car/src/main/java/androidx/car/widget/ListItem.java
+++ b/car/src/main/java/androidx/car/widget/ListItem.java
@@ -1,17 +1,17 @@
 package androidx.car.widget;
 
 import android.car.drivingstate.CarUxRestrictions;
-import androidx.annotation.CallSuper;
-import androidx.annotation.Nullable;
-import androidx.annotation.StyleRes;
-import androidx.recyclerview.widget.RecyclerView;
 import android.view.View;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Function;
 
+import androidx.annotation.CallSuper;
+import androidx.annotation.Nullable;
+import androidx.annotation.StyleRes;
 import androidx.car.R;
+import androidx.recyclerview.widget.RecyclerView;
 
 /**
  * Definition of items that can be inserted into {@link ListItemAdapter}.
@@ -41,7 +41,7 @@
      *
      * @return type of this ListItem.
      */
-    abstract int getViewType();
+    public abstract int getViewType();
 
     /**
      * Called when ListItem is bound to its ViewHolder.
@@ -134,7 +134,6 @@
         return mHideDivider;
     };
 
-
     /**
      * Does the work that moves the ListItem from dirty state to clean state, i.e. the work required
      * the first time this ListItem {@code bind}s to {@link ListItem.ViewHolder}.
@@ -181,8 +180,8 @@
      * }
      * </pre>
      *
-     * @params binder to interact with subviews in {@code ViewHolder}.
-     * @params cleanUp view binder to revert the effect of {@code binder}. cleanUp binders will be
+     * @param binder to interact with subviews in {@code ViewHolder}.
+     * @param cleanUp view binder to revert the effect of {@code binder}. cleanUp binders will be
      *                 stored in {@link ListItem.ViewHolder} and should be invoked via
      *                 {@link ViewHolder#cleanUp()} before {@code ViewHolder} is recycled.
      *                 This is to avoid changed made to ViewHolder lingers around when ViewHolder is
diff --git a/car/src/main/java/androidx/car/widget/ListItemAdapter.java b/car/src/main/java/androidx/car/widget/ListItemAdapter.java
index 4d58488..b8fc9d9 100644
--- a/car/src/main/java/androidx/car/widget/ListItemAdapter.java
+++ b/car/src/main/java/androidx/car/widget/ListItemAdapter.java
@@ -67,29 +67,37 @@
 
         /**
          * Sets the background color of each item.
+         * Background can be configured by {@link R.styleable#ListItem_listItemBackgroundColor}.
          */
-        public static final int NONE = 0;
+        public static final int SOLID = 0;
+        /**
+         * Sets the background color of each item to none (transparent).
+         */
+        public static final int NONE = 1;
         /**
          * Sets each item in {@link CardView} with a rounded corner background and shadow.
          */
-        public static final int CARD = 1;
+        public static final int CARD = 2;
         /**
          * Sets background of each item so the combined list looks like one elongated card, namely
          * top and bottom item will have rounded corner at only top/bottom side respectively. If
          * only one item exists, it will have both top and bottom rounded corner.
          */
-        public static final int PANEL = 2;
+        public static final int PANEL = 3;
     }
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({
+        BackgroundStyle.SOLID,
         BackgroundStyle.NONE,
         BackgroundStyle.CARD,
-        BackgroundStyle.PANEL })
+        BackgroundStyle.PANEL
+    })
     private @interface ListBackgroundStyle {}
 
     static final int LIST_ITEM_TYPE_TEXT = 1;
     static final int LIST_ITEM_TYPE_SEEKBAR = 2;
+    static final int LIST_ITEM_TYPE_SUBHEADER = 3;
 
     private final SparseIntArray mViewHolderLayoutResIds = new SparseIntArray();
     private final SparseArray<Function<View, ListItem.ViewHolder>> mViewHolderCreator =
@@ -109,8 +117,11 @@
 
     private int mMaxItems = PagedListView.ItemCap.UNLIMITED;
 
+    /**
+     * Defaults {@link BackgroundStyle} to {@link BackgroundStyle#SOLID}.
+     */
     public ListItemAdapter(Context context, ListItemProvider itemProvider) {
-        this(context, itemProvider, BackgroundStyle.NONE);
+        this(context, itemProvider, BackgroundStyle.SOLID);
     }
 
     public ListItemAdapter(Context context, ListItemProvider itemProvider,
@@ -123,6 +134,8 @@
                 R.layout.car_list_item_text_content, TextListItem::createViewHolder);
         registerListItemViewType(LIST_ITEM_TYPE_SEEKBAR,
                 R.layout.car_list_item_seekbar_content, SeekbarListItem::createViewHolder);
+        registerListItemViewType(LIST_ITEM_TYPE_SUBHEADER,
+                R.layout.car_list_item_subheader_content, SubheaderListItem::createViewHolder);
 
         mUxRestrictionsHelper =
                 new CarUxRestrictionsHelper(context, carUxRestrictions -> {
@@ -215,31 +228,27 @@
      */
     private ViewGroup createListItemContainer() {
         ViewGroup container;
-        switch (mBackgroundStyle) {
-            case BackgroundStyle.NONE:
-            case BackgroundStyle.PANEL:
-                FrameLayout frameLayout = new FrameLayout(mContext);
-                frameLayout.setLayoutParams(new RecyclerView.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+        if (mBackgroundStyle == BackgroundStyle.CARD) {
+            CardView card = new CardView(mContext);
+            RecyclerView.LayoutParams cardLayoutParams = new RecyclerView.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+            cardLayoutParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.car_padding_3);
+            card.setLayoutParams(cardLayoutParams);
+            card.setRadius(mContext.getResources().getDimensionPixelSize(R.dimen.car_radius_1));
+            card.setCardBackgroundColor(mListItemBackgroundColor);
+
+            container = card;
+        } else {
+            FrameLayout frameLayout = new FrameLayout(mContext);
+            frameLayout.setLayoutParams(new RecyclerView.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+            // Skip setting background color for NONE.
+            if (mBackgroundStyle != BackgroundStyle.NONE) {
                 frameLayout.setBackgroundColor(mListItemBackgroundColor);
+            }
 
-                container = frameLayout;
-                break;
-            case BackgroundStyle.CARD:
-                CardView card = new CardView(mContext);
-                RecyclerView.LayoutParams cardLayoutParams = new RecyclerView.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-                cardLayoutParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
-                        R.dimen.car_padding_3);
-                card.setLayoutParams(cardLayoutParams);
-                card.setRadius(mContext.getResources().getDimensionPixelSize(R.dimen.car_radius_1));
-                card.setCardBackgroundColor(mListItemBackgroundColor);
-
-                container = card;
-                break;
-            default:
-                throw new IllegalArgumentException("Unknown background style. "
-                    + "Expected constants in class ListItemAdapter.BackgroundStyle.");
+            container = frameLayout;
         }
         return container;
     }
diff --git a/car/src/main/java/androidx/car/widget/PagedListView.java b/car/src/main/java/androidx/car/widget/PagedListView.java
index d677489..4d16e0c 100644
--- a/car/src/main/java/androidx/car/widget/PagedListView.java
+++ b/car/src/main/java/androidx/car/widget/PagedListView.java
@@ -18,6 +18,8 @@
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -28,18 +30,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Parcelable;
-import androidx.annotation.ColorRes;
-import androidx.annotation.IdRes;
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.UiThread;
-import androidx.annotation.VisibleForTesting;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.OrientationHelper;
-import androidx.recyclerview.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -48,7 +38,21 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import androidx.annotation.ColorRes;
+import androidx.annotation.IdRes;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.UiThread;
+import androidx.annotation.VisibleForTesting;
 import androidx.car.R;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.OrientationHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.lang.annotation.Retention;
 
 /**
  * View that wraps a {@link RecyclerView} and a scroll bar that has
@@ -87,7 +91,6 @@
      */
     private static final int PAGINATION_HOLD_DELAY_MS = 400;
 
-
     /**
      * When doing a snap, offset the snap by this number of position and then do a smooth scroll to
      * the final position.
@@ -104,6 +107,13 @@
     @VisibleForTesting
     PagedScrollBarView mScrollBarView;
 
+    /**
+     * AlphaJumpOverlayView that will be null until the first time you tap the alpha jump button, at
+     * which point we'll construct it and add it to the view hierarchy as a child of this frame
+     * layout.
+     */
+    @Nullable private AlphaJumpOverlayView mAlphaJumpView;
+
     private int mRowsPerPage = -1;
     private RecyclerView.Adapter<? extends RecyclerView.ViewHolder> mAdapter;
 
@@ -181,6 +191,7 @@
             Gutter.END,
             Gutter.BOTH,
     })
+    @Retention(SOURCE)
     public @interface Gutter {
         /**
          * No gutter on either side of the list items. The items will span the full width of the
@@ -254,7 +265,7 @@
         mSnapHelper = new PagedSnapHelper(context);
         mSnapHelper.attachToRecyclerView(mRecyclerView);
 
-        mRecyclerView.setOnScrollListener(mRecyclerViewOnScrollListener);
+        mRecyclerView.addOnScrollListener(mRecyclerViewOnScrollListener);
         mRecyclerView.getRecycledViewPool().setMaxRecycledViews(0, 12);
 
         int defaultGutterSize = getResources().getDimensionPixelSize(R.dimen.car_margin);
@@ -307,23 +318,31 @@
         setFocusable(false);
 
         mScrollBarEnabled = a.getBoolean(R.styleable.PagedListView_scrollBarEnabled, true);
-        mScrollBarView = (PagedScrollBarView) findViewById(R.id.paged_scroll_view);
-        mScrollBarView.setPaginationListener(direction -> {
-            switch (direction) {
-                case PagedScrollBarView.PaginationListener.PAGE_UP:
-                    pageUp();
-                    if (mOnScrollListener != null) {
-                        mOnScrollListener.onScrollUpButtonClicked();
-                    }
-                    break;
-                case PagedScrollBarView.PaginationListener.PAGE_DOWN:
-                    pageDown();
-                    if (mOnScrollListener != null) {
-                        mOnScrollListener.onScrollDownButtonClicked();
-                    }
-                    break;
-                default:
-                    Log.e(TAG, "Unknown pagination direction (" + direction + ")");
+        mScrollBarView = findViewById(R.id.paged_scroll_view);
+        mScrollBarView.setPaginationListener(new PagedScrollBarView.PaginationListener() {
+            @Override
+            public void onPaginate(int direction) {
+                switch (direction) {
+                    case PagedScrollBarView.PaginationListener.PAGE_UP:
+                        pageUp();
+                        if (mOnScrollListener != null) {
+                            mOnScrollListener.onScrollUpButtonClicked();
+                        }
+                        break;
+                    case PagedScrollBarView.PaginationListener.PAGE_DOWN:
+                        pageDown();
+                        if (mOnScrollListener != null) {
+                            mOnScrollListener.onScrollDownButtonClicked();
+                        }
+                        break;
+                    default:
+                        Log.e(TAG, "Unknown pagination direction (" + direction + ")");
+                }
+            }
+
+            @Override
+            public void onAlphaJump() {
+                showAlphaJump();
             }
         });
 
@@ -525,7 +544,6 @@
         if ((layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
             PointF vector = ((RecyclerView.SmoothScroller.ScrollVectorProvider) layoutManager)
                     .computeScrollVectorForPosition(position);
-
             // A positive value in the vector means scrolling down, so should offset by scrolling to
             // an item previous in the list.
             int offsetDirection = (vector == null || vector.y > 0) ? -1 : 1;
@@ -576,6 +594,7 @@
         mRecyclerView.setAdapter(adapter);
 
         updateMaxItems();
+        updateAlphaJump();
     }
 
     /**
@@ -836,6 +855,13 @@
         int screenSize = mRecyclerView.getHeight();
         int scrollDistance = screenSize;
 
+        // If the last item is partially visible, page down should bring it to the top.
+        View lastChild = mRecyclerView.getChildAt(mRecyclerView.getChildCount() - 1);
+        if (mRecyclerView.getLayoutManager().isViewPartiallyVisible(lastChild,
+                /* completelyVisible= */ false, /* acceptEndPointInclusion= */ false)) {
+            scrollDistance = orientationHelper.getDecoratedStart(lastChild);
+        }
+
         // The iteration order matters. In case where there are 2 items longer than screen size, we
         // want to focus on upcoming view (the one at the bottom of screen).
         for (int i = mRecyclerView.getChildCount() - 1; i >= 0; i--) {
@@ -1109,6 +1135,21 @@
         dispatchThawSelfOnly(container);
     }
 
+    private void updateAlphaJump() {
+        boolean supportsAlphaJump = (mAdapter instanceof IAlphaJumpAdapter);
+        mScrollBarView.setShowAlphaJump(supportsAlphaJump);
+    }
+
+    private void showAlphaJump() {
+        if (mAlphaJumpView == null && mAdapter instanceof IAlphaJumpAdapter) {
+            mAlphaJumpView = new AlphaJumpOverlayView(getContext());
+            mAlphaJumpView.init(this, (IAlphaJumpAdapter) mAdapter);
+            addView(mAlphaJumpView);
+        }
+
+        mAlphaJumpView.setVisibility(View.VISIBLE);
+    }
+
     private final RecyclerView.OnScrollListener mRecyclerViewOnScrollListener =
             new RecyclerView.OnScrollListener() {
                 @Override
@@ -1162,6 +1203,12 @@
         public void onScrollUpButtonClicked() {}
         /** Called when scroll down button is clicked */
         public void onScrollDownButtonClicked() {}
+        /** Called when the alpha jump button is clicked. */
+        public void onAlphaJumpButtonClicked() {}
+        /** Called when scrolling to the previous page via up gesture */
+        public void onGestureUp() {}
+        /** Called when scrolling to the next page via down gesture */
+        public void onGestureDown() {}
 
         /**
          * Called when RecyclerView.OnScrollListener#onScrolled is called. See
diff --git a/car/src/main/java/androidx/car/widget/PagedScrollBarView.java b/car/src/main/java/androidx/car/widget/PagedScrollBarView.java
index 8d26916..aa62813 100644
--- a/car/src/main/java/androidx/car/widget/PagedScrollBarView.java
+++ b/car/src/main/java/androidx/car/widget/PagedScrollBarView.java
@@ -20,9 +20,6 @@
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
-import androidx.annotation.ColorRes;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.content.ContextCompat;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -31,12 +28,15 @@
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.TextView;
 
+import androidx.annotation.ColorRes;
+import androidx.annotation.VisibleForTesting;
 import androidx.car.R;
+import androidx.core.content.ContextCompat;
 
 /** A custom view to provide list scroll behaviour -- up/down buttons and scroll indicator. */
-public class PagedScrollBarView extends FrameLayout
-        implements View.OnClickListener, View.OnLongClickListener {
+public class PagedScrollBarView extends FrameLayout {
     private static final float BUTTON_DISABLED_ALPHA = 0.2f;
 
     @DayNightStyle private int mDayNightStyle;
@@ -48,10 +48,21 @@
 
         /** Called when the linked view should be paged in the given direction */
         void onPaginate(int direction);
+
+        /**
+         * Called when the 'alpha jump' button is clicked and the linked view should switch into
+         * alpha jump mode, where we display a list of buttons to allow the user to quickly scroll
+         * to a certain point in the list, bypassing a lot of manual scrolling.
+         */
+        void onAlphaJump();
     }
 
     private final ImageView mUpButton;
+    private final PaginateButtonClickListener mUpButtonClickListener;
     private final ImageView mDownButton;
+    private final PaginateButtonClickListener mDownButtonClickListener;
+    private final TextView mAlphaJumpButton;
+    private final AlphaJumpButtonClickListener mAlphaJumpButtonClickListener;
     private final View mScrollThumb;
     /** The "filler" view between the up and down buttons */
     private final View mFiller;
@@ -78,28 +89,21 @@
         inflater.inflate(R.layout.car_paged_scrollbar_buttons, this /* root */,
                 true /* attachToRoot */);
 
-        mUpButton = (ImageView) findViewById(R.id.page_up);
-        mUpButton.setOnClickListener(this);
-        mUpButton.setOnLongClickListener(this);
-        mDownButton = (ImageView) findViewById(R.id.page_down);
-        mDownButton.setOnClickListener(this);
-        mDownButton.setOnLongClickListener(this);
+        mUpButtonClickListener = new PaginateButtonClickListener(PaginationListener.PAGE_UP);
+        mDownButtonClickListener = new PaginateButtonClickListener(PaginationListener.PAGE_DOWN);
+        mAlphaJumpButtonClickListener = new AlphaJumpButtonClickListener();
+
+        mUpButton = findViewById(R.id.page_up);
+        mUpButton.setOnClickListener(mUpButtonClickListener);
+        mDownButton = findViewById(R.id.page_down);
+        mDownButton.setOnClickListener(mDownButtonClickListener);
+        mAlphaJumpButton = findViewById(R.id.alpha_jump);
+        mAlphaJumpButton.setOnClickListener(mAlphaJumpButtonClickListener);
 
         mScrollThumb = findViewById(R.id.scrollbar_thumb);
         mFiller = findViewById(R.id.filler);
     }
 
-    @Override
-    public void onClick(View v) {
-        dispatchPageClick(v);
-    }
-
-    @Override
-    public boolean onLongClick(View v) {
-        dispatchPageClick(v);
-        return true;
-    }
-
     /** Sets the icon to be used for the up button. */
     public void setUpButtonIcon(Drawable icon) {
         mUpButton.setImageDrawable(icon);
@@ -116,7 +120,9 @@
      * @param listener The listener to set.
      */
     public void setPaginationListener(PaginationListener listener) {
-        mPaginationListener = listener;
+        mUpButtonClickListener.setPaginationListener(listener);
+        mDownButtonClickListener.setPaginationListener(listener);
+        mAlphaJumpButtonClickListener.setPaginationListener(listener);
     }
 
     /** Returns {@code true} if the "up" button is pressed */
@@ -129,6 +135,10 @@
         return mDownButton.isPressed();
     }
 
+    void setShowAlphaJump(boolean show) {
+        mAlphaJumpButton.setVisibility(show ? View.VISIBLE : View.GONE);
+    }
+
     /**
      * Sets the range, offset and extent of the scroll bar. The range represents the size of a
      * container for the scrollbar thumb; offset is the distance from the start of the container
@@ -261,7 +271,7 @@
                 upDownBackgroundResId = R.drawable.car_button_ripple_background_night;
                 break;
             case DayNightStyle.FORCE_DAY:
-                tintResId =  R.color.car_tint_dark;
+                tintResId = R.color.car_tint_dark;
                 thumbColorResId = R.color.car_scrollbar_thumb_dark;
                 upDownBackgroundResId = R.drawable.car_button_ripple_background_day;
                 break;
@@ -281,6 +291,8 @@
 
         mDownButton.setColorFilter(tint, PorterDuff.Mode.SRC_IN);
         mDownButton.setBackgroundResource(upDownBackgroundResId);
+
+        mAlphaJumpButton.setBackgroundResource(upDownBackgroundResId);
     }
 
     private void setScrollbarThumbColor(@ColorRes int color) {
@@ -293,18 +305,6 @@
         return ((GradientDrawable) mScrollThumb.getBackground()).getColor().getDefaultColor();
     }
 
-    private void dispatchPageClick(View v) {
-        final PaginationListener listener = mPaginationListener;
-        if (listener == null) {
-            return;
-        }
-
-        int direction = v.getId() == R.id.page_up
-                ? PaginationListener.PAGE_UP
-                : PaginationListener.PAGE_DOWN;
-        listener.onPaginate(direction);
-    }
-
     /** Moves the given view to the specified 'y' position. */
     private void moveY(final View view, float newPosition, boolean animate) {
         final int duration = animate ? 200 : 0;
@@ -314,4 +314,40 @@
                 .setInterpolator(mPaginationInterpolator)
                 .start();
     }
+
+    private static class PaginateButtonClickListener implements View.OnClickListener {
+        private final int mPaginateDirection;
+        private PaginationListener mPaginationListener;
+
+        PaginateButtonClickListener(int paginateDirection) {
+            mPaginateDirection = paginateDirection;
+        }
+
+        public void setPaginationListener(PaginationListener listener) {
+            mPaginationListener = listener;
+        }
+
+        @Override
+        public void onClick(View v) {
+            if (mPaginationListener != null) {
+                mPaginationListener.onPaginate(mPaginateDirection);
+            }
+        }
+    }
+
+    private static class AlphaJumpButtonClickListener implements View.OnClickListener {
+        private PaginationListener mPaginationListener;
+
+        public void setPaginationListener(PaginationListener listener) {
+            mPaginationListener = listener;
+        }
+
+        @Override
+        public void onClick(View v) {
+            if (mPaginationListener != null) {
+                mPaginationListener.onAlphaJump();
+            }
+        }
+
+    }
 }
diff --git a/car/src/main/java/androidx/car/widget/SeekbarListItem.java b/car/src/main/java/androidx/car/widget/SeekbarListItem.java
index 027f495..368e6c0 100644
--- a/car/src/main/java/androidx/car/widget/SeekbarListItem.java
+++ b/car/src/main/java/androidx/car/widget/SeekbarListItem.java
@@ -142,7 +142,7 @@
      * Used by {@link ListItemAdapter} to choose layout to inflate for view holder.
      */
     @Override
-    int getViewType() {
+    public int getViewType() {
         return ListItemAdapter.LIST_ITEM_TYPE_SEEKBAR;
     }
 
diff --git a/car/src/main/java/androidx/car/widget/SubheaderListItem.java b/car/src/main/java/androidx/car/widget/SubheaderListItem.java
new file mode 100644
index 0000000..dd5a00b
--- /dev/null
+++ b/car/src/main/java/androidx/car/widget/SubheaderListItem.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2018 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.car.drivingstate.CarUxRestrictions;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.DimenRes;
+import androidx.annotation.IntDef;
+import androidx.annotation.StyleRes;
+import androidx.car.R;
+import androidx.car.utils.CarUxRestrictionsUtils;
+
+import java.lang.annotation.Retention;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class to build a sub-header list item.
+ *
+ * <p>A sub-header list item consists of a one-line text. Its margin can be adjusted to match the
+ * rest of {@link ListItem} through {@link #setTextStartMarginType(int)}.
+ */
+public class SubheaderListItem extends ListItem<SubheaderListItem.ViewHolder> {
+
+    /**
+     * Creates a {@link SubheaderListItem.ViewHolder}.
+     */
+    public static ViewHolder createViewHolder(View itemView) {
+        return new ViewHolder(itemView);
+    }
+
+    private final Context mContext;
+
+    private final List<ViewBinder<ViewHolder>> mBinders = new ArrayList<>();
+
+    @StyleRes private int mListItemSubheaderTextAppearance;
+    private String mText;
+
+    public SubheaderListItem(Context context, String text) {
+
+        mContext = context;
+        mText = text;
+        mTextStartMarginType = TEXT_START_MARGIN_TYPE_NONE;
+
+        TypedArray a = context.getTheme().obtainStyledAttributes(R.styleable.ListItem);
+        mListItemSubheaderTextAppearance = a.getResourceId(
+                R.styleable.ListItem_listItemSubheaderTextAppearance,
+                R.style.TextAppearance_Car_Subheader);
+        a.recycle();
+
+        markDirty();
+    }
+
+    @IntDef({
+            TEXT_START_MARGIN_TYPE_NONE, TEXT_START_MARGIN_TYPE_LARGE,
+            TEXT_START_MARGIN_TYPE_SMALL})
+    @Retention(SOURCE)
+    public @interface TextStartMarginType {}
+
+    /**
+     * Sets start margin of text the same as {@link TextListItem#setPrimaryActionNoIcon()}.
+     */
+    public static final int TEXT_START_MARGIN_TYPE_NONE = 0;
+    /**
+     * Sets start margin of text the same as {@link TextListItem#setPrimaryActionIcon(int, boolean)}
+     * with {@code useLargeIcon} set to {@code false}.
+     */
+    public static final int TEXT_START_MARGIN_TYPE_SMALL = 1;
+    /**
+     * Sets start margin of text the same as {@link TextListItem#setPrimaryActionIcon(int, boolean)}
+     * with {@code useLargeIcon} set to {@code true}.
+     */
+    public static final int TEXT_START_MARGIN_TYPE_LARGE = 2;
+    @TextStartMarginType private int mTextStartMarginType;
+
+    /**
+     * Sets the start margin of text. Defaults to {@link #TEXT_START_MARGIN_TYPE_NONE}.
+     */
+    public void setTextStartMarginType(@TextStartMarginType int type) {
+        mTextStartMarginType = type;
+        markDirty();
+    }
+
+    /**
+     * Sets the text to be displayed.
+     */
+    public void setText(String text) {
+        mText = text;
+        markDirty();
+    }
+
+    /**
+     * Used by {@link ListItemAdapter} to choose layout to inflate for view holder.
+     */
+    @Override
+    public int getViewType() {
+        return ListItemAdapter.LIST_ITEM_TYPE_SUBHEADER;
+    }
+
+    /**
+     * Calculates layout params for views in {@link ViewHolder}.
+     */
+    @Override
+    protected void resolveDirtyState() {
+        mBinders.clear();
+
+        setItemLayoutHeight();
+        setText();
+    }
+
+    /**
+     * Applies ViewBinders to adjust view layout params.
+     */
+    @Override
+    protected void onBind(ViewHolder viewHolder) {
+        for (ViewBinder binder : mBinders) {
+            binder.bind(viewHolder);
+        }
+    }
+
+    private void setItemLayoutHeight() {
+        int height = mContext.getResources().getDimensionPixelSize(R.dimen.car_sub_header_height);
+        mBinders.add(vh -> {
+            vh.itemView.getLayoutParams().height = height;
+            vh.itemView.requestLayout();
+        });
+    }
+
+    private void setText() {
+        @DimenRes int textStartMarginDimen;
+        switch (mTextStartMarginType) {
+            case TEXT_START_MARGIN_TYPE_NONE:
+                textStartMarginDimen = R.dimen.car_keyline_1;
+                break;
+            case TEXT_START_MARGIN_TYPE_LARGE:
+                textStartMarginDimen = R.dimen.car_keyline_4;
+                break;
+            case TEXT_START_MARGIN_TYPE_SMALL:
+                textStartMarginDimen = R.dimen.car_keyline_3;
+                break;
+            default:
+                throw new IllegalStateException("Unknown text start margin type.");
+        }
+
+        mBinders.add(vh -> {
+            vh.getText().setText(mText);
+            vh.getText().setTextAppearance(mListItemSubheaderTextAppearance);
+
+            ViewGroup.MarginLayoutParams layoutParams =
+                    (ViewGroup.MarginLayoutParams) vh.getText().getLayoutParams();
+            layoutParams.setMarginStart(
+                    mContext.getResources().getDimensionPixelSize(textStartMarginDimen));
+            vh.getText().requestLayout();
+        });
+    }
+
+    /**
+     * Holds views of SubHeaderListItem.
+     */
+    public static class ViewHolder extends ListItem.ViewHolder {
+
+        private TextView mText;
+
+        public ViewHolder(View itemView) {
+            super(itemView);
+            mText = itemView.findViewById(R.id.text);
+        }
+
+        /**
+         * Update children views to comply with car UX restrictions.
+         *
+         * <p>{@code Text} might be truncated to meet length limit required by regulation.
+         *
+         * @param restrictions current car UX restrictions.
+         */
+        @Override
+        void complyWithUxRestrictions(CarUxRestrictions restrictions) {
+            CarUxRestrictionsUtils.comply(itemView.getContext(), restrictions, getText());
+        }
+
+        public TextView getText() {
+            return mText;
+        }
+    }
+}
diff --git a/collection/src/main/java/androidx/collection/ContainerHelpers.java b/collection/src/main/java/androidx/collection/ContainerHelpers.java
index 4c969d4..f385f9b 100644
--- a/collection/src/main/java/androidx/collection/ContainerHelpers.java
+++ b/collection/src/main/java/androidx/collection/ContainerHelpers.java
@@ -79,4 +79,7 @@
         }
         return ~lo;  // value not present
     }
+
+    private ContainerHelpers() {
+    }
 }
diff --git a/compat/OWNERS b/compat/OWNERS
index cf4df58..2dc467e 100644
--- a/compat/OWNERS
+++ b/compat/OWNERS
@@ -1,4 +1,5 @@
 aurimas@google.com
 cinek@google.com
 clarabayarri@google.com
-juliacr@google.com
\ No newline at end of file
+juliacr@google.com
+jmonk@google.com
\ No newline at end of file
diff --git a/compat/api/current.txt b/compat/api/current.txt
index 31d571a..1ceedbe 100644
--- a/compat/api/current.txt
+++ b/compat/api/current.txt
@@ -82,7 +82,7 @@
   }
 
   public class AppLaunchChecker {
-    ctor public AppLaunchChecker();
+    ctor public deprecated AppLaunchChecker();
     method public static boolean hasStartedFromLauncher(android.content.Context);
     method public static void onActivityCreate(android.app.Activity);
   }
@@ -157,7 +157,7 @@
   }
 
   public class NotificationCompat {
-    ctor public NotificationCompat();
+    ctor public deprecated NotificationCompat();
     method public static androidx.core.app.NotificationCompat.Action getAction(android.app.Notification, int);
     method public static int getActionCount(android.app.Notification);
     method public static int getBadgeIconType(android.app.Notification);
@@ -444,7 +444,8 @@
   public static class NotificationCompat.MessagingStyle extends androidx.core.app.NotificationCompat.Style {
     ctor public NotificationCompat.MessagingStyle(java.lang.CharSequence);
     method public void addCompatExtras(android.os.Bundle);
-    method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public deprecated androidx.core.app.NotificationCompat.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(java.lang.CharSequence, long, androidx.core.app.Person);
     method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message);
     method public static androidx.core.app.NotificationCompat.MessagingStyle extractMessagingStyleFromNotification(android.app.Notification);
     method public java.lang.CharSequence getConversationTitle();
@@ -457,11 +458,13 @@
   }
 
   public static final class NotificationCompat.MessagingStyle.Message {
-    ctor public NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
+    ctor public NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, androidx.core.app.Person);
+    ctor public deprecated NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
     method public java.lang.String getDataMimeType();
     method public android.net.Uri getDataUri();
     method public android.os.Bundle getExtras();
-    method public java.lang.CharSequence getSender();
+    method public androidx.core.app.Person getPerson();
+    method public deprecated java.lang.CharSequence getSender();
     method public java.lang.CharSequence getText();
     method public long getTimestamp();
     method public androidx.core.app.NotificationCompat.MessagingStyle.Message setData(java.lang.String, android.net.Uri);
@@ -571,6 +574,29 @@
     field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
   }
 
+  public class Person {
+    method public static androidx.core.app.Person fromBundle(android.os.Bundle);
+    method public android.graphics.Bitmap getIcon();
+    method public java.lang.String getKey();
+    method public java.lang.CharSequence getName();
+    method public java.lang.String getUri();
+    method public boolean isBot();
+    method public boolean isImportant();
+    method public androidx.core.app.Person.Builder toBuilder();
+    method public android.os.Bundle toBundle();
+  }
+
+  public static class Person.Builder {
+    ctor public Person.Builder();
+    method public androidx.core.app.Person build();
+    method public androidx.core.app.Person.Builder setBot(boolean);
+    method public androidx.core.app.Person.Builder setIcon(android.graphics.Bitmap);
+    method public androidx.core.app.Person.Builder setImportant(boolean);
+    method public androidx.core.app.Person.Builder setKey(java.lang.String);
+    method public androidx.core.app.Person.Builder setName(java.lang.CharSequence);
+    method public androidx.core.app.Person.Builder setUri(java.lang.String);
+  }
+
   public final class RemoteInput {
     method public static void addDataResultToIntent(androidx.core.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
     method public static void addResultsToIntent(androidx.core.app.RemoteInput[], android.content.Intent, android.os.Bundle);
@@ -779,6 +805,10 @@
     field public static final deprecated int CONFIG_UI_MODE = 512; // 0x200
   }
 
+  public final class PackageInfoCompat {
+    method public static long getLongVersionCode(android.content.pm.PackageInfo);
+  }
+
   public class ShortcutInfoCompat {
     method public android.content.ComponentName getActivity();
     method public java.lang.CharSequence getDisabledMessage();
@@ -1062,6 +1092,7 @@
     method public static deprecated boolean isAtLeastO();
     method public static deprecated boolean isAtLeastOMR1();
     method public static boolean isAtLeastP();
+    method public static boolean isAtLeastQ();
   }
 
   public final class CancellationSignal {
@@ -1293,6 +1324,10 @@
     method public java.io.FileOutputStream startWrite() throws java.io.IOException;
   }
 
+  public abstract interface Consumer<T> {
+    method public abstract void accept(T);
+  }
+
   public class ObjectsCompat {
     method public static boolean equals(java.lang.Object, java.lang.Object);
     method public static int hash(java.lang.Object...);
@@ -1873,6 +1908,7 @@
     method public static deprecated int getScaledPagingTouchSlop(android.view.ViewConfiguration);
     method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
     method public static deprecated boolean hasPermanentMenuKey(android.view.ViewConfiguration);
+    method public static boolean shouldShowMenuShortcutsWhenKeyboardPresent(android.view.ViewConfiguration, android.content.Context);
   }
 
   public final class ViewGroupCompat {
@@ -2409,7 +2445,7 @@
 package androidx.core.view.inputmethod {
 
   public final class EditorInfoCompat {
-    ctor public EditorInfoCompat();
+    ctor public deprecated EditorInfoCompat();
     method public static java.lang.String[] getContentMimeTypes(android.view.inputmethod.EditorInfo);
     method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, java.lang.String[]);
     field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
@@ -2417,7 +2453,7 @@
   }
 
   public final class InputConnectionCompat {
-    ctor public InputConnectionCompat();
+    ctor public deprecated InputConnectionCompat();
     method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
     method public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
     field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1
diff --git a/compat/res/values-as/strings.xml b/compat/res/values-as/strings.xml
new file mode 100644
index 0000000..b9c349e
--- /dev/null
+++ b/compat/res/values-as/strings.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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="7988687684186075107">"৯৯৯+"</string>
+</resources>
diff --git a/compat/res/values-or/strings.xml b/compat/res/values-or/strings.xml
new file mode 100644
index 0000000..f544aef
--- /dev/null
+++ b/compat/res/values-or/strings.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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="7988687684186075107">"999+"</string>
+</resources>
diff --git a/compat/src/androidTest/AndroidManifest.xml b/compat/src/androidTest/AndroidManifest.xml
index 2cad9bf..064f6e9 100644
--- a/compat/src/androidTest/AndroidManifest.xml
+++ b/compat/src/androidTest/AndroidManifest.xml
@@ -52,7 +52,6 @@
 
         <activity android:name="androidx.core.widget.ContentLoadingProgressBarActivity"/>
 
-        <activity android:name="androidx.core.widget.TextViewTestActivity"/>
         <activity android:name="androidx.core.app.FrameMetricsActivity"/>
         <activity android:name="androidx.core.app.FrameMetricsSubActivity"/>
 
diff --git a/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java b/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
index b4117e3..a882891 100644
--- a/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
+++ b/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
@@ -44,6 +44,8 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.BaseInstrumentationTestCase;
 
+import androidx.core.app.NotificationCompat.MessagingStyle.Message;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -315,18 +317,18 @@
     public void testMessage_setAndGetExtras() throws Throwable {
         String extraKey = "extra_key";
         CharSequence extraValue = "extra_value";
-        NotificationCompat.MessagingStyle.Message m =
-                new NotificationCompat.MessagingStyle.Message("text", 0 /*timestamp */, "sender");
+        Message m =
+                new Message("text", 0 /*timestamp */, "sender");
         m.getExtras().putCharSequence(extraKey, extraValue);
         assertEquals(extraValue, m.getExtras().getCharSequence(extraKey));
 
-        ArrayList<NotificationCompat.MessagingStyle.Message> messages = new ArrayList<>(1);
+        ArrayList<Message> messages = new ArrayList<>(1);
         messages.add(m);
         Bundle[] bundleArray =
-                NotificationCompat.MessagingStyle.Message.getBundleArrayForMessages(messages);
+                Message.getBundleArrayForMessages(messages);
         assertEquals(1, bundleArray.length);
-        NotificationCompat.MessagingStyle.Message fromBundle =
-                NotificationCompat.MessagingStyle.Message.getMessageFromBundle(bundleArray[0]);
+        Message fromBundle =
+                Message.getMessageFromBundle(bundleArray[0]);
         assertEquals(extraValue, fromBundle.getExtras().getCharSequence(extraKey));
     }
 
@@ -526,71 +528,119 @@
     }
 
     @Test
-    public void messagingStyle_isGroupConversation() {
+    public void testMessagingStyle_message() {
+        NotificationCompat.MessagingStyle messagingStyle =
+                new NotificationCompat.MessagingStyle("self name");
+        Person person = new Person.Builder().setName("test name").setKey("key").build();
+        Person person2 = new Person.Builder()
+                .setName("test name 2").setKey("key 2").setImportant(true).build();
+        messagingStyle.addMessage("text", 200, person);
+        messagingStyle.addMessage("text2", 300, person2);
+
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
+                .setSmallIcon(1)
+                .setContentTitle("test title")
+                .setStyle(messagingStyle)
+                .build();
+
+        List<Message> result = NotificationCompat.MessagingStyle
+                .extractMessagingStyleFromNotification(notification)
+                .getMessages();
+
+        assertEquals(2, result.size());
+        assertEquals("text", result.get(0).getText());
+        assertEquals(200, result.get(0).getTimestamp());
+        assertEquals("test name", result.get(0).getPerson().getName());
+        assertEquals("key", result.get(0).getPerson().getKey());
+        assertEquals("text2", result.get(1).getText());
+        assertEquals(300, result.get(1).getTimestamp());
+        assertEquals("test name 2", result.get(1).getPerson().getName());
+        assertEquals("key 2", result.get(1).getPerson().getKey());
+        assertTrue(result.get(1).getPerson().isImportant());
+    }
+
+    @Test
+    public void testMessagingStyle_isGroupConversation() {
         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
         NotificationCompat.MessagingStyle messagingStyle =
                 new NotificationCompat.MessagingStyle("self name")
                         .setGroupConversation(true)
                         .setConversationTitle("test conversation title");
-        new NotificationCompat.Builder(mContext, "test id")
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
-        assertTrue(messagingStyle.isGroupConversation());
+        NotificationCompat.MessagingStyle result =
+                NotificationCompat.MessagingStyle
+                        .extractMessagingStyleFromNotification(notification);
+
+        assertTrue(result.isGroupConversation());
     }
 
     @Test
-    public void messagingStyle_isGroupConversation_noConversationTitle() {
+    public void testMessagingStyle_isGroupConversation_noConversationTitle() {
         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
         NotificationCompat.MessagingStyle messagingStyle =
                 new NotificationCompat.MessagingStyle("self name")
                         .setGroupConversation(true)
                         .setConversationTitle(null);
-        new NotificationCompat.Builder(mContext, "test id")
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
-        assertTrue(messagingStyle.isGroupConversation());
+        NotificationCompat.MessagingStyle result =
+                NotificationCompat.MessagingStyle
+                        .extractMessagingStyleFromNotification(notification);
+
+        assertTrue(result.isGroupConversation());
     }
 
     @Test
-    public void messagingStyle_isGroupConversation_withConversationTitle_legacy() {
+    public void testMessagingStyle_isGroupConversation_withConversationTitle_legacy() {
         // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
         NotificationCompat.MessagingStyle messagingStyle =
                 new NotificationCompat.MessagingStyle("self name")
                         .setConversationTitle("test conversation title");
-        new NotificationCompat.Builder(mContext, "test id")
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
-        assertTrue(messagingStyle.isGroupConversation());
+        NotificationCompat.MessagingStyle result =
+                NotificationCompat.MessagingStyle
+                        .extractMessagingStyleFromNotification(notification);
+
+        assertTrue(result.isGroupConversation());
     }
 
     @Test
-    public void messagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
+    public void testMessagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
         // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
         NotificationCompat.MessagingStyle messagingStyle =
                 new NotificationCompat.MessagingStyle("self name")
                         .setConversationTitle(null);
-        new NotificationCompat.Builder(mContext, "test id")
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
-        assertFalse(messagingStyle.isGroupConversation());
+        NotificationCompat.MessagingStyle result =
+                NotificationCompat.MessagingStyle
+                        .extractMessagingStyleFromNotification(notification);
+
+        assertFalse(result.isGroupConversation());
     }
 
     @Test
-    public void messagingStyle_isGroupConversation_withConversationTitle_legacyWithOverride() {
+    public void testMessagingStyle_isGroupConversation_withConversationTitle_legacyWithOverride() {
         // #setGroupConversation should always take precedence over legacy behavior, so a non-null
         // title shouldn't affect #isGroupConversation.
         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
@@ -598,17 +648,21 @@
                 new NotificationCompat.MessagingStyle("self name")
                         .setGroupConversation(false)
                         .setConversationTitle("test conversation title");
-        new NotificationCompat.Builder(mContext, "test id")
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
-        assertFalse(messagingStyle.isGroupConversation());
+        NotificationCompat.MessagingStyle result =
+                NotificationCompat.MessagingStyle
+                        .extractMessagingStyleFromNotification(notification);
+
+        assertFalse(result.isGroupConversation());
     }
 
     @Test
-    public void messagingStyle_isGroupConversation_withoutConversationTitle_legacyWithOverride() {
+    public void testMessagingStyle_isGroupConversation_withoutTitle_legacyWithOverride() {
         // #setGroupConversation should always take precedence over legacy behavior, so a null
         // title shouldn't affect #isGroupConversation.
         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
@@ -616,13 +670,17 @@
                 new NotificationCompat.MessagingStyle("self name")
                         .setGroupConversation(true)
                         .setConversationTitle(null);
-        new NotificationCompat.Builder(mContext, "test id")
+        Notification notification = new NotificationCompat.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
                 .setStyle(messagingStyle)
                 .build();
 
-        assertTrue(messagingStyle.isGroupConversation());
+        NotificationCompat.MessagingStyle result =
+                NotificationCompat.MessagingStyle
+                        .extractMessagingStyleFromNotification(notification);
+
+        assertTrue(result.isGroupConversation());
     }
 
     @Test
@@ -641,6 +699,17 @@
     }
 
     @Test
+    public void testMessagingStyleMessage_bundle_legacySender() {
+        Bundle legacyBundle = new Bundle();
+        legacyBundle.putCharSequence(Message.KEY_TEXT, "message");
+        legacyBundle.putLong(Message.KEY_TIMESTAMP, 100);
+        legacyBundle.putCharSequence(Message.KEY_SENDER, "sender");
+
+        Message result = Message.getMessageFromBundle(legacyBundle);
+        assertEquals("sender", result.getPerson().getName());
+    }
+
+    @Test
     public void action_builder_hasDefault() {
         NotificationCompat.Action action =
                 new NotificationCompat.Action.Builder(0, "Test Title", null).build();
diff --git a/compat/src/androidTest/java/androidx/core/app/PersonTest.java b/compat/src/androidTest/java/androidx/core/app/PersonTest.java
new file mode 100644
index 0000000..20b2090
--- /dev/null
+++ b/compat/src/androidTest/java/androidx/core/app/PersonTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2018 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.core.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PersonTest {
+    private static final CharSequence TEST_NAME = "Example Name";
+    private static final Bitmap TEST_ICON = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+    private static final String TEST_URI = "mailto:example@example.com";
+    private static final String TEST_KEY = "example-key";
+    private static final boolean TEST_IS_BOT = true;
+    private static final boolean TEST_IS_IMPORTANT = true;
+
+    @Test
+    @SdkSuppress(minSdkVersion = 12)
+    public void bundle() {
+        Person person = new Person.Builder()
+                .setImportant(TEST_IS_IMPORTANT)
+                .setBot(TEST_IS_BOT)
+                .setKey(TEST_KEY)
+                .setUri(TEST_URI)
+                .setIcon(TEST_ICON)
+                .setName(TEST_NAME)
+                .build();
+
+        Bundle personBundle = person.toBundle();
+        Person result = Person.fromBundle(personBundle);
+
+        assertEquals(TEST_NAME, result.getName());
+        assertEquals(TEST_URI, result.getUri());
+        assertEquals(TEST_KEY, result.getKey());
+        assertEquals(TEST_IS_BOT, result.isBot());
+        assertEquals(TEST_IS_IMPORTANT, result.isImportant());
+
+        // Requires SDK >= 12
+        assertTrue(TEST_ICON.sameAs(result.getIcon()));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 12)
+    public void toBuilder() {
+        Person person = new Person.Builder()
+                .setImportant(TEST_IS_IMPORTANT)
+                .setBot(TEST_IS_BOT)
+                .setKey(TEST_KEY)
+                .setUri(TEST_URI)
+                .setIcon(TEST_ICON)
+                .setName(TEST_NAME)
+                .build();
+        Person result = person.toBuilder().build();
+
+        assertEquals(TEST_NAME, result.getName());
+        assertEquals(TEST_URI, result.getUri());
+        assertEquals(TEST_KEY, result.getKey());
+        assertEquals(TEST_IS_BOT, result.isBot());
+        assertEquals(TEST_IS_IMPORTANT, result.isImportant());
+
+        // Requires SDK >= 12
+        assertTrue(TEST_ICON.sameAs(result.getIcon()));
+    }
+
+    @Test
+    public void getName() {
+        Person person = new Person.Builder().setName(TEST_NAME).build();
+        assertEquals(TEST_NAME, person.getName());
+    }
+
+    @Test
+    public void getIcon() {
+        Person person = new Person.Builder().setIcon(TEST_ICON).build();
+        assertEquals(TEST_ICON, person.getIcon());
+    }
+
+    @Test
+    public void getUri() {
+        Person person = new Person.Builder().setUri(TEST_URI).build();
+        assertEquals(TEST_URI, person.getUri());
+    }
+
+    @Test
+    public void getKey() {
+        Person person = new Person.Builder().setKey(TEST_KEY).build();
+        assertEquals(TEST_KEY, person.getKey());
+    }
+
+    @Test
+    public void isBot() {
+        Person person = new Person.Builder().setBot(TEST_IS_BOT).build();
+        assertEquals(TEST_IS_BOT, person.isBot());
+    }
+
+    @Test
+    public void isImportant() {
+        Person person = new Person.Builder().setImportant(TEST_IS_IMPORTANT).build();
+        assertEquals(TEST_IS_IMPORTANT, person.isImportant());
+    }
+}
diff --git a/compat/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatTest.java b/compat/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatTest.java
new file mode 100644
index 0000000..e3e5504
--- /dev/null
+++ b/compat/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018 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.core.content.pm;
+
+import static android.os.Build.VERSION_CODES.P;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.pm.PackageInfo;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public final class PackageInfoCompatTest {
+    @Test
+    public void getLongVersionCodeLowerBitsOnly() {
+        PackageInfo info = new PackageInfo();
+        info.versionCode = 12345;
+
+        assertEquals(12345L, PackageInfoCompat.getLongVersionCode(info));
+    }
+
+    @SdkSuppress(minSdkVersion = P)
+    @Test
+    public void getLongVersionCodeLowerAndUpperBits() {
+        PackageInfo info = new PackageInfo();
+        info.setLongVersionCode(Long.MAX_VALUE);
+
+        assertEquals(Long.MAX_VALUE, PackageInfoCompat.getLongVersionCode(info));
+    }
+}
diff --git a/compat/src/androidTest/java/androidx/core/graphics/drawable/IconCompatTest.java b/compat/src/androidTest/java/androidx/core/graphics/drawable/IconCompatTest.java
index 75ad1d5..2ca517f 100644
--- a/compat/src/androidTest/java/androidx/core/graphics/drawable/IconCompatTest.java
+++ b/compat/src/androidTest/java/androidx/core/graphics/drawable/IconCompatTest.java
@@ -32,6 +32,7 @@
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -258,7 +259,7 @@
     public void testBitmapIconCompat_getType() {
         IconCompat icon = IconCompat.createWithBitmap(Bitmap.createBitmap(16, 16,
                 Bitmap.Config.ARGB_8888));
-        assertEquals(IconCompat.TYPE_BITMAP, icon.getType());
+        assertEquals(Icon.TYPE_BITMAP, icon.getType());
     }
 
     @Test
@@ -266,7 +267,7 @@
         byte[] data = new byte[4];
         data[0] = data[1] = data[2] = data[3] = (byte) 255;
         IconCompat icon = IconCompat.createWithData(data, 0, 4);
-        assertEquals(IconCompat.TYPE_DATA, icon.getType());
+        assertEquals(Icon.TYPE_DATA, icon.getType());
     }
 
     @Test
@@ -278,11 +279,11 @@
             String filePath = file.toURI().getPath();
 
             IconCompat icon = IconCompat.createWithContentUri(Uri.fromFile(file));
-            assertEquals(IconCompat.TYPE_URI, icon.getType());
+            assertEquals(Icon.TYPE_URI, icon.getType());
             assertEquals(filePath, icon.getUri().getPath());
 
             icon = IconCompat.createWithContentUri(file.toURI().toString());
-            assertEquals(IconCompat.TYPE_URI, icon.getType());
+            assertEquals(Icon.TYPE_URI, icon.getType());
             assertEquals(filePath, icon.getUri().getPath());
         } finally {
             file.delete();
@@ -292,7 +293,7 @@
     @Test
     public void testResourceIconCompat_getType() {
         IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.bmp_test);
-        assertEquals(IconCompat.TYPE_RESOURCE, icon.getType());
+        assertEquals(Icon.TYPE_RESOURCE, icon.getType());
         assertEquals("androidx.core.test", icon.getResPackage());
         assertEquals(R.drawable.bmp_test, icon.getResId());
     }
diff --git a/compat/src/main/AndroidManifest.xml b/compat/src/main/AndroidManifest.xml
index d18ee92..6c4338d 100644
--- a/compat/src/main/AndroidManifest.xml
+++ b/compat/src/main/AndroidManifest.xml
@@ -13,4 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest package="androidx.core"/>
+<manifest package="androidx.core" xmlns:android="http://schemas.android.com/apk/res/android">
+    <application
+        android:appComponentFactory="androidx.core.app.CoreComponentFactory" />
+</manifest>
diff --git a/compat/src/main/java/androidx/core/app/ActivityOptionsCompat.java b/compat/src/main/java/androidx/core/app/ActivityOptionsCompat.java
index 49f6c4a..19beb5d 100644
--- a/compat/src/main/java/androidx/core/app/ActivityOptionsCompat.java
+++ b/compat/src/main/java/androidx/core/app/ActivityOptionsCompat.java
@@ -273,17 +273,25 @@
 
         @Override
         public void requestUsageTimeReport(PendingIntent receiver) {
-            mActivityOptions.requestUsageTimeReport(receiver);
+            if (Build.VERSION.SDK_INT >= 23) {
+                mActivityOptions.requestUsageTimeReport(receiver);
+            }
         }
 
         @Override
         public ActivityOptionsCompat setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
+            if (Build.VERSION.SDK_INT < 24) {
+                return this;
+            }
             return new ActivityOptionsCompatImpl(
                     mActivityOptions.setLaunchBounds(screenSpacePixelRect));
         }
 
         @Override
         public Rect getLaunchBounds() {
+            if (Build.VERSION.SDK_INT < 24) {
+                return null;
+            }
             return mActivityOptions.getLaunchBounds();
         }
     }
diff --git a/compat/src/main/java/androidx/core/app/AppLaunchChecker.java b/compat/src/main/java/androidx/core/app/AppLaunchChecker.java
index 8b8d308..8170aeb 100644
--- a/compat/src/main/java/androidx/core/app/AppLaunchChecker.java
+++ b/compat/src/main/java/androidx/core/app/AppLaunchChecker.java
@@ -80,4 +80,10 @@
             sp.edit().putBoolean(KEY_STARTED_FROM_LAUNCHER, true).apply();
         }
     }
+
+    /** @deprecated This type should not be instantiated as it contains only static methods. */
+    @Deprecated
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    public AppLaunchChecker() {
+    }
 }
diff --git a/compat/src/main/java/androidx/core/app/BundleCompat.java b/compat/src/main/java/androidx/core/app/BundleCompat.java
index c1bb802..7b6968d 100644
--- a/compat/src/main/java/androidx/core/app/BundleCompat.java
+++ b/compat/src/main/java/androidx/core/app/BundleCompat.java
@@ -41,6 +41,9 @@
         private static Method sPutIBinderMethod;
         private static boolean sPutIBinderMethodFetched;
 
+        private BundleCompatBaseImpl() {
+        }
+
         public static IBinder getBinder(Bundle bundle, String key) {
             if (!sGetIBinderMethodFetched) {
                 try {
diff --git a/compat/src/main/java/androidx/core/app/CoreComponentFactory.java b/compat/src/main/java/androidx/core/app/CoreComponentFactory.java
new file mode 100644
index 0000000..ef6acc1
--- /dev/null
+++ b/compat/src/main/java/androidx/core/app/CoreComponentFactory.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2018 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.core.app;
+
+import android.app.Activity;
+import android.app.AppComponentFactory;
+import android.app.Application;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ContentProvider;
+import android.content.Intent;
+
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+
+/**
+ * Instance of AppComponentFactory for support libraries.
+ * @see CompatWrapped
+ * @hide
+ */
+@RequiresApi(api = 28)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class CoreComponentFactory extends AppComponentFactory {
+    private static final String TAG = "CoreComponentFactory";
+
+    @Override
+    public Activity instantiateActivity(ClassLoader cl, String className, Intent intent)
+            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+        return checkCompatWrapper(super.instantiateActivity(cl, className, intent));
+    }
+
+    @Override
+    public Application instantiateApplication(ClassLoader cl, String className)
+            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+        return checkCompatWrapper(super.instantiateApplication(cl, className));
+    }
+
+    @Override
+    public BroadcastReceiver instantiateReceiver(ClassLoader cl, String className, Intent intent)
+            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+        return checkCompatWrapper(super.instantiateReceiver(cl, className, intent));
+    }
+
+    @Override
+    public ContentProvider instantiateProvider(ClassLoader cl, String className)
+            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+        return checkCompatWrapper(super.instantiateProvider(cl, className));
+    }
+
+    @Override
+    public Service instantiateService(ClassLoader cl, String className, Intent intent)
+            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+        return checkCompatWrapper(super.instantiateService(cl, className, intent));
+    }
+
+    private <T> T checkCompatWrapper(T obj) {
+        if (obj instanceof CompatWrapped) {
+            T wrapper = (T) ((CompatWrapped) obj).getWrapper();
+            if (wrapper != null) {
+                return wrapper;
+            }
+        }
+        return obj;
+    }
+
+    /**
+     * Implement this interface to allow a different class to be returned when instantiating
+     * on certain API levels.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public interface CompatWrapped {
+        /**
+         * Called while this class is being instantiated by the OS.
+         *
+         * If an object is returned then it will be used in place of the class.
+         * Note: this will not be called on API <= 27.
+         *
+         * Example:
+         * <pre class="prettyprint">
+         * {@literal
+         * public AndroidXContentProvider extends ContentProvider implements CompatWrapped {
+         *     ...
+         *
+         *     public Object getWrapper() {
+         *         if (SDK_INT >= 29) {
+         *             return new AndroidXContentProviderV29(this);
+         *         }
+         *         return null;
+         *     }
+         * }
+         * }
+         * </pre>
+         */
+        Object getWrapper();
+    }
+
+}
diff --git a/compat/src/main/java/androidx/core/app/NotificationCompat.java b/compat/src/main/java/androidx/core/app/NotificationCompat.java
index b8b5205..aba40d1 100644
--- a/compat/src/main/java/androidx/core/app/NotificationCompat.java
+++ b/compat/src/main/java/androidx/core/app/NotificationCompat.java
@@ -2214,9 +2214,14 @@
          * @see Message#Message(CharSequence, long, CharSequence)
          *
          * @return this object for method chaining
+         *
+         * @deprecated Use {@link #addMessage(CharSequence, long, Person)} or
+         * {@link #addMessage(Message)}
          */
+        @Deprecated
         public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
-            mMessages.add(new Message(text, timestamp, sender));
+            mMessages.add(
+                    new Message(text, timestamp, new Person.Builder().setName(sender).build()));
             if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
                 mMessages.remove(0);
             }
@@ -2224,8 +2229,23 @@
         }
 
         /**
+         * Adds a message for display by this notification. Convenience call for
+         * {@link #addMessage(Message)}.
+         *
+         * @see Message#Message(CharSequence, long, Person)
+         *
+         * @return this for method chaining
+         */
+        public MessagingStyle addMessage(CharSequence text, long timestamp, Person person) {
+            addMessage(new Message(text, timestamp, person));
+            return this;
+        }
+
+        /**
          * Adds a {@link Message} for display in this notification.
+         *
          * @param message The {@link Message} to be displayed
+         *
          * @return this object for method chaining
          */
         public MessagingStyle addMessage(Message message) {
@@ -2313,6 +2333,12 @@
         @RestrictTo(LIBRARY_GROUP)
         @Override
         public void apply(NotificationBuilderWithBuilderAccessor builder) {
+            // This is called because we need to apply legacy logic before writing MessagingInfo
+            // data into the bundle. This does nothing in >= P, but in < P this will apply the
+            // correct group conversation status to new fields which will then be decoded properly
+            // by #extractMessagingStyleFromNotification.
+            setGroupConversation(isGroupConversation());
+
             if (Build.VERSION.SDK_INT >= 24) {
                 Notification.MessagingStyle style =
                         new Notification.MessagingStyle(mUserDisplayName)
@@ -2456,24 +2482,42 @@
         }
 
         public static final class Message {
-
             static final String KEY_TEXT = "text";
             static final String KEY_TIMESTAMP = "time";
             static final String KEY_SENDER = "sender";
             static final String KEY_DATA_MIME_TYPE = "type";
             static final String KEY_DATA_URI= "uri";
             static final String KEY_EXTRAS_BUNDLE = "extras";
+            static final String KEY_PERSON = "person";
 
             private final CharSequence mText;
             private final long mTimestamp;
-            private final CharSequence mSender;
+            @Nullable private final Person mPerson;
 
             private Bundle mExtras = new Bundle();
-            private String mDataMimeType;
-            private Uri mDataUri;
+            @Nullable private String mDataMimeType;
+            @Nullable private Uri mDataUri;
+
+            /**
+             * Creates a new {@link Message} with the given text, timestamp, and sender.
+             *
+             * @param text A {@link CharSequence} to be displayed as the message content
+             * @param timestamp Time at which the message arrived in ms since Unix epoch
+             * @param person A {@link Person} whose {@link Person#getName()} value is used as the
+             * display name for the sender. This should be {@code null} for messages by the current
+             * user, in which case, the platform will insert
+             * {@link MessagingStyle#getUserDisplayName()}. A {@link Person}'s key should be
+             * consistent during re-posts of the notification.
+             */
+            public Message(CharSequence text, long timestamp, @Nullable Person person) {
+                mText = text;
+                mTimestamp = timestamp;
+                mPerson = person;
+            }
 
             /**
              * Constructor
+             *
              * @param text A {@link CharSequence} to be displayed as the message content
              * @param timestamp Time at which the message arrived in ms since Unix epoch
              * @param sender A {@link CharSequence} to be used for displaying the name of the
@@ -2481,17 +2525,19 @@
              * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
              * Should be unique amongst all individuals in the conversation, and should be
              * consistent during re-posts of the notification.
+             *
+             * @deprecated Use the alternative constructor instead.
              */
+            @Deprecated
             public Message(CharSequence text, long timestamp, CharSequence sender){
-                mText = text;
-                mTimestamp = timestamp;
-                mSender = sender;
+                this(text, timestamp, new Person.Builder().setName(sender).build());
             }
 
             /**
              * Sets a binary blob of data and an associated MIME type for a message. In the case
              * where the platform doesn't support the MIME type, the original text provided in the
              * constructor will be used.
+             *
              * @param dataMimeType The MIME type of the content. See
              * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
              * types on Android and Android Wear.
@@ -2513,6 +2559,7 @@
              *       Note that once added to the system MediaStore the content is accessible to any
              *       app on the device.</li>
              * </ol>
+             *
              * @return this object for method chaining
              */
             public Message setData(String dataMimeType, Uri dataUri) {
@@ -2525,34 +2572,41 @@
              * Get the text to be used for this message, or the fallback text if a type and content
              * Uri have been set
              */
+            @NonNull
             public CharSequence getText() {
                 return mText;
             }
 
-            /**
-             * Get the time at which this message arrived in ms since Unix epoch
-             */
+            /** Get the time at which this message arrived in ms since Unix epoch. */
             public long getTimestamp() {
                 return mTimestamp;
             }
 
-            /**
-             * Get the extras Bundle for this message.
-             */
+            /** Get the extras Bundle for this message. */
+            @NonNull
             public Bundle getExtras() {
                 return mExtras;
             }
 
             /**
              * Get the text used to display the contact's name in the messaging experience
+             *
+             * @deprecated Use {@link #getPerson()}
              */
+            @Deprecated
+            @Nullable
             public CharSequence getSender() {
-                return mSender;
+                return mPerson.getName();
             }
 
-            /**
-             * Get the MIME type of the data pointed to by the Uri
-             */
+            /** Returns the {@link Person} sender of this message. */
+            @Nullable
+            public Person getPerson() {
+                return mPerson;
+            }
+
+            /** Get the MIME type of the data pointed to by the URI. */
+            @Nullable
             public String getDataMimeType() {
                 return mDataMimeType;
             }
@@ -2561,6 +2615,7 @@
              * Get the the Uri pointing to the content of the message. Can be null, in which case
              * {@see #getText()} is used.
              */
+            @Nullable
             public Uri getDataUri() {
                 return mDataUri;
             }
@@ -2571,8 +2626,8 @@
                     bundle.putCharSequence(KEY_TEXT, mText);
                 }
                 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
-                if (mSender != null) {
-                    bundle.putCharSequence(KEY_SENDER, mSender);
+                if (mPerson != null) {
+                    bundle.putBundle(KEY_PERSON, mPerson.toBundle());
                 }
                 if (mDataMimeType != null) {
                     bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
@@ -2586,6 +2641,7 @@
                 return bundle;
             }
 
+            @NonNull
             static Bundle[] getBundleArrayForMessages(List<Message> messages) {
                 Bundle[] bundles = new Bundle[messages.size()];
                 final int N = messages.size();
@@ -2595,6 +2651,7 @@
                 return bundles;
             }
 
+            @NonNull
             static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
                 List<Message> messages = new ArrayList<>(bundles.length);
                 for (int i = 0; i < bundles.length; i++) {
@@ -2608,23 +2665,38 @@
                 return messages;
             }
 
+            @Nullable
             static Message getMessageFromBundle(Bundle bundle) {
                 try {
                     if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
                         return null;
-                    } else {
-                        Message message = new Message(bundle.getCharSequence(KEY_TEXT),
-                                bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
-                        if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
-                                bundle.containsKey(KEY_DATA_URI)) {
-                            message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
-                                    (Uri) bundle.getParcelable(KEY_DATA_URI));
-                        }
-                        if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
-                            message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
-                        }
-                        return message;
                     }
+
+                    Message message;
+                    if (bundle.containsKey(KEY_SENDER)) {
+                        // Legacy sender
+                        message = new Message(
+                                bundle.getCharSequence(KEY_TEXT),
+                                bundle.getLong(KEY_TIMESTAMP),
+                                new Person.Builder()
+                                        .setName(bundle.getCharSequence(KEY_SENDER))
+                                        .build());
+                    } else {
+                        message = new Message(
+                                bundle.getCharSequence(KEY_TEXT),
+                                bundle.getLong(KEY_TIMESTAMP),
+                                Person.fromBundle(bundle.getBundle(KEY_PERSON)));
+                    }
+
+                    if (bundle.containsKey(KEY_DATA_MIME_TYPE)
+                            && bundle.containsKey(KEY_DATA_URI)) {
+                        message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
+                                (Uri) bundle.getParcelable(KEY_DATA_URI));
+                    }
+                    if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
+                        message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
+                    }
+                    return message;
                 } catch (ClassCastException e) {
                     return null;
                 }
@@ -5113,4 +5185,10 @@
             return GROUP_ALERT_ALL;
         }
     }
+
+    /** @deprecated This type should not be instantiated as it contains only static methods. */
+    @Deprecated
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    public NotificationCompat() {
+    }
 }
diff --git a/compat/src/main/java/androidx/core/app/NotificationCompatJellybean.java b/compat/src/main/java/androidx/core/app/NotificationCompatJellybean.java
index 3157aad..f334fc1 100644
--- a/compat/src/main/java/androidx/core/app/NotificationCompatJellybean.java
+++ b/compat/src/main/java/androidx/core/app/NotificationCompatJellybean.java
@@ -338,4 +338,7 @@
         bundle.putParcelableArray(key, typedArray);
         return typedArray;
     }
+
+    private NotificationCompatJellybean() {
+    }
 }
diff --git a/compat/src/main/java/androidx/core/app/Person.java b/compat/src/main/java/androidx/core/app/Person.java
new file mode 100644
index 0000000..3bda510
--- /dev/null
+++ b/compat/src/main/java/androidx/core/app/Person.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2018 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.core.app;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Provides an immutable reference to an entity that appears repeatedly on different surfaces of the
+ * platform. For example, this could represent the sender of a message.
+ */
+public class Person {
+    private static final String NAME_KEY = "name";
+    private static final String ICON_KEY = "icon";
+    private static final String URI_KEY = "uri";
+    private static final String KEY_KEY = "key";
+    private static final String IS_BOT_KEY = "isBot";
+    private static final String IS_IMPORTANT_KEY = "isImportant";
+
+    /**
+     * Extracts and returns the {@link Person} written to the {@code bundle}. A bundle can be
+     * created from a {@link Person} using {@link #toBundle()}.
+     */
+    public static Person fromBundle(Bundle bundle) {
+        return new Builder()
+                .setName(bundle.getCharSequence(NAME_KEY))
+                .setIcon((Bitmap) bundle.getParcelable(ICON_KEY))
+                .setUri(bundle.getString(URI_KEY))
+                .setKey(bundle.getString(KEY_KEY))
+                .setBot(bundle.getBoolean(IS_BOT_KEY))
+                .setImportant(bundle.getBoolean(IS_IMPORTANT_KEY))
+                .build();
+    }
+
+    @Nullable private CharSequence mName;
+    @Nullable private Bitmap mIcon;
+    @Nullable private String mUri;
+    @Nullable private String mKey;
+    private boolean mIsBot;
+    private boolean mIsImportant;
+
+    private Person(Builder builder) {
+        mName = builder.mName;
+        mIcon = builder.mIcon;
+        mUri = builder.mUri;
+        mKey = builder.mKey;
+        mIsBot = builder.mIsBot;
+        mIsImportant = builder.mIsImportant;
+    }
+
+    /**
+     * Writes and returns a new {@link Bundle} that represents this {@link Person}. This bundle can
+     * be converted back by using {@link #fromBundle(Bundle)}.
+     */
+    public Bundle toBundle() {
+        Bundle result = new Bundle();
+        result.putCharSequence(NAME_KEY, mName);
+        result.putParcelable(ICON_KEY, mIcon);
+        result.putString(URI_KEY, mUri);
+        result.putString(KEY_KEY, mKey);
+        result.putBoolean(IS_BOT_KEY, mIsBot);
+        result.putBoolean(IS_IMPORTANT_KEY, mIsImportant);
+        return result;
+    }
+
+    /** Creates and returns a new {@link Builder} initialized with this Person's data. */
+    public Builder toBuilder() {
+        return new Builder(this);
+    }
+
+    /**
+     * Returns the name for this {@link Person} or {@code null} if no name was provided. This could
+     * be a full name, nickname, username, etc.
+     */
+    @Nullable
+    public CharSequence getName() {
+        return mName;
+    }
+
+    /** Returns the icon for this {@link Person} or {@code null} if no icon was provided. */
+    @Nullable
+    public Bitmap getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Returns the raw URI for this {@link Person} or {@code null} if no URI was provided. A URI can
+     * be any of the following:
+     * <ul>
+     *     <li>The {@code String} representation of a
+     *     {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}</li>
+     *     <li>A {@code mailto:} schema*</li>
+     *     <li>A {@code tel:} schema*</li>
+     * </ul>
+     *
+     * <p>*Note for these schemas, the path portion of the URI must exist in the contacts
+     * database in their appropriate column, otherwise the reference should be discarded.
+     */
+    @Nullable
+    public String getUri() {
+        return mUri;
+    }
+
+    /**
+     * Returns the key for this {@link Person} or {@code null} if no key was provided. This is
+     * provided as a unique identifier between other {@link Person}s.
+     */
+    @Nullable
+    public String getKey() {
+        return mKey;
+    }
+
+    /**
+     * Returns whether or not this {@link Person} is a machine rather than a human. Used primarily
+     * to identify automated tooling.
+     */
+    public boolean isBot() {
+        return mIsBot;
+    }
+
+    /**
+     * Returns whether or not this {@link Person} is important to the user of this device with
+     * regards to how frequently they interact.
+     */
+    public boolean isImportant() {
+        return mIsImportant;
+    }
+
+    /** Builder for the immutable {@link Person} class. */
+    public static class Builder {
+        @Nullable private CharSequence mName;
+        @Nullable private Bitmap mIcon;
+        @Nullable private String mUri;
+        @Nullable private String mKey;
+        private boolean mIsBot;
+        private boolean mIsImportant;
+
+        /** Creates a new, empty {@link Builder}. */
+        public Builder() { }
+
+        private Builder(Person person) {
+            mName = person.mName;
+            mIcon = person.mIcon;
+            mUri = person.mUri;
+            mKey = person.mKey;
+            mIsBot = person.mIsBot;
+            mIsImportant = person.mIsImportant;
+        }
+
+        /**
+         * Give this {@link Person} a name to use for display. This can be, for example, a full
+         * name, nickname, username, etc.
+         */
+        @NonNull
+        public Builder setName(@Nullable CharSequence name) {
+            mName = name;
+            return this;
+        }
+
+        /**
+         * Set an icon for this {@link Person}.
+         *
+         * <p>The system will prefer this icon over any images that are resolved from
+         * {@link #setUri(String)}.
+         */
+        @NonNull
+        public Builder setIcon(@Nullable Bitmap icon) {
+            mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Set a URI for this {@link Person} which can be any of the following:
+         * <ul>
+         *     <li>The {@code String} representation of a
+         *     {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}</li>
+         *     <li>A {@code mailto:} schema*</li>
+         *     <li>A {@code tel:} schema*</li>
+         * </ul>
+         *
+         * <p>*Note for these schemas, the path portion of the URI must exist in the contacts
+         * database in their appropriate column, otherwise the reference will be discarded.
+         */
+        @NonNull
+        public Builder setUri(@Nullable String uri) {
+            mUri = uri;
+            return this;
+        }
+
+        /**
+         * Set a unique identifier for this {@link Person}. This is especially useful if the
+         * {@link #setName(CharSequence)} value isn't unique. This value is preferred for
+         * identification, but if it's not provided, the person's name will be used in its place.
+         */
+        @NonNull
+        public Builder setKey(@Nullable String key) {
+            mKey = key;
+            return this;
+        }
+
+        /**
+         * Sets whether or not this {@link Person} represents a machine rather than a human. This is
+         * used primarily for testing and automated tooling.
+         */
+        @NonNull
+        public Builder setBot(boolean bot) {
+            mIsBot = bot;
+            return this;
+        }
+
+        /**
+         * Sets whether this is an important person. Use this method to denote users who frequently
+         * interact with the user of this device when {@link #setUri(String)} isn't provided with
+         * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, and instead with
+         * the {@code mailto:} or {@code tel:} schemas.
+         */
+        @NonNull
+        public Builder setImportant(boolean important) {
+            mIsImportant = important;
+            return this;
+        }
+
+        /** Creates and returns the {@link Person} this builder represents. */
+        @NonNull
+        public Person build() {
+            return new Person(this);
+        }
+    }
+}
diff --git a/compat/src/main/java/androidx/core/content/pm/PackageInfoCompat.java b/compat/src/main/java/androidx/core/content/pm/PackageInfoCompat.java
new file mode 100644
index 0000000..71c53f2
--- /dev/null
+++ b/compat/src/main/java/androidx/core/content/pm/PackageInfoCompat.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2018 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.core.content.pm;
+
+import android.content.pm.PackageInfo;
+
+import androidx.annotation.NonNull;
+import androidx.core.os.BuildCompat;
+
+/** Helper for accessing features in {@link PackageInfo}. */
+public final class PackageInfoCompat {
+    /**
+     * Return {@link android.R.attr#versionCode} and {@link android.R.attr#versionCodeMajor}
+     * combined together as a single long value. The {@code versionCodeMajor} is placed in the
+     * upper 32 bits on Android P or newer, otherwise these bits are all set to 0.
+     *
+     * @see PackageInfo#getLongVersionCode()
+     */
+    public static long getLongVersionCode(@NonNull PackageInfo info) {
+        if (BuildCompat.isAtLeastP()) {
+            return info.getLongVersionCode();
+        }
+
+        return info.versionCode;
+    }
+
+    private PackageInfoCompat() {
+    }
+}
diff --git a/compat/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java b/compat/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
index d2f2f22..dd8d933 100644
--- a/compat/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
+++ b/compat/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
@@ -308,4 +308,7 @@
             }
         }
     }
+
+    private FontResourcesParserCompat() {
+    }
 }
diff --git a/compat/src/main/java/androidx/core/content/res/TypedArrayUtils.java b/compat/src/main/java/androidx/core/content/res/TypedArrayUtils.java
index 2aa8449..0bb2498 100644
--- a/compat/src/main/java/androidx/core/content/res/TypedArrayUtils.java
+++ b/compat/src/main/java/androidx/core/content/res/TypedArrayUtils.java
@@ -294,4 +294,7 @@
         }
         return fallbackAttr;
     }
+
+    private TypedArrayUtils() {
+    }
 }
diff --git a/compat/src/main/java/androidx/core/graphics/PathParser.java b/compat/src/main/java/androidx/core/graphics/PathParser.java
index 6f9d83d..2d0282c 100644
--- a/compat/src/main/java/androidx/core/graphics/PathParser.java
+++ b/compat/src/main/java/androidx/core/graphics/PathParser.java
@@ -758,4 +758,7 @@
             }
         }
     }
+
+    private PathParser() {
+    }
 }
diff --git a/compat/src/main/java/androidx/core/graphics/drawable/IconCompat.java b/compat/src/main/java/androidx/core/graphics/drawable/IconCompat.java
index 6dbe266..366ddf3 100644
--- a/compat/src/main/java/androidx/core/graphics/drawable/IconCompat.java
+++ b/compat/src/main/java/androidx/core/graphics/drawable/IconCompat.java
@@ -16,6 +16,12 @@
 
 package androidx.core.graphics.drawable;
 
+import static android.graphics.drawable.Icon.TYPE_ADAPTIVE_BITMAP;
+import static android.graphics.drawable.Icon.TYPE_BITMAP;
+import static android.graphics.drawable.Icon.TYPE_DATA;
+import static android.graphics.drawable.Icon.TYPE_RESOURCE;
+import static android.graphics.drawable.Icon.TYPE_URI;
+
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
@@ -57,6 +63,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.core.content.ContextCompat;
 import androidx.core.content.res.ResourcesCompat;
+import androidx.core.os.BuildCompat;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -79,39 +86,11 @@
      */
     public static final int TYPE_UNKOWN = -1;
 
-    // TODO: Switch these to the public constants in the beta branch.
     /**
      * @hide
      */
     @RestrictTo(LIBRARY)
-    public static final int TYPE_BITMAP = 1;
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY)
-    public static final int TYPE_RESOURCE = 2;
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY)
-    public static final int TYPE_DATA = 3;
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY)
-    public static final int TYPE_URI = 4;
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY)
-    public static final int TYPE_ADAPTIVE_BITMAP = 5;
-
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY)
-    @IntDef({TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP})
+    @IntDef({TYPE_UNKOWN, TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP})
     @Retention(RetentionPolicy.SOURCE)
     public @interface IconType {
     }
@@ -645,8 +624,10 @@
      */
     @IconType
     @RequiresApi(23)
-    public static int getType(Icon icon) {
-        // TODO: Switch to public APIs on P+ in beta branch.
+    public static int getType(@NonNull Icon icon) {
+        if (BuildCompat.isAtLeastP()) {
+            return icon.getType();
+        }
         try {
             return (int) icon.getClass().getMethod("getType").invoke(icon);
         } catch (IllegalAccessException e) {
@@ -671,8 +652,10 @@
      */
     @Nullable
     @RequiresApi(23)
-    public static String getResPackage(Icon icon) {
-        // TODO: Switch to public APIs on P+ in beta branch.
+    public static String getResPackage(@NonNull Icon icon) {
+        if (BuildCompat.isAtLeastP()) {
+            return icon.getResPackage();
+        }
         try {
             return (String) icon.getClass().getMethod("getResPackage").invoke(icon);
         } catch (IllegalAccessException e) {
@@ -697,8 +680,10 @@
      */
     @IdRes
     @RequiresApi(23)
-    public static int getResId(Icon icon) {
-        // TODO: Switch to public APIs on P+ in beta branch.
+    public static int getResId(@NonNull Icon icon) {
+        if (BuildCompat.isAtLeastP()) {
+            return icon.getResId();
+        }
         try {
             return (int) icon.getClass().getMethod("getResId").invoke(icon);
         } catch (IllegalAccessException e) {
@@ -723,8 +708,10 @@
      */
     @Nullable
     @RequiresApi(23)
-    public Uri getUri(Icon icon) {
-        // TODO: Switch to public APIs on P+ in beta branch.
+    public Uri getUri(@NonNull Icon icon) {
+        if (BuildCompat.isAtLeastP()) {
+            return icon.getUri();
+        }
         try {
             return (Uri) icon.getClass().getMethod("getUri").invoke(icon);
         } catch (IllegalAccessException e) {
diff --git a/compat/src/main/java/androidx/core/graphics/drawable/WrappedDrawableApi14.java b/compat/src/main/java/androidx/core/graphics/drawable/WrappedDrawableApi14.java
index b57ff37..fde4e85 100644
--- a/compat/src/main/java/androidx/core/graphics/drawable/WrappedDrawableApi14.java
+++ b/compat/src/main/java/androidx/core/graphics/drawable/WrappedDrawableApi14.java
@@ -27,6 +27,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 
 /**
  * Drawable which delegates all calls to its wrapped {@link Drawable}.
@@ -192,11 +193,13 @@
     }
 
     @Override
+    @RequiresApi(19)
     public void setAutoMirrored(boolean mirrored) {
         mDrawable.setAutoMirrored(mirrored);
     }
 
     @Override
+    @RequiresApi(19)
     public boolean isAutoMirrored() {
         return mDrawable.isAutoMirrored();
     }
diff --git a/compat/src/main/java/androidx/core/os/BuildCompat.java b/compat/src/main/java/androidx/core/os/BuildCompat.java
index fe3f4be..277a724 100644
--- a/compat/src/main/java/androidx/core/os/BuildCompat.java
+++ b/compat/src/main/java/androidx/core/os/BuildCompat.java
@@ -32,7 +32,7 @@
      * @return {@code true} if N APIs are available for use
      * @deprecated Android N is a finalized release and this method is no longer necessary. It will
      *             be removed in a future release of the Support Library. Instead, use
-     *             {@code Build.SDK_INT >= Build.VERSION_CODES#N}.
+     *             {@code Build.SDK_INT >= Build.VERSION_CODES.N}.
      */
     @Deprecated
     public static boolean isAtLeastN() {
@@ -45,7 +45,7 @@
      * @return {@code true} if N MR1 APIs are available for use
      * @deprecated Android N MR1 is a finalized release and this method is no longer necessary. It
      *             will be removed in a future release of the Support Library. Instead, use
-     *             {@code Build.SDK_INT >= Build.VERSION_CODES#N_MR1}.
+     *             {@code Build.SDK_INT >= Build.VERSION_CODES.N_MR1}.
      */
     @Deprecated
     public static boolean isAtLeastNMR1() {
@@ -58,7 +58,7 @@
      * @return {@code true} if O APIs are available for use, {@code false} otherwise
      * @deprecated Android O is a finalized release and this method is no longer necessary. It will
      *             be removed in a future release of the Support Library. Instead use
-     *             {@code Build.SDK_INT >= Build.VERSION_CODES#O}.
+     *             {@code Build.SDK_INT >= Build.VERSION_CODES.O}.
      */
     @Deprecated
     public static boolean isAtLeastO() {
@@ -71,7 +71,7 @@
      * @return {@code true} if O MR1 APIs are available for use, {@code false} otherwise
      * @deprecated Android O MR1 is a finalized release and this method is no longer necessary. It
      *             will be removed in a future release of the Support Library. Instead, use
-     *             {@code Build.SDK_INT >= Build.VERSION_CODES#O_MR1}.
+     *             {@code Build.SDK_INT >= Build.VERSION_CODES.O_MR1}.
      */
     @Deprecated
     public static boolean isAtLeastOMR1() {
@@ -83,11 +83,24 @@
      * <p>
      * <strong>Note:</strong> This method will return {@code false} on devices running release
      * versions of Android. When Android P is finalized for release, this method will be deprecated
-     * and all calls should be replaced with {@code Build.SDK_INT >= Build.VERSION_CODES#P}.
+     * and all calls should be replaced with {@code Build.SDK_INT >= Build.VERSION_CODES.P}.
      *
      * @return {@code true} if P APIs are available for use, {@code false} otherwise
      */
     public static boolean isAtLeastP() {
-        return VERSION.CODENAME.equals("P");
+        return VERSION.CODENAME.equals("P") || VERSION.CODENAME.equals("Q");
+    }
+
+    /**
+     * Checks if the device is running on a pre-release version of Android Q or newer.
+     * <p>
+     * <strong>Note:</strong> This method will return {@code false} on devices running release
+     * versions of Android. When Android Q is finalized for release, this method will be deprecated
+     * and all calls should be replaced with {@code Build.SDK_INT >= Build.VERSION_CODES.Q}.
+     *
+     * @return {@code true} if Q APIs are available for use, {@code false} otherwise
+     */
+    public static boolean isAtLeastQ() {
+        return VERSION.CODENAME.equals("Q");
     }
 }
diff --git a/compat/src/main/java/androidx/core/os/LocaleHelper.java b/compat/src/main/java/androidx/core/os/LocaleHelper.java
index f8fa5c7..001d657 100644
--- a/compat/src/main/java/androidx/core/os/LocaleHelper.java
+++ b/compat/src/main/java/androidx/core/os/LocaleHelper.java
@@ -69,4 +69,7 @@
 
         return buf.toString();
     }
+
+    private LocaleHelper() {
+    }
 }
diff --git a/compat/src/main/java/androidx/core/text/HtmlCompat.java b/compat/src/main/java/androidx/core/text/HtmlCompat.java
index b846805..a5b97ea 100644
--- a/compat/src/main/java/androidx/core/text/HtmlCompat.java
+++ b/compat/src/main/java/androidx/core/text/HtmlCompat.java
@@ -18,6 +18,8 @@
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import android.annotation.SuppressLint;
 import android.graphics.Color;
 import android.os.Build;
@@ -33,6 +35,8 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 
+import java.lang.annotation.Retention;
+
 /**
  * Backwards compatible version of {@link Html}.
  */
@@ -119,6 +123,7 @@
             FROM_HTML_MODE_LEGACY
     }, flag = true)
     @RestrictTo(LIBRARY)
+    @Retention(SOURCE)
     @interface FromHtmlFlags {
     }
 
@@ -128,6 +133,7 @@
             TO_HTML_PARAGRAPH_LINES_INDIVIDUAL
     })
     @RestrictTo(LIBRARY)
+    @Retention(SOURCE)
     @interface ToHtmlOptions {
     }
 
diff --git a/compat/src/main/java/androidx/core/text/util/FindAddress.java b/compat/src/main/java/androidx/core/text/util/FindAddress.java
index b0523db..d66fa05 100644
--- a/compat/src/main/java/androidx/core/text/util/FindAddress.java
+++ b/compat/src/main/java/androidx/core/text/util/FindAddress.java
@@ -513,4 +513,7 @@
         }
         return null;
     }
+
+    private FindAddress() {
+    }
 }
diff --git a/compat/src/main/java/androidx/core/util/Consumer.java b/compat/src/main/java/androidx/core/util/Consumer.java
new file mode 100644
index 0000000..28e0fb8
--- /dev/null
+++ b/compat/src/main/java/androidx/core/util/Consumer.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 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.core.util;
+
+/**
+ * Compat version of {@link java.util.function.Consumer}
+ * @param <T> the type of the input to the operation
+ */
+public interface Consumer<T> {
+
+    /**
+     * Performs this operation on the given argument.
+     *
+     * @param t the input argument
+     */
+    void accept(T t);
+}
diff --git a/compat/src/main/java/androidx/core/util/DebugUtils.java b/compat/src/main/java/androidx/core/util/DebugUtils.java
index f8767b9..20486f2 100644
--- a/compat/src/main/java/androidx/core/util/DebugUtils.java
+++ b/compat/src/main/java/androidx/core/util/DebugUtils.java
@@ -45,4 +45,7 @@
             out.append(Integer.toHexString(System.identityHashCode(cls)));
         }
     }
+
+    private DebugUtils() {
+    }
 }
diff --git a/compat/src/main/java/androidx/core/util/Preconditions.java b/compat/src/main/java/androidx/core/util/Preconditions.java
index eb5bdd6..612a93e 100644
--- a/compat/src/main/java/androidx/core/util/Preconditions.java
+++ b/compat/src/main/java/androidx/core/util/Preconditions.java
@@ -462,4 +462,7 @@
 
         return value;
     }
+
+    private Preconditions() {
+    }
 }
diff --git a/compat/src/main/java/androidx/core/view/AccessibilityDelegateCompat.java b/compat/src/main/java/androidx/core/view/AccessibilityDelegateCompat.java
index 0f36f42..7dfad9c 100644
--- a/compat/src/main/java/androidx/core/view/AccessibilityDelegateCompat.java
+++ b/compat/src/main/java/androidx/core/view/AccessibilityDelegateCompat.java
@@ -25,6 +25,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
 
+import androidx.annotation.RequiresApi;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.core.view.accessibility.AccessibilityNodeProviderCompat;
 
@@ -92,6 +93,7 @@
         }
 
         @Override
+        @RequiresApi(16)
         public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
             AccessibilityNodeProviderCompat provider =
                     mCompat.getAccessibilityNodeProvider(host);
diff --git a/compat/src/main/java/androidx/core/view/ViewCompat.java b/compat/src/main/java/androidx/core/view/ViewCompat.java
index 60e5f56..7b76c38 100644
--- a/compat/src/main/java/androidx/core/view/ViewCompat.java
+++ b/compat/src/main/java/androidx/core/view/ViewCompat.java
@@ -51,6 +51,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.Px;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.core.view.accessibility.AccessibilityNodeProviderCompat;
@@ -2195,6 +2196,7 @@
 
             v.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
                 @Override
+                @RequiresApi(21) // TODO remove https://issuetracker.google.com/issues/76458979
                 public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
                     WindowInsetsCompat compatInsets = WindowInsetsCompat.wrap(insets);
                     compatInsets = listener.onApplyWindowInsets(view, compatInsets);
diff --git a/compat/src/main/java/androidx/core/view/ViewConfigurationCompat.java b/compat/src/main/java/androidx/core/view/ViewConfigurationCompat.java
index 89c3ca5..2e47e55 100644
--- a/compat/src/main/java/androidx/core/view/ViewConfigurationCompat.java
+++ b/compat/src/main/java/androidx/core/view/ViewConfigurationCompat.java
@@ -17,6 +17,7 @@
 package androidx.core.view;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.Build;
 import android.util.Log;
 import android.util.TypedValue;
@@ -130,5 +131,21 @@
         return config.getScaledTouchSlop() / 2;
     }
 
+    /**
+     * Check if shortcuts should be displayed in menus.
+     *
+     * @return {@code True} if shortcuts should be displayed in menus.
+     */
+    public static boolean shouldShowMenuShortcutsWhenKeyboardPresent(ViewConfiguration config,
+            @NonNull Context context) {
+        if (android.os.Build.VERSION.SDK_INT >= 28) {
+            return config.shouldShowMenuShortcutsWhenKeyboardPresent();
+        }
+        final Resources res = context.getResources();
+        final int platformResId = res.getIdentifier(
+                "config_showMenuShortcutsWhenKeyboardPresent", "bool", "android");
+        return platformResId != 0 && res.getBoolean(platformResId);
+    }
+
     private ViewConfigurationCompat() {}
 }
diff --git a/compat/src/main/java/androidx/core/view/inputmethod/EditorInfoCompat.java b/compat/src/main/java/androidx/core/view/inputmethod/EditorInfoCompat.java
index 3d0eee3..3575d88 100644
--- a/compat/src/main/java/androidx/core/view/inputmethod/EditorInfoCompat.java
+++ b/compat/src/main/java/androidx/core/view/inputmethod/EditorInfoCompat.java
@@ -120,4 +120,9 @@
         }
     }
 
+    /** @deprecated This type should not be instantiated as it contains only static methods. */
+    @Deprecated
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    public EditorInfoCompat() {
+    }
 }
diff --git a/compat/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java b/compat/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java
index e9b10a6..ce1b502 100644
--- a/compat/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java
+++ b/compat/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java
@@ -231,4 +231,9 @@
         }
     }
 
+    /** @deprecated This type should not be instantiated as it contains only static methods. */
+    @Deprecated
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    public InputConnectionCompat() {
+    }
 }
diff --git a/compat/src/main/java/androidx/core/widget/TextViewCompat.java b/compat/src/main/java/androidx/core/widget/TextViewCompat.java
index d67d61c..eaae58c 100644
--- a/compat/src/main/java/androidx/core/widget/TextViewCompat.java
+++ b/compat/src/main/java/androidx/core/widget/TextViewCompat.java
@@ -18,6 +18,7 @@
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
@@ -489,138 +490,149 @@
         // intent after selection to not be displayed in the menu, although they should be.
         // Here we fix this, by removing the menu items created by the framework code, and
         // adding them (and the missing ones) back correctly.
-        textView.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
-            // This constant should be correlated with its definition in the
-            // android.widget.Editor class.
-            private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
+        textView.setCustomSelectionActionModeCallback(new OreoCallback(callback, textView));
+    }
 
-            // References to the MenuBuilder class and its removeItemAt(int) method.
-            // Since in most cases the menu instance processed by this callback is going
-            // to be a MenuBuilder, we keep these references to avoid querying for them
-            // frequently by reflection in recomputeProcessTextMenuItems.
-            private Class mMenuBuilderClass;
-            private Method mMenuBuilderRemoveItemAtMethod;
-            private boolean mCanUseMenuBuilderReferences;
-            private boolean mInitializedMenuBuilderReferences = false;
+    @TargetApi(26) // TODO was anonymous but https://issuetracker.google.com/issues/76458979
+    private static class OreoCallback implements ActionMode.Callback {
+        // This constant should be correlated with its definition in the
+        // android.widget.Editor class.
+        private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
+        private final ActionMode.Callback mCallback;
+        private final TextView mTextView;
 
-            @Override
-            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-                return callback.onCreateActionMode(mode, menu);
-            }
+        // References to the MenuBuilder class and its removeItemAt(int) method.
+        // Since in most cases the menu instance processed by this callback is going
+        // to be a MenuBuilder, we keep these references to avoid querying for them
+        // frequently by reflection in recomputeProcessTextMenuItems.
+        private Class mMenuBuilderClass;
+        private Method mMenuBuilderRemoveItemAtMethod;
+        private boolean mCanUseMenuBuilderReferences;
+        private boolean mInitializedMenuBuilderReferences;
 
-            @Override
-            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-                recomputeProcessTextMenuItems(menu);
-                return callback.onPrepareActionMode(mode, menu);
-            }
+        OreoCallback(ActionMode.Callback callback, TextView textView) {
+            mCallback = callback;
+            mTextView = textView;
+            mInitializedMenuBuilderReferences = false;
+        }
 
-            @Override
-            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-                return callback.onActionItemClicked(mode, item);
-            }
+        @Override
+        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+            return mCallback.onCreateActionMode(mode, menu);
+        }
 
-            @Override
-            public void onDestroyActionMode(ActionMode mode) {
-                callback.onDestroyActionMode(mode);
-            }
+        @Override
+        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+            recomputeProcessTextMenuItems(menu);
+            return mCallback.onPrepareActionMode(mode, menu);
+        }
 
-            private void recomputeProcessTextMenuItems(final Menu menu) {
-                final Context context = textView.getContext();
-                final PackageManager packageManager = context.getPackageManager();
+        @Override
+        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            return mCallback.onActionItemClicked(mode, item);
+        }
 
-                if (!mInitializedMenuBuilderReferences) {
-                    mInitializedMenuBuilderReferences = true;
-                    try {
-                        mMenuBuilderClass =
-                                Class.forName("com.android.internal.view.menu.MenuBuilder");
-                        mMenuBuilderRemoveItemAtMethod = mMenuBuilderClass
-                                .getDeclaredMethod("removeItemAt", Integer.TYPE);
-                        mCanUseMenuBuilderReferences = true;
-                    } catch (ClassNotFoundException | NoSuchMethodException e) {
-                        mMenuBuilderClass = null;
-                        mMenuBuilderRemoveItemAtMethod = null;
-                        mCanUseMenuBuilderReferences = false;
-                    }
-                }
-                // Remove the menu items created for ACTION_PROCESS_TEXT handlers.
+        @Override
+        public void onDestroyActionMode(ActionMode mode) {
+            mCallback.onDestroyActionMode(mode);
+        }
+
+        private void recomputeProcessTextMenuItems(final Menu menu) {
+            final Context context = mTextView.getContext();
+            final PackageManager packageManager = context.getPackageManager();
+
+            if (!mInitializedMenuBuilderReferences) {
+                mInitializedMenuBuilderReferences = true;
                 try {
-                    final Method removeItemAtMethod =
-                            (mCanUseMenuBuilderReferences && mMenuBuilderClass.isInstance(menu))
-                                    ? mMenuBuilderRemoveItemAtMethod
-                                    : menu.getClass()
-                                            .getDeclaredMethod("removeItemAt", Integer.TYPE);
-                    for (int i = menu.size() - 1; i >= 0; --i) {
-                        final MenuItem item = menu.getItem(i);
-                        if (item.getIntent() != null && Intent.ACTION_PROCESS_TEXT
-                                .equals(item.getIntent().getAction())) {
-                            removeItemAtMethod.invoke(menu, i);
-                        }
-                    }
-                } catch (NoSuchMethodException | IllegalAccessException
-                        | InvocationTargetException e) {
-                    // There is a menu custom implementation used which is not providing
-                    // a removeItemAt(int) menu. There is nothing we can do in this case.
-                    return;
-                }
-
-                // Populate the menu again with the ACTION_PROCESS_TEXT handlers.
-                final List<ResolveInfo> supportedActivities =
-                        getSupportedActivities(context, packageManager);
-                for (int i = 0; i < supportedActivities.size(); ++i) {
-                    final ResolveInfo info = supportedActivities.get(i);
-                    menu.add(Menu.NONE, Menu.NONE,
-                            MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i,
-                            info.loadLabel(packageManager))
-                            .setIntent(createProcessTextIntentForResolveInfo(info, textView))
-                            .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+                    mMenuBuilderClass =
+                            Class.forName("com.android.internal.view.menu.MenuBuilder");
+                    mMenuBuilderRemoveItemAtMethod = mMenuBuilderClass
+                            .getDeclaredMethod("removeItemAt", Integer.TYPE);
+                    mCanUseMenuBuilderReferences = true;
+                } catch (ClassNotFoundException | NoSuchMethodException e) {
+                    mMenuBuilderClass = null;
+                    mMenuBuilderRemoveItemAtMethod = null;
+                    mCanUseMenuBuilderReferences = false;
                 }
             }
-
-            private List<ResolveInfo> getSupportedActivities(final Context context,
-                    final PackageManager packageManager) {
-                final List<ResolveInfo> supportedActivities = new ArrayList<>();
-                boolean canStartActivityForResult = context instanceof Activity;
-                if (!canStartActivityForResult) {
-                    return supportedActivities;
-                }
-                final List<ResolveInfo> unfiltered =
-                        packageManager.queryIntentActivities(createProcessTextIntent(), 0);
-                for (ResolveInfo info : unfiltered) {
-                    if (isSupportedActivity(info, context)) {
-                        supportedActivities.add(info);
+            // Remove the menu items created for ACTION_PROCESS_TEXT handlers.
+            try {
+                final Method removeItemAtMethod =
+                        (mCanUseMenuBuilderReferences && mMenuBuilderClass.isInstance(menu))
+                                ? mMenuBuilderRemoveItemAtMethod
+                                : menu.getClass()
+                                        .getDeclaredMethod("removeItemAt", Integer.TYPE);
+                for (int i = menu.size() - 1; i >= 0; --i) {
+                    final MenuItem item = menu.getItem(i);
+                    if (item.getIntent() != null && Intent.ACTION_PROCESS_TEXT
+                            .equals(item.getIntent().getAction())) {
+                        removeItemAtMethod.invoke(menu, i);
                     }
                 }
+            } catch (NoSuchMethodException | IllegalAccessException
+                    | InvocationTargetException e) {
+                // There is a menu custom implementation used which is not providing
+                // a removeItemAt(int) menu. There is nothing we can do in this case.
+                return;
+            }
+
+            // Populate the menu again with the ACTION_PROCESS_TEXT handlers.
+            final List<ResolveInfo> supportedActivities =
+                    getSupportedActivities(context, packageManager);
+            for (int i = 0; i < supportedActivities.size(); ++i) {
+                final ResolveInfo info = supportedActivities.get(i);
+                menu.add(Menu.NONE, Menu.NONE,
+                        MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i,
+                        info.loadLabel(packageManager))
+                        .setIntent(createProcessTextIntentForResolveInfo(info, mTextView))
+                        .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            }
+        }
+
+        private List<ResolveInfo> getSupportedActivities(final Context context,
+                final PackageManager packageManager) {
+            final List<ResolveInfo> supportedActivities = new ArrayList<>();
+            boolean canStartActivityForResult = context instanceof Activity;
+            if (!canStartActivityForResult) {
                 return supportedActivities;
             }
-
-            private boolean isSupportedActivity(final ResolveInfo info, final Context context) {
-                if (context.getPackageName().equals(info.activityInfo.packageName)) {
-                    return true;
+            final List<ResolveInfo> unfiltered =
+                    packageManager.queryIntentActivities(createProcessTextIntent(), 0);
+            for (ResolveInfo info : unfiltered) {
+                if (isSupportedActivity(info, context)) {
+                    supportedActivities.add(info);
                 }
-                if (!info.activityInfo.exported) {
-                    return false;
-                }
-                return info.activityInfo.permission == null
-                        || context.checkSelfPermission(info.activityInfo.permission)
-                            == PackageManager.PERMISSION_GRANTED;
             }
+            return supportedActivities;
+        }
 
-            private Intent createProcessTextIntentForResolveInfo(final ResolveInfo info,
-                    final TextView textView11) {
-                return createProcessTextIntent()
-                        .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, !isEditable(textView11))
-                        .setClassName(info.activityInfo.packageName, info.activityInfo.name);
+        private boolean isSupportedActivity(final ResolveInfo info, final Context context) {
+            if (context.getPackageName().equals(info.activityInfo.packageName)) {
+                return true;
             }
+            if (!info.activityInfo.exported) {
+                return false;
+            }
+            return info.activityInfo.permission == null
+                    || context.checkSelfPermission(info.activityInfo.permission)
+                        == PackageManager.PERMISSION_GRANTED;
+        }
 
-            private boolean isEditable(final TextView textView11) {
-                return textView11 instanceof Editable
-                        && textView11.onCheckIsTextEditor()
-                        && textView11.isEnabled();
-            }
+        private Intent createProcessTextIntentForResolveInfo(final ResolveInfo info,
+                final TextView textView11) {
+            return createProcessTextIntent()
+                    .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, !isEditable(textView11))
+                    .setClassName(info.activityInfo.packageName, info.activityInfo.name);
+        }
 
-            private Intent createProcessTextIntent() {
-                return new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/plain");
-            }
-        });
+        private boolean isEditable(final TextView textView11) {
+            return textView11 instanceof Editable
+                    && textView11.onCheckIsTextEditor()
+                    && textView11.isEnabled();
+        }
+
+        private Intent createProcessTextIntent() {
+            return new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/plain");
+        }
     }
 }
diff --git a/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/ViewGroupUtils.java b/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/ViewGroupUtils.java
index 8f13761..fc87dd2 100644
--- a/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/ViewGroupUtils.java
+++ b/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/ViewGroupUtils.java
@@ -92,4 +92,7 @@
             m.preConcat(view.getMatrix());
         }
     }
+
+    private ViewGroupUtils() {
+    }
 }
diff --git a/core/ktx/OWNERS b/core/ktx/OWNERS
new file mode 100644
index 0000000..e450f4c
--- /dev/null
+++ b/core/ktx/OWNERS
@@ -0,0 +1 @@
+jakew@google.com
diff --git a/core/ktx/api/0.1.txt b/core/ktx/api/0.1.txt
new file mode 100644
index 0000000..e324352
--- /dev/null
+++ b/core/ktx/api/0.1.txt
@@ -0,0 +1,673 @@
+package androidx.animation {
+
+  public final class AnimatorKt {
+    ctor public AnimatorKt();
+    method public static final android.animation.Animator.AnimatorListener addListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onEnd = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onStart = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onCancel = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onRepeat = "null");
+    method @RequiresApi(19) public static final android.animation.Animator.AnimatorPauseListener addPauseListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onResume = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onPause = "null");
+    method public static final android.animation.Animator.AnimatorListener doOnCancel(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static final android.animation.Animator.AnimatorListener doOnEnd(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static final android.animation.Animator.AnimatorPauseListener doOnPause(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static final android.animation.Animator.AnimatorListener doOnRepeat(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static final android.animation.Animator.AnimatorPauseListener doOnResume(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static final android.animation.Animator.AnimatorListener doOnStart(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.content {
+
+  public final class ContentValuesKt {
+    ctor public ContentValuesKt();
+    method public static final error.NonExistentClass contentValuesOf(kotlin.Pair<String,?>... pairs);
+  }
+
+  public final class ContextKt {
+    ctor public ContextKt();
+    method public static final void withStyledAttributes(android.content.Context, android.util.AttributeSet? set = "null", int[] attrs, @AttrRes int defStyleAttr = "0", @StyleRes int defStyleRes = "0", kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+    method public static final void withStyledAttributes(android.content.Context, @StyleRes int resourceId, int[] attrs, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+  }
+
+  public final class SharedPreferencesKt {
+    ctor public SharedPreferencesKt();
+    method public static final void edit(android.content.SharedPreferences, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.content.res {
+
+  public final class TypedArrayKt {
+    ctor public TypedArrayKt();
+    method public static final boolean getBooleanOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @ColorInt public static final int getColorOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static final android.content.res.ColorStateList getColorStateListOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static final float getDimensionOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static final int getDimensionPixelOffsetOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static final int getDimensionPixelSizeOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static final android.graphics.drawable.Drawable getDrawableOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static final float getFloatOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @RequiresApi(26) public static final android.graphics.Typeface getFontOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static final int getIntOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static final int getIntegerOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static final String getStringOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static final CharSequence[] getTextArrayOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static final CharSequence getTextOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+  }
+
+}
+
+package androidx.database {
+
+  public final class CursorKt {
+    ctor public CursorKt();
+    method public static final byte[] getBlob(android.database.Cursor, String columnName);
+    method public static final byte[]? getBlobOrNull(android.database.Cursor, int index);
+    method public static final error.NonExistentClass getBlobOrNull(android.database.Cursor, String columnName);
+    method public static final double getDouble(android.database.Cursor, String columnName);
+    method public static final Double? getDoubleOrNull(android.database.Cursor, int index);
+    method public static final error.NonExistentClass getDoubleOrNull(android.database.Cursor, String columnName);
+    method public static final float getFloat(android.database.Cursor, String columnName);
+    method public static final Float? getFloatOrNull(android.database.Cursor, int index);
+    method public static final error.NonExistentClass getFloatOrNull(android.database.Cursor, String columnName);
+    method public static final int getInt(android.database.Cursor, String columnName);
+    method public static final Integer? getIntOrNull(android.database.Cursor, int index);
+    method public static final error.NonExistentClass getIntOrNull(android.database.Cursor, String columnName);
+    method public static final long getLong(android.database.Cursor, String columnName);
+    method public static final Long? getLongOrNull(android.database.Cursor, int index);
+    method public static final error.NonExistentClass getLongOrNull(android.database.Cursor, String columnName);
+    method public static final short getShort(android.database.Cursor, String columnName);
+    method public static final Short? getShortOrNull(android.database.Cursor, int index);
+    method public static final error.NonExistentClass getShortOrNull(android.database.Cursor, String columnName);
+    method public static final String getString(android.database.Cursor, String columnName);
+    method public static final String? getStringOrNull(android.database.Cursor, int index);
+    method public static final error.NonExistentClass getStringOrNull(android.database.Cursor, String columnName);
+  }
+
+}
+
+package androidx.database.sqlite {
+
+  public final class SQLiteDatabaseKt {
+    ctor public SQLiteDatabaseKt();
+    method public static final <T> T! transaction(android.database.sqlite.SQLiteDatabase, boolean exclusive = "true", kotlin.jvm.functions.Function1<? super android.database.sqlite.SQLiteDatabase,? extends T> body);
+  }
+
+}
+
+package androidx.graphics {
+
+  public final class BitmapKt {
+    ctor public BitmapKt();
+    method public static final android.graphics.Bitmap applyCanvas(android.graphics.Bitmap, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static final android.graphics.Bitmap createBitmap(int width, int height, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888");
+    method @RequiresApi(26) public static final android.graphics.Bitmap createBitmap(int width, int height, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888", boolean hasAlpha = "true", android.graphics.ColorSpace colorSpace = "ColorSpace.get(ColorSpace.Named.SRGB)");
+    method public static final operator int get(android.graphics.Bitmap, int x, int y);
+    method public static final android.graphics.Bitmap scale(android.graphics.Bitmap, int width, int height, boolean filter = "true");
+    method public static final operator void set(android.graphics.Bitmap, int x, int y, @ColorInt int color);
+  }
+
+  public final class CanvasKt {
+    ctor public CanvasKt();
+    method public static final void withRotation(android.graphics.Canvas, float degrees = "0.0f", float pivotX = "0.0f", float pivotY = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static final void withSave(android.graphics.Canvas, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static final void withScale(android.graphics.Canvas, float x = "1.0f", float y = "1.0f", float pivotX = "0.0f", float pivotY = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static final void withSkew(android.graphics.Canvas, float x = "0.0f", float y = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static final void withTranslation(android.graphics.Canvas, float x = "0.0f", float y = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class ColorKt {
+    ctor public ColorKt();
+    method @RequiresApi(26) public static final operator float component1(android.graphics.Color);
+    method public static final operator int component1(int);
+    method @RequiresApi(26) public static final operator float component1(long);
+    method @RequiresApi(26) public static final operator float component2(android.graphics.Color);
+    method public static final operator int component2(int);
+    method @RequiresApi(26) public static final operator float component2(long);
+    method @RequiresApi(26) public static final operator float component3(android.graphics.Color);
+    method public static final operator int component3(int);
+    method @RequiresApi(26) public static final operator float component3(long);
+    method @RequiresApi(26) public static final operator float component4(android.graphics.Color);
+    method public static final operator int component4(int);
+    method @RequiresApi(26) public static final operator float component4(long);
+    method public static final int getAlpha(int);
+    method @RequiresApi(26) public static final float getAlpha(long);
+    method public static final int getBlue(int);
+    method @RequiresApi(26) public static final float getBlue(long);
+    method @RequiresApi(26) public static final android.graphics.ColorSpace getColorSpace(long);
+    method public static final int getGreen(int);
+    method @RequiresApi(26) public static final float getGreen(long);
+    method @RequiresApi(26) public static final float getLuminance(int);
+    method @RequiresApi(26) public static final float getLuminance(long);
+    method public static final int getRed(int);
+    method @RequiresApi(26) public static final float getRed(long);
+    method @RequiresApi(26) public static final boolean isSrgb(long);
+    method @RequiresApi(26) public static final boolean isWideGamut(long);
+    method @RequiresApi(26) public static final operator android.graphics.Color plus(android.graphics.Color, android.graphics.Color c);
+    method @RequiresApi(26) public static final android.graphics.Color toColor(int);
+    method @RequiresApi(26) public static final android.graphics.Color toColor(long);
+    method @RequiresApi(26) @ColorInt public static final int toColorInt(long);
+    method @RequiresApi(26) @ColorLong public static final long toColorLong(int);
+  }
+
+  public final class MatrixKt {
+    ctor public MatrixKt();
+    method public static final error.NonExistentClass rotationMatrix(float degrees, float px = "0.0f", float py = "0.0f");
+    method public static final error.NonExistentClass scaleMatrix(float sx = "1.0f", float sy = "1.0f");
+    method public static final operator error.NonExistentClass times(android.graphics.Matrix, android.graphics.Matrix m);
+    method public static final error.NonExistentClass translationMatrix(float tx = "0.0f", float ty = "0.0f");
+    method public static final error.NonExistentClass values(android.graphics.Matrix);
+  }
+
+  public final class PathKt {
+    ctor public PathKt();
+    method @RequiresApi(19) public static final infix android.graphics.Path and(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(26) public static final Iterable<androidx.graphics.PathSegment> flatten(android.graphics.Path, float error = "0.5f");
+    method @RequiresApi(19) public static final operator android.graphics.Path minus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static final infix android.graphics.Path or(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static final operator android.graphics.Path plus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static final infix android.graphics.Path xor(android.graphics.Path, android.graphics.Path p);
+  }
+
+  public final class PathSegment {
+    ctor public PathSegment(android.graphics.PointF start, float startFraction, android.graphics.PointF end, float endFraction);
+    method public final android.graphics.PointF component1();
+    method public final float component2();
+    method public final android.graphics.PointF component3();
+    method public final float component4();
+    method public final androidx.graphics.PathSegment copy(android.graphics.PointF start, float startFraction, android.graphics.PointF end, float endFraction);
+    method public final android.graphics.PointF getEnd();
+    method public final float getEndFraction();
+    method public final android.graphics.PointF getStart();
+    method public final float getStartFraction();
+  }
+
+  public final class PictureKt {
+    ctor public PictureKt();
+    method public static final android.graphics.Picture record(android.graphics.Picture, int width, int height, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class PointKt {
+    ctor public PointKt();
+    method public static final operator int component1(android.graphics.Point);
+    method public static final operator float component1(android.graphics.PointF);
+    method public static final operator int component2(android.graphics.Point);
+    method public static final operator float component2(android.graphics.PointF);
+    method public static final operator android.graphics.Point minus(android.graphics.Point, android.graphics.Point p);
+    method public static final operator android.graphics.PointF minus(android.graphics.PointF, android.graphics.PointF p);
+    method public static final operator android.graphics.Point minus(android.graphics.Point, int xy);
+    method public static final operator android.graphics.PointF minus(android.graphics.PointF, float xy);
+    method public static final operator android.graphics.Point plus(android.graphics.Point, android.graphics.Point p);
+    method public static final operator android.graphics.PointF plus(android.graphics.PointF, android.graphics.PointF p);
+    method public static final operator android.graphics.Point plus(android.graphics.Point, int xy);
+    method public static final operator android.graphics.PointF plus(android.graphics.PointF, float xy);
+    method public static final android.graphics.Point toPoint(android.graphics.PointF);
+    method public static final android.graphics.PointF toPointF(android.graphics.Point);
+    method public static final operator android.graphics.Point unaryMinus(android.graphics.Point);
+    method public static final operator android.graphics.PointF unaryMinus(android.graphics.PointF);
+  }
+
+  public final class PorterDuffKt {
+    ctor public PorterDuffKt();
+    method public static final android.graphics.PorterDuffColorFilter toColorFilter(android.graphics.PorterDuff.Mode, int color);
+    method public static final android.graphics.PorterDuffXfermode toXfermode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final class RectKt {
+    ctor public RectKt();
+    method public static final infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r);
+    method public static final infix android.graphics.RectF and(android.graphics.RectF, android.graphics.RectF r);
+    method public static final operator int component1(android.graphics.Rect);
+    method public static final operator float component1(android.graphics.RectF);
+    method public static final operator int component2(android.graphics.Rect);
+    method public static final operator float component2(android.graphics.RectF);
+    method public static final operator int component3(android.graphics.Rect);
+    method public static final operator float component3(android.graphics.RectF);
+    method public static final operator int component4(android.graphics.Rect);
+    method public static final operator float component4(android.graphics.RectF);
+    method public static final operator boolean contains(android.graphics.Rect, android.graphics.Point p);
+    method public static final operator boolean contains(android.graphics.RectF, android.graphics.PointF p);
+    method public static final operator android.graphics.Region minus(android.graphics.Rect, android.graphics.Rect r);
+    method public static final operator android.graphics.Region minus(android.graphics.RectF, android.graphics.RectF r);
+    method public static final operator android.graphics.Rect minus(android.graphics.Rect, int xy);
+    method public static final operator android.graphics.RectF minus(android.graphics.RectF, float xy);
+    method public static final operator android.graphics.Rect minus(android.graphics.Rect, android.graphics.Point xy);
+    method public static final operator android.graphics.RectF minus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static final infix android.graphics.Rect or(android.graphics.Rect, android.graphics.Rect r);
+    method public static final infix android.graphics.RectF or(android.graphics.RectF, android.graphics.RectF r);
+    method public static final operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Rect r);
+    method public static final operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.RectF r);
+    method public static final operator android.graphics.Rect plus(android.graphics.Rect, int xy);
+    method public static final operator android.graphics.RectF plus(android.graphics.RectF, float xy);
+    method public static final operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Point xy);
+    method public static final operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static final android.graphics.Rect toRect(android.graphics.RectF);
+    method public static final android.graphics.RectF toRectF(android.graphics.Rect);
+    method public static final android.graphics.Region toRegion(android.graphics.Rect);
+    method public static final android.graphics.Region toRegion(android.graphics.RectF);
+    method public static final error.NonExistentClass transform(android.graphics.RectF, android.graphics.Matrix m);
+    method public static final infix android.graphics.Region xor(android.graphics.Rect, android.graphics.Rect r);
+    method public static final infix android.graphics.Region xor(android.graphics.RectF, android.graphics.RectF r);
+  }
+
+  public final class RegionKt {
+    ctor public RegionKt();
+    method public static final infix android.graphics.Region and(android.graphics.Region, android.graphics.Rect r);
+    method public static final infix android.graphics.Region and(android.graphics.Region, android.graphics.Region r);
+    method public static final operator boolean contains(android.graphics.Region, android.graphics.Point p);
+    method public static final void forEach(android.graphics.Region, kotlin.jvm.functions.Function1<? super android.graphics.Rect,kotlin.Unit> action);
+    method public static final operator java.util.Iterator<android.graphics.Rect> iterator(android.graphics.Region);
+    method public static final operator android.graphics.Region minus(android.graphics.Region, android.graphics.Rect r);
+    method public static final operator android.graphics.Region minus(android.graphics.Region, android.graphics.Region r);
+    method public static final operator android.graphics.Region not(android.graphics.Region);
+    method public static final infix android.graphics.Region or(android.graphics.Region, android.graphics.Rect r);
+    method public static final infix android.graphics.Region or(android.graphics.Region, android.graphics.Region r);
+    method public static final operator android.graphics.Region plus(android.graphics.Region, android.graphics.Rect r);
+    method public static final operator android.graphics.Region plus(android.graphics.Region, android.graphics.Region r);
+    method public static final operator android.graphics.Region unaryMinus(android.graphics.Region);
+    method public static final infix android.graphics.Region xor(android.graphics.Region, android.graphics.Rect r);
+    method public static final infix android.graphics.Region xor(android.graphics.Region, android.graphics.Region r);
+  }
+
+  public final class ShaderKt {
+    ctor public ShaderKt();
+    method public static final void transform(android.graphics.Shader, kotlin.jvm.functions.Function1<? super android.graphics.Matrix,kotlin.Unit> block);
+  }
+
+}
+
+package androidx.graphics.drawable {
+
+  public final class BitmapDrawableKt {
+    ctor public BitmapDrawableKt();
+    method public static final android.graphics.drawable.BitmapDrawable toDrawable(android.graphics.Bitmap, android.content.res.Resources resources);
+  }
+
+  public final class ColorDrawableKt {
+    ctor public ColorDrawableKt();
+    method public static final android.graphics.drawable.ColorDrawable toDrawable(int);
+    method @RequiresApi(26) public static final android.graphics.drawable.ColorDrawable toDrawable(android.graphics.Color);
+  }
+
+  public final class DrawableKt {
+    ctor public DrawableKt();
+    method public static final android.graphics.Bitmap toBitmap(android.graphics.drawable.Drawable, int width = "intrinsicWidth", int height = "intrinsicHeight", android.graphics.Bitmap.Config? config = "null");
+    method public static final void updateBounds(android.graphics.drawable.Drawable, int left = "bounds.left", int top = "bounds.top", int right = "bounds.right", int bottom = "bounds.bottom");
+  }
+
+  public final class IconKt {
+    ctor public IconKt();
+    method @RequiresApi(26) public static final android.graphics.drawable.Icon toAdaptiveIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static final android.graphics.drawable.Icon toIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static final android.graphics.drawable.Icon toIcon(android.net.Uri);
+    method @RequiresApi(26) public static final android.graphics.drawable.Icon toIcon(byte[]);
+  }
+
+}
+
+package androidx.net {
+
+  public final class UriKt {
+    ctor public UriKt();
+    method public static final android.net.Uri toUri(String);
+  }
+
+}
+
+package androidx.os {
+
+  public final class BundleKt {
+    ctor public BundleKt();
+    method public static final error.NonExistentClass bundleOf(kotlin.Pair<String,?>... pairs);
+  }
+
+  public final class HandlerKt {
+    ctor public HandlerKt();
+    method public static final error.NonExistentClass postAtTime(android.os.Handler, long uptimeMillis, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static final void postDelayed(android.os.Handler, Runnable runnable, Object? token, long delayInMillis);
+    method public static final error.NonExistentClass postDelayed(android.os.Handler, long delayInMillis, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static final error.NonExistentClass postDelayed(android.os.Handler, long amount, java.util.concurrent.TimeUnit unit, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method @RequiresApi(26) public static final error.NonExistentClass postDelayed(android.os.Handler, java.time.Duration duration, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+  }
+
+  public final class PersistableBundleKt {
+    ctor public PersistableBundleKt();
+    method @RequiresApi(21) public static final error.NonExistentClass persistableBundleOf(kotlin.Pair<String,?>... pairs);
+  }
+
+  public final class TraceKt {
+    ctor public TraceKt();
+    method public static final <T> T! trace(String sectionName, kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+}
+
+package androidx.text {
+
+  public final class SpannableStringBuilderKt {
+    ctor public SpannableStringBuilderKt();
+    method public static final android.text.SpannableStringBuilder backgroundColor(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static final android.text.SpannableStringBuilder bold(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static final android.text.SpannedString buildSpannedString(kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static final android.text.SpannableStringBuilder color(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static final android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object[] spans, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static final android.text.SpannableStringBuilder italic(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static final android.text.SpannableStringBuilder underline(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+  }
+
+}
+
+package androidx.time {
+
+  public final class DayOfWeekKt {
+    ctor public DayOfWeekKt();
+    method @RequiresApi(26) public static final java.time.DayOfWeek asDayOfWeek(int);
+    method @RequiresApi(26) public static final int asInt(java.time.DayOfWeek);
+  }
+
+  public final class DurationKt {
+    ctor public DurationKt();
+    method @RequiresApi(26) public static final operator long component1(java.time.Duration);
+    method @RequiresApi(26) public static final operator int component2(java.time.Duration);
+    method @RequiresApi(26) public static final operator java.time.Duration div(java.time.Duration, long divisor);
+    method @RequiresApi(260) public static final java.time.Duration hours(int);
+    method @RequiresApi(260) public static final java.time.Duration hours(long);
+    method @RequiresApi(26) public static final java.time.Duration millis(int);
+    method @RequiresApi(26) public static final java.time.Duration millis(long);
+    method @RequiresApi(260) public static final java.time.Duration minutes(int);
+    method @RequiresApi(260) public static final java.time.Duration minutes(long);
+    method @RequiresApi(26) public static final java.time.Duration nanos(int);
+    method @RequiresApi(26) public static final java.time.Duration nanos(long);
+    method @RequiresApi(26) public static final java.time.Duration seconds(int);
+    method @RequiresApi(26) public static final java.time.Duration seconds(long);
+    method @RequiresApi(26) public static final operator java.time.Duration times(java.time.Duration, long multiplicand);
+    method @RequiresApi(26) public static final operator java.time.Duration unaryMinus(java.time.Duration);
+  }
+
+  public final class InstantKt {
+    ctor public InstantKt();
+    method @RequiresApi(26) public static final java.time.Instant asEpochMillis(long);
+    method @RequiresApi(26) public static final java.time.Instant asEpochSeconds(long);
+    method @RequiresApi(26) public static final operator long component1(java.time.Instant);
+    method @RequiresApi(26) public static final operator int component2(java.time.Instant);
+  }
+
+  public final class LocalDateKt {
+    ctor public LocalDateKt();
+    method @RequiresApi(26) public static final operator int component1(java.time.LocalDate);
+    method @RequiresApi(26) public static final operator java.time.Month component2(java.time.LocalDate);
+    method @RequiresApi(26) public static final operator int component3(java.time.LocalDate);
+  }
+
+  public final class LocalDateTimeKt {
+    ctor public LocalDateTimeKt();
+    method @RequiresApi(26) public static final operator java.time.LocalDate component1(java.time.LocalDateTime);
+    method @RequiresApi(26) public static final operator java.time.LocalTime component2(java.time.LocalDateTime);
+  }
+
+  public final class LocalTimeKt {
+    ctor public LocalTimeKt();
+    method @RequiresApi(26) public static final operator int component1(java.time.LocalTime);
+    method @RequiresApi(26) public static final operator int component2(java.time.LocalTime);
+    method @RequiresApi(26) public static final operator int component3(java.time.LocalTime);
+    method @RequiresApi(26) public static final operator int component4(java.time.LocalTime);
+  }
+
+  public final class MonthDayKt {
+    ctor public MonthDayKt();
+    method @RequiresApi(26) public static final operator java.time.Month component1(java.time.MonthDay);
+    method @RequiresApi(26) public static final operator int component2(java.time.MonthDay);
+  }
+
+  public final class MonthKt {
+    ctor public MonthKt();
+    method @RequiresApi(26) public static final int asInt(java.time.Month);
+    method @RequiresApi(26) public static final java.time.Month asMonth(int);
+  }
+
+  public final class OffsetDateTimeKt {
+    ctor public OffsetDateTimeKt();
+    method @RequiresApi(26) public static final operator java.time.LocalDateTime component1(java.time.OffsetDateTime);
+    method @RequiresApi(26) public static final operator java.time.ZoneOffset component2(java.time.OffsetDateTime);
+  }
+
+  public final class OffsetTimeKt {
+    ctor public OffsetTimeKt();
+    method @RequiresApi(26) public static final operator java.time.LocalTime component1(java.time.OffsetTime);
+    method @RequiresApi(26) public static final operator java.time.ZoneOffset component2(java.time.OffsetTime);
+  }
+
+  public final class PeriodKt {
+    ctor public PeriodKt();
+    method @RequiresApi(26) public static final operator int component1(java.time.Period);
+    method @RequiresApi(26) public static final operator int component2(java.time.Period);
+    method @RequiresApi(26) public static final operator int component3(java.time.Period);
+    method @RequiresApi(26) public static final java.time.Period days(int);
+    method @RequiresApi(26) public static final java.time.Period months(int);
+    method @RequiresApi(26) public static final operator java.time.Period times(java.time.Period, int multiplicand);
+    method @RequiresApi(26) public static final operator java.time.Period unaryMinus(java.time.Period);
+    method @RequiresApi(26) public static final java.time.Period years(int);
+  }
+
+  public final class YearKt {
+    ctor public YearKt();
+    method @RequiresApi(26) public static final int asInt(java.time.Year);
+    method @RequiresApi(26) public static final java.time.Year asYear(int);
+  }
+
+  public final class YearMonthKt {
+    ctor public YearMonthKt();
+    method @RequiresApi(26) public static final operator int component1(java.time.YearMonth);
+    method @RequiresApi(26) public static final operator java.time.Month component2(java.time.YearMonth);
+  }
+
+  public final class ZonedDateTimeKt {
+    ctor public ZonedDateTimeKt();
+    method @RequiresApi(26) public static final operator java.time.LocalDateTime component1(java.time.ZonedDateTime);
+    method @RequiresApi(26) public static final operator java.time.ZoneId component2(java.time.ZonedDateTime);
+  }
+
+}
+
+package androidx.transition {
+
+  public final class TransitionKt {
+    ctor public TransitionKt();
+    method @RequiresApi(19) public static final void addListener(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onEnd = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onStart = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onCancel = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onResume = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onPause = "null");
+    method @RequiresApi(19) public static final void doOnCancel(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static final void doOnEnd(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static final void doOnPause(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static final void doOnResume(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static final void doOnStart(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.util {
+
+  public final class ArrayMapKt {
+    ctor public ArrayMapKt();
+    method @RequiresApi(19) public static final <K, V> android.util.ArrayMap<K,V> arrayMapOf();
+    method @RequiresApi(19) public static final <K, V> android.util.ArrayMap<K,V> arrayMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
+  }
+
+  public final class ArraySetKt {
+    ctor public ArraySetKt();
+    method @RequiresApi(23) public static final <T> android.util.ArraySet<T> arraySetOf();
+    method @RequiresApi(23) public static final <T> android.util.ArraySet<T> arraySetOf(T... values);
+  }
+
+  public final class AtomicFileKt {
+    ctor public AtomicFileKt();
+    method @RequiresApi(17) public static final byte[] readBytes(android.util.AtomicFile);
+    method @RequiresApi(17) public static final String readText(android.util.AtomicFile, java.nio.charset.Charset charset = "Charsets.UTF_8");
+    method @RequiresApi(17) public static final void tryWrite(android.util.AtomicFile, kotlin.jvm.functions.Function1<? super java.io.FileOutputStream,kotlin.Unit> block);
+    method @RequiresApi(17) public static final void writeBytes(android.util.AtomicFile, byte[] array);
+    method @RequiresApi(17) public static final void writeText(android.util.AtomicFile, String text, java.nio.charset.Charset charset = "Charsets.UTF_8");
+  }
+
+  public final class HalfKt {
+    ctor public HalfKt();
+    method @RequiresApi(26) public static final android.util.Half toHalf(short);
+    method @RequiresApi(26) public static final android.util.Half toHalf(float);
+    method @RequiresApi(26) public static final android.util.Half toHalf(double);
+    method @RequiresApi(26) public static final android.util.Half toHalf(String);
+  }
+
+  public final class LongSparseArrayKt {
+    ctor public LongSparseArrayKt();
+    method @RequiresApi(16) public static final operator <T> boolean contains(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static final <T> boolean containsKey(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static final <T> boolean containsValue(android.util.LongSparseArray<T>, T! value);
+    method @RequiresApi(16) public static final <T> void forEach(android.util.LongSparseArray<T>, kotlin.jvm.functions.Function2<? super Long,? super T,kotlin.Unit> action);
+    method @RequiresApi(16) public static final <T> T! getOrDefault(android.util.LongSparseArray<T>, long key, T! defaultValue);
+    method @RequiresApi(16) public static final <T> T! getOrElse(android.util.LongSparseArray<T>, long key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method @RequiresApi(16) public static final <T> int getSize(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static final <T> boolean isEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static final <T> boolean isNotEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static final <T> kotlin.collections.LongIterator keyIterator(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static final operator <T> android.util.LongSparseArray<T> plus(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static final <T> void putAll(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static final <T> boolean remove(android.util.LongSparseArray<T>, long key, T! value);
+    method @RequiresApi(16) public static final operator <T> void set(android.util.LongSparseArray<T>, long key, T! value);
+    method @RequiresApi(16) public static final <T> java.util.Iterator<T> valueIterator(android.util.LongSparseArray<T>);
+  }
+
+  public final class PairKt {
+    ctor public PairKt();
+    method public static final operator <F, S> F! component1(android.util.Pair<F,S>);
+    method public static final operator <F, S> S! component2(android.util.Pair<F,S>);
+    method public static final <F, S> android.util.Pair<F,S> toAndroidPair(kotlin.Pair<? extends F,? extends S>);
+    method public static final <F, S> kotlin.Pair<F,S> toKotlinPair(android.util.Pair<F,S>);
+  }
+
+  public final class RangeKt {
+    ctor public RangeKt();
+    method @RequiresApi(21) public static final infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> and(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static final operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, T value);
+    method @RequiresApi(21) public static final operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static final infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> rangeTo(T, T that);
+    method @RequiresApi(21) public static final <T extends java.lang.Comparable<? super T>> kotlin.ranges.ClosedRange<T> toClosedRange(android.util.Range<T>);
+    method @RequiresApi(21) public static final <T extends java.lang.Comparable<? super T>> android.util.Range<T> toRange(kotlin.ranges.ClosedRange<T>);
+  }
+
+  public final class SizeKt {
+    ctor public SizeKt();
+    method @RequiresApi(21) public static final operator int component1(android.util.Size);
+    method @RequiresApi(21) public static final operator float component1(android.util.SizeF);
+    method @RequiresApi(21) public static final operator int component2(android.util.Size);
+    method @RequiresApi(21) public static final operator float component2(android.util.SizeF);
+  }
+
+  public final class SparseArrayKt {
+    ctor public SparseArrayKt();
+    method public static final operator <T> boolean contains(android.util.SparseArray<T>, int key);
+    method public static final <T> boolean containsKey(android.util.SparseArray<T>, int key);
+    method public static final <T> boolean containsValue(android.util.SparseArray<T>, T! value);
+    method public static final <T> void forEach(android.util.SparseArray<T>, kotlin.jvm.functions.Function2<? super Integer,? super T,kotlin.Unit> action);
+    method public static final <T> T! getOrDefault(android.util.SparseArray<T>, int key, T! defaultValue);
+    method public static final <T> T! getOrElse(android.util.SparseArray<T>, int key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method public static final <T> int getSize(android.util.SparseArray<T>);
+    method public static final <T> boolean isEmpty(android.util.SparseArray<T>);
+    method public static final <T> boolean isNotEmpty(android.util.SparseArray<T>);
+    method public static final <T> kotlin.collections.IntIterator keyIterator(android.util.SparseArray<T>);
+    method public static final operator <T> android.util.SparseArray<T> plus(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static final <T> void putAll(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static final <T> boolean remove(android.util.SparseArray<T>, int key, T! value);
+    method public static final operator <T> void set(android.util.SparseArray<T>, int key, T! value);
+    method public static final <T> java.util.Iterator<T> valueIterator(android.util.SparseArray<T>);
+  }
+
+  public final class SparseBooleanArrayKt {
+    ctor public SparseBooleanArrayKt();
+    method public static final operator boolean contains(android.util.SparseBooleanArray, int key);
+    method public static final boolean containsKey(android.util.SparseBooleanArray, int key);
+    method public static final boolean containsValue(android.util.SparseBooleanArray, boolean value);
+    method public static final void forEach(android.util.SparseBooleanArray, kotlin.jvm.functions.Function2<? super Integer,? super Boolean,kotlin.Unit> action);
+    method public static final boolean getOrDefault(android.util.SparseBooleanArray, int key, boolean defaultValue);
+    method public static final error.NonExistentClass getOrElse(android.util.SparseBooleanArray, int key, kotlin.jvm.functions.Function0<Boolean> defaultValue);
+    method public static final int getSize(android.util.SparseBooleanArray);
+    method public static final boolean isEmpty(android.util.SparseBooleanArray);
+    method public static final boolean isNotEmpty(android.util.SparseBooleanArray);
+    method public static final kotlin.collections.IntIterator keyIterator(android.util.SparseBooleanArray);
+    method public static final operator android.util.SparseBooleanArray plus(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static final void putAll(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static final boolean remove(android.util.SparseBooleanArray, int key, boolean value);
+    method public static final void removeAt(android.util.SparseBooleanArray, int index);
+    method public static final operator void set(android.util.SparseBooleanArray, int key, boolean value);
+    method public static final kotlin.collections.BooleanIterator valueIterator(android.util.SparseBooleanArray);
+  }
+
+  public final class SparseIntArrayKt {
+    ctor public SparseIntArrayKt();
+    method public static final operator boolean contains(android.util.SparseIntArray, int key);
+    method public static final boolean containsKey(android.util.SparseIntArray, int key);
+    method public static final boolean containsValue(android.util.SparseIntArray, int value);
+    method public static final void forEach(android.util.SparseIntArray, kotlin.jvm.functions.Function2<? super Integer,? super Integer,kotlin.Unit> action);
+    method public static final int getOrDefault(android.util.SparseIntArray, int key, int defaultValue);
+    method public static final error.NonExistentClass getOrElse(android.util.SparseIntArray, int key, kotlin.jvm.functions.Function0<Integer> defaultValue);
+    method public static final int getSize(android.util.SparseIntArray);
+    method public static final boolean isEmpty(android.util.SparseIntArray);
+    method public static final boolean isNotEmpty(android.util.SparseIntArray);
+    method public static final kotlin.collections.IntIterator keyIterator(android.util.SparseIntArray);
+    method public static final operator android.util.SparseIntArray plus(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static final void putAll(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static final boolean remove(android.util.SparseIntArray, int key, int value);
+    method public static final operator void set(android.util.SparseIntArray, int key, int value);
+    method public static final kotlin.collections.IntIterator valueIterator(android.util.SparseIntArray);
+  }
+
+  public final class SparseLongArrayKt {
+    ctor public SparseLongArrayKt();
+    method @RequiresApi(18) public static final operator boolean contains(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static final boolean containsKey(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static final boolean containsValue(android.util.SparseLongArray, long value);
+    method @RequiresApi(18) public static final void forEach(android.util.SparseLongArray, kotlin.jvm.functions.Function2<? super Integer,? super Long,kotlin.Unit> action);
+    method @RequiresApi(18) public static final long getOrDefault(android.util.SparseLongArray, int key, long defaultValue);
+    method @RequiresApi(18) public static final error.NonExistentClass getOrElse(android.util.SparseLongArray, int key, kotlin.jvm.functions.Function0<Long> defaultValue);
+    method @RequiresApi(18) public static final int getSize(android.util.SparseLongArray);
+    method @RequiresApi(18) public static final boolean isEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static final boolean isNotEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static final kotlin.collections.IntIterator keyIterator(android.util.SparseLongArray);
+    method @RequiresApi(18) public static final operator android.util.SparseLongArray plus(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static final void putAll(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static final boolean remove(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static final operator void set(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static final kotlin.collections.LongIterator valueIterator(android.util.SparseLongArray);
+  }
+
+}
+
+package androidx.view {
+
+  public final class ViewGroupKt {
+    ctor public ViewGroupKt();
+    method public static final operator boolean contains(android.view.ViewGroup, android.view.View view);
+    method public static final void forEach(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static final void forEachIndexed(android.view.ViewGroup, kotlin.jvm.functions.Function2<? super Integer,? super android.view.View,kotlin.Unit> action);
+    method public static final operator android.view.View get(android.view.ViewGroup, int index);
+    method public static final int getSize(android.view.ViewGroup);
+    method public static final boolean isEmpty(android.view.ViewGroup);
+    method public static final boolean isNotEmpty(android.view.ViewGroup);
+    method public static final operator java.util.Iterator<android.view.View> iterator(android.view.ViewGroup);
+    method public static final operator void minusAssign(android.view.ViewGroup, android.view.View view);
+    method public static final operator void plusAssign(android.view.ViewGroup, android.view.View view);
+    method public static final void setMargins(android.view.ViewGroup.MarginLayoutParams, int size);
+    method public static final void updateMargins(android.view.ViewGroup.MarginLayoutParams, int left = "leftMargin", int top = "topMargin", int right = "rightMargin", int bottom = "bottomMargin");
+    method @RequiresApi(17) public static final void updateMarginsRelative(android.view.ViewGroup.MarginLayoutParams, int start = "marginStart", int top = "topMargin", int end = "marginEnd", int bottom = "bottomMargin");
+  }
+
+  public final class ViewKt {
+    ctor public ViewKt();
+    method public static final void doOnLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static final void doOnNextLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static final void doOnPreDraw(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static final Runnable postDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method @RequiresApi(16) public static final Runnable postOnAnimationDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static final void setPadding(android.view.View, int size);
+    method public static final android.graphics.Bitmap toBitmap(android.view.View, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888");
+    method public static final void updatePadding(android.view.View, int left = "paddingLeft", int top = "paddingTop", int right = "paddingRight", int bottom = "paddingBottom");
+    method @RequiresApi(17) public static final void updatePaddingRelative(android.view.View, int start = "paddingStart", int top = "paddingTop", int end = "paddingEnd", int bottom = "paddingBottom");
+  }
+
+}
+
diff --git a/core/ktx/api/0.2.txt b/core/ktx/api/0.2.txt
new file mode 100644
index 0000000..5fbfe40
--- /dev/null
+++ b/core/ktx/api/0.2.txt
@@ -0,0 +1,742 @@
+package androidx.animation {
+
+  public final class AnimatorKt {
+    ctor public AnimatorKt();
+    method public static android.animation.Animator.AnimatorListener addListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onEnd = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onStart = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onCancel = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onRepeat = "null");
+    method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener addPauseListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onResume = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onPause = "null");
+    method public static android.animation.Animator.AnimatorListener doOnCancel(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static android.animation.Animator.AnimatorListener doOnEnd(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener doOnPause(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static android.animation.Animator.AnimatorListener doOnRepeat(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener doOnResume(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static android.animation.Animator.AnimatorListener doOnStart(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.content {
+
+  public final class ContentValuesKt {
+    ctor public ContentValuesKt();
+    method public static error.NonExistentClass contentValuesOf(kotlin.Pair<String,?>... pairs);
+  }
+
+  public final class ContextKt {
+    ctor public ContextKt();
+    method public static void withStyledAttributes(android.content.Context, android.util.AttributeSet? set = "null", int[] attrs, @AttrRes int defStyleAttr = "0", @StyleRes int defStyleRes = "0", kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+    method public static void withStyledAttributes(android.content.Context, @StyleRes int resourceId, int[] attrs, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+  }
+
+  public final class SharedPreferencesKt {
+    ctor public SharedPreferencesKt();
+    method public static void edit(android.content.SharedPreferences, boolean commit = "false", kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.content.res {
+
+  public final class TypedArrayKt {
+    ctor public TypedArrayKt();
+    method public static boolean getBooleanOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @ColorInt public static int getColorOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.content.res.ColorStateList getColorStateListOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getDimensionOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelOffsetOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelSizeOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.graphics.drawable.Drawable getDrawableOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getFloatOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @RequiresApi(26) public static android.graphics.Typeface getFontOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntegerOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @AnyRes public static int getResourceIdOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static String getStringOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence[] getTextArrayOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence getTextOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static <R> R! use(android.content.res.TypedArray, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,? extends R> block);
+  }
+
+}
+
+package androidx.database {
+
+  public final class CursorKt {
+    ctor public CursorKt();
+    method public static byte[] getBlob(android.database.Cursor, String columnName);
+    method public static byte[]? getBlobOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getBlobOrNull(android.database.Cursor, String columnName);
+    method public static double getDouble(android.database.Cursor, String columnName);
+    method public static Double? getDoubleOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getDoubleOrNull(android.database.Cursor, String columnName);
+    method public static float getFloat(android.database.Cursor, String columnName);
+    method public static Float? getFloatOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getFloatOrNull(android.database.Cursor, String columnName);
+    method public static int getInt(android.database.Cursor, String columnName);
+    method public static Integer? getIntOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getIntOrNull(android.database.Cursor, String columnName);
+    method public static long getLong(android.database.Cursor, String columnName);
+    method public static Long? getLongOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getLongOrNull(android.database.Cursor, String columnName);
+    method public static short getShort(android.database.Cursor, String columnName);
+    method public static Short? getShortOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getShortOrNull(android.database.Cursor, String columnName);
+    method public static String getString(android.database.Cursor, String columnName);
+    method public static String? getStringOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getStringOrNull(android.database.Cursor, String columnName);
+  }
+
+}
+
+package androidx.database.sqlite {
+
+  public final class SQLiteDatabaseKt {
+    ctor public SQLiteDatabaseKt();
+    method public static <T> T! transaction(android.database.sqlite.SQLiteDatabase, boolean exclusive = "true", kotlin.jvm.functions.Function1<? super android.database.sqlite.SQLiteDatabase,? extends T> body);
+  }
+
+}
+
+package androidx.graphics {
+
+  public final class BitmapKt {
+    ctor public BitmapKt();
+    method public static android.graphics.Bitmap applyCanvas(android.graphics.Bitmap, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static android.graphics.Bitmap createBitmap(int width, int height, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888");
+    method @RequiresApi(26) public static android.graphics.Bitmap createBitmap(int width, int height, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888", boolean hasAlpha = "true", android.graphics.ColorSpace colorSpace = "ColorSpace.get(ColorSpace.Named.SRGB)");
+    method public static operator int get(android.graphics.Bitmap, int x, int y);
+    method public static android.graphics.Bitmap scale(android.graphics.Bitmap, int width, int height, boolean filter = "true");
+    method public static operator void set(android.graphics.Bitmap, int x, int y, @ColorInt int color);
+  }
+
+  public final class CanvasKt {
+    ctor public CanvasKt();
+    method public static void withRotation(android.graphics.Canvas, float degrees = "0.0f", float pivotX = "0.0f", float pivotY = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static void withSave(android.graphics.Canvas, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static void withScale(android.graphics.Canvas, float x = "1.0f", float y = "1.0f", float pivotX = "0.0f", float pivotY = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static void withSkew(android.graphics.Canvas, float x = "0.0f", float y = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static void withTranslation(android.graphics.Canvas, float x = "0.0f", float y = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class ColorKt {
+    ctor public ColorKt();
+    method @RequiresApi(26) public static operator float component1(android.graphics.Color);
+    method public static operator int component1(int);
+    method @RequiresApi(26) public static operator float component1(long);
+    method @RequiresApi(26) public static operator float component2(android.graphics.Color);
+    method public static operator int component2(int);
+    method @RequiresApi(26) public static operator float component2(long);
+    method @RequiresApi(26) public static operator float component3(android.graphics.Color);
+    method public static operator int component3(int);
+    method @RequiresApi(26) public static operator float component3(long);
+    method @RequiresApi(26) public static operator float component4(android.graphics.Color);
+    method public static operator int component4(int);
+    method @RequiresApi(26) public static operator float component4(long);
+    method public static int getAlpha(int);
+    method @RequiresApi(26) public static float getAlpha(long);
+    method public static int getBlue(int);
+    method @RequiresApi(26) public static float getBlue(long);
+    method @RequiresApi(26) public static android.graphics.ColorSpace getColorSpace(long);
+    method public static int getGreen(int);
+    method @RequiresApi(26) public static float getGreen(long);
+    method @RequiresApi(26) public static float getLuminance(int);
+    method @RequiresApi(26) public static float getLuminance(long);
+    method public static int getRed(int);
+    method @RequiresApi(26) public static float getRed(long);
+    method @RequiresApi(26) public static boolean isSrgb(long);
+    method @RequiresApi(26) public static boolean isWideGamut(long);
+    method @RequiresApi(26) public static operator android.graphics.Color plus(android.graphics.Color, android.graphics.Color c);
+    method @RequiresApi(26) public static android.graphics.Color toColor(int);
+    method @RequiresApi(26) public static android.graphics.Color toColor(long);
+    method @RequiresApi(26) @ColorInt public static int toColorInt(long);
+    method @ColorInt public static int toColorInt(String);
+    method @RequiresApi(26) @ColorLong public static long toColorLong(int);
+  }
+
+  public final class MatrixKt {
+    ctor public MatrixKt();
+    method public static error.NonExistentClass rotationMatrix(float degrees, float px = "0.0f", float py = "0.0f");
+    method public static error.NonExistentClass scaleMatrix(float sx = "1.0f", float sy = "1.0f");
+    method public static operator error.NonExistentClass times(android.graphics.Matrix, android.graphics.Matrix m);
+    method public static error.NonExistentClass translationMatrix(float tx = "0.0f", float ty = "0.0f");
+    method public static error.NonExistentClass values(android.graphics.Matrix);
+  }
+
+  public final class PathKt {
+    ctor public PathKt();
+    method @RequiresApi(19) public static infix android.graphics.Path and(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(26) public static Iterable<androidx.graphics.PathSegment> flatten(android.graphics.Path, float error = "0.5f");
+    method @RequiresApi(19) public static operator android.graphics.Path minus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static infix android.graphics.Path or(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static operator android.graphics.Path plus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static infix android.graphics.Path xor(android.graphics.Path, android.graphics.Path p);
+  }
+
+  public final class PathSegment {
+    ctor public PathSegment(android.graphics.PointF start, float startFraction, android.graphics.PointF end, float endFraction);
+    method public android.graphics.PointF component1();
+    method public float component2();
+    method public android.graphics.PointF component3();
+    method public float component4();
+    method public androidx.graphics.PathSegment copy(android.graphics.PointF start, float startFraction, android.graphics.PointF end, float endFraction);
+    method public android.graphics.PointF getEnd();
+    method public float getEndFraction();
+    method public android.graphics.PointF getStart();
+    method public float getStartFraction();
+  }
+
+  public final class PictureKt {
+    ctor public PictureKt();
+    method public static android.graphics.Picture record(android.graphics.Picture, int width, int height, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class PointKt {
+    ctor public PointKt();
+    method public static operator int component1(android.graphics.Point);
+    method public static operator float component1(android.graphics.PointF);
+    method public static operator int component2(android.graphics.Point);
+    method public static operator float component2(android.graphics.PointF);
+    method public static operator android.graphics.Point minus(android.graphics.Point, android.graphics.Point p);
+    method public static operator android.graphics.PointF minus(android.graphics.PointF, android.graphics.PointF p);
+    method public static operator android.graphics.Point minus(android.graphics.Point, int xy);
+    method public static operator android.graphics.PointF minus(android.graphics.PointF, float xy);
+    method public static operator android.graphics.Point plus(android.graphics.Point, android.graphics.Point p);
+    method public static operator android.graphics.PointF plus(android.graphics.PointF, android.graphics.PointF p);
+    method public static operator android.graphics.Point plus(android.graphics.Point, int xy);
+    method public static operator android.graphics.PointF plus(android.graphics.PointF, float xy);
+    method public static android.graphics.Point toPoint(android.graphics.PointF);
+    method public static android.graphics.PointF toPointF(android.graphics.Point);
+    method public static operator android.graphics.Point unaryMinus(android.graphics.Point);
+    method public static operator android.graphics.PointF unaryMinus(android.graphics.PointF);
+  }
+
+  public final class PorterDuffKt {
+    ctor public PorterDuffKt();
+    method public static android.graphics.PorterDuffColorFilter toColorFilter(android.graphics.PorterDuff.Mode, int color);
+    method public static android.graphics.PorterDuffXfermode toXfermode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final class RectKt {
+    ctor public RectKt();
+    method public static infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r);
+    method public static infix android.graphics.RectF and(android.graphics.RectF, android.graphics.RectF r);
+    method public static operator int component1(android.graphics.Rect);
+    method public static operator float component1(android.graphics.RectF);
+    method public static operator int component2(android.graphics.Rect);
+    method public static operator float component2(android.graphics.RectF);
+    method public static operator int component3(android.graphics.Rect);
+    method public static operator float component3(android.graphics.RectF);
+    method public static operator int component4(android.graphics.Rect);
+    method public static operator float component4(android.graphics.RectF);
+    method public static operator boolean contains(android.graphics.Rect, android.graphics.Point p);
+    method public static operator boolean contains(android.graphics.RectF, android.graphics.PointF p);
+    method public static operator android.graphics.Region minus(android.graphics.Rect, android.graphics.Rect r);
+    method public static operator android.graphics.Region minus(android.graphics.RectF, android.graphics.RectF r);
+    method public static operator android.graphics.Rect minus(android.graphics.Rect, int xy);
+    method public static operator android.graphics.RectF minus(android.graphics.RectF, float xy);
+    method public static operator android.graphics.Rect minus(android.graphics.Rect, android.graphics.Point xy);
+    method public static operator android.graphics.RectF minus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static infix android.graphics.Rect or(android.graphics.Rect, android.graphics.Rect r);
+    method public static infix android.graphics.RectF or(android.graphics.RectF, android.graphics.RectF r);
+    method public static operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Rect r);
+    method public static operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.RectF r);
+    method public static operator android.graphics.Rect plus(android.graphics.Rect, int xy);
+    method public static operator android.graphics.RectF plus(android.graphics.RectF, float xy);
+    method public static operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Point xy);
+    method public static operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static android.graphics.Rect toRect(android.graphics.RectF);
+    method public static android.graphics.RectF toRectF(android.graphics.Rect);
+    method public static android.graphics.Region toRegion(android.graphics.Rect);
+    method public static android.graphics.Region toRegion(android.graphics.RectF);
+    method public static error.NonExistentClass transform(android.graphics.RectF, android.graphics.Matrix m);
+    method public static infix android.graphics.Region xor(android.graphics.Rect, android.graphics.Rect r);
+    method public static infix android.graphics.Region xor(android.graphics.RectF, android.graphics.RectF r);
+  }
+
+  public final class RegionKt {
+    ctor public RegionKt();
+    method public static infix android.graphics.Region and(android.graphics.Region, android.graphics.Rect r);
+    method public static infix android.graphics.Region and(android.graphics.Region, android.graphics.Region r);
+    method public static operator boolean contains(android.graphics.Region, android.graphics.Point p);
+    method public static void forEach(android.graphics.Region, kotlin.jvm.functions.Function1<? super android.graphics.Rect,kotlin.Unit> action);
+    method public static operator java.util.Iterator<android.graphics.Rect> iterator(android.graphics.Region);
+    method public static operator android.graphics.Region minus(android.graphics.Region, android.graphics.Rect r);
+    method public static operator android.graphics.Region minus(android.graphics.Region, android.graphics.Region r);
+    method public static operator android.graphics.Region not(android.graphics.Region);
+    method public static infix android.graphics.Region or(android.graphics.Region, android.graphics.Rect r);
+    method public static infix android.graphics.Region or(android.graphics.Region, android.graphics.Region r);
+    method public static operator android.graphics.Region plus(android.graphics.Region, android.graphics.Rect r);
+    method public static operator android.graphics.Region plus(android.graphics.Region, android.graphics.Region r);
+    method public static operator android.graphics.Region unaryMinus(android.graphics.Region);
+    method public static infix android.graphics.Region xor(android.graphics.Region, android.graphics.Rect r);
+    method public static infix android.graphics.Region xor(android.graphics.Region, android.graphics.Region r);
+  }
+
+  public final class ShaderKt {
+    ctor public ShaderKt();
+    method public static void transform(android.graphics.Shader, kotlin.jvm.functions.Function1<? super android.graphics.Matrix,kotlin.Unit> block);
+  }
+
+}
+
+package androidx.graphics.drawable {
+
+  public final class BitmapDrawableKt {
+    ctor public BitmapDrawableKt();
+    method public static android.graphics.drawable.BitmapDrawable toDrawable(android.graphics.Bitmap, android.content.res.Resources resources);
+  }
+
+  public final class ColorDrawableKt {
+    ctor public ColorDrawableKt();
+    method public static android.graphics.drawable.ColorDrawable toDrawable(int);
+    method @RequiresApi(26) public static android.graphics.drawable.ColorDrawable toDrawable(android.graphics.Color);
+  }
+
+  public final class DrawableKt {
+    ctor public DrawableKt();
+    method public static android.graphics.Bitmap toBitmap(android.graphics.drawable.Drawable, @Px int width = "intrinsicWidth", @Px int height = "intrinsicHeight", android.graphics.Bitmap.Config? config = "null");
+    method public static void updateBounds(android.graphics.drawable.Drawable, @Px int left = "bounds.left", @Px int top = "bounds.top", @Px int right = "bounds.right", @Px int bottom = "bounds.bottom");
+  }
+
+  public final class IconKt {
+    ctor public IconKt();
+    method @RequiresApi(26) public static android.graphics.drawable.Icon toAdaptiveIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static android.graphics.drawable.Icon toIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static android.graphics.drawable.Icon toIcon(android.net.Uri);
+    method @RequiresApi(26) public static android.graphics.drawable.Icon toIcon(byte[]);
+  }
+
+}
+
+package androidx.net {
+
+  public final class UriKt {
+    ctor public UriKt();
+    method public static android.net.Uri toUri(String);
+  }
+
+}
+
+package androidx.os {
+
+  public final class BundleKt {
+    ctor public BundleKt();
+    method public static error.NonExistentClass bundleOf(kotlin.Pair<String,?>... pairs);
+  }
+
+  public final class FileKt {
+    ctor public FileKt();
+    method public static android.net.Uri toUri(java.io.File);
+  }
+
+  public final class HandlerKt {
+    ctor public HandlerKt();
+    method public static error.NonExistentClass postAtTime(android.os.Handler, long uptimeMillis, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static void postDelayed(android.os.Handler, Runnable runnable, Object? token, long delayInMillis);
+    method public static error.NonExistentClass postDelayed(android.os.Handler, long delayInMillis, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static error.NonExistentClass postDelayed(android.os.Handler, long amount, java.util.concurrent.TimeUnit unit, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method @RequiresApi(26) public static error.NonExistentClass postDelayed(android.os.Handler, java.time.Duration duration, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+  }
+
+  public final class PersistableBundleKt {
+    ctor public PersistableBundleKt();
+    method @RequiresApi(21) public static error.NonExistentClass persistableBundleOf(kotlin.Pair<String,?>... pairs);
+  }
+
+  public final class TraceKt {
+    ctor public TraceKt();
+    method public static <T> T! trace(String sectionName, kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+}
+
+package androidx.text {
+
+  public final class CharSequenceKt {
+    ctor public CharSequenceKt();
+    method public static boolean isDigitsOnly(CharSequence);
+    method public static int trimmedLength(CharSequence);
+  }
+
+  public final class SpannableStringBuilderKt {
+    ctor public SpannableStringBuilderKt();
+    method public static android.text.SpannableStringBuilder backgroundColor(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder bold(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannedString buildSpannedString(kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder color(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object[] spans, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object span, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder italic(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder scale(android.text.SpannableStringBuilder, float proportion, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder strikeThrough(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder underline(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+  }
+
+  public final class SpannableStringKt {
+    ctor public SpannableStringKt();
+    method public static error.NonExistentClass clearSpans(android.text.Spannable);
+    method public static operator void minusAssign(android.text.Spannable, Object span);
+    method public static operator void plusAssign(android.text.Spannable, Object span);
+    method public static android.text.Spannable toSpannable(CharSequence);
+  }
+
+  public final class SpannedStringKt {
+    ctor public SpannedStringKt();
+    method public static android.text.Spanned toSpanned(CharSequence);
+  }
+
+  public final class StringKt {
+    ctor public StringKt();
+    method public static String htmlEncode(String);
+  }
+
+}
+
+package androidx.time {
+
+  public final class DayOfWeekKt {
+    ctor public DayOfWeekKt();
+    method @RequiresApi(26) deprecated public static java.time.DayOfWeek asDayOfWeek(int);
+    method @RequiresApi(26) deprecated public static int asInt(java.time.DayOfWeek);
+  }
+
+  public final class DeprecationKt {
+    ctor public DeprecationKt();
+  }
+
+  public final class DurationKt {
+    ctor public DurationKt();
+    method @RequiresApi(26) deprecated public static operator long component1(java.time.Duration);
+    method @RequiresApi(26) deprecated public static operator int component2(java.time.Duration);
+    method @RequiresApi(26) deprecated public static operator java.time.Duration div(java.time.Duration, long divisor);
+    method @RequiresApi(26) deprecated public static java.time.Duration hours(int);
+    method @RequiresApi(26) deprecated public static java.time.Duration hours(long);
+    method @RequiresApi(26) deprecated public static java.time.Duration millis(int);
+    method @RequiresApi(26) deprecated public static java.time.Duration millis(long);
+    method @RequiresApi(26) deprecated public static java.time.Duration minutes(int);
+    method @RequiresApi(26) deprecated public static java.time.Duration minutes(long);
+    method @RequiresApi(26) deprecated public static java.time.Duration nanos(int);
+    method @RequiresApi(26) deprecated public static java.time.Duration nanos(long);
+    method @RequiresApi(26) deprecated public static java.time.Duration seconds(int);
+    method @RequiresApi(26) deprecated public static java.time.Duration seconds(long);
+    method @RequiresApi(26) deprecated public static operator java.time.Duration times(java.time.Duration, long multiplicand);
+    method @RequiresApi(26) deprecated public static operator java.time.Duration unaryMinus(java.time.Duration);
+  }
+
+  public final class InstantKt {
+    ctor public InstantKt();
+    method @RequiresApi(26) deprecated public static java.time.Instant asEpochMillis(long);
+    method @RequiresApi(26) deprecated public static java.time.Instant asEpochSeconds(long);
+    method @RequiresApi(26) deprecated public static operator long component1(java.time.Instant);
+    method @RequiresApi(26) deprecated public static operator int component2(java.time.Instant);
+  }
+
+  public final class LocalDateKt {
+    ctor public LocalDateKt();
+    method @RequiresApi(26) deprecated public static operator int component1(java.time.LocalDate);
+    method @RequiresApi(26) deprecated public static operator java.time.Month component2(java.time.LocalDate);
+    method @RequiresApi(26) deprecated public static operator int component3(java.time.LocalDate);
+  }
+
+  public final class LocalDateTimeKt {
+    ctor public LocalDateTimeKt();
+    method @RequiresApi(26) deprecated public static operator java.time.LocalDate component1(java.time.LocalDateTime);
+    method @RequiresApi(26) deprecated public static operator java.time.LocalTime component2(java.time.LocalDateTime);
+  }
+
+  public final class LocalTimeKt {
+    ctor public LocalTimeKt();
+    method @RequiresApi(26) deprecated public static operator int component1(java.time.LocalTime);
+    method @RequiresApi(26) deprecated public static operator int component2(java.time.LocalTime);
+    method @RequiresApi(26) deprecated public static operator int component3(java.time.LocalTime);
+    method @RequiresApi(26) deprecated public static operator int component4(java.time.LocalTime);
+  }
+
+  public final class MonthDayKt {
+    ctor public MonthDayKt();
+    method @RequiresApi(26) deprecated public static operator java.time.Month component1(java.time.MonthDay);
+    method @RequiresApi(26) deprecated public static operator int component2(java.time.MonthDay);
+  }
+
+  public final class MonthKt {
+    ctor public MonthKt();
+    method @RequiresApi(26) deprecated public static int asInt(java.time.Month);
+    method @RequiresApi(26) deprecated public static java.time.Month asMonth(int);
+  }
+
+  public final class OffsetDateTimeKt {
+    ctor public OffsetDateTimeKt();
+    method @RequiresApi(26) deprecated public static operator java.time.LocalDateTime component1(java.time.OffsetDateTime);
+    method @RequiresApi(26) deprecated public static operator java.time.ZoneOffset component2(java.time.OffsetDateTime);
+  }
+
+  public final class OffsetTimeKt {
+    ctor public OffsetTimeKt();
+    method @RequiresApi(26) deprecated public static operator java.time.LocalTime component1(java.time.OffsetTime);
+    method @RequiresApi(26) deprecated public static operator java.time.ZoneOffset component2(java.time.OffsetTime);
+  }
+
+  public final class PeriodKt {
+    ctor public PeriodKt();
+    method @RequiresApi(26) deprecated public static operator int component1(java.time.Period);
+    method @RequiresApi(26) deprecated public static operator int component2(java.time.Period);
+    method @RequiresApi(26) deprecated public static operator int component3(java.time.Period);
+    method @RequiresApi(26) deprecated public static java.time.Period days(int);
+    method @RequiresApi(26) deprecated public static java.time.Period months(int);
+    method @RequiresApi(26) deprecated public static operator java.time.Period times(java.time.Period, int multiplicand);
+    method @RequiresApi(26) deprecated public static operator java.time.Period unaryMinus(java.time.Period);
+    method @RequiresApi(26) deprecated public static java.time.Period years(int);
+  }
+
+  public final class YearKt {
+    ctor public YearKt();
+    method @RequiresApi(26) deprecated public static int asInt(java.time.Year);
+    method @RequiresApi(26) deprecated public static java.time.Year asYear(int);
+  }
+
+  public final class YearMonthKt {
+    ctor public YearMonthKt();
+    method @RequiresApi(26) deprecated public static operator int component1(java.time.YearMonth);
+    method @RequiresApi(26) deprecated public static operator java.time.Month component2(java.time.YearMonth);
+  }
+
+  public final class ZonedDateTimeKt {
+    ctor public ZonedDateTimeKt();
+    method @RequiresApi(26) deprecated public static operator java.time.LocalDateTime component1(java.time.ZonedDateTime);
+    method @RequiresApi(26) deprecated public static operator java.time.ZoneId component2(java.time.ZonedDateTime);
+  }
+
+}
+
+package androidx.transition {
+
+  public final class TransitionKt {
+    ctor public TransitionKt();
+    method @RequiresApi(19) public static void addListener(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onEnd = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onStart = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onCancel = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onResume = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onPause = "null");
+    method @RequiresApi(19) public static void doOnCancel(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static void doOnEnd(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static void doOnPause(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static void doOnResume(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static void doOnStart(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.util {
+
+  public final class ArrayMapKt {
+    ctor public ArrayMapKt();
+    method @RequiresApi(19) public static <K, V> android.util.ArrayMap<K,V> arrayMapOf();
+    method @RequiresApi(19) public static <K, V> android.util.ArrayMap<K,V> arrayMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
+  }
+
+  public final class ArraySetKt {
+    ctor public ArraySetKt();
+    method @RequiresApi(23) public static <T> android.util.ArraySet<T> arraySetOf();
+    method @RequiresApi(23) public static <T> android.util.ArraySet<T> arraySetOf(T... values);
+  }
+
+  public final class AtomicFileKt {
+    ctor public AtomicFileKt();
+    method @RequiresApi(17) public static byte[] readBytes(android.util.AtomicFile);
+    method @RequiresApi(17) public static String readText(android.util.AtomicFile, java.nio.charset.Charset charset = "Charsets.UTF_8");
+    method @RequiresApi(17) public static void tryWrite(android.util.AtomicFile, kotlin.jvm.functions.Function1<? super java.io.FileOutputStream,kotlin.Unit> block);
+    method @RequiresApi(17) public static void writeBytes(android.util.AtomicFile, byte[] array);
+    method @RequiresApi(17) public static void writeText(android.util.AtomicFile, String text, java.nio.charset.Charset charset = "Charsets.UTF_8");
+  }
+
+  public final class HalfKt {
+    ctor public HalfKt();
+    method @RequiresApi(26) public static android.util.Half toHalf(short);
+    method @RequiresApi(26) public static android.util.Half toHalf(float);
+    method @RequiresApi(26) public static android.util.Half toHalf(double);
+    method @RequiresApi(26) public static android.util.Half toHalf(String);
+  }
+
+  public final class LocaleKt {
+    ctor public LocaleKt();
+    method @RequiresApi(17) public static int getLayoutDirection(java.util.Locale);
+  }
+
+  public final class LongSparseArrayKt {
+    ctor public LongSparseArrayKt();
+    method @RequiresApi(16) public static operator <T> boolean contains(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static <T> boolean containsKey(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static <T> boolean containsValue(android.util.LongSparseArray<T>, T! value);
+    method @RequiresApi(16) public static <T> void forEach(android.util.LongSparseArray<T>, kotlin.jvm.functions.Function2<? super Long,? super T,kotlin.Unit> action);
+    method @RequiresApi(16) public static <T> T! getOrDefault(android.util.LongSparseArray<T>, long key, T! defaultValue);
+    method @RequiresApi(16) public static <T> T! getOrElse(android.util.LongSparseArray<T>, long key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method @RequiresApi(16) public static <T> int getSize(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static <T> boolean isEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static <T> boolean isNotEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static <T> kotlin.collections.LongIterator keyIterator(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static operator <T> android.util.LongSparseArray<T> plus(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> void putAll(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> boolean remove(android.util.LongSparseArray<T>, long key, T! value);
+    method @RequiresApi(16) public static operator <T> void set(android.util.LongSparseArray<T>, long key, T! value);
+    method @RequiresApi(16) public static <T> java.util.Iterator<T> valueIterator(android.util.LongSparseArray<T>);
+  }
+
+  public final class LruCacheKt {
+    ctor public LruCacheKt();
+    method public static <K, V> android.util.LruCache<K,V> lruCache(int maxSize, kotlin.jvm.functions.Function2<? super K,? super V,Integer> sizeOf = "{ _, _ -> 1 }", kotlin.jvm.functions.Function1<? super K,? extends V> create = "{ null as V? }", kotlin.jvm.functions.Function4<? super Boolean,? super K,? super V,? super V,kotlin.Unit> onEntryRemoved = "{ _, _, _, _ -> }");
+  }
+
+  public final class PairKt {
+    ctor public PairKt();
+    method public static operator <F, S> F! component1(android.util.Pair<F,S>);
+    method public static operator <F, S> S! component2(android.util.Pair<F,S>);
+    method public static <F, S> android.util.Pair<F,S> toAndroidPair(kotlin.Pair<? extends F,? extends S>);
+    method public static <F, S> kotlin.Pair<F,S> toKotlinPair(android.util.Pair<F,S>);
+  }
+
+  public final class RangeKt {
+    ctor public RangeKt();
+    method @RequiresApi(21) public static infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> and(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, T value);
+    method @RequiresApi(21) public static operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> rangeTo(T, T that);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> kotlin.ranges.ClosedRange<T> toClosedRange(android.util.Range<T>);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> android.util.Range<T> toRange(kotlin.ranges.ClosedRange<T>);
+  }
+
+  public final class SizeKt {
+    ctor public SizeKt();
+    method @RequiresApi(21) public static operator int component1(android.util.Size);
+    method @RequiresApi(21) public static operator float component1(android.util.SizeF);
+    method @RequiresApi(21) public static operator int component2(android.util.Size);
+    method @RequiresApi(21) public static operator float component2(android.util.SizeF);
+  }
+
+  public final class SparseArrayKt {
+    ctor public SparseArrayKt();
+    method public static operator <T> boolean contains(android.util.SparseArray<T>, int key);
+    method public static <T> boolean containsKey(android.util.SparseArray<T>, int key);
+    method public static <T> boolean containsValue(android.util.SparseArray<T>, T! value);
+    method public static <T> void forEach(android.util.SparseArray<T>, kotlin.jvm.functions.Function2<? super Integer,? super T,kotlin.Unit> action);
+    method public static <T> T! getOrDefault(android.util.SparseArray<T>, int key, T! defaultValue);
+    method public static <T> T! getOrElse(android.util.SparseArray<T>, int key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method public static <T> int getSize(android.util.SparseArray<T>);
+    method public static <T> boolean isEmpty(android.util.SparseArray<T>);
+    method public static <T> boolean isNotEmpty(android.util.SparseArray<T>);
+    method public static <T> kotlin.collections.IntIterator keyIterator(android.util.SparseArray<T>);
+    method public static operator <T> android.util.SparseArray<T> plus(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> void putAll(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> boolean remove(android.util.SparseArray<T>, int key, T! value);
+    method public static operator <T> void set(android.util.SparseArray<T>, int key, T! value);
+    method public static <T> java.util.Iterator<T> valueIterator(android.util.SparseArray<T>);
+  }
+
+  public final class SparseBooleanArrayKt {
+    ctor public SparseBooleanArrayKt();
+    method public static operator boolean contains(android.util.SparseBooleanArray, int key);
+    method public static boolean containsKey(android.util.SparseBooleanArray, int key);
+    method public static boolean containsValue(android.util.SparseBooleanArray, boolean value);
+    method public static void forEach(android.util.SparseBooleanArray, kotlin.jvm.functions.Function2<? super Integer,? super Boolean,kotlin.Unit> action);
+    method public static boolean getOrDefault(android.util.SparseBooleanArray, int key, boolean defaultValue);
+    method public static error.NonExistentClass getOrElse(android.util.SparseBooleanArray, int key, kotlin.jvm.functions.Function0<Boolean> defaultValue);
+    method public static int getSize(android.util.SparseBooleanArray);
+    method public static boolean isEmpty(android.util.SparseBooleanArray);
+    method public static boolean isNotEmpty(android.util.SparseBooleanArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseBooleanArray);
+    method public static operator android.util.SparseBooleanArray plus(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static void putAll(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static boolean remove(android.util.SparseBooleanArray, int key, boolean value);
+    method public static void removeAt(android.util.SparseBooleanArray, int index);
+    method public static operator void set(android.util.SparseBooleanArray, int key, boolean value);
+    method public static kotlin.collections.BooleanIterator valueIterator(android.util.SparseBooleanArray);
+  }
+
+  public final class SparseIntArrayKt {
+    ctor public SparseIntArrayKt();
+    method public static operator boolean contains(android.util.SparseIntArray, int key);
+    method public static boolean containsKey(android.util.SparseIntArray, int key);
+    method public static boolean containsValue(android.util.SparseIntArray, int value);
+    method public static void forEach(android.util.SparseIntArray, kotlin.jvm.functions.Function2<? super Integer,? super Integer,kotlin.Unit> action);
+    method public static int getOrDefault(android.util.SparseIntArray, int key, int defaultValue);
+    method public static error.NonExistentClass getOrElse(android.util.SparseIntArray, int key, kotlin.jvm.functions.Function0<Integer> defaultValue);
+    method public static int getSize(android.util.SparseIntArray);
+    method public static boolean isEmpty(android.util.SparseIntArray);
+    method public static boolean isNotEmpty(android.util.SparseIntArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseIntArray);
+    method public static operator android.util.SparseIntArray plus(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static void putAll(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static boolean remove(android.util.SparseIntArray, int key, int value);
+    method public static operator void set(android.util.SparseIntArray, int key, int value);
+    method public static kotlin.collections.IntIterator valueIterator(android.util.SparseIntArray);
+  }
+
+  public final class SparseLongArrayKt {
+    ctor public SparseLongArrayKt();
+    method @RequiresApi(18) public static operator boolean contains(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static boolean containsKey(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static boolean containsValue(android.util.SparseLongArray, long value);
+    method @RequiresApi(18) public static void forEach(android.util.SparseLongArray, kotlin.jvm.functions.Function2<? super Integer,? super Long,kotlin.Unit> action);
+    method @RequiresApi(18) public static long getOrDefault(android.util.SparseLongArray, int key, long defaultValue);
+    method @RequiresApi(18) public static error.NonExistentClass getOrElse(android.util.SparseLongArray, int key, kotlin.jvm.functions.Function0<Long> defaultValue);
+    method @RequiresApi(18) public static int getSize(android.util.SparseLongArray);
+    method @RequiresApi(18) public static boolean isEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static boolean isNotEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static kotlin.collections.IntIterator keyIterator(android.util.SparseLongArray);
+    method @RequiresApi(18) public static operator android.util.SparseLongArray plus(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static void putAll(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static boolean remove(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static operator void set(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static kotlin.collections.LongIterator valueIterator(android.util.SparseLongArray);
+  }
+
+}
+
+package androidx.view {
+
+  public final class MenuKt {
+    ctor public MenuKt();
+    method public static operator boolean contains(android.view.Menu, android.view.MenuItem item);
+    method public static void forEach(android.view.Menu, kotlin.jvm.functions.Function1<? super android.view.MenuItem,kotlin.Unit> action);
+    method public static void forEachIndexed(android.view.Menu, kotlin.jvm.functions.Function2<? super Integer,? super android.view.MenuItem,kotlin.Unit> action);
+    method public static operator android.view.MenuItem get(android.view.Menu, int index);
+    method public static int getSize(android.view.Menu);
+    method public static boolean isEmpty(android.view.Menu);
+    method public static boolean isNotEmpty(android.view.Menu);
+    method public static operator java.util.Iterator<android.view.MenuItem> iterator(android.view.Menu);
+  }
+
+  public final class ViewGroupKt {
+    ctor public ViewGroupKt();
+    method public static operator boolean contains(android.view.ViewGroup, android.view.View view);
+    method public static void forEach(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static void forEachIndexed(android.view.ViewGroup, kotlin.jvm.functions.Function2<? super Integer,? super android.view.View,kotlin.Unit> action);
+    method public static operator android.view.View get(android.view.ViewGroup, int index);
+    method public static kotlin.sequences.Sequence<android.view.View> getChildren(android.view.ViewGroup);
+    method public static int getSize(android.view.ViewGroup);
+    method public static boolean isEmpty(android.view.ViewGroup);
+    method public static boolean isNotEmpty(android.view.ViewGroup);
+    method public static operator java.util.Iterator<android.view.View> iterator(android.view.ViewGroup);
+    method public static operator void minusAssign(android.view.ViewGroup, android.view.View view);
+    method public static operator void plusAssign(android.view.ViewGroup, android.view.View view);
+    method public static void setMargins(android.view.ViewGroup.MarginLayoutParams, @Px int size);
+    method public static void updateLayoutParams(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.ViewGroup.LayoutParams,kotlin.Unit> block);
+    method public static void updateMargins(android.view.ViewGroup.MarginLayoutParams, @Px int left = "leftMargin", @Px int top = "topMargin", @Px int right = "rightMargin", @Px int bottom = "bottomMargin");
+    method @RequiresApi(17) public static void updateMarginsRelative(android.view.ViewGroup.MarginLayoutParams, @Px int start = "marginStart", @Px int top = "topMargin", @Px int end = "marginEnd", @Px int bottom = "bottomMargin");
+  }
+
+  public final class ViewKt {
+    ctor public ViewKt();
+    method public static void doOnLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static void doOnNextLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static void doOnPreDraw(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static boolean isGone(android.view.View);
+    method public static boolean isInvisible(android.view.View);
+    method public static boolean isVisible(android.view.View);
+    method public static Runnable postDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method @RequiresApi(16) public static Runnable postOnAnimationDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static void setGone(android.view.View, boolean value);
+    method public static void setInvisible(android.view.View, boolean value);
+    method public static void setPadding(android.view.View, @Px int size);
+    method public static void setVisible(android.view.View, boolean value);
+    method public static android.graphics.Bitmap toBitmap(android.view.View, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888");
+    method public static void updatePadding(android.view.View, @Px int left = "paddingLeft", @Px int top = "paddingTop", @Px int right = "paddingRight", @Px int bottom = "paddingBottom");
+    method @RequiresApi(17) public static void updatePaddingRelative(android.view.View, @Px int start = "paddingStart", @Px int top = "paddingTop", @Px int end = "paddingEnd", @Px int bottom = "paddingBottom");
+  }
+
+}
+
diff --git a/core/ktx/api/current.txt b/core/ktx/api/current.txt
new file mode 100644
index 0000000..4c56ac9
--- /dev/null
+++ b/core/ktx/api/current.txt
@@ -0,0 +1,658 @@
+package androidx.core.animation {
+
+  public final class AnimatorKt {
+    ctor public AnimatorKt();
+    method public static android.animation.Animator.AnimatorListener addListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onEnd = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onStart = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onCancel = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onRepeat = "null");
+    method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener addPauseListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onResume = "null", kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>? onPause = "null");
+    method public static android.animation.Animator.AnimatorListener doOnCancel(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static android.animation.Animator.AnimatorListener doOnEnd(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener doOnPause(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static android.animation.Animator.AnimatorListener doOnRepeat(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener doOnResume(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static android.animation.Animator.AnimatorListener doOnStart(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.content {
+
+  public final class ContentValuesKt {
+    ctor public ContentValuesKt();
+    method public static error.NonExistentClass contentValuesOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class ContextKt {
+    ctor public ContextKt();
+    method public static void withStyledAttributes(android.content.Context, android.util.AttributeSet? set = "null", int[] attrs, @AttrRes int defStyleAttr = "0", @StyleRes int defStyleRes = "0", kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+    method public static void withStyledAttributes(android.content.Context, @StyleRes int resourceId, int[] attrs, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+  }
+
+  public final class SharedPreferencesKt {
+    ctor public SharedPreferencesKt();
+    method public static void edit(android.content.SharedPreferences, boolean commit = "false", kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.content.res {
+
+  public final class TypedArrayKt {
+    ctor public TypedArrayKt();
+    method public static boolean getBooleanOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @ColorInt public static int getColorOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.content.res.ColorStateList getColorStateListOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getDimensionOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelOffsetOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelSizeOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.graphics.drawable.Drawable getDrawableOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getFloatOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @RequiresApi(26) public static android.graphics.Typeface getFontOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntegerOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @AnyRes public static int getResourceIdOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static String getStringOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence[] getTextArrayOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence getTextOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static <R> R! use(android.content.res.TypedArray, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,? extends R> block);
+  }
+
+}
+
+package androidx.core.database {
+
+  public final class CursorKt {
+    ctor public CursorKt();
+    method public static byte[] getBlob(android.database.Cursor, String columnName);
+    method public static byte[]? getBlobOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getBlobOrNull(android.database.Cursor, String columnName);
+    method public static double getDouble(android.database.Cursor, String columnName);
+    method public static Double? getDoubleOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getDoubleOrNull(android.database.Cursor, String columnName);
+    method public static float getFloat(android.database.Cursor, String columnName);
+    method public static Float? getFloatOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getFloatOrNull(android.database.Cursor, String columnName);
+    method public static int getInt(android.database.Cursor, String columnName);
+    method public static Integer? getIntOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getIntOrNull(android.database.Cursor, String columnName);
+    method public static long getLong(android.database.Cursor, String columnName);
+    method public static Long? getLongOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getLongOrNull(android.database.Cursor, String columnName);
+    method public static short getShort(android.database.Cursor, String columnName);
+    method public static Short? getShortOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getShortOrNull(android.database.Cursor, String columnName);
+    method public static String getString(android.database.Cursor, String columnName);
+    method public static String? getStringOrNull(android.database.Cursor, int index);
+    method public static error.NonExistentClass getStringOrNull(android.database.Cursor, String columnName);
+  }
+
+}
+
+package androidx.core.database.sqlite {
+
+  public final class SQLiteDatabaseKt {
+    ctor public SQLiteDatabaseKt();
+    method public static <T> T! transaction(android.database.sqlite.SQLiteDatabase, boolean exclusive = "true", kotlin.jvm.functions.Function1<? super android.database.sqlite.SQLiteDatabase,? extends T> body);
+  }
+
+}
+
+package androidx.core.graphics {
+
+  public final class BitmapKt {
+    ctor public BitmapKt();
+    method public static android.graphics.Bitmap applyCanvas(android.graphics.Bitmap, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static android.graphics.Bitmap createBitmap(int width, int height, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888");
+    method @RequiresApi(26) public static android.graphics.Bitmap createBitmap(int width, int height, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888", boolean hasAlpha = "true", android.graphics.ColorSpace colorSpace = "ColorSpace.get(ColorSpace.Named.SRGB)");
+    method public static operator int get(android.graphics.Bitmap, int x, int y);
+    method public static android.graphics.Bitmap scale(android.graphics.Bitmap, int width, int height, boolean filter = "true");
+    method public static operator void set(android.graphics.Bitmap, int x, int y, @ColorInt int color);
+  }
+
+  public final class CanvasKt {
+    ctor public CanvasKt();
+    method public static void withMatrix(android.graphics.Canvas, android.graphics.Matrix matrix = "Matrix()", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static void withRotation(android.graphics.Canvas, float degrees = "0.0f", float pivotX = "0.0f", float pivotY = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static void withSave(android.graphics.Canvas, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static void withScale(android.graphics.Canvas, float x = "1.0f", float y = "1.0f", float pivotX = "0.0f", float pivotY = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static void withSkew(android.graphics.Canvas, float x = "0.0f", float y = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static void withTranslation(android.graphics.Canvas, float x = "0.0f", float y = "0.0f", kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class ColorKt {
+    ctor public ColorKt();
+    method @RequiresApi(26) public static operator float component1(android.graphics.Color);
+    method public static operator int component1(int);
+    method @RequiresApi(26) public static operator float component1(long);
+    method @RequiresApi(26) public static operator float component2(android.graphics.Color);
+    method public static operator int component2(int);
+    method @RequiresApi(26) public static operator float component2(long);
+    method @RequiresApi(26) public static operator float component3(android.graphics.Color);
+    method public static operator int component3(int);
+    method @RequiresApi(26) public static operator float component3(long);
+    method @RequiresApi(26) public static operator float component4(android.graphics.Color);
+    method public static operator int component4(int);
+    method @RequiresApi(26) public static operator float component4(long);
+    method public static int getAlpha(int);
+    method @RequiresApi(26) public static float getAlpha(long);
+    method public static int getBlue(int);
+    method @RequiresApi(26) public static float getBlue(long);
+    method @RequiresApi(26) public static android.graphics.ColorSpace getColorSpace(long);
+    method public static int getGreen(int);
+    method @RequiresApi(26) public static float getGreen(long);
+    method @RequiresApi(26) public static float getLuminance(int);
+    method @RequiresApi(26) public static float getLuminance(long);
+    method public static int getRed(int);
+    method @RequiresApi(26) public static float getRed(long);
+    method @RequiresApi(26) public static boolean isSrgb(long);
+    method @RequiresApi(26) public static boolean isWideGamut(long);
+    method @RequiresApi(26) public static operator android.graphics.Color plus(android.graphics.Color, android.graphics.Color c);
+    method @RequiresApi(26) public static android.graphics.Color toColor(int);
+    method @RequiresApi(26) public static android.graphics.Color toColor(long);
+    method @RequiresApi(26) @ColorInt public static int toColorInt(long);
+    method @ColorInt public static int toColorInt(String);
+    method @RequiresApi(26) @ColorLong public static long toColorLong(int);
+  }
+
+  public final class MatrixKt {
+    ctor public MatrixKt();
+    method public static error.NonExistentClass rotationMatrix(float degrees, float px = "0.0f", float py = "0.0f");
+    method public static error.NonExistentClass scaleMatrix(float sx = "1.0f", float sy = "1.0f");
+    method public static operator error.NonExistentClass times(android.graphics.Matrix, android.graphics.Matrix m);
+    method public static error.NonExistentClass translationMatrix(float tx = "0.0f", float ty = "0.0f");
+    method public static error.NonExistentClass values(android.graphics.Matrix);
+  }
+
+  public final class PathKt {
+    ctor public PathKt();
+    method @RequiresApi(19) public static infix android.graphics.Path and(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(26) public static Iterable<androidx.core.graphics.PathSegment> flatten(android.graphics.Path, float error = "0.5f");
+    method @RequiresApi(19) public static operator android.graphics.Path minus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static infix android.graphics.Path or(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static operator android.graphics.Path plus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static infix android.graphics.Path xor(android.graphics.Path, android.graphics.Path p);
+  }
+
+  public final class PathSegment {
+    ctor public PathSegment(android.graphics.PointF start, float startFraction, android.graphics.PointF end, float endFraction);
+    method public android.graphics.PointF component1();
+    method public float component2();
+    method public android.graphics.PointF component3();
+    method public float component4();
+    method public androidx.core.graphics.PathSegment copy(android.graphics.PointF start, float startFraction, android.graphics.PointF end, float endFraction);
+    method public android.graphics.PointF getEnd();
+    method public float getEndFraction();
+    method public android.graphics.PointF getStart();
+    method public float getStartFraction();
+  }
+
+  public final class PictureKt {
+    ctor public PictureKt();
+    method public static android.graphics.Picture record(android.graphics.Picture, int width, int height, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class PointKt {
+    ctor public PointKt();
+    method public static operator int component1(android.graphics.Point);
+    method public static operator float component1(android.graphics.PointF);
+    method public static operator int component2(android.graphics.Point);
+    method public static operator float component2(android.graphics.PointF);
+    method public static operator android.graphics.Point minus(android.graphics.Point, android.graphics.Point p);
+    method public static operator android.graphics.PointF minus(android.graphics.PointF, android.graphics.PointF p);
+    method public static operator android.graphics.Point minus(android.graphics.Point, int xy);
+    method public static operator android.graphics.PointF minus(android.graphics.PointF, float xy);
+    method public static operator android.graphics.Point plus(android.graphics.Point, android.graphics.Point p);
+    method public static operator android.graphics.PointF plus(android.graphics.PointF, android.graphics.PointF p);
+    method public static operator android.graphics.Point plus(android.graphics.Point, int xy);
+    method public static operator android.graphics.PointF plus(android.graphics.PointF, float xy);
+    method public static android.graphics.Point toPoint(android.graphics.PointF);
+    method public static android.graphics.PointF toPointF(android.graphics.Point);
+    method public static operator android.graphics.Point unaryMinus(android.graphics.Point);
+    method public static operator android.graphics.PointF unaryMinus(android.graphics.PointF);
+  }
+
+  public final class PorterDuffKt {
+    ctor public PorterDuffKt();
+    method public static android.graphics.PorterDuffColorFilter toColorFilter(android.graphics.PorterDuff.Mode, int color);
+    method public static android.graphics.PorterDuffXfermode toXfermode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final class RectKt {
+    ctor public RectKt();
+    method public static infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r);
+    method public static infix android.graphics.RectF and(android.graphics.RectF, android.graphics.RectF r);
+    method public static operator int component1(android.graphics.Rect);
+    method public static operator float component1(android.graphics.RectF);
+    method public static operator int component2(android.graphics.Rect);
+    method public static operator float component2(android.graphics.RectF);
+    method public static operator int component3(android.graphics.Rect);
+    method public static operator float component3(android.graphics.RectF);
+    method public static operator int component4(android.graphics.Rect);
+    method public static operator float component4(android.graphics.RectF);
+    method public static operator boolean contains(android.graphics.Rect, android.graphics.Point p);
+    method public static operator boolean contains(android.graphics.RectF, android.graphics.PointF p);
+    method public static operator android.graphics.Region minus(android.graphics.Rect, android.graphics.Rect r);
+    method public static operator android.graphics.Region minus(android.graphics.RectF, android.graphics.RectF r);
+    method public static operator android.graphics.Rect minus(android.graphics.Rect, int xy);
+    method public static operator android.graphics.RectF minus(android.graphics.RectF, float xy);
+    method public static operator android.graphics.Rect minus(android.graphics.Rect, android.graphics.Point xy);
+    method public static operator android.graphics.RectF minus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static infix android.graphics.Rect or(android.graphics.Rect, android.graphics.Rect r);
+    method public static infix android.graphics.RectF or(android.graphics.RectF, android.graphics.RectF r);
+    method public static operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Rect r);
+    method public static operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.RectF r);
+    method public static operator android.graphics.Rect plus(android.graphics.Rect, int xy);
+    method public static operator android.graphics.RectF plus(android.graphics.RectF, float xy);
+    method public static operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Point xy);
+    method public static operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static android.graphics.Rect toRect(android.graphics.RectF);
+    method public static android.graphics.RectF toRectF(android.graphics.Rect);
+    method public static android.graphics.Region toRegion(android.graphics.Rect);
+    method public static android.graphics.Region toRegion(android.graphics.RectF);
+    method public static error.NonExistentClass transform(android.graphics.RectF, android.graphics.Matrix m);
+    method public static infix android.graphics.Region xor(android.graphics.Rect, android.graphics.Rect r);
+    method public static infix android.graphics.Region xor(android.graphics.RectF, android.graphics.RectF r);
+  }
+
+  public final class RegionKt {
+    ctor public RegionKt();
+    method public static infix android.graphics.Region and(android.graphics.Region, android.graphics.Rect r);
+    method public static infix android.graphics.Region and(android.graphics.Region, android.graphics.Region r);
+    method public static operator boolean contains(android.graphics.Region, android.graphics.Point p);
+    method public static void forEach(android.graphics.Region, kotlin.jvm.functions.Function1<? super android.graphics.Rect,kotlin.Unit> action);
+    method public static operator java.util.Iterator<android.graphics.Rect> iterator(android.graphics.Region);
+    method public static operator android.graphics.Region minus(android.graphics.Region, android.graphics.Rect r);
+    method public static operator android.graphics.Region minus(android.graphics.Region, android.graphics.Region r);
+    method public static operator android.graphics.Region not(android.graphics.Region);
+    method public static infix android.graphics.Region or(android.graphics.Region, android.graphics.Rect r);
+    method public static infix android.graphics.Region or(android.graphics.Region, android.graphics.Region r);
+    method public static operator android.graphics.Region plus(android.graphics.Region, android.graphics.Rect r);
+    method public static operator android.graphics.Region plus(android.graphics.Region, android.graphics.Region r);
+    method public static operator android.graphics.Region unaryMinus(android.graphics.Region);
+    method public static infix android.graphics.Region xor(android.graphics.Region, android.graphics.Rect r);
+    method public static infix android.graphics.Region xor(android.graphics.Region, android.graphics.Region r);
+  }
+
+  public final class ShaderKt {
+    ctor public ShaderKt();
+    method public static void transform(android.graphics.Shader, kotlin.jvm.functions.Function1<? super android.graphics.Matrix,kotlin.Unit> block);
+  }
+
+}
+
+package androidx.core.graphics.drawable {
+
+  public final class BitmapDrawableKt {
+    ctor public BitmapDrawableKt();
+    method public static android.graphics.drawable.BitmapDrawable toDrawable(android.graphics.Bitmap, android.content.res.Resources resources);
+  }
+
+  public final class ColorDrawableKt {
+    ctor public ColorDrawableKt();
+    method public static android.graphics.drawable.ColorDrawable toDrawable(int);
+    method @RequiresApi(26) public static android.graphics.drawable.ColorDrawable toDrawable(android.graphics.Color);
+  }
+
+  public final class DrawableKt {
+    ctor public DrawableKt();
+    method public static android.graphics.Bitmap toBitmap(android.graphics.drawable.Drawable, @Px int width = "intrinsicWidth", @Px int height = "intrinsicHeight", android.graphics.Bitmap.Config? config = "null");
+    method public static void updateBounds(android.graphics.drawable.Drawable, @Px int left = "bounds.left", @Px int top = "bounds.top", @Px int right = "bounds.right", @Px int bottom = "bounds.bottom");
+  }
+
+  public final class IconKt {
+    ctor public IconKt();
+    method @RequiresApi(26) public static android.graphics.drawable.Icon toAdaptiveIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static android.graphics.drawable.Icon toIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static android.graphics.drawable.Icon toIcon(android.net.Uri);
+    method @RequiresApi(26) public static android.graphics.drawable.Icon toIcon(byte[]);
+  }
+
+}
+
+package androidx.core.net {
+
+  public final class UriKt {
+    ctor public UriKt();
+    method public static java.io.File toFile(android.net.Uri);
+    method public static android.net.Uri toUri(String);
+    method public static android.net.Uri toUri(java.io.File);
+  }
+
+}
+
+package androidx.core.os {
+
+  public final class BundleKt {
+    ctor public BundleKt();
+    method public static error.NonExistentClass bundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class HandlerKt {
+    ctor public HandlerKt();
+    method public static Runnable postAtTime(android.os.Handler, long uptimeMillis, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static Runnable postDelayed(android.os.Handler, long delayInMillis, Object? token = "null", kotlin.jvm.functions.Function0<kotlin.Unit> action);
+  }
+
+  public final class PersistableBundleKt {
+    ctor public PersistableBundleKt();
+    method @RequiresApi(21) public static error.NonExistentClass persistableBundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class TraceKt {
+    ctor public TraceKt();
+    method public static <T> T! trace(String sectionName, kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+}
+
+package androidx.core.preference {
+
+  public final class PreferenceGroupKt {
+    ctor public PreferenceGroupKt();
+    method public static operator boolean contains(android.preference.PreferenceGroup, android.preference.Preference preference);
+    method public static void forEach(android.preference.PreferenceGroup, kotlin.jvm.functions.Function1<? super android.preference.Preference,kotlin.Unit> action);
+    method public static void forEachIndexed(android.preference.PreferenceGroup, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.preference.Preference,kotlin.Unit> action);
+    method public static operator android.preference.Preference get(android.preference.PreferenceGroup, CharSequence key);
+    method public static operator android.preference.Preference get(android.preference.PreferenceGroup, int index);
+    method public static int getSize(android.preference.PreferenceGroup);
+    method public static boolean isEmpty(android.preference.PreferenceGroup);
+    method public static boolean isNotEmpty(android.preference.PreferenceGroup);
+    method public static operator java.util.Iterator<android.preference.Preference> iterator(android.preference.PreferenceGroup);
+    method public static operator void minusAssign(android.preference.PreferenceGroup, android.preference.Preference preference);
+    method public static operator void plusAssign(android.preference.PreferenceGroup, android.preference.Preference preference);
+  }
+
+}
+
+package androidx.core.text {
+
+  public final class CharSequenceKt {
+    ctor public CharSequenceKt();
+    method public static boolean isDigitsOnly(CharSequence);
+    method public static int trimmedLength(CharSequence);
+  }
+
+  public final class HtmlKt {
+    ctor public HtmlKt();
+    method public static android.text.Spanned parseAsHtml(String, int flags = "FROM_HTML_MODE_LEGACY", android.text.Html.ImageGetter? imageGetter = "null", android.text.Html.TagHandler? tagHandler = "null");
+    method public static String toHtml(android.text.Spanned, int option = "TO_HTML_PARAGRAPH_LINES_CONSECUTIVE");
+  }
+
+  public final class SpannableStringBuilderKt {
+    ctor public SpannableStringBuilderKt();
+    method public static android.text.SpannableStringBuilder backgroundColor(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder bold(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannedString buildSpannedString(kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder color(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object[] spans, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object span, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder italic(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder scale(android.text.SpannableStringBuilder, float proportion, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder strikeThrough(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static android.text.SpannableStringBuilder underline(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+  }
+
+  public final class SpannableStringKt {
+    ctor public SpannableStringKt();
+    method public static error.NonExistentClass clearSpans(android.text.Spannable);
+    method public static operator void minusAssign(android.text.Spannable, Object span);
+    method public static operator void plusAssign(android.text.Spannable, Object span);
+    method public static operator void set(android.text.Spannable, int start, int end, Object span);
+    method public static operator void set(android.text.Spannable, kotlin.ranges.IntRange range, Object span);
+    method public static android.text.Spannable toSpannable(CharSequence);
+  }
+
+  public final class SpannedStringKt {
+    ctor public SpannedStringKt();
+    method public static android.text.Spanned toSpanned(CharSequence);
+  }
+
+  public final class StringKt {
+    ctor public StringKt();
+    method public static String htmlEncode(String);
+  }
+
+}
+
+package androidx.core.transition {
+
+  public final class TransitionKt {
+    ctor public TransitionKt();
+    method @RequiresApi(19) public static void addListener(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onEnd = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onStart = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onCancel = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onResume = "null", kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>? onPause = "null");
+    method @RequiresApi(19) public static void doOnCancel(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static void doOnEnd(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static void doOnPause(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static void doOnResume(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static void doOnStart(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.util {
+
+  public final class ArrayMapKt {
+    ctor public ArrayMapKt();
+    method @RequiresApi(19) public static <K, V> android.util.ArrayMap<K,V> arrayMapOf();
+    method @RequiresApi(19) public static <K, V> android.util.ArrayMap<K,V> arrayMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
+  }
+
+  public final class ArraySetKt {
+    ctor public ArraySetKt();
+    method @RequiresApi(23) public static <T> android.util.ArraySet<T> arraySetOf();
+    method @RequiresApi(23) public static <T> android.util.ArraySet<T> arraySetOf(T... values);
+  }
+
+  public final class AtomicFileKt {
+    ctor public AtomicFileKt();
+    method @RequiresApi(17) public static byte[] readBytes(android.util.AtomicFile);
+    method @RequiresApi(17) public static String readText(android.util.AtomicFile, java.nio.charset.Charset charset = "Charsets.UTF_8");
+    method @RequiresApi(17) public static void tryWrite(android.util.AtomicFile, kotlin.jvm.functions.Function1<? super java.io.FileOutputStream,kotlin.Unit> block);
+    method @RequiresApi(17) public static void writeBytes(android.util.AtomicFile, byte[] array);
+    method @RequiresApi(17) public static void writeText(android.util.AtomicFile, String text, java.nio.charset.Charset charset = "Charsets.UTF_8");
+  }
+
+  public final class HalfKt {
+    ctor public HalfKt();
+    method @RequiresApi(26) public static android.util.Half toHalf(short);
+    method @RequiresApi(26) public static android.util.Half toHalf(float);
+    method @RequiresApi(26) public static android.util.Half toHalf(double);
+    method @RequiresApi(26) public static android.util.Half toHalf(String);
+  }
+
+  public final class LocaleKt {
+    ctor public LocaleKt();
+    method @RequiresApi(17) public static int getLayoutDirection(java.util.Locale);
+  }
+
+  public final class LongSparseArrayKt {
+    ctor public LongSparseArrayKt();
+    method @RequiresApi(16) public static operator <T> boolean contains(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static <T> boolean containsKey(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static <T> boolean containsValue(android.util.LongSparseArray<T>, T! value);
+    method @RequiresApi(16) public static <T> void forEach(android.util.LongSparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Long,? super T,kotlin.Unit> action);
+    method @RequiresApi(16) public static <T> T! getOrDefault(android.util.LongSparseArray<T>, long key, T! defaultValue);
+    method @RequiresApi(16) public static <T> T! getOrElse(android.util.LongSparseArray<T>, long key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method @RequiresApi(16) public static <T> int getSize(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static <T> boolean isEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static <T> boolean isNotEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static <T> kotlin.collections.LongIterator keyIterator(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static operator <T> android.util.LongSparseArray<T> plus(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> void putAll(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> boolean remove(android.util.LongSparseArray<T>, long key, T! value);
+    method @RequiresApi(16) public static operator <T> void set(android.util.LongSparseArray<T>, long key, T! value);
+    method @RequiresApi(16) public static <T> java.util.Iterator<T> valueIterator(android.util.LongSparseArray<T>);
+  }
+
+  public final class LruCacheKt {
+    ctor public LruCacheKt();
+    method public static <K, V> android.util.LruCache<K,V> lruCache(int maxSize, kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Integer> sizeOf = "{ _, _ -> 1 }", kotlin.jvm.functions.Function1<? super K,? extends V> create = "{ null as V? }", kotlin.jvm.functions.Function4<? super java.lang.Boolean,? super K,? super V,? super V,kotlin.Unit> onEntryRemoved = "{ _, _, _, _ -> }");
+  }
+
+  public final class PairKt {
+    ctor public PairKt();
+    method public static operator <F, S> F! component1(android.util.Pair<F,S>);
+    method public static operator <F, S> S! component2(android.util.Pair<F,S>);
+    method public static <F, S> android.util.Pair<F,S> toAndroidPair(kotlin.Pair<? extends F,? extends S>);
+    method public static <F, S> kotlin.Pair<F,S> toKotlinPair(android.util.Pair<F,S>);
+  }
+
+  public final class RangeKt {
+    ctor public RangeKt();
+    method @RequiresApi(21) public static infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> and(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, T value);
+    method @RequiresApi(21) public static operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> rangeTo(T, T that);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> kotlin.ranges.ClosedRange<T> toClosedRange(android.util.Range<T>);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> android.util.Range<T> toRange(kotlin.ranges.ClosedRange<T>);
+  }
+
+  public final class SizeKt {
+    ctor public SizeKt();
+    method @RequiresApi(21) public static operator int component1(android.util.Size);
+    method @RequiresApi(21) public static operator float component1(android.util.SizeF);
+    method @RequiresApi(21) public static operator int component2(android.util.Size);
+    method @RequiresApi(21) public static operator float component2(android.util.SizeF);
+  }
+
+  public final class SparseArrayKt {
+    ctor public SparseArrayKt();
+    method public static operator <T> boolean contains(android.util.SparseArray<T>, int key);
+    method public static <T> boolean containsKey(android.util.SparseArray<T>, int key);
+    method public static <T> boolean containsValue(android.util.SparseArray<T>, T! value);
+    method public static <T> void forEach(android.util.SparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> action);
+    method public static <T> T! getOrDefault(android.util.SparseArray<T>, int key, T! defaultValue);
+    method public static <T> T! getOrElse(android.util.SparseArray<T>, int key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method public static <T> int getSize(android.util.SparseArray<T>);
+    method public static <T> boolean isEmpty(android.util.SparseArray<T>);
+    method public static <T> boolean isNotEmpty(android.util.SparseArray<T>);
+    method public static <T> kotlin.collections.IntIterator keyIterator(android.util.SparseArray<T>);
+    method public static operator <T> android.util.SparseArray<T> plus(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> void putAll(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> boolean remove(android.util.SparseArray<T>, int key, T! value);
+    method public static operator <T> void set(android.util.SparseArray<T>, int key, T! value);
+    method public static <T> java.util.Iterator<T> valueIterator(android.util.SparseArray<T>);
+  }
+
+  public final class SparseBooleanArrayKt {
+    ctor public SparseBooleanArrayKt();
+    method public static operator boolean contains(android.util.SparseBooleanArray, int key);
+    method public static boolean containsKey(android.util.SparseBooleanArray, int key);
+    method public static boolean containsValue(android.util.SparseBooleanArray, boolean value);
+    method public static void forEach(android.util.SparseBooleanArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Boolean,kotlin.Unit> action);
+    method public static boolean getOrDefault(android.util.SparseBooleanArray, int key, boolean defaultValue);
+    method public static error.NonExistentClass getOrElse(android.util.SparseBooleanArray, int key, kotlin.jvm.functions.Function0<java.lang.Boolean> defaultValue);
+    method public static int getSize(android.util.SparseBooleanArray);
+    method public static boolean isEmpty(android.util.SparseBooleanArray);
+    method public static boolean isNotEmpty(android.util.SparseBooleanArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseBooleanArray);
+    method public static operator android.util.SparseBooleanArray plus(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static void putAll(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static boolean remove(android.util.SparseBooleanArray, int key, boolean value);
+    method public static operator void set(android.util.SparseBooleanArray, int key, boolean value);
+    method public static kotlin.collections.BooleanIterator valueIterator(android.util.SparseBooleanArray);
+  }
+
+  public final class SparseIntArrayKt {
+    ctor public SparseIntArrayKt();
+    method public static operator boolean contains(android.util.SparseIntArray, int key);
+    method public static boolean containsKey(android.util.SparseIntArray, int key);
+    method public static boolean containsValue(android.util.SparseIntArray, int value);
+    method public static void forEach(android.util.SparseIntArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+    method public static int getOrDefault(android.util.SparseIntArray, int key, int defaultValue);
+    method public static error.NonExistentClass getOrElse(android.util.SparseIntArray, int key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
+    method public static int getSize(android.util.SparseIntArray);
+    method public static boolean isEmpty(android.util.SparseIntArray);
+    method public static boolean isNotEmpty(android.util.SparseIntArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseIntArray);
+    method public static operator android.util.SparseIntArray plus(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static void putAll(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static boolean remove(android.util.SparseIntArray, int key, int value);
+    method public static operator void set(android.util.SparseIntArray, int key, int value);
+    method public static kotlin.collections.IntIterator valueIterator(android.util.SparseIntArray);
+  }
+
+  public final class SparseLongArrayKt {
+    ctor public SparseLongArrayKt();
+    method @RequiresApi(18) public static operator boolean contains(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static boolean containsKey(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static boolean containsValue(android.util.SparseLongArray, long value);
+    method @RequiresApi(18) public static void forEach(android.util.SparseLongArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Long,kotlin.Unit> action);
+    method @RequiresApi(18) public static long getOrDefault(android.util.SparseLongArray, int key, long defaultValue);
+    method @RequiresApi(18) public static error.NonExistentClass getOrElse(android.util.SparseLongArray, int key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
+    method @RequiresApi(18) public static int getSize(android.util.SparseLongArray);
+    method @RequiresApi(18) public static boolean isEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static boolean isNotEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static kotlin.collections.IntIterator keyIterator(android.util.SparseLongArray);
+    method @RequiresApi(18) public static operator android.util.SparseLongArray plus(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static void putAll(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static boolean remove(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static operator void set(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static kotlin.collections.LongIterator valueIterator(android.util.SparseLongArray);
+  }
+
+}
+
+package androidx.core.view {
+
+  public final class MenuKt {
+    ctor public MenuKt();
+    method public static operator boolean contains(android.view.Menu, android.view.MenuItem item);
+    method public static void forEach(android.view.Menu, kotlin.jvm.functions.Function1<? super android.view.MenuItem,kotlin.Unit> action);
+    method public static void forEachIndexed(android.view.Menu, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.MenuItem,kotlin.Unit> action);
+    method public static operator android.view.MenuItem get(android.view.Menu, int index);
+    method public static int getSize(android.view.Menu);
+    method public static boolean isEmpty(android.view.Menu);
+    method public static boolean isNotEmpty(android.view.Menu);
+    method public static operator java.util.Iterator<android.view.MenuItem> iterator(android.view.Menu);
+  }
+
+  public final class ViewGroupKt {
+    ctor public ViewGroupKt();
+    method public static operator boolean contains(android.view.ViewGroup, android.view.View view);
+    method public static void forEach(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static void forEachIndexed(android.view.ViewGroup, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.View,kotlin.Unit> action);
+    method public static operator android.view.View get(android.view.ViewGroup, int index);
+    method public static kotlin.sequences.Sequence<android.view.View> getChildren(android.view.ViewGroup);
+    method public static int getSize(android.view.ViewGroup);
+    method public static boolean isEmpty(android.view.ViewGroup);
+    method public static boolean isNotEmpty(android.view.ViewGroup);
+    method public static operator java.util.Iterator<android.view.View> iterator(android.view.ViewGroup);
+    method public static operator void minusAssign(android.view.ViewGroup, android.view.View view);
+    method public static operator void plusAssign(android.view.ViewGroup, android.view.View view);
+    method public static void setMargins(android.view.ViewGroup.MarginLayoutParams, @Px int size);
+    method public static void updateMargins(android.view.ViewGroup.MarginLayoutParams, @Px int left = "leftMargin", @Px int top = "topMargin", @Px int right = "rightMargin", @Px int bottom = "bottomMargin");
+    method @RequiresApi(17) public static void updateMarginsRelative(android.view.ViewGroup.MarginLayoutParams, @Px int start = "marginStart", @Px int top = "topMargin", @Px int end = "marginEnd", @Px int bottom = "bottomMargin");
+  }
+
+  public final class ViewKt {
+    ctor public ViewKt();
+    method @RequiresApi(16) public static void announceForAccessibility(android.view.View, @StringRes int resource);
+    method public static void doOnLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static void doOnNextLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static void doOnPreDraw(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static boolean isGone(android.view.View);
+    method public static boolean isInvisible(android.view.View);
+    method public static boolean isVisible(android.view.View);
+    method public static Runnable postDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method @RequiresApi(16) public static Runnable postOnAnimationDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static void setGone(android.view.View, boolean value);
+    method public static void setInvisible(android.view.View, boolean value);
+    method public static void setPadding(android.view.View, @Px int size);
+    method public static void setVisible(android.view.View, boolean value);
+    method public static android.graphics.Bitmap toBitmap(android.view.View, android.graphics.Bitmap.Config config = "Bitmap.Config.ARGB_8888");
+    method public static void updateLayoutParams(android.view.View, kotlin.jvm.functions.Function1<? super android.view.ViewGroup.LayoutParams,kotlin.Unit> block);
+    method public static void updatePadding(android.view.View, @Px int left = "paddingLeft", @Px int top = "paddingTop", @Px int right = "paddingRight", @Px int bottom = "paddingBottom");
+    method @RequiresApi(17) public static void updatePaddingRelative(android.view.View, @Px int start = "paddingStart", @Px int top = "paddingTop", @Px int end = "paddingEnd", @Px int bottom = "paddingBottom");
+  }
+
+}
+
+package androidx.core.widget {
+
+  public final class ToastKt {
+    ctor public ToastKt();
+    method public static android.widget.Toast toast(android.content.Context, CharSequence text, int duration = "Toast.LENGTH_SHORT");
+    method public static android.widget.Toast toast(android.content.Context, @StringRes int resId, int duration = "Toast.LENGTH_SHORT");
+  }
+
+}
+
diff --git a/core/ktx/build.gradle b/core/ktx/build.gradle
new file mode 100644
index 0000000..bce0757
--- /dev/null
+++ b/core/ktx/build.gradle
@@ -0,0 +1,37 @@
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+    id("org.jetbrains.kotlin.android")
+}
+
+android {
+    buildTypes {
+        debug {
+            testCoverageEnabled = false // Breaks Kotlin compiler.
+        }
+    }
+}
+
+dependencies {
+    api(KOTLIN_STDLIB)
+    api(project(":annotation"))
+    api(project(":core"))
+
+    androidTestImplementation(JUNIT)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(TEST_RULES)
+    androidTestImplementation(TRUTH)
+    androidTestImplementation(project(":internal-testutils-ktx"))
+}
+
+supportLibrary {
+    name = "Core Kotlin Extensions"
+    publish = true
+    mavenVersion = LibraryVersions.SUPPORT_LIBRARY
+    mavenGroup = LibraryGroups.CORE
+    inceptionYear = "2018"
+    description = "Kotlin extensions for 'core' artifact"
+}
diff --git a/core/ktx/src/androidTest/AndroidManifest.xml b/core/ktx/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..259b616
--- /dev/null
+++ b/core/ktx/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="androidx.core.ktx.test">
+    <application>
+        <activity android:name="androidx.core.TestActivity"/>
+        <activity android:name="androidx.core.TestPreferenceActivity"/>
+    </application>
+</manifest>
diff --git a/core/ktx/src/androidTest/assets/red.png b/core/ktx/src/androidTest/assets/red.png
new file mode 100644
index 0000000..6292f4b
--- /dev/null
+++ b/core/ktx/src/androidTest/assets/red.png
Binary files differ
diff --git a/core/ktx/src/androidTest/font_licenses.txt b/core/ktx/src/androidTest/font_licenses.txt
new file mode 100644
index 0000000..0b83a9a
--- /dev/null
+++ b/core/ktx/src/androidTest/font_licenses.txt
@@ -0,0 +1,93 @@
+Copyright 2006 The Inconsolata Project Authors

+

+This Font Software is licensed under the SIL Open Font License, Version 1.1.

+This license is copied below, and is also available with a FAQ at:

+http://scripts.sil.org/OFL

+

+

+-----------------------------------------------------------

+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007

+-----------------------------------------------------------

+

+PREAMBLE

+The goals of the Open Font License (OFL) are to stimulate worldwide

+development of collaborative font projects, to support the font creation

+efforts of academic and linguistic communities, and to provide a free and

+open framework in which fonts may be shared and improved in partnership

+with others.

+

+The OFL allows the licensed fonts to be used, studied, modified and

+redistributed freely as long as they are not sold by themselves. The

+fonts, including any derivative works, can be bundled, embedded, 

+redistributed and/or sold with any software provided that any reserved

+names are not used by derivative works. The fonts and derivatives,

+however, cannot be released under any other type of license. The

+requirement for fonts to remain under this license does not apply

+to any document created using the fonts or their derivatives.

+

+DEFINITIONS

+"Font Software" refers to the set of files released by the Copyright

+Holder(s) under this license and clearly marked as such. This may

+include source files, build scripts and documentation.

+

+"Reserved Font Name" refers to any names specified as such after the

+copyright statement(s).

+

+"Original Version" refers to the collection of Font Software components as

+distributed by the Copyright Holder(s).

+

+"Modified Version" refers to any derivative made by adding to, deleting,

+or substituting -- in part or in whole -- any of the components of the

+Original Version, by changing formats or by porting the Font Software to a

+new environment.

+

+"Author" refers to any designer, engineer, programmer, technical

+writer or other person who contributed to the Font Software.

+

+PERMISSION & CONDITIONS

+Permission is hereby granted, free of charge, to any person obtaining

+a copy of the Font Software, to use, study, copy, merge, embed, modify,

+redistribute, and sell modified and unmodified copies of the Font

+Software, subject to the following conditions:

+

+1) Neither the Font Software nor any of its individual components,

+in Original or Modified Versions, may be sold by itself.

+

+2) Original or Modified Versions of the Font Software may be bundled,

+redistributed and/or sold with any software, provided that each copy

+contains the above copyright notice and this license. These can be

+included either as stand-alone text files, human-readable headers or

+in the appropriate machine-readable metadata fields within text or

+binary files as long as those fields can be easily viewed by the user.

+

+3) No Modified Version of the Font Software may use the Reserved Font

+Name(s) unless explicit written permission is granted by the corresponding

+Copyright Holder. This restriction only applies to the primary font name as

+presented to the users.

+

+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font

+Software shall not be used to promote, endorse or advertise any

+Modified Version, except to acknowledge the contribution(s) of the

+Copyright Holder(s) and the Author(s) or with their explicit written

+permission.

+

+5) The Font Software, modified or unmodified, in part or in whole,

+must be distributed entirely under this license, and must not be

+distributed under any other license. The requirement for fonts to

+remain under this license does not apply to any document created

+using the Font Software.

+

+TERMINATION

+This license becomes null and void if any of the above conditions are

+not met.

+

+DISCLAIMER

+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,

+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF

+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT

+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE

+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,

+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL

+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING

+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM

+OTHER DEALINGS IN THE FONT SOFTWARE.

diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItemVisitor.kt b/core/ktx/src/androidTest/java/androidx/core/TestActivity.kt
similarity index 63%
copy from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItemVisitor.kt
copy to core/ktx/src/androidTest/java/androidx/core/TestActivity.kt
index 7c99fd9..b0132ab 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItemVisitor.kt
+++ b/core/ktx/src/androidTest/java/androidx/core/TestActivity.kt
@@ -11,18 +11,18 @@
  * 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.
  */
 
-package android.support.tools.jetifier.core.archive
+package androidx.core
 
-/**
- * Visitor for [ArchiveItem]
- */
-interface ArchiveItemVisitor {
+import android.app.Activity
+import android.os.Bundle
+import androidx.core.ktx.test.R
 
-    fun visit(archive: Archive)
-
-    fun visit(archiveFile: ArchiveFile)
-
+class TestActivity : Activity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.test_activity)
+    }
 }
diff --git a/core/ktx/src/androidTest/java/androidx/core/TestPreferenceActivity.kt b/core/ktx/src/androidTest/java/androidx/core/TestPreferenceActivity.kt
new file mode 100644
index 0000000..43e3dee
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/TestPreferenceActivity.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 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.core
+
+import android.app.Activity
+import android.os.Bundle
+import android.preference.PreferenceFragment
+import androidx.core.ktx.test.R
+
+class TestPreferenceActivity : Activity() {
+
+    companion object {
+        const val TAG = "TestPreferenceActivity"
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        fragmentManager.beginTransaction()
+            .add(android.R.id.content, TestPreferenceFragment(), TAG)
+            .commitNow()
+    }
+
+    class TestPreferenceFragment : PreferenceFragment() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            super.onCreate(savedInstanceState)
+            addPreferencesFromResource(R.xml.preferences)
+        }
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/animation/AnimatorTest.kt b/core/ktx/src/androidTest/java/androidx/core/animation/AnimatorTest.kt
new file mode 100644
index 0000000..de298a5
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/animation/AnimatorTest.kt
@@ -0,0 +1,115 @@
+/*
+ * 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 androidx.core.animation
+
+import android.animation.Animator
+import android.animation.ObjectAnimator
+import android.support.test.InstrumentationRegistry
+import android.support.test.annotation.UiThreadTest
+import android.support.test.filters.SdkSuppress
+import android.support.test.runner.AndroidJUnit4
+import android.view.View
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class AnimatorTest {
+    private val context = InstrumentationRegistry.getContext()
+    private val view = View(context)
+
+    private lateinit var animator: Animator
+
+    @Before fun before() {
+        animator = ObjectAnimator.ofFloat(view, View.ALPHA, 0f, 1f)
+    }
+
+    @Test fun testDoOnStart() {
+        var called = false
+        animator.doOnStart {
+            called = true
+        }
+
+        animator.listeners.forEach { it.onAnimationStart(animator) }
+        assertTrue(called)
+    }
+
+    @Test fun testDoOnEnd() {
+        var called = false
+        animator.doOnEnd {
+            called = true
+        }
+
+        animator.listeners.forEach { it.onAnimationEnd(animator) }
+        assertTrue(called)
+    }
+
+    @Test fun testDoOnCancel() {
+        var cancelCalled = false
+        animator.doOnCancel {
+            cancelCalled = true
+        }
+
+        animator.listeners.forEach { it.onAnimationCancel(animator) }
+        assertTrue(cancelCalled)
+    }
+
+    @Test fun testDoOnRepeat() {
+        var called = false
+        animator.doOnRepeat {
+            called = true
+        }
+
+        animator.listeners.forEach { it.onAnimationRepeat(animator) }
+        assertTrue(called)
+    }
+
+    @UiThreadTest
+    @SdkSuppress(minSdkVersion = 19)
+    @Test fun testDoOnPause() {
+        var called = false
+        animator.doOnPause {
+            called = true
+        }
+
+        // Start and pause and assert doOnPause was called
+        animator.start()
+        animator.pause()
+        assertTrue(called)
+
+        animator.cancel()
+    }
+
+    @UiThreadTest
+    @SdkSuppress(minSdkVersion = 19)
+    @Test fun testDoOnResume() {
+        var called = false
+        animator.doOnResume {
+            called = true
+        }
+
+        animator.start()
+        animator.pause()
+
+        // Now resume and assert doOnResume was called
+        animator.resume()
+        assertTrue(called)
+
+        animator.cancel()
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/content/ContentValuesTest.kt b/core/ktx/src/androidTest/java/androidx/core/content/ContentValuesTest.kt
new file mode 100644
index 0000000..746a634
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/content/ContentValuesTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 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.core.content
+
+import androidx.testutils.assertThrows
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+import java.util.concurrent.atomic.AtomicInteger
+
+class ContentValuesTest {
+    @Test fun valuesOfValid() {
+        val values = contentValuesOf(
+            "null" to null,
+            "string" to "string",
+            "byte" to 1.toByte(),
+            "short" to 1.toShort(),
+            "int" to 1,
+            "long" to 1L,
+            "float" to 1f,
+            "double" to 1.0,
+            "boolean" to true,
+            "byteArray" to byteArrayOf()
+        )
+        assertEquals(10, values.size())
+        assertNull(values.get("null"))
+        assertEquals("string", values.get("string"))
+        assertEquals(1.toByte(), values.get("byte"))
+        assertEquals(1.toShort(), values.get("short"))
+        assertEquals(1, values.get("int"))
+        assertEquals(1L, values.get("long"))
+        assertEquals(1f, values.get("float"))
+        assertEquals(1.0, values.get("double"))
+        assertEquals(true, values.get("boolean"))
+        assertArrayEquals(byteArrayOf(), values.get("byteArray") as ByteArray)
+    }
+
+    @Test fun valuesOfInvalid() {
+        assertThrows<IllegalArgumentException> {
+            contentValuesOf("nope" to AtomicInteger(1))
+        }.hasMessageThat().isEqualTo(
+            "Illegal value type java.util.concurrent.atomic.AtomicInteger for key \"nope\"")
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/content/ContextTest.kt b/core/ktx/src/androidTest/java/androidx/core/content/ContextTest.kt
new file mode 100644
index 0000000..09d2355
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/content/ContextTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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 androidx.core.content
+
+import android.content.ContextWrapper
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import androidx.core.ktx.test.R
+import androidx.core.getAttributeSet
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class ContextTest {
+    private val context = InstrumentationRegistry.getContext()
+
+    @SdkSuppress(minSdkVersion = 23)
+    @Test fun systemService() {
+        var lookup: Class<*>? = null
+        val context = object : ContextWrapper(context) {
+            override fun getSystemServiceName(serviceClass: Class<*>): String? {
+                lookup = serviceClass
+                return if (serviceClass == Unit::class.java) "unit" else null
+            }
+
+            override fun getSystemService(name: String): Any? {
+                return if (name == "unit") Unit else null
+            }
+        }
+        val actual = context.systemService<Unit>()
+        assertEquals(Unit::class.java, lookup)
+        assertSame(Unit, actual)
+    }
+
+    @Test fun withStyledAttributes() {
+        context.withStyledAttributes(attrs = intArrayOf(android.R.attr.textColorPrimary)) {
+            val resourceId = getResourceId(0, -1)
+            assertTrue(resourceId != 1)
+        }
+
+        context.withStyledAttributes(
+            android.R.style.Theme_Light,
+            intArrayOf(android.R.attr.textColorPrimary)
+        ) {
+            val resourceId = getResourceId(0, -1)
+            assertTrue(resourceId != 1)
+        }
+
+        val attrs = context.getAttributeSet(R.layout.test_attrs)
+        context.withStyledAttributes(attrs, R.styleable.SampleAttrs) {
+            assertTrue(getInt(R.styleable.SampleAttrs_sample, -1) != -1)
+        }
+
+        context.withStyledAttributes(attrs, R.styleable.SampleAttrs, 0, 0) {
+            assertTrue(getInt(R.styleable.SampleAttrs_sample, -1) != -1)
+        }
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/content/SharedPreferencesTest.kt b/core/ktx/src/androidTest/java/androidx/core/content/SharedPreferencesTest.kt
new file mode 100644
index 0000000..bf1e67d
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/content/SharedPreferencesTest.kt
@@ -0,0 +1,48 @@
+/*
+ * 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 androidx.core.content
+
+import android.support.test.InstrumentationRegistry
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class SharedPreferencesTest {
+    private val context = InstrumentationRegistry.getContext()
+
+    @Test fun editApply() {
+        val preferences = context.getSharedPreferences("prefs", 0)
+
+        preferences.edit {
+            putString("test_key1", "test_value")
+            putInt("test_key2", 100)
+        }
+
+        assertEquals("test_value", preferences.getString("test_key1", null))
+        assertEquals(100, preferences.getInt("test_key2", 0))
+    }
+
+    @Test fun editCommit() {
+        val preferences = context.getSharedPreferences("prefs", 0)
+        preferences.edit(commit = true) {
+            putString("test_key1", "test_value")
+            putInt("test_key2", 100)
+        }
+
+        assertEquals("test_value", preferences.getString("test_key1", null))
+        assertEquals(100, preferences.getInt("test_key2", 0))
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/content/res/TypedArrayTest.kt b/core/ktx/src/androidTest/java/androidx/core/content/res/TypedArrayTest.kt
new file mode 100644
index 0000000..fe576df
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/content/res/TypedArrayTest.kt
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2018 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.core.content.res
+
+import android.graphics.Color
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import androidx.core.ktx.test.R
+import androidx.testutils.assertThrows
+import androidx.core.getAttributeSet
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class TypedArrayTest {
+    private val context = InstrumentationRegistry.getContext()
+
+    @Test fun boolean() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertTrue(array.getBooleanOrThrow(R.styleable.TypedArrayTypes_boolean_present))
+
+        assertThrows<IllegalArgumentException> {
+            array.getBooleanOrThrow(R.styleable.TypedArrayTypes_boolean_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun color() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertEquals(Color.WHITE, array.getColorOrThrow(R.styleable.TypedArrayTypes_color_present))
+
+        assertThrows<IllegalArgumentException> {
+            array.getColorOrThrow(R.styleable.TypedArrayTypes_color_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun colorStateList() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+        val stateList = array.getColorStateListOrThrow(R.styleable.TypedArrayTypes_color_present)
+
+        assertEquals(Color.WHITE, stateList.defaultColor)
+
+        assertThrows<IllegalArgumentException> {
+            array.getColorStateListOrThrow(R.styleable.TypedArrayTypes_color_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun dimension() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertEquals(1f, array.getDimensionOrThrow(R.styleable.TypedArrayTypes_dimension_present))
+
+        assertThrows<IllegalArgumentException> {
+            array.getDimensionOrThrow(R.styleable.TypedArrayTypes_dimension_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun dimensionPixelSize() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertEquals(1,
+            array.getDimensionPixelSizeOrThrow(R.styleable.TypedArrayTypes_dimension_present))
+
+        assertThrows<IllegalArgumentException> {
+            array.getDimensionPixelSizeOrThrow(R.styleable.TypedArrayTypes_dimension_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun dimensionPixelOffset() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertEquals(1,
+            array.getDimensionPixelOffsetOrThrow(R.styleable.TypedArrayTypes_dimension_present))
+
+        assertThrows<IllegalArgumentException> {
+            array.getDimensionPixelOffsetOrThrow(R.styleable.TypedArrayTypes_dimension_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun drawable() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertNotNull(array.getDrawableOrThrow(R.styleable.TypedArrayTypes_drawable_present))
+
+        assertThrows<IllegalArgumentException> {
+            array.getDrawableOrThrow(R.styleable.TypedArrayTypes_drawable_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun float() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertEquals(0.1f, array.getFloatOrThrow(R.styleable.TypedArrayTypes_float_present))
+
+        assertThrows<IllegalArgumentException> {
+            array.getFloatOrThrow(R.styleable.TypedArrayTypes_float_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun font() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertNotNull(array.getFontOrThrow(R.styleable.TypedArrayTypes_font_present))
+
+        assertThrows<IllegalArgumentException> {
+            array.getFontOrThrow(R.styleable.TypedArrayTypes_font_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun int() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertEquals(1, array.getIntOrThrow(R.styleable.TypedArrayTypes_integer_present))
+
+        assertThrows<IllegalArgumentException> {
+            array.getIntOrThrow(R.styleable.TypedArrayTypes_integer_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun integer() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertEquals(1, array.getIntegerOrThrow(R.styleable.TypedArrayTypes_integer_present))
+
+        assertThrows<IllegalArgumentException> {
+            array.getIntegerOrThrow(R.styleable.TypedArrayTypes_integer_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test
+    fun resourceId() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertEquals(
+            R.font.inconsolata_regular,
+            array.getResourceIdOrThrow(R.styleable.TypedArrayTypes_resource_present)
+        )
+
+        assertThrows<IllegalArgumentException> {
+            array.getResourceIdOrThrow(R.styleable.TypedArrayTypes_resource_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun string() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertEquals("Hello", array.getStringOrThrow(R.styleable.TypedArrayTypes_string_present))
+
+        assertThrows<IllegalArgumentException> {
+            array.getStringOrThrow(R.styleable.TypedArrayTypes_string_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun text() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        assertEquals("Hello", array.getTextOrThrow(R.styleable.TypedArrayTypes_string_present))
+
+        assertThrows<IllegalArgumentException> {
+            array.getTextOrThrow(R.styleable.TypedArrayTypes_string_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun textArray() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        val text = array.getTextArrayOrThrow(R.styleable.TypedArrayTypes_text_array_present)
+        assertEquals("Hello", text[0].toString())
+        assertEquals("World", text[1].toString())
+
+        assertThrows<IllegalArgumentException> {
+            array.getTextOrThrow(R.styleable.TypedArrayTypes_text_array_absent)
+        }.hasMessageThat().isEqualTo("Attribute not defined in set.")
+    }
+
+    @Test fun useRecyclesArray() {
+        val attrs = context.getAttributeSet(R.layout.typed_array)
+        val array = context.obtainStyledAttributes(attrs, R.styleable.TypedArrayTypes)
+
+        val result = array.use {
+            it.getBoolean(R.styleable.TypedArrayTypes_boolean_present, false)
+        }
+        assertTrue(result)
+
+        assertThrows<RuntimeException> {
+            array.recycle()
+        }
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/database/CursorTest.kt b/core/ktx/src/androidTest/java/androidx/core/database/CursorTest.kt
new file mode 100644
index 0000000..86d0731
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/database/CursorTest.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 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.core.database
+
+import android.database.Cursor
+import android.database.MatrixCursor
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+class CursorTest {
+    @Test fun blobByName() {
+        val cursor = scalarCursor(byteArrayOf(0x01))
+        val blob = cursor.getBlob("data")
+        assertArrayEquals(byteArrayOf(0x01), blob)
+    }
+
+    @Test fun doubleByName() {
+        val cursor = scalarCursor(1.5)
+        val double = cursor.getDouble("data")
+        assertEquals(1.5, double, 0.0)
+    }
+
+    @Test fun floatByName() {
+        val cursor = scalarCursor(1.5f)
+        val float = cursor.getFloat("data")
+        assertEquals(1.5f, float, 0f)
+    }
+
+    @Test fun intByName() {
+        val cursor = scalarCursor(1)
+        val int = cursor.getInt("data")
+        assertEquals(1, int)
+    }
+
+    @Test fun longByName() {
+        val cursor = scalarCursor(1L)
+        val long = cursor.getLong("data")
+        assertEquals(1L, long)
+    }
+
+    @Test fun shortByName() {
+        val cursor = scalarCursor(1.toShort())
+        val short = cursor.getShort("data")
+        assertEquals(1.toShort(), short)
+    }
+
+    @Test fun stringByName() {
+        val cursor = scalarCursor("hey")
+        val string = cursor.getString("data")
+        assertEquals("hey", string)
+    }
+
+    @Test fun blobOrNullByIndex() {
+        val cursor = scalarCursor(null)
+        val blob = cursor.getBlobOrNull(0)
+        assertNull(blob)
+    }
+
+    @Test fun doubleOrNullByIndex() {
+        val cursor = scalarCursor(null)
+        val double = cursor.getDoubleOrNull(0)
+        assertNull(double)
+    }
+
+    @Test fun floatOrNullByIndex() {
+        val cursor = scalarCursor(null)
+        val float = cursor.getFloatOrNull(0)
+        assertNull(float)
+    }
+
+    @Test fun intOrNullByIndex() {
+        val cursor = scalarCursor(null)
+        val int = cursor.getIntOrNull(0)
+        assertNull(int)
+    }
+
+    @Test fun longOrNullByIndex() {
+        val cursor = scalarCursor(null)
+        val long = cursor.getLongOrNull(0)
+        assertNull(long)
+    }
+
+    @Test fun shortOrNullByIndex() {
+        val cursor = scalarCursor(null)
+        val short = cursor.getShortOrNull(0)
+        assertNull(short)
+    }
+
+    @Test fun stringOrNullByIndex() {
+        val cursor = scalarCursor(null)
+        val string = cursor.getStringOrNull(0)
+        assertNull(string)
+    }
+
+    @Test fun blobOrNullByName() {
+        val cursor = scalarCursor(null)
+        val blob = cursor.getBlobOrNull("data")
+        assertNull(blob)
+    }
+
+    @Test fun doubleOrNullByName() {
+        val cursor = scalarCursor(null)
+        val double = cursor.getDoubleOrNull("data")
+        assertNull(double)
+    }
+
+    @Test fun floatOrNullByName() {
+        val cursor = scalarCursor(null)
+        val float = cursor.getFloatOrNull("data")
+        assertNull(float)
+    }
+
+    @Test fun intOrNullByName() {
+        val cursor = scalarCursor(null)
+        val int = cursor.getIntOrNull("data")
+        assertNull(int)
+    }
+
+    @Test fun longOrNullByName() {
+        val cursor = scalarCursor(null)
+        val long = cursor.getLongOrNull("data")
+        assertNull(long)
+    }
+
+    @Test fun shortOrNullByName() {
+        val cursor = scalarCursor(null)
+        val short = cursor.getShortOrNull("data")
+        assertNull(short)
+    }
+
+    @Test fun stringOrNullByName() {
+        val cursor = scalarCursor(null)
+        val string = cursor.getStringOrNull("data")
+        assertNull(string)
+    }
+
+    private fun scalarCursor(item: Any?): Cursor = MatrixCursor(arrayOf("data")).apply {
+        addRow(arrayOf(item))
+        moveToFirst() // Prepare for consumers to read.
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/database/sqlite/SQLiteDatabaseTest.kt b/core/ktx/src/androidTest/java/androidx/core/database/sqlite/SQLiteDatabaseTest.kt
new file mode 100644
index 0000000..c8ed67d
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/database/sqlite/SQLiteDatabaseTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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.core.database.sqlite
+
+import android.content.ContentValues
+import android.database.sqlite.SQLiteDatabase
+import android.database.sqlite.SQLiteOpenHelper
+import android.support.test.InstrumentationRegistry
+import androidx.testutils.assertThrows
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class SQLiteDatabaseTest {
+    private val context = InstrumentationRegistry.getContext()
+    private val openHelper = object : SQLiteOpenHelper(context, null, null, 1) {
+        override fun onCreate(db: SQLiteDatabase) {
+            db.execSQL("CREATE TABLE test(name TEXT)")
+        }
+
+        override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
+        }
+    }
+    private val db = openHelper.writableDatabase
+
+    @Test fun throwingBodyNotSuccessful() {
+        val exception = RuntimeException()
+        assertThrows<RuntimeException> {
+            db.transaction {
+                insert("test", null, ContentValues().apply { put("name", "Alice") })
+                throw exception
+            }
+        }.isSameAs(exception)
+
+        val query = db.rawQuery("SELECT COUNT(*) FROM test", emptyArray())
+        query.moveToFirst()
+        assertEquals(0L, query.getLong(0))
+        query.close()
+    }
+
+    @Test fun bodyReturnValue() {
+        val result = db.transaction {
+            "Hey"
+        }
+        assertEquals("Hey", result)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/extensions.kt b/core/ktx/src/androidTest/java/androidx/core/extensions.kt
new file mode 100644
index 0000000..dc07142
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/extensions.kt
@@ -0,0 +1,34 @@
+/*
+ * 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 androidx.core
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.util.Xml
+import androidx.annotation.LayoutRes
+import org.xmlpull.v1.XmlPullParser
+
+@SuppressLint("ResourceType")
+fun Context.getAttributeSet(@LayoutRes layoutId: Int): AttributeSet {
+    val parser = resources.getXml(layoutId)
+    var type = parser.next()
+    while (type != XmlPullParser.START_TAG) {
+        type = parser.next()
+    }
+    return Xml.asAttributeSet(parser)
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/BitmapTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/BitmapTest.kt
new file mode 100644
index 0000000..25b318b
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/BitmapTest.kt
@@ -0,0 +1,71 @@
+/*
+ * 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 androidx.core.graphics
+
+import android.graphics.Bitmap
+import android.graphics.ColorSpace
+import android.support.test.filters.SdkSuppress
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class BitmapTest {
+    @Test fun create() {
+        val bitmap = createBitmap(7, 9)
+        assertEquals(7, bitmap.width)
+        assertEquals(9, bitmap.height)
+        assertEquals(Bitmap.Config.ARGB_8888, bitmap.config)
+    }
+
+    @Test fun createWithConfig() {
+        val bitmap = createBitmap(7, 9, config = Bitmap.Config.RGB_565)
+        assertEquals(Bitmap.Config.RGB_565, bitmap.config)
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun createWithColorSpace() {
+        val colorSpace = ColorSpace.get(ColorSpace.Named.ADOBE_RGB)
+        val bitmap = createBitmap(7, 9, colorSpace = colorSpace)
+        assertEquals(colorSpace, bitmap.colorSpace)
+    }
+
+    @Test fun scale() {
+        val b = createBitmap(7, 9).scale(3, 5)
+        assertEquals(3, b.width)
+        assertEquals(5, b.height)
+    }
+
+    @Test fun applyCanvas() {
+        val p = createBitmap(2, 2).applyCanvas {
+            drawColor(0x40302010)
+        }.getPixel(1, 1)
+
+        assertEquals(0x40302010, p)
+    }
+
+    @Test fun getPixel() {
+        val b = createBitmap(2, 2).applyCanvas {
+            drawColor(0x40302010)
+        }
+        assertEquals(0x40302010, b[1, 1])
+    }
+
+    @Test fun setPixel() {
+        val b = createBitmap(2, 2)
+        b[1, 1] = 0x40302010
+        assertEquals(0x40302010, b[1, 1])
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/CanvasTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/CanvasTest.kt
new file mode 100644
index 0000000..e940aa9
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/CanvasTest.kt
@@ -0,0 +1,127 @@
+/*
+ * 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 androidx.core.graphics
+
+import android.graphics.Canvas
+import android.graphics.Matrix
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class CanvasTest {
+    private val values = FloatArray(9)
+    private val canvas = Canvas(createBitmap(1, 1))
+
+    @Suppress("DEPRECATION")
+    @Test fun withSave() {
+        val beforeCount = canvas.saveCount
+
+        canvas.matrix.getValues(values)
+        val x = values[Matrix.MTRANS_X]
+        val y = values[Matrix.MTRANS_Y]
+
+        canvas.withSave {
+            assertThat(beforeCount).isLessThan(saveCount)
+            translate(10.0f, 10.0f)
+        }
+
+        canvas.matrix.getValues(values)
+        assertEquals(x, values[Matrix.MTRANS_X])
+        assertEquals(y, values[Matrix.MTRANS_Y])
+
+        assertEquals(beforeCount, canvas.saveCount)
+    }
+
+    @Test fun withTranslation() {
+        val beforeCount = canvas.saveCount
+        canvas.withTranslation(x = 16.0f, y = 32.0f) {
+            assertThat(beforeCount).isLessThan(saveCount)
+
+            @Suppress("DEPRECATION")
+            matrix.getValues(values) // will work for a software canvas
+
+            assertEquals(16.0f, values[Matrix.MTRANS_X])
+            assertEquals(32.0f, values[Matrix.MTRANS_Y])
+        }
+        assertEquals(beforeCount, canvas.saveCount)
+    }
+
+    @Test fun withRotation() {
+        val beforeCount = canvas.saveCount
+        canvas.withRotation(degrees = 90.0f, pivotX = 16.0f, pivotY = 32.0f) {
+            assertThat(beforeCount).isLessThan(saveCount)
+
+            @Suppress("DEPRECATION")
+            matrix.getValues(values) // will work for a software canvas
+
+            assertEquals(48.0f, values[Matrix.MTRANS_X])
+            assertEquals(16.0f, values[Matrix.MTRANS_Y])
+            assertEquals(-1.0f, values[Matrix.MSKEW_X])
+            assertEquals(1.0f, values[Matrix.MSKEW_Y])
+        }
+        assertEquals(beforeCount, canvas.saveCount)
+    }
+
+    @Test fun withScale() {
+        val beforeCount = canvas.saveCount
+        canvas.withScale(x = 2.0f, y = 4.0f, pivotX = 16.0f, pivotY = 32.0f) {
+            assertThat(beforeCount).isLessThan(saveCount)
+
+            @Suppress("DEPRECATION")
+            matrix.getValues(values) // will work for a software canvas
+
+            assertEquals(-16.0f, values[Matrix.MTRANS_X])
+            assertEquals(-96.0f, values[Matrix.MTRANS_Y])
+            assertEquals(2.0f, values[Matrix.MSCALE_X])
+            assertEquals(4.0f, values[Matrix.MSCALE_Y])
+        }
+        assertEquals(beforeCount, canvas.saveCount)
+    }
+
+    @Test fun withSkew() {
+        val beforeCount = canvas.saveCount
+        canvas.withSkew(x = 2.0f, y = 4.0f) {
+            assertThat(beforeCount).isLessThan(saveCount)
+
+            @Suppress("DEPRECATION")
+            matrix.getValues(values) // will work for a software canvas
+
+            assertEquals(2.0f, values[Matrix.MSKEW_X])
+            assertEquals(4.0f, values[Matrix.MSKEW_Y])
+        }
+        assertEquals(beforeCount, canvas.saveCount)
+    }
+
+    @Suppress("DEPRECATION")
+    @Test fun withMatrix() {
+        val originMatrix = canvas.matrix
+
+        val inputMatrix = Matrix()
+        inputMatrix.postTranslate(16.0f, 32.0f)
+        inputMatrix.postRotate(90.0f, 16.0f, 32.0f)
+        inputMatrix.postScale(2.0f, 4.0f, 16.0f, 32.0f)
+
+        val beforeCount = canvas.saveCount
+        canvas.withMatrix(inputMatrix) {
+            assertThat(beforeCount).isLessThan(saveCount)
+            assertEquals(inputMatrix, matrix)
+        }
+
+        assertEquals(originMatrix, canvas.matrix)
+        assertEquals(beforeCount, canvas.saveCount)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/ColorTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/ColorTest.kt
new file mode 100644
index 0000000..5c647d1
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/ColorTest.kt
@@ -0,0 +1,127 @@
+/*
+ * 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 androidx.core.graphics
+
+import android.graphics.Color
+import android.graphics.ColorSpace
+import android.support.test.filters.SdkSuppress
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class ColorTest {
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun destructuringColor() {
+        val (r, g, b, a) = 0x337f3010.toColor()
+        assertEquals(0.5f, r, 1e-2f)
+        assertEquals(0.19f, g, 1e-2f)
+        assertEquals(0.06f, b, 1e-2f)
+        assertEquals(0.2f, a, 1e-2f)
+    }
+
+    @Test fun destructuringInt() {
+        val (a, r, g, b) = 0x337f3010
+        assertEquals(0x33, a)
+        assertEquals(0x7f, r)
+        assertEquals(0x30, g)
+        assertEquals(0x10, b)
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun intToColor() = assertEquals(Color.valueOf(0x337f3010), 0x337f3010.toColor())
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun intToColorLong() = assertEquals(Color.pack(0x337f3010), 0x337f3010.toColorLong())
+
+    @Test fun alpha() = assertEquals(0x33, 0x337f3010.alpha)
+    @Test fun red() = assertEquals(0x7f, 0x337f3010.red)
+    @Test fun green() = assertEquals(0x30, 0x337f3010.green)
+    @Test fun blue() = assertEquals(0x10, 0x337f3010.blue)
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun luminance() = assertEquals(0.212f, 0xff7f7f7f.toInt().luminance, 1e-3f)
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun longToColor() {
+        assertEquals(Color.valueOf(0x337f3010), Color.pack(0x337f3010).toColor())
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun longToColorInt() = assertEquals(0x337f3010, Color.pack(0x337f3010).toColorInt())
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun destructuringLong() {
+        val (r, g, b, a) = Color.pack(0x337f3010)
+        assertEquals(0.20f, a, 1e-2f)
+        assertEquals(0.50f, r, 1e-2f)
+        assertEquals(0.19f, g, 1e-2f)
+        assertEquals(0.06f, b, 1e-2f)
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun alphaLong() = assertEquals(0.20f, Color.pack(0x337f3010).alpha, 1e-2f)
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun redLong() = assertEquals(0.50f, Color.pack(0x337f3010).red, 1e-2f)
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun greenLong() = assertEquals(0.19f, Color.pack(0x337f3010).green, 1e-2f)
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun blueLong() = assertEquals(0.06f, Color.pack(0x337f3010).blue, 1e-2f)
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun luminanceLong() {
+        assertEquals(0.212f, Color.pack(0xff7f7f7f.toInt()).luminance, 1e-3f)
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun isSrgb() {
+        assertTrue(0x337f3010.toColorLong().isSrgb)
+        val c = Color.pack(1.0f, 0.0f, 0.0f, 1.0f, ColorSpace.get(ColorSpace.Named.BT2020))
+        assertFalse(c.isSrgb)
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun isWideGamut() {
+        assertFalse(0x337f3010.toColorLong().isWideGamut)
+        val c = Color.pack(1.0f, 0.0f, 0.0f, 1.0f, ColorSpace.get(ColorSpace.Named.BT2020))
+        assertTrue(c.isWideGamut)
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun getColorSpace() {
+        val sRGB = ColorSpace.get(ColorSpace.Named.SRGB)
+        assertEquals(sRGB, 0x337f3010.toColorLong().colorSpace)
+
+        val bt2020 = ColorSpace.get(ColorSpace.Named.BT2020)
+        val c = Color.pack(1.0f, 0.0f, 0.0f, 1.0f, bt2020)
+        assertEquals(bt2020, c.colorSpace)
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun addColorsSameColorSpace() {
+        val (r, g, b, a) = 0x7f7f0000.toColor() + 0x7f007f00.toColor()
+        assertEquals(0.16f, r, 1e-2f)
+        assertEquals(0.33f, g, 1e-2f)
+        assertEquals(0.00f, b, 1e-2f)
+        assertEquals(0.75f, a, 1e-2f)
+    }
+
+    @Test fun stringToColorInt() = assertEquals(Color.GREEN, "#00ff00".toColorInt())
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/MatrixTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/MatrixTest.kt
new file mode 100644
index 0000000..61fce4f
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/MatrixTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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.core.graphics
+
+import android.graphics.Matrix
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class MatrixTest {
+    @Test fun translationMatrix() {
+        val r = translationMatrix(2.0f, 3.0f).values()
+        assertEquals(1.0f, r[Matrix.MSCALE_X])
+        assertEquals(0.0f, r[Matrix.MSKEW_X])
+        assertEquals(2.0f, r[Matrix.MTRANS_X])
+        assertEquals(0.0f, r[Matrix.MSKEW_Y])
+        assertEquals(1.0f, r[Matrix.MSCALE_Y])
+        assertEquals(3.0f, r[Matrix.MTRANS_Y])
+    }
+
+    @Test fun scaleMatrix() {
+        val r = scaleMatrix(2.0f, 3.0f).values()
+        assertEquals(2.0f, r[Matrix.MSCALE_X])
+        assertEquals(0.0f, r[Matrix.MSKEW_X])
+        assertEquals(0.0f, r[Matrix.MTRANS_X])
+        assertEquals(0.0f, r[Matrix.MSKEW_Y])
+        assertEquals(3.0f, r[Matrix.MSCALE_Y])
+        assertEquals(0.0f, r[Matrix.MTRANS_Y])
+    }
+
+    @Test fun rotationMatrix() {
+        val r = rotationMatrix(90.0f, 2.0f, 3.0f).values()
+        assertEquals(0.0f, r[Matrix.MSCALE_X])
+        assertEquals(-1.0f, r[Matrix.MSKEW_X])
+        assertEquals(5.0f, r[Matrix.MTRANS_X])
+        assertEquals(1.0f, r[Matrix.MSKEW_Y])
+        assertEquals(0.0f, r[Matrix.MSCALE_Y])
+        assertEquals(1.0f, r[Matrix.MTRANS_Y])
+    }
+
+    @Test fun multiply() {
+        val t = translationMatrix(2.0f, 3.0f)
+        val s = scaleMatrix(2.0f, 3.0f)
+        val r = (s * t).values()
+
+        assertEquals(4.0f, r[Matrix.MTRANS_X], 1e-4f)
+        assertEquals(9.0f, r[Matrix.MTRANS_Y], 1e-4f)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/PathTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/PathTest.kt
new file mode 100644
index 0000000..36f2cba
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/PathTest.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 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.core.graphics
+
+import android.graphics.Path
+import android.graphics.RectF
+import android.support.test.filters.SdkSuppress
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class PathTest {
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun testFlatten() {
+        val p = Path()
+
+        // Start with several moves
+        p.moveTo(5.0f, 5.0f)
+        p.moveTo(10.0f, 10.0f)
+        p.lineTo(20.0f, 20.0f)
+        p.lineTo(30.0f, 10.0f)
+        // Several moves in the middle
+        p.moveTo(40.0f, 10.0f)
+        p.moveTo(50.0f, 10.0f)
+        p.lineTo(60.0f, 20.0f)
+        // End with several moves
+        p.moveTo(10.0f, 10.0f)
+        p.moveTo(30.0f, 30.0f)
+
+        var count = 0
+        p.flatten().forEach {
+            count++
+            assertNotEquals(it.startFraction, it.endFraction)
+        }
+        assertEquals(3, count)
+    }
+
+    @SdkSuppress(minSdkVersion = 19)
+    @Test fun testUnion() {
+        val r1 = Path().apply { addRect(0.0f, 0.0f, 10.0f, 10.0f, Path.Direction.CW) }
+        val r2 = Path().apply { addRect(5.0f, 0.0f, 15.0f, 15.0f, Path.Direction.CW) }
+
+        val p = r1 + r2
+        val r = RectF()
+        p.computeBounds(r, true)
+
+        assertEquals(RectF(0.0f, 0.0f, 15.0f, 15.0f), r)
+    }
+
+    @SdkSuppress(minSdkVersion = 19)
+    @Test fun testAnd() {
+        val r1 = Path().apply { addRect(0.0f, 0.0f, 10.0f, 10.0f, Path.Direction.CW) }
+        val r2 = Path().apply { addRect(5.0f, 0.0f, 15.0f, 15.0f, Path.Direction.CW) }
+
+        val p = r1 and r2
+        val r = RectF()
+        p.computeBounds(r, true)
+
+        assertEquals(RectF(0.0f, 0.0f, 15.0f, 15.0f), r)
+    }
+
+    @SdkSuppress(minSdkVersion = 19)
+    @Test fun testDifference() {
+        val r1 = Path().apply { addRect(0.0f, 0.0f, 10.0f, 10.0f, Path.Direction.CW) }
+        val r2 = Path().apply { addRect(5.0f, 0.0f, 15.0f, 15.0f, Path.Direction.CW) }
+
+        val p = r1 - r2
+        val r = RectF()
+        p.computeBounds(r, true)
+
+        assertEquals(RectF(0.0f, 0.0f, 5.0f, 10.0f), r)
+    }
+
+    @SdkSuppress(minSdkVersion = 19)
+    @Test fun testIntersection() {
+        val r1 = Path().apply { addRect(0.0f, 0.0f, 10.0f, 10.0f, Path.Direction.CW) }
+        val r2 = Path().apply { addRect(5.0f, 0.0f, 15.0f, 15.0f, Path.Direction.CW) }
+
+        val p = r1 or r2
+        val r = RectF()
+        p.computeBounds(r, true)
+
+        assertEquals(RectF(5.0f, 0.0f, 10.0f, 10.0f), r)
+    }
+
+    @SdkSuppress(minSdkVersion = 19)
+    @Test fun testEmptyIntersection() {
+        val r1 = Path().apply { addRect(0.0f, 0.0f, 2.0f, 2.0f, Path.Direction.CW) }
+        val r2 = Path().apply { addRect(5.0f, 5.0f, 7.0f, 7.0f, Path.Direction.CW) }
+
+        val p = r1 or r2
+        assertTrue(p.isEmpty)
+    }
+
+    @SdkSuppress(minSdkVersion = 19)
+    @Test fun testXor() {
+        val r1 = Path().apply { addRect(0.0f, 0.0f, 10.0f, 10.0f, Path.Direction.CW) }
+        val r2 = Path().apply { addRect(5.0f, 5.0f, 15.0f, 15.0f, Path.Direction.CW) }
+
+        val p = r1 xor r2
+        val r = RectF()
+        p.computeBounds(r, true)
+
+        assertEquals(RectF(0.0f, 0.0f, 15.0f, 15.0f), r)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/PictureTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/PictureTest.kt
new file mode 100644
index 0000000..6d0029f
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/PictureTest.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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.core.graphics
+
+import android.graphics.Color
+import android.graphics.Picture
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class PictureTest {
+    @Test fun record() {
+        val p = Picture().record(1, 1) {
+            drawColor(Color.RED)
+        }
+        val v = createBitmap(1, 1).applyCanvas {
+            drawPicture(p)
+        }.getPixel(0, 0)
+        assertEquals(0xffff0000.toInt(), v)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/PointTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/PointTest.kt
new file mode 100644
index 0000000..791d35f
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/PointTest.kt
@@ -0,0 +1,106 @@
+/*
+ * 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 androidx.core.graphics
+
+import android.graphics.Point
+import android.graphics.PointF
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class PointTest {
+    @Test fun destructuringInt() {
+        val (x, y) = Point(2, 3)
+        assertEquals(2, x)
+        assertEquals(3, y)
+    }
+
+    @Test fun destructuringFloat() {
+        val (x, y) = PointF(2.0f, 3.0f)
+        assertEquals(2.0f, x)
+        assertEquals(3.0f, y)
+    }
+
+    @Test fun offsetInt() {
+        val (x, y) = Point(2, 3) + 2
+        assertEquals(4, x)
+        assertEquals(5, y)
+    }
+
+    @Test fun offsetFloat() {
+        val (x, y) = PointF(2.0f, 3.0f) + 2.0f
+        assertEquals(4.0f, x)
+        assertEquals(5.0f, y)
+    }
+
+    @Test fun offsetPoint() {
+        val (x, y) = Point(2, 3) + Point(1, 2)
+        assertEquals(3, x)
+        assertEquals(5, y)
+    }
+
+    @Test fun offsetPointF() {
+        val (x, y) = PointF(2.0f, 3.0f) + PointF(1.0f, 2.0f)
+        assertEquals(3.0f, x)
+        assertEquals(5.0f, y)
+    }
+
+    @Test fun negativeOffsetInt() {
+        val (x, y) = Point(2, 3) - 2
+        assertEquals(0, x)
+        assertEquals(1, y)
+    }
+
+    @Test fun negativeOffsetFloat() {
+        val (x, y) = PointF(2.0f, 3.0f) - 2.0f
+        assertEquals(0.0f, x)
+        assertEquals(1.0f, y)
+    }
+
+    @Test fun negativeOffsetPoint() {
+        val (x, y) = Point(2, 3) - Point(1, 2)
+        assertEquals(1, x)
+        assertEquals(1, y)
+    }
+
+    @Test fun negativeOffsetPointF() {
+        val (x, y) = PointF(2.0f, 3.0f) - PointF(1.0f, 2.0f)
+        assertEquals(1.0f, x)
+        assertEquals(1.0f, y)
+    }
+
+    @Test fun negateInt() {
+        val (x, y) = -Point(2, 3)
+        assertEquals(-2, x)
+        assertEquals(-3, y)
+    }
+
+    @Test fun negateFloat() {
+        val (x, y) = -PointF(2.0f, 3.0f)
+        assertEquals(-2.0f, x)
+        assertEquals(-3.0f, y)
+    }
+
+    @Test fun toPointF() {
+        val pointF = Point(1, 2).toPointF()
+        assertEquals(1f, pointF.x, 0f)
+        assertEquals(2f, pointF.y, 0f)
+    }
+
+    @Test fun toPoint() {
+        assertEquals(Point(1, 2), PointF(1.1f, 2.8f).toPoint())
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/PorterDuffTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/PorterDuffTest.kt
new file mode 100644
index 0000000..9b44a50
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/PorterDuffTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 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.core.graphics
+
+import android.graphics.Paint
+import android.graphics.PorterDuff
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class PorterDuffTest {
+    @Test fun xfermode() {
+        val p = createBitmap(1, 1).applyCanvas {
+            val p = Paint().apply { color = 0xffffffff.toInt() }
+            drawRect(0f, 0f, 1f, 1f, p)
+
+            p.color = 0x7f00ff00
+            p.xfermode = PorterDuff.Mode.SRC.toXfermode()
+            drawRect(0f, 0f, 1f, 1f, p)
+        }.getPixel(0, 0)
+
+        assertEquals(0x7f00ff00, p)
+    }
+
+    @Test fun colorFilter() {
+        val p = createBitmap(1, 1).applyCanvas {
+            val p = Paint().apply { color = 0xffffffff.toInt() }
+            drawRect(0f, 0f, 1f, 1f, p)
+
+            p.color = 0xff000000.toInt()
+            p.colorFilter = PorterDuff.Mode.SRC.toColorFilter(0xff00ff00.toInt())
+            drawRect(0f, 0f, 1f, 1f, p)
+        }.getPixel(0, 0)
+
+        assertEquals(0xff00ff00.toInt(), p)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/RectTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/RectTest.kt
new file mode 100644
index 0000000..8b2613f
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/RectTest.kt
@@ -0,0 +1,232 @@
+/*
+ * 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 androidx.core.graphics
+
+import android.graphics.Matrix
+import android.graphics.Point
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.RectF
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class RectTest {
+    @Test fun destructuringInt() {
+        val (l, t, r, b) = Rect(4, 8, 16, 24)
+        assertEquals(4, l)
+        assertEquals(8, t)
+        assertEquals(16, r)
+        assertEquals(24, b)
+    }
+
+    @Test fun destructuringFloat() {
+        val (l, t, r, b) = RectF(4.0f, 8.0f, 16.0f, 24.0f)
+        assertEquals(4.0f, l)
+        assertEquals(8.0f, t)
+        assertEquals(16.0f, r)
+        assertEquals(24.0f, b)
+    }
+
+    @Test fun unionInt() {
+        val (l, t, r, b) = Rect(0, 0, 4, 4) + Rect(-1, -1, 6, 6)
+        assertEquals(-1, l)
+        assertEquals(-1, t)
+        assertEquals(6, r)
+        assertEquals(6, b)
+    }
+
+    @Test fun unionAsAndInt() {
+        val (l, t, r, b) = Rect(0, 0, 4, 4) and Rect(-1, -1, 6, 6)
+        assertEquals(-1, l)
+        assertEquals(-1, t)
+        assertEquals(6, r)
+        assertEquals(6, b)
+    }
+
+    @Test fun unionFloat() {
+        val (l, t, r, b) = RectF(0.0f, 0.0f, 4.0f, 4.0f) + RectF(-1.0f, -1.0f, 6.0f, 6.0f)
+        assertEquals(-1.0f, l)
+        assertEquals(-1.0f, t)
+        assertEquals(6.0f, r)
+        assertEquals(6.0f, b)
+    }
+
+    @Test fun unionAsAndFloat() {
+        val (l, t, r, b) = RectF(0.0f, 0.0f, 4.0f, 4.0f) and RectF(-1.0f, -1.0f, 6.0f, 6.0f)
+        assertEquals(-1.0f, l)
+        assertEquals(-1.0f, t)
+        assertEquals(6.0f, r)
+        assertEquals(6.0f, b)
+    }
+
+    @Test fun differenceInt() {
+        val r = Rect(0, 0, 4, 4) - Rect(-1, 2, 6, 6)
+        assertEquals(Rect(0, 0, 4, 2), r.bounds)
+    }
+
+    @Test fun differenceFloat() {
+        val r = RectF(0.0f, 0.0f, 4.0f, 4.0f) - RectF(-1.0f, 2.0f, 6.0f, 6.0f)
+        assertEquals(Rect(0, 0, 4, 2), r.bounds)
+    }
+
+    @Test fun intersectionAsOrInt() {
+        val (l, t, r, b) = Rect(0, 0, 4, 4) or Rect(2, 2, 6, 6)
+        assertEquals(2, l)
+        assertEquals(2, t)
+        assertEquals(4, r)
+        assertEquals(4, b)
+    }
+
+    @Test fun intersectionAsOrFloat() {
+        val (l, t, r, b) = RectF(0.0f, 0.0f, 4.0f, 4.0f) or RectF(2.0f, 2.0f, 6.0f, 6.0f)
+        assertEquals(2.0f, l)
+        assertEquals(2.0f, t)
+        assertEquals(4.0f, r)
+        assertEquals(4.0f, b)
+    }
+
+    @Test fun xorInt() {
+        val r = Rect(0, 0, 4, 4) xor Rect(2, 2, 6, 6)
+        assertEquals(Rect(0, 0, 4, 4) and Rect(2, 2, 6, 6), r.bounds)
+        assertFalse(r.contains(3, 3))
+    }
+
+    @Test fun xorFloat() {
+        val r = RectF(0.0f, 0.0f, 4.0f, 4.0f) xor RectF(2.0f, 2.0f, 6.0f, 6.0f)
+        assertEquals(Rect(0, 0, 4, 4) and Rect(2, 2, 6, 6), r.bounds)
+        assertFalse(r.contains(3, 3))
+    }
+
+    @Test fun offsetInt() {
+        val (l, t, r, b) = Rect(0, 0, 2, 2) + 2
+        assertEquals(l, 2)
+        assertEquals(t, 2)
+        assertEquals(r, 4)
+        assertEquals(b, 4)
+    }
+
+    @Test fun offsetPoint() {
+        val (l, t, r, b) = Rect(0, 0, 2, 2) + Point(1, 2)
+        assertEquals(l, 1)
+        assertEquals(t, 2)
+        assertEquals(r, 3)
+        assertEquals(b, 4)
+    }
+
+    @Test fun offsetFloat() {
+        val (l, t, r, b) = RectF(0.0f, 0.0f, 2.0f, 2.0f) + 2.0f
+        assertEquals(l, 2.0f)
+        assertEquals(t, 2.0f)
+        assertEquals(r, 4.0f)
+        assertEquals(b, 4.0f)
+    }
+
+    @Test fun offsetPointF() {
+        val (l, t, r, b) = RectF(0.0f, 0.0f, 2.0f, 2.0f) + PointF(1.0f, 2.0f)
+        assertEquals(l, 1.0f)
+        assertEquals(t, 2.0f)
+        assertEquals(r, 3.0f)
+        assertEquals(b, 4.0f)
+    }
+
+    @Test fun negativeOffsetInt() {
+        val (l, t, r, b) = Rect(0, 0, 2, 2) - 2
+        assertEquals(l, -2)
+        assertEquals(t, -2)
+        assertEquals(r, 0)
+        assertEquals(b, 0)
+    }
+
+    @Test fun negativeOffsetPoint() {
+        val (l, t, r, b) = Rect(0, 0, 2, 2) - Point(1, 2)
+        assertEquals(l, -1)
+        assertEquals(t, -2)
+        assertEquals(r, 1)
+        assertEquals(b, 0)
+    }
+
+    @Test fun negativeOffsetFloat() {
+        val (l, t, r, b) = RectF(0.0f, 0.0f, 2.0f, 2.0f) - 2.0f
+        assertEquals(l, -2.0f)
+        assertEquals(t, -2.0f)
+        assertEquals(r, 0.0f)
+        assertEquals(b, 0.0f)
+    }
+
+    @Test fun negativeOffsetPointF() {
+        val (l, t, r, b) = RectF(0.0f, 0.0f, 2.0f, 2.0f) - PointF(1.0f, 2.0f)
+        assertEquals(l, -1.0f)
+        assertEquals(t, -2.0f)
+        assertEquals(r, 1.0f)
+        assertEquals(b, 0.0f)
+    }
+
+    @Test fun pointInside() {
+        assertTrue(Point(1, 1) in Rect(0, 0, 2, 2))
+        assertTrue(PointF(1.0f, 1.0f) in RectF(0.0f, 0.0f, 2.0f, 2.0f))
+    }
+
+    @Test fun toRect() {
+        assertEquals(
+            Rect(0, 1, 2, 3),
+            RectF(0f, 1f, 2f, 3f).toRect())
+
+        assertEquals(
+            Rect(0, 1, 2, 3),
+            RectF(0.1f, 1.1f, 1.9f, 2.9f).toRect())
+    }
+
+    @Test fun toRectF() {
+        val rectF = Rect(0, 1, 2, 3).toRectF()
+        assertEquals(0f, rectF.left, 0f)
+        assertEquals(1f, rectF.top, 0f)
+        assertEquals(2f, rectF.right, 0f)
+        assertEquals(3f, rectF.bottom, 0f)
+    }
+
+    @Test fun toRegionInt() {
+        assertEquals(Rect(1, 1, 5, 5), Rect(1, 1, 5, 5).toRegion().bounds)
+    }
+
+    @Test fun toRegionFloat() {
+        assertEquals(Rect(1, 1, 5, 5), RectF(1.1f, 1.1f, 4.8f, 4.8f).toRegion().bounds)
+    }
+
+    @Test fun transformRectToRect() {
+        val m = Matrix()
+        m.setScale(2.0f, 2.0f)
+
+        val r = RectF(2.0f, 2.0f, 5.0f, 7.0f).transform(m)
+        assertEquals(4f, r.left, 0f)
+        assertEquals(4f, r.top, 0f)
+        assertEquals(10f, r.right, 0f)
+        assertEquals(14f, r.bottom, 0f)
+    }
+
+    @Test fun transformRectNotPreserved() {
+        val m = Matrix()
+        m.setRotate(45.0f)
+
+        val (l, t, r, b) = RectF(-1.0f, -1.0f, 1.0f, 1.0f).transform(m)
+        assertEquals(-1.414f, l, 1e-3f)
+        assertEquals(-1.414f, t, 1e-3f)
+        assertEquals( 1.414f, r, 1e-3f)
+        assertEquals( 1.414f, b, 1e-3f)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/RegionTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/RegionTest.kt
new file mode 100644
index 0000000..21e6443
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/RegionTest.kt
@@ -0,0 +1,157 @@
+/*
+ * 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 androidx.core.graphics
+
+import android.graphics.Point
+import android.graphics.Rect
+import android.graphics.Region
+import androidx.testutils.assertThrows
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class RegionTest {
+    @Test fun containsPoint() {
+        assertFalse(Point(1, 1) in Region())
+        assertTrue(Point(1, 1) in Region(0, 0, 2, 2))
+    }
+
+    @Test fun unionRect() {
+        val r = Region(0, 0, 2, 2) + Rect(4, 4, 6, 6)
+        assertFalse(Point(3, 3) in r)
+        assertTrue(Point(1, 1) in r)
+        assertTrue(Point(5, 5) in r)
+    }
+
+    @Test fun unionRegion() {
+        val r = Region(0, 0, 2, 2) + Region(4, 4, 6, 6)
+        assertFalse(Point(3, 3) in r)
+        assertTrue(Point(1, 1) in r)
+        assertTrue(Point(5, 5) in r)
+    }
+
+    @Test fun unionAsAndRect() {
+        val r = Region(0, 0, 2, 2) and Rect(4, 4, 6, 6)
+        assertFalse(Point(3, 3) in r)
+        assertTrue(Point(1, 1) in r)
+        assertTrue(Point(5, 5) in r)
+    }
+
+    @Test fun unionAsAndRegion() {
+        val r = Region(0, 0, 2, 2) and Region(4, 4, 6, 6)
+        assertFalse(Point(3, 3) in r)
+        assertTrue(Point(1, 1) in r)
+        assertTrue(Point(5, 5) in r)
+    }
+
+    @Test fun differenceRect() {
+        val r = Region(0, 0, 4, 4) - Rect(2, 2, 6, 6)
+        assertFalse(Point(3, 3) in r)
+        assertTrue(Point(1, 1) in r)
+        assertFalse(Point(5, 5) in r)
+    }
+
+    @Test fun differenceRegion() {
+        val r = Region(0, 0, 4, 4) - Region(2, 2, 6, 6)
+        assertFalse(Point(3, 3) in r)
+        assertTrue(Point(1, 1) in r)
+        assertFalse(Point(5, 5) in r)
+    }
+
+    @Test fun unaryMinus() {
+        val r = Rect(0, 0, 10, 10) - Rect(4, 4, 6, 6)
+        assertTrue(Point(1, 1) in r)
+        assertFalse(Point(5, 5) in r)
+
+        val i = -r
+        assertFalse(Point(1, 1) in i)
+        assertTrue(Point(5, 5) in i)
+    }
+
+    @Test fun not() {
+        val r = Rect(0, 0, 10, 10) - Rect(4, 4, 6, 6)
+        assertTrue(Point(1, 1) in r)
+        assertFalse(Point(5, 5) in r)
+
+        val i = !r
+        assertFalse(Point(1, 1) in i)
+        assertTrue(Point(5, 5) in i)
+    }
+
+    @Test fun orRect() {
+        val r = Region(0, 0, 4, 4) or Rect(2, 2, 6, 6)
+        assertFalse(Point(1, 1) in r)
+        assertTrue(Point(3, 3) in r)
+    }
+
+    @Test fun orRegion() {
+        val r = Region(0, 0, 4, 4) or Region(2, 2, 6, 6)
+        assertFalse(Point(1, 1) in r)
+        assertTrue(Point(3, 3) in r)
+    }
+
+    @Test fun xorRect() {
+        val r = Region(0, 0, 4, 4) xor Rect(2, 2, 6, 6)
+        assertFalse(Point(3, 3) in r)
+        assertTrue(Point(1, 1) in r)
+    }
+
+    @Test fun xorRegion() {
+        val r = Region(0, 0, 4, 4) xor Region(2, 2, 6, 6)
+        assertFalse(Point(3, 3) in r)
+        assertTrue(Point(1, 1) in r)
+    }
+
+    @Test fun iteratorForLoop() {
+        val region = Region(0, 0, 4, 4) -
+                Rect(2, 2, 6, 6)
+        var count = 0
+        var r = Rect()
+        for (rect in region) {
+            count++
+            assertNotSame(r, rect)
+            r = rect
+        }
+        assertEquals(2, count)
+    }
+
+    @Test fun iteratorOutOfBounds() {
+        val region = Region(0, 0, 4, 4) -
+                Rect(2, 2, 6, 6)
+        val it = region.iterator()
+        it.next()
+        it.next()
+        assertThrows<IndexOutOfBoundsException> {
+            it.next()
+        }
+    }
+
+    @Test fun iteratorForEach() {
+        val region = Region(0, 0, 4, 4) -
+                Rect(2, 2, 6, 6)
+        var count = 0
+        var r = Rect()
+        region.forEach {
+            count++
+            assertNotSame(r, it)
+            r = it
+        }
+        assertEquals(2, count)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/ShaderTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/ShaderTest.kt
new file mode 100644
index 0000000..ad2b63c
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/ShaderTest.kt
@@ -0,0 +1,44 @@
+/*
+ * 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 androidx.core.graphics
+
+import android.graphics.Matrix
+import android.graphics.Shader
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class ShaderTest {
+    @Test
+    fun testTransform() {
+        @Suppress("DEPRECATION")
+        val shader = Shader()
+        val values = FloatArray(9)
+        val matrix = Matrix()
+
+        shader.transform {
+            setTranslate(10f, 30f)
+        }
+
+        // Now read matrix from Shader
+        shader.getLocalMatrix(matrix)
+        matrix.getValues(values)
+
+        // Assert that the values are as expected
+        assertEquals(10f, values[Matrix.MTRANS_X])
+        assertEquals(30f, values[Matrix.MTRANS_Y])
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/BitmapDrawableTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/BitmapDrawableTest.kt
new file mode 100644
index 0000000..4c4ceb6
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/BitmapDrawableTest.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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.core.graphics.drawable
+
+import android.support.test.InstrumentationRegistry
+import androidx.core.graphics.createBitmap
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class BitmapDrawableTest {
+    private val context = InstrumentationRegistry.getContext()
+
+    @Test fun fromBitmap() {
+        val b = createBitmap(1, 1)
+        val drawable = b.toDrawable(context.resources)
+        assertEquals(b, drawable.bitmap)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/ColorDrawableTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/ColorDrawableTest.kt
new file mode 100644
index 0000000..ad7a39b
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/ColorDrawableTest.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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.core.graphics.drawable
+
+import android.graphics.Color
+import android.support.test.filters.SdkSuppress
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class ColorDrawableTest {
+    @Test fun fromInt() {
+        val drawable = Color.CYAN.toDrawable()
+        assertEquals(Color.CYAN, drawable.color)
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test fun fromColor() {
+        val drawable = Color.valueOf(Color.CYAN).toDrawable()
+        assertEquals(Color.CYAN, drawable.color)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/DrawableTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/DrawableTest.kt
new file mode 100644
index 0000000..6455d0a
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/DrawableTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 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.core.graphics.drawable
+
+import android.graphics.Bitmap.Config
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import android.support.test.InstrumentationRegistry
+import androidx.core.graphics.createBitmap
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class DrawableTest {
+    private val context = InstrumentationRegistry.getContext()
+    private val resources = context.resources
+
+    @Test fun bitmapDrawableNoSizeNoConfig() {
+        val original = createBitmap(10, 10).apply {
+            eraseColor(Color.RED)
+        }
+        val drawable = BitmapDrawable(resources, original)
+
+        val bitmap = drawable.toBitmap()
+        assertEquals(10, bitmap.width)
+        assertEquals(10, bitmap.height)
+        assertEquals(Config.ARGB_8888, bitmap.config)
+        assertEquals(Color.RED, bitmap.getPixel(5, 5))
+    }
+
+    @Test fun bitmapDrawableNoSizeRetainedConfig() {
+        val original = createBitmap(10, 10).apply {
+            eraseColor(Color.RED)
+        }
+        val drawable = BitmapDrawable(resources, original)
+
+        val bitmap = drawable.toBitmap(config = Config.ARGB_8888)
+        assertEquals(10, bitmap.width)
+        assertEquals(10, bitmap.height)
+        assertEquals(Config.ARGB_8888, bitmap.config)
+        assertEquals(Color.RED, bitmap.getPixel(5, 5))
+    }
+
+    @Test fun bitmapDrawableNoSizeDifferentConfig() {
+        val original = createBitmap(10, 10).apply {
+            eraseColor(Color.RED)
+        }
+        val drawable = BitmapDrawable(resources, original)
+
+        val bitmap = drawable.toBitmap(config = Config.ARGB_8888)
+        assertEquals(10, bitmap.width)
+        assertEquals(10, bitmap.height)
+        assertEquals(Config.ARGB_8888, bitmap.config)
+        assertEquals(Color.RED, bitmap.getPixel(5, 5))
+    }
+
+    @Test fun bitmapDrawableDifferentSizeNoConfig() {
+        val original = createBitmap(10, 10).apply {
+            eraseColor(Color.RED)
+        }
+        val drawable = BitmapDrawable(resources, original)
+
+        val bitmap = drawable.toBitmap(20, 20)
+        assertEquals(20, bitmap.width)
+        assertEquals(20, bitmap.height)
+        assertEquals(Config.ARGB_8888, bitmap.config)
+        assertEquals(Color.RED, bitmap.getPixel(10, 10))
+    }
+
+    @Test fun bitmapDrawableDifferentSizeDifferentConfig() {
+        val original = createBitmap(10, 10).apply {
+            eraseColor(Color.RED)
+        }
+        val drawable = BitmapDrawable(resources, original)
+
+        val bitmap = drawable.toBitmap(20, 20, Config.ARGB_8888)
+        assertEquals(20, bitmap.width)
+        assertEquals(20, bitmap.height)
+        assertEquals(Config.ARGB_8888, bitmap.config)
+        assertEquals(Color.RED, bitmap.getPixel(10, 10))
+    }
+
+    @Test fun drawableNoConfig() {
+        val drawable = object : ColorDrawable(Color.RED) {
+            override fun getIntrinsicWidth() = 10
+            override fun getIntrinsicHeight() = 10
+        }
+
+        val bitmap = drawable.toBitmap()
+        assertEquals(10, bitmap.width)
+        assertEquals(10, bitmap.height)
+        assertEquals(Config.ARGB_8888, bitmap.config)
+        assertEquals(Color.RED, bitmap.getPixel(5, 5))
+    }
+
+    @Test fun drawableConfig() {
+        val drawable = object : ColorDrawable(Color.RED) {
+            override fun getIntrinsicWidth() = 10
+            override fun getIntrinsicHeight() = 10
+        }
+
+        val bitmap = drawable.toBitmap(config = Config.RGB_565)
+        assertEquals(10, bitmap.width)
+        assertEquals(10, bitmap.height)
+        assertEquals(Config.RGB_565, bitmap.config)
+        assertEquals(Color.RED, bitmap.getPixel(5, 5))
+    }
+
+    @Test fun drawableSize() {
+        val drawable = object : ColorDrawable(Color.RED) {
+            override fun getIntrinsicWidth() = 10
+            override fun getIntrinsicHeight() = 10
+        }
+
+        val bitmap = drawable.toBitmap(20, 20)
+        assertEquals(20, bitmap.width)
+        assertEquals(20, bitmap.height)
+        assertEquals(Config.ARGB_8888, bitmap.config)
+        assertEquals(Color.RED, bitmap.getPixel(10, 10))
+    }
+
+    @Test fun oldBoundsRestored() {
+        val drawable = object : ColorDrawable(Color.RED) {
+            override fun getIntrinsicWidth() = 10
+            override fun getIntrinsicHeight() = 10
+        }
+        drawable.setBounds(2, 2, 8, 8)
+
+        val bitmap = drawable.toBitmap()
+        assertEquals(10, bitmap.width)
+        assertEquals(10, bitmap.height)
+
+        assertEquals(2, drawable.bounds.left)
+        assertEquals(2, drawable.bounds.top)
+        assertEquals(8, drawable.bounds.right)
+        assertEquals(8, drawable.bounds.bottom)
+    }
+
+    @Test fun updateBoundsTest() {
+        val drawable = object : ColorDrawable(Color.RED) {
+            override fun getIntrinsicWidth() = 10
+            override fun getIntrinsicHeight() = 10
+        }
+        drawable.setBounds(0, 0, 10, 10)
+
+        drawable.updateBounds(1, 2, 3, 4)
+
+        assertEquals(1, drawable.bounds.left)
+        assertEquals(2, drawable.bounds.top)
+        assertEquals(3, drawable.bounds.right)
+        assertEquals(4, drawable.bounds.bottom)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/IconTest.kt b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/IconTest.kt
new file mode 100644
index 0000000..e2dd84c
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/graphics/drawable/IconTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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.core.graphics.drawable
+
+import android.graphics.Bitmap
+import android.graphics.Bitmap.Config.ARGB_8888
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.drawable.Icon
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import androidx.core.graphics.createBitmap
+import androidx.core.net.toUri
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import java.io.File
+
+@SdkSuppress(minSdkVersion = 26)
+class IconTest {
+    private val context = InstrumentationRegistry.getContext()
+
+    @Test fun fromBitmapAdaptive() {
+        val density = context.resources.displayMetrics.density
+
+        val edge = (108.0f * density + 0.5f).toInt()
+        val bitmap = Bitmap.createBitmap(edge, edge, ARGB_8888).apply {
+            eraseColor(Color.RED)
+        }
+        val icon = bitmap.toAdaptiveIcon()
+
+        val rendered = icon.toIntrinsicBitmap()
+        val masked = (72.0f * density + 0.5f).toInt()
+        assertEquals(masked, rendered.width)
+        assertEquals(masked, rendered.height)
+        // Grab a pixel from the middle to ensure we are not being masked.
+        assertEquals(Color.RED, rendered.getPixel(masked / 2, masked / 2))
+    }
+
+    @Test fun fromBitmap() {
+        val bitmap = createBitmap(1, 1).apply {
+            eraseColor(Color.RED)
+        }
+        val icon = bitmap.toIcon()
+
+        val rendered = icon.toIntrinsicBitmap()
+        assertEquals(1, rendered.width)
+        assertEquals(1, rendered.height)
+        assertEquals(Color.RED, rendered.getPixel(0, 0))
+    }
+
+    @Test fun fromUri() {
+        // Icon can't read from file:///android_asset/red.png so copy to a real file.
+        val cacheFile = File(context.cacheDir, "red.png")
+        context.assets.open("red.png").use { cacheFile.writeBytes(it.readBytes()) }
+
+        val uri = cacheFile.toUri()
+        val icon = uri.toIcon()
+
+        val rendered = icon.toIntrinsicBitmap()
+        assertEquals(1, rendered.width)
+        assertEquals(1, rendered.height)
+        assertEquals(Color.RED, rendered.getPixel(0, 0))
+    }
+
+    @Test fun fromByteArray() {
+        val bytes = context.assets.open("red.png").use { it.readBytes() }
+        val icon = bytes.toIcon()
+
+        val rendered = icon.toIntrinsicBitmap()
+        assertEquals(1, rendered.width)
+        assertEquals(1, rendered.height)
+        assertEquals(Color.RED, rendered.getPixel(0, 0))
+    }
+
+    private fun Icon.toIntrinsicBitmap(): Bitmap {
+        val drawable = loadDrawable(context)
+        val bitmap = createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight)
+        drawable.setBounds(0, 0, drawable.intrinsicHeight, drawable.intrinsicHeight)
+        drawable.draw(Canvas(bitmap))
+        return bitmap
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/net/UriTest.kt b/core/ktx/src/androidTest/java/androidx/core/net/UriTest.kt
new file mode 100644
index 0000000..d25de35
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/net/UriTest.kt
@@ -0,0 +1,39 @@
+/*
+ * 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 androidx.core.net
+
+import android.net.Uri
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import java.io.File
+
+class UriTest {
+    @Test fun uriFromString() {
+        val string = "https://test.example.com/foo?bar#baz"
+        assertEquals(Uri.parse(string), string.toUri())
+    }
+
+    @Test fun uriFromFile() {
+        val file = File("/path/to/my/file")
+        assertEquals(Uri.fromFile(file), file.toUri())
+    }
+
+    @Test fun fileFromUri() {
+        val uri = Uri.parse("path/to/my/file")
+        assertEquals(File(uri.path), uri.toFile())
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/os/BundleTest.kt b/core/ktx/src/androidTest/java/androidx/core/os/BundleTest.kt
new file mode 100644
index 0000000..924e9ad
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/os/BundleTest.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2018 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.core.os
+
+import android.graphics.Rect
+import android.os.Binder
+import android.os.Bundle
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import android.util.Size
+import android.util.SizeF
+import android.view.View
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertSame
+import org.junit.Test
+import java.util.concurrent.atomic.AtomicInteger
+
+class BundleTest {
+    @Test fun bundleOfValid() {
+        val bundleValue = Bundle()
+        val charSequenceValue = "hey"
+        val parcelableValue = Rect(1, 2, 3, 4)
+        val serializableValue = AtomicInteger(1)
+
+        val bundle = bundleOf(
+            "null" to null,
+
+            "boolean" to true,
+            "byte" to 1.toByte(),
+            "char" to 'a',
+            "double" to 1.0,
+            "float" to 1f,
+            "int" to 1,
+            "long" to 1L,
+            "short" to 1.toShort(),
+
+            "bundle" to bundleValue,
+            "charSequence" to charSequenceValue,
+            "parcelable" to parcelableValue,
+
+            "booleanArray" to booleanArrayOf(),
+            "byteArray" to byteArrayOf(),
+            "charArray" to charArrayOf(),
+            "doubleArray" to doubleArrayOf(),
+            "floatArray" to floatArrayOf(),
+            "intArray" to intArrayOf(),
+            "longArray" to longArrayOf(),
+            "shortArray" to shortArrayOf(),
+
+            "parcelableArray" to arrayOf(parcelableValue),
+            "stringArray" to arrayOf("hey"),
+            "charSequenceArray" to arrayOf<CharSequence>("hey"),
+            "serializableArray" to arrayOf(serializableValue),
+
+            "serializable" to serializableValue
+        )
+
+        assertEquals(25, bundle.size())
+
+        assertNull(bundle["null"])
+
+        assertEquals(true, bundle["boolean"])
+        assertEquals(1.toByte(), bundle["byte"])
+        assertEquals('a', bundle["char"])
+        assertEquals(1.0, bundle["double"])
+        assertEquals(1f, bundle["float"])
+        assertEquals(1, bundle["int"])
+        assertEquals(1L, bundle["long"])
+        assertEquals(1.toShort(), bundle["short"])
+
+        assertSame(bundleValue, bundle["bundle"])
+        assertSame(charSequenceValue, bundle["charSequence"])
+        assertSame(parcelableValue, bundle["parcelable"])
+
+        assertArrayEquals(booleanArrayOf(), bundle["booleanArray"] as BooleanArray)
+        assertArrayEquals(byteArrayOf(), bundle["byteArray"] as ByteArray)
+        assertArrayEquals(charArrayOf(), bundle["charArray"] as CharArray)
+        assertArrayEquals(doubleArrayOf(), bundle["doubleArray"] as DoubleArray, 0.0)
+        assertArrayEquals(floatArrayOf(), bundle["floatArray"] as FloatArray, 0f)
+        assertArrayEquals(intArrayOf(), bundle["intArray"] as IntArray)
+        assertArrayEquals(longArrayOf(), bundle["longArray"] as LongArray)
+        assertArrayEquals(shortArrayOf(), bundle["shortArray"] as ShortArray)
+
+        assertThat(bundle["parcelableArray"] as Array<*>).asList().containsExactly(parcelableValue)
+        assertThat(bundle["stringArray"] as Array<*>).asList().containsExactly("hey")
+        assertThat(bundle["charSequenceArray"] as Array<*>).asList().containsExactly("hey")
+        assertThat(bundle["serializableArray"] as Array<*>).asList()
+            .containsExactly(serializableValue)
+
+        assertSame(serializableValue, bundle["serializable"])
+    }
+
+    @SdkSuppress(minSdkVersion = 18)
+    @Test fun bundleOfValidApi18() {
+        val binderValue = Binder()
+        val bundle = bundleOf("binder" to binderValue)
+        assertSame(binderValue, bundle["binder"])
+    }
+
+    @SdkSuppress(minSdkVersion = 21)
+    @Test fun bundleOfValidApi21() {
+        val sizeValue = Size(1, 1)
+        val sizeFValue = SizeF(1f, 1f)
+
+        val bundle = bundleOf(
+            "size" to sizeValue,
+            "sizeF" to sizeFValue
+        )
+
+        assertSame(sizeValue, bundle["size"])
+        assertSame(sizeFValue, bundle["sizeF"])
+    }
+
+    @Test fun bundleOfInvalid() {
+        assertThrows<IllegalArgumentException> {
+            bundleOf("nope" to View(InstrumentationRegistry.getContext()))
+        }.hasMessageThat().isEqualTo("Illegal value type android.view.View for key \"nope\"")
+
+        assertThrows<IllegalArgumentException> {
+            bundleOf("nopes" to arrayOf(View(InstrumentationRegistry.getContext())))
+        }.hasMessageThat().isEqualTo("Illegal value array type android.view.View for key \"nopes\"")
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/os/HandlerTest.kt b/core/ktx/src/androidTest/java/androidx/core/os/HandlerTest.kt
new file mode 100644
index 0000000..e0fdf4e
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/os/HandlerTest.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 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.core.os
+
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.SystemClock
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeUnit.MILLISECONDS
+import java.util.concurrent.TimeUnit.SECONDS
+
+class HandlerTest {
+    private val handlerThread = HandlerThread("handler-test")
+    private lateinit var handler: Handler
+
+    @Before fun before() {
+        handlerThread.start()
+        handler = Handler(handlerThread.looper)
+    }
+
+    @After fun after() {
+        handlerThread.quit()
+    }
+
+    @Test fun postDelayedLambdaMillis() {
+        var called = 0
+        handler.postDelayed(10) {
+            called++
+        }
+
+        handler.await(20, MILLISECONDS)
+        assertEquals(1, called)
+    }
+
+    @Test fun postDelayedLambdaMillisRemoved() {
+        var called = 0
+        val runnable = handler.postDelayed(10) {
+            called++
+        }
+        handler.removeCallbacks(runnable)
+
+        handler.await(20, MILLISECONDS)
+        assertEquals(0, called)
+    }
+
+    @Test fun postAtTimeLambda() {
+        var called = 0
+        handler.postAtTime(SystemClock.uptimeMillis() + 10) {
+            called++
+        }
+
+        handler.await(20, MILLISECONDS)
+        assertEquals(1, called)
+    }
+
+    @Test fun postAtTimeLambdaRemoved() {
+        var called = 0
+        val runnable = handler.postAtTime(SystemClock.uptimeMillis() + 10) {
+            called++
+        }
+        handler.removeCallbacks(runnable)
+
+        handler.await(20, MILLISECONDS)
+        assertEquals(0, called)
+    }
+
+    @Test fun postAtTimeLambdaWithTokenRuns() {
+        val token = Any()
+        var called = 0
+        handler.postAtTime(SystemClock.uptimeMillis() + 10, token) {
+            called++
+        }
+
+        handler.await(20, MILLISECONDS)
+        assertEquals(1, called)
+    }
+
+    @Test fun postAtTimeLambdaWithTokenCancelWithToken() {
+        // This test uses the token to cancel the runnable as it's the only way we have to verify
+        // that the Runnable was actually posted with the token.
+
+        val token = Any()
+        var called = 0
+        handler.postAtTime(SystemClock.uptimeMillis() + 10, token) {
+            called++
+        }
+        handler.removeCallbacksAndMessages(token)
+
+        handler.await(20, MILLISECONDS)
+        assertEquals(0, called)
+    }
+
+    private fun Handler.await(amount: Long, unit: TimeUnit) {
+        val latch = CountDownLatch(1)
+        postDelayed(latch::countDown, unit.toMillis(amount))
+
+        // Wait up to 1s longer than desired to account for time skew.
+        val wait = unit.toMillis(amount) + SECONDS.toMillis(1)
+        assertTrue(latch.await(wait, MILLISECONDS))
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/os/PersistableBundleTest.kt b/core/ktx/src/androidTest/java/androidx/core/os/PersistableBundleTest.kt
new file mode 100644
index 0000000..8683155
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/os/PersistableBundleTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 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.core.os
+
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import android.view.View
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 21)
+class PersistableBundleTest {
+    @Test fun persistableBundleOfValid() {
+        val bundle = persistableBundleOf(
+            "null" to null,
+
+            "double" to 1.0,
+            "int" to 1,
+            "long" to 1L,
+
+            "string" to "hey",
+
+            "doubleArray" to doubleArrayOf(),
+            "intArray" to intArrayOf(),
+            "longArray" to longArrayOf(),
+
+            "stringArray" to arrayOf("hey")
+        )
+
+        assertEquals(9, bundle.size())
+
+        assertNull(bundle["null"])
+
+        assertEquals(1.0, bundle["double"])
+        assertEquals(1, bundle["int"])
+        assertEquals(1L, bundle["long"])
+
+        assertEquals("hey", bundle["string"])
+
+        assertArrayEquals(doubleArrayOf(), bundle["doubleArray"] as DoubleArray, 0.0)
+        assertArrayEquals(intArrayOf(), bundle["intArray"] as IntArray)
+        assertArrayEquals(longArrayOf(), bundle["longArray"] as LongArray)
+
+        assertThat(bundle["stringArray"] as Array<*>).asList().containsExactly("hey")
+    }
+
+    @SdkSuppress(minSdkVersion = 22)
+    @Test fun persistableBundleOfValidApi22() {
+        val bundle = persistableBundleOf(
+            "boolean" to true,
+            "booleanArray" to booleanArrayOf()
+        )
+
+        assertEquals(true, bundle["boolean"])
+        assertArrayEquals(booleanArrayOf(), bundle["booleanArray"] as BooleanArray)
+    }
+
+    @Test fun persistableBundleOfInvalid() {
+        assertThrows<IllegalArgumentException> {
+            persistableBundleOf("nope" to View(InstrumentationRegistry.getContext()))
+        }.hasMessageThat().isEqualTo("Illegal value type android.view.View for key \"nope\"")
+
+        assertThrows<IllegalArgumentException> {
+            persistableBundleOf("nopes" to arrayOf(View(InstrumentationRegistry.getContext())))
+        }.hasMessageThat().isEqualTo("Illegal value array type android.view.View for key \"nopes\"")
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/preference/PreferenceGroupTest.kt b/core/ktx/src/androidTest/java/androidx/core/preference/PreferenceGroupTest.kt
new file mode 100644
index 0000000..0653a28
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/preference/PreferenceGroupTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2018 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.core.preference
+
+import android.preference.Preference
+import android.preference.PreferenceFragment
+import android.preference.PreferenceGroup
+import android.support.test.InstrumentationRegistry
+import android.support.test.rule.ActivityTestRule
+import androidx.core.TestPreferenceActivity
+import androidx.testutils.assertThrows
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class PreferenceGroupTest {
+
+    @JvmField
+    @Rule
+    val rule = ActivityTestRule(TestPreferenceActivity::class.java)
+    private val context = InstrumentationRegistry.getContext()
+    private lateinit var preferenceGroup: PreferenceGroup
+
+    @Before fun setup() {
+        preferenceGroup = (rule
+            .activity
+            .fragmentManager
+            .findFragmentByTag(TestPreferenceActivity.TAG) as PreferenceFragment).preferenceScreen
+    }
+
+    @Test fun get() {
+        val key = "key"
+        val preference = Preference(context)
+        preference.key = key
+        preferenceGroup.addPreference(preference)
+        assertSame(preference, preferenceGroup[key])
+        assertSame(preference, preferenceGroup[0])
+    }
+
+    @Test fun contains() {
+        val preference = Preference(context)
+        assertFalse(preference in preferenceGroup)
+        assertTrue(preference !in preferenceGroup)
+        preferenceGroup.addPreference(preference)
+        assertFalse(preference !in preferenceGroup)
+        assertTrue(preference in preferenceGroup)
+        preferenceGroup.removePreference(preference)
+        assertFalse(preference in preferenceGroup)
+        assertTrue(preference !in preferenceGroup)
+    }
+
+    @Test fun plusAssign() {
+        assertEquals(0, preferenceGroup.preferenceCount)
+
+        val preference1 = Preference(context)
+        preferenceGroup += preference1
+        assertEquals(1, preferenceGroup.preferenceCount)
+        assertSame(preference1, preferenceGroup.getPreference(0))
+
+        val preference2 = Preference(context)
+        preferenceGroup += preference2
+        assertEquals(2, preferenceGroup.preferenceCount)
+        assertSame(preference2, preferenceGroup.getPreference(1))
+    }
+
+    @Test fun minusAssign() {
+        val preference1 = Preference(context)
+        preferenceGroup.addPreference(preference1)
+        val preference2 = Preference(context)
+        preferenceGroup.addPreference(preference2)
+
+        assertEquals(2, preferenceGroup.preferenceCount)
+
+        preferenceGroup -= preference2
+        assertEquals(1, preferenceGroup.preferenceCount)
+        assertSame(preference1, preferenceGroup.getPreference(0))
+
+        preferenceGroup -= preference1
+        assertEquals(0, preferenceGroup.preferenceCount)
+    }
+
+    @Test fun size() {
+        assertEquals(0, preferenceGroup.size)
+
+        val preference = Preference(context)
+        preferenceGroup.addPreference(preference)
+        assertEquals(1, preferenceGroup.size)
+
+        preferenceGroup.removePreference(preference)
+        assertEquals(0, preferenceGroup.size)
+    }
+
+    @Test fun isEmpty() {
+        assertTrue(preferenceGroup.isEmpty())
+        preferenceGroup.addPreference(Preference(context))
+        assertFalse(preferenceGroup.isEmpty())
+    }
+
+    @Test fun isNotEmpty() {
+        assertFalse(preferenceGroup.isNotEmpty())
+        preferenceGroup.addPreference(Preference(context))
+        assertTrue(preferenceGroup.isNotEmpty())
+    }
+
+    @Test fun forEach() {
+        preferenceGroup.forEach {
+            fail("Empty preference group should not invoke lambda")
+        }
+
+        val preference1 = Preference(context).apply { key = "ASD" }
+        preferenceGroup.addPreference(preference1)
+        val preference2 = Preference(context)
+        preferenceGroup.addPreference(preference2)
+
+        val preferences = mutableListOf<Preference>()
+        preferenceGroup.forEach {
+            preferences += it
+        }
+        assertThat(preferences).containsExactly(preference1, preference2)
+    }
+
+    @Test fun forEachIndexed() {
+        preferenceGroup.forEach {
+            fail("Empty preference group should not invoke lambda")
+        }
+
+        val preference1 = Preference(context)
+        preferenceGroup.addPreference(preference1)
+        val preference2 = Preference(context)
+        preferenceGroup.addPreference(preference2)
+
+        val preferences = mutableListOf<Preference>()
+        preferenceGroup.forEachIndexed { index, preference ->
+            assertEquals(index, preferences.size)
+            preferences += preference
+        }
+        assertThat(preferences).containsExactly(preference1, preference2)
+    }
+
+    @Test fun iterator() {
+        val preference1 = Preference(context)
+        preferenceGroup.addPreference(preference1)
+        val preference2 = Preference(context)
+        preferenceGroup.addPreference(preference2)
+
+        val iterator = preferenceGroup.iterator()
+        assertTrue(iterator.hasNext())
+        assertSame(preference1, iterator.next())
+        assertTrue(iterator.hasNext())
+        assertSame(preference2, iterator.next())
+        assertFalse(iterator.hasNext())
+        assertThrows<IndexOutOfBoundsException> {
+            iterator.next()
+        }
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/text/CharSequenceTest.kt b/core/ktx/src/androidTest/java/androidx/core/text/CharSequenceTest.kt
new file mode 100644
index 0000000..0ce0a3c
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/text/CharSequenceTest.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.core.text
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class CharSequenceTest {
+    @Test fun isDigitsOnly() {
+        assertTrue("012345".isDigitsOnly())
+        assertFalse("0123 abc".isDigitsOnly())
+    }
+
+    @Test fun trimmedLength() {
+        assertEquals(6, "string    ".trimmedLength())
+        assertEquals(6, "    string".trimmedLength())
+        assertEquals(6, "string".trimmedLength())
+        assertEquals(0, "".trimmedLength())
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/text/HtmlTest.kt b/core/ktx/src/androidTest/java/androidx/core/text/HtmlTest.kt
new file mode 100644
index 0000000..0fef5ed
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/text/HtmlTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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.core.text
+
+import android.text.Html.FROM_HTML_MODE_COMPACT
+import android.text.Html.ImageGetter
+import android.text.Html.TagHandler
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class HtmlTest {
+    private val imageGetter = ImageGetter { null }
+    private val tagHandler = TagHandler { _, _, _, _ -> }
+
+    @Test fun parseAsHtml() {
+        val parsed = "<b>Hi</b> © > <".parseAsHtml().toString()
+        assertEquals("Hi \u00a9 > <", parsed)
+    }
+
+    @Test fun parseAsHtmlFlags() {
+        val parsed = "<b>Hi</b> © > <".parseAsHtml(FROM_HTML_MODE_COMPACT).toString()
+        assertEquals("Hi \u00a9 > <", parsed)
+    }
+
+    @Test fun parseAsHtmlImageGetterTagHandler() {
+        val parsed = "<b>Hi</b> © > <"
+            .parseAsHtml(FROM_HTML_MODE_COMPACT, imageGetter, tagHandler)
+            .toString()
+        assertEquals("Hi \u00a9 > <", parsed)
+    }
+
+    @Test fun parseAsHtmlFlagsImageGetterTagHandler() {
+        val parsed = "<b>Hi</b> © > <"
+            .parseAsHtml(imageGetter = imageGetter, tagHandler = tagHandler)
+            .toString()
+        assertEquals("Hi \u00a9 > <", parsed)
+    }
+
+    @Test fun convertToHtml() {
+        val html = buildSpannedString {
+            bold {
+                append("Hi")
+            }
+        }.toHtml()
+        assertTrue(html, html.contains("<b>Hi</b>"))
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/text/SpannableStringBuilderTest.kt b/core/ktx/src/androidTest/java/androidx/core/text/SpannableStringBuilderTest.kt
new file mode 100644
index 0000000..5d67e83
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/text/SpannableStringBuilderTest.kt
@@ -0,0 +1,267 @@
+/*
+ * 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 androidx.core.text
+
+import android.graphics.Color.RED
+import android.graphics.Color.YELLOW
+import android.graphics.Typeface.BOLD
+import android.graphics.Typeface.ITALIC
+import android.text.SpannedString
+import android.text.style.BackgroundColorSpan
+import android.text.style.BulletSpan
+import android.text.style.ForegroundColorSpan
+import android.text.style.RelativeSizeSpan
+import android.text.style.StrikethroughSpan
+import android.text.style.StyleSpan
+import android.text.style.SubscriptSpan
+import android.text.style.SuperscriptSpan
+import android.text.style.UnderlineSpan
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertSame
+import org.junit.Test
+
+class SpannableStringBuilderTest {
+    @Test fun builder() {
+        val result: SpannedString = buildSpannedString {
+            append("Hello,")
+            append(" World")
+        }
+        assertEquals("Hello, World", result.toString())
+    }
+
+    @Test fun builderInSpan() {
+        val bold = StyleSpan(BOLD)
+        val result: SpannedString = buildSpannedString {
+            append("Hello, ")
+            inSpans(bold) {
+                append("World")
+            }
+        }
+        assertEquals("Hello, World", result.toString())
+
+        val spans = result.getSpans<Any>()
+        assertEquals(1, spans.size)
+
+        val boldSpan = spans.filterIsInstance<StyleSpan>().single()
+        assertSame(bold, boldSpan)
+        assertEquals(7, result.getSpanStart(bold))
+        assertEquals(12, result.getSpanEnd(bold))
+    }
+
+    @Test fun builderBold() {
+        val result: SpannedString = buildSpannedString {
+            append("Hello, ")
+            bold {
+                append("World")
+            }
+        }
+        assertEquals("Hello, World", result.toString())
+
+        val spans = result.getSpans<Any>()
+        assertEquals(1, spans.size)
+
+        val bold = spans.filterIsInstance<StyleSpan>().single()
+        assertEquals(BOLD, bold.style)
+        assertEquals(7, result.getSpanStart(bold))
+        assertEquals(12, result.getSpanEnd(bold))
+    }
+
+    @Test fun builderItalic() {
+        val result: SpannedString = buildSpannedString {
+            append("Hello, ")
+            italic {
+                append("World")
+            }
+        }
+        assertEquals("Hello, World", result.toString())
+
+        val spans = result.getSpans<Any>()
+        assertEquals(1, spans.size)
+
+        val italic = spans.filterIsInstance<StyleSpan>().single()
+        assertEquals(ITALIC, italic.style)
+        assertEquals(7, result.getSpanStart(italic))
+        assertEquals(12, result.getSpanEnd(italic))
+    }
+
+    @Test fun builderUnderline() {
+        val result: SpannedString = buildSpannedString {
+            append("Hello, ")
+            underline {
+                append("World")
+            }
+        }
+        assertEquals("Hello, World", result.toString())
+
+        val spans = result.getSpans<Any>()
+        assertEquals(1, spans.size)
+
+        val underline = spans.filterIsInstance<UnderlineSpan>().single()
+        assertEquals(7, result.getSpanStart(underline))
+        assertEquals(12, result.getSpanEnd(underline))
+    }
+
+    @Test fun builderColor() {
+        val result: SpannedString = buildSpannedString {
+            append("Hello, ")
+            color(RED) {
+                append("World")
+            }
+        }
+        assertEquals("Hello, World", result.toString())
+
+        val spans = result.getSpans<Any>()
+        assertEquals(1, spans.size)
+
+        val color = spans.filterIsInstance<ForegroundColorSpan>().single()
+        assertEquals(RED, color.foregroundColor)
+        assertEquals(7, result.getSpanStart(color))
+        assertEquals(12, result.getSpanEnd(color))
+    }
+
+    @Test fun builderBackgroundColor() {
+        val result: SpannedString = buildSpannedString {
+            append("Hello, ")
+            backgroundColor(RED) {
+                append("World")
+            }
+        }
+        assertEquals("Hello, World", result.toString())
+
+        val spans = result.getSpans<Any>()
+        assertEquals(1, spans.size)
+
+        val color = spans.filterIsInstance<BackgroundColorSpan>().single()
+        assertEquals(RED, color.backgroundColor)
+        assertEquals(7, result.getSpanStart(color))
+        assertEquals(12, result.getSpanEnd(color))
+    }
+
+    @Test fun builderStrikeThrough() {
+        val result: SpannedString = buildSpannedString {
+            append("Hello, ")
+            strikeThrough {
+                append("World")
+            }
+        }
+        assertEquals("Hello, World", result.toString())
+
+        val spans = result.getSpans<Any>()
+        assertEquals(1, spans.size)
+
+        val strikeThrough = spans.filterIsInstance<StrikethroughSpan>().single()
+        assertEquals(7, result.getSpanStart(strikeThrough))
+        assertEquals(12, result.getSpanEnd(strikeThrough))
+    }
+
+    @Test fun builderScale() {
+        val result: SpannedString = buildSpannedString {
+            append("Hello, ")
+            scale(2f) {
+                append("World")
+            }
+        }
+        assertEquals("Hello, World", result.toString())
+
+        val spans = result.getSpans<Any>()
+        assertEquals(1, spans.size)
+
+        val scale = spans.filterIsInstance<RelativeSizeSpan>().single()
+        assertEquals(2f, scale.sizeChange)
+        assertEquals(7, result.getSpanStart(scale))
+        assertEquals(12, result.getSpanEnd(scale))
+    }
+
+    @Test fun nested() {
+        val result: SpannedString = buildSpannedString {
+            color(RED) {
+                append('H')
+                inSpans(SubscriptSpan()) {
+                    append('e')
+                }
+                append('l')
+                inSpans(SuperscriptSpan()) {
+                    append('l')
+                }
+                append('o')
+            }
+            append(", ")
+            backgroundColor(YELLOW) {
+                append('W')
+                underline {
+                    append('o')
+                    bold {
+                        append('r')
+                    }
+                    append('l')
+                }
+                append('d')
+            }
+        }
+        assertEquals("Hello, World", result.toString())
+
+        val spans = result.getSpans<Any>()
+        assertEquals(6, spans.size)
+
+        val color = spans.filterIsInstance<ForegroundColorSpan>().single()
+        assertEquals(RED, color.foregroundColor)
+        assertEquals(0, result.getSpanStart(color))
+        assertEquals(5, result.getSpanEnd(color))
+
+        val subscript = spans.filterIsInstance<SubscriptSpan>().single()
+        assertEquals(1, result.getSpanStart(subscript))
+        assertEquals(2, result.getSpanEnd(subscript))
+
+        val superscript = spans.filterIsInstance<SuperscriptSpan>().single()
+        assertEquals(3, result.getSpanStart(superscript))
+        assertEquals(4, result.getSpanEnd(superscript))
+
+        val backgroundColor = spans.filterIsInstance<BackgroundColorSpan>().single()
+        assertEquals(YELLOW, backgroundColor.backgroundColor)
+        assertEquals(7, result.getSpanStart(backgroundColor))
+        assertEquals(12, result.getSpanEnd(backgroundColor))
+
+        val underline = spans.filterIsInstance<UnderlineSpan>().single()
+        assertEquals(8, result.getSpanStart(underline))
+        assertEquals(11, result.getSpanEnd(underline))
+
+        val bold = spans.filterIsInstance<StyleSpan>().single()
+        assertEquals(BOLD, bold.style)
+        assertEquals(9, result.getSpanStart(bold))
+        assertEquals(10, result.getSpanEnd(bold))
+    }
+
+    @Test fun multipleSpans() {
+        val result: SpannedString = buildSpannedString {
+            append("Hello, ")
+            inSpans(BulletSpan(), UnderlineSpan()) {
+                append("World")
+            }
+        }
+        assertEquals("Hello, World", result.toString())
+
+        val spans = result.getSpans<Any>()
+        assertEquals(2, spans.size)
+
+        val bullet = spans.filterIsInstance<BulletSpan>().single()
+        assertEquals(7, result.getSpanStart(bullet))
+        assertEquals(12, result.getSpanEnd(bullet))
+        val underline = spans.filterIsInstance<UnderlineSpan>().single()
+        assertEquals(7, result.getSpanStart(underline))
+        assertEquals(12, result.getSpanEnd(underline))
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/text/SpannableStringTest.kt b/core/ktx/src/androidTest/java/androidx/core/text/SpannableStringTest.kt
new file mode 100644
index 0000000..5fe3c5d
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/text/SpannableStringTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 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.core.text
+
+import android.graphics.Typeface.BOLD
+import android.text.SpannableString
+import android.text.style.StyleSpan
+import android.text.style.UnderlineSpan
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class SpannableStringTest {
+
+    @Test fun toSpannableString() = assertTrue("Hello, World".toSpannable() is SpannableString)
+
+    @Test fun plusAssign() {
+        val s = "Hello, World".toSpannable()
+
+        val bold = StyleSpan(BOLD)
+        s += bold
+        assertEquals(0, s.getSpanStart(bold))
+        assertEquals(s.length, s.getSpanEnd(bold))
+    }
+
+    @Test fun minusAssign() {
+        val s = "Hello, World".toSpannable()
+        val bold = StyleSpan(BOLD)
+        s += bold
+        assertTrue(s.getSpans<Any>().isNotEmpty())
+        s -= bold
+        assertTrue(s.getSpans<Any>().isEmpty())
+    }
+
+    @Test fun clearSpans() {
+        val s = "Hello, World".toSpannable()
+        s += StyleSpan(BOLD)
+        s += UnderlineSpan()
+        assertTrue(s.getSpans<Any>().isNotEmpty())
+        s.clearSpans()
+        assertTrue(s.getSpans<Any>().isEmpty())
+    }
+
+    @Test fun setIndices() {
+        val s = "Hello, World".toSpannable()
+        s[0, 5] = StyleSpan(BOLD)
+        s[7, 12] = UnderlineSpan()
+
+        val spans = s.getSpans<Any>()
+
+        val bold = spans.filterIsInstance<StyleSpan>().single()
+        assertEquals(0, s.getSpanStart(bold))
+        assertEquals(5, s.getSpanEnd(bold))
+
+        val underline = spans.filterIsInstance<UnderlineSpan>().single()
+        assertEquals(7, s.getSpanStart(underline))
+        assertEquals(12, s.getSpanEnd(underline))
+    }
+
+    @Test fun setRange() {
+        val s = "Hello, World".toSpannable()
+        s[0..5] = StyleSpan(BOLD)
+        s[7..12] = UnderlineSpan()
+
+        val spans = s.getSpans<Any>()
+
+        val bold = spans.filterIsInstance<StyleSpan>().single()
+        assertEquals(0, s.getSpanStart(bold))
+        assertEquals(5, s.getSpanEnd(bold))
+
+        val underline = spans.filterIsInstance<UnderlineSpan>().single()
+        assertEquals(7, s.getSpanStart(underline))
+        assertEquals(12, s.getSpanEnd(underline))
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/text/SpannedStringTest.kt b/core/ktx/src/androidTest/java/androidx/core/text/SpannedStringTest.kt
new file mode 100644
index 0000000..cb8930a
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/text/SpannedStringTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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.core.text
+
+import android.graphics.Typeface.BOLD
+import android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE
+import android.text.SpannedString
+import android.text.style.StyleSpan
+import android.text.style.UnderlineSpan
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class SpannedStringTest {
+
+    @Test fun toSpanned() = assertTrue("Hello, World".toSpanned() is SpannedString)
+
+    @Test fun getSpans() {
+        val bold = StyleSpan(BOLD)
+        val underline = UnderlineSpan()
+
+        val s = "Hello, World".toSpannable()
+        s.setSpan(bold, 0, 5, SPAN_INCLUSIVE_EXCLUSIVE)
+        s.setSpan(underline, 7, 12, SPAN_INCLUSIVE_EXCLUSIVE)
+
+        assertSame(bold, s.getSpans<StyleSpan>().single())
+        assertSame(underline, s.getSpans<UnderlineSpan>().single())
+        assertEquals(s.getSpans<Any>().size, 2)
+
+        assertSame(bold, s.getSpans<Any>(0, 6).single())
+        assertSame(underline, s.getSpans<Any>(6, 12).single())
+    }
+}
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt b/core/ktx/src/androidTest/java/androidx/core/text/StringTest.kt
similarity index 69%
copy from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
copy to core/ktx/src/androidTest/java/androidx/core/text/StringTest.kt
index 67f6d84..2812799 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
+++ b/core/ktx/src/androidTest/java/androidx/core/text/StringTest.kt
@@ -11,14 +11,16 @@
  * 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.
  */
 
-package android.support.tools.jetifier.core
+package androidx.core.text
 
-import java.io.File
+import org.junit.Assert.assertEquals
+import org.junit.Test
 
-/**
- * Represents a source file ([from]) to be mapped to a target file ([to]).
- */
-data class FileMapping(val from: File, val to: File)
\ No newline at end of file
+class StringTest {
+    @Test fun htmlEncode() {
+        assertEquals("&lt;&gt; &amp; &quot; &#39;", """<> & " '""".htmlEncode())
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/transition/TransitionTest.kt b/core/ktx/src/androidTest/java/androidx/core/transition/TransitionTest.kt
new file mode 100644
index 0000000..c3a2d22
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/transition/TransitionTest.kt
@@ -0,0 +1,95 @@
+/*
+ * 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 androidx.core.transition
+
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import android.support.test.rule.ActivityTestRule
+import android.transition.Fade
+import android.transition.Transition
+import android.transition.TransitionManager
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.core.TestActivity
+import androidx.core.ktx.test.R
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicBoolean
+
+@SdkSuppress(minSdkVersion = 19)
+class TransitionTest {
+    @JvmField @Rule val rule = ActivityTestRule<TestActivity>(TestActivity::class.java)
+
+    private lateinit var transition: Transition
+
+    @Before fun setup() {
+        transition = Fade().setDuration(50)
+    }
+
+    @Test fun testDoOnStart() {
+        val called = AtomicBoolean()
+        transition.doOnStart {
+            called.set(true)
+        }
+
+        startTransition(transition)
+        assertTrue(called.get())
+    }
+
+    @Test fun testDoOnEnd() {
+        val called = AtomicBoolean()
+        transition.doOnEnd {
+            called.set(true)
+        }
+
+        val latch = CountDownLatch(1)
+        transition.addListener(object : Transition.TransitionListener {
+            override fun onTransitionEnd(transition: Transition?) {
+                latch.countDown()
+            }
+
+            override fun onTransitionResume(transition: Transition?) = Unit
+            override fun onTransitionPause(transition: Transition?) = Unit
+            override fun onTransitionCancel(transition: Transition?) = Unit
+            override fun onTransitionStart(transition: Transition?) = Unit
+        })
+
+        startTransition(transition)
+
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+        assertTrue(called.get())
+    }
+
+    private fun startTransition(t: Transition) {
+        rule.runOnUiThread {
+            val sceneRoot = rule.activity.findViewById<ViewGroup>(R.id.root)
+            val view = rule.activity.findViewById<ImageView>(R.id.image_view)
+
+            TransitionManager.beginDelayedTransition(sceneRoot, t)
+
+            view.visibility = View.INVISIBLE
+        }
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/ArrayMapTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/ArrayMapTest.kt
new file mode 100644
index 0000000..5eddef2
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/ArrayMapTest.kt
@@ -0,0 +1,40 @@
+/*
+ * 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 19)
+class ArrayMapTest {
+    @Test fun empty() {
+        val map = arrayMapOf<String, String>()
+        assertEquals(0, map.size)
+    }
+
+    @Test fun nonEmpty() {
+        val map = arrayMapOf("foo" to "bar", "bar" to "baz")
+        assertThat(map).containsExactly("foo", "bar", "bar", "baz")
+    }
+
+    @Test fun duplicateKeyKeepsLast() {
+        val map = arrayMapOf("foo" to "bar", "foo" to "baz")
+        assertThat(map).containsExactly("foo", "baz")
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/ArraySetTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/ArraySetTest.kt
new file mode 100644
index 0000000..934f18c
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/ArraySetTest.kt
@@ -0,0 +1,35 @@
+/*
+ * 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 23)
+class ArraySetTest {
+    @Test fun empty() {
+        val set = arraySetOf<String>()
+        assertEquals(0, set.size)
+    }
+
+    @Test fun nonEmpty() {
+        val set = arraySetOf("foo", "bar", "baz")
+        assertThat(set).containsExactly("foo", "bar", "baz")
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/AtomicFileTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/AtomicFileTest.kt
new file mode 100644
index 0000000..59fc8d9
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/AtomicFileTest.kt
@@ -0,0 +1,109 @@
+/*
+ * 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import android.util.AtomicFile
+import androidx.testutils.assertThrows
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import java.io.IOException
+
+@SdkSuppress(minSdkVersion = 17)
+class AtomicFileTest {
+    @get:Rule val temporaryFolder = TemporaryFolder()
+
+    private lateinit var file: AtomicFile
+
+    @Before fun before() {
+        file = AtomicFile(temporaryFolder.newFile())
+    }
+
+    @Test fun tryWriteSuccess() {
+        file.tryWrite {
+            it.write(byteArrayOf(0, 1, 2))
+        }
+        val bytes = file.openRead().use { it.readBytes() }
+        assertArrayEquals(byteArrayOf(0, 1, 2), bytes)
+    }
+
+    @Test fun tryWriteFail() {
+        val os = file.startWrite()
+        os.write(byteArrayOf(0, 1, 2))
+        file.finishWrite(os)
+
+        val failure = IOException("Broken!")
+        assertThrows<IOException> {
+            file.tryWrite {
+                it.write(byteArrayOf(3, 4, 5))
+                throw failure
+            }
+        }.isSameAs(failure)
+
+        val bytes = file.openRead().use { it.readBytes() }
+        assertArrayEquals(byteArrayOf(0, 1, 2), bytes)
+    }
+
+    @Test fun writeBytes() {
+        file.writeBytes(byteArrayOf(0, 1, 2))
+
+        val bytes = file.openRead().use { it.readBytes() }
+        assertArrayEquals(byteArrayOf(0, 1, 2), bytes)
+    }
+
+    @Test fun writeText() {
+        file.writeText("Hey")
+
+        val bytes = file.openRead().use { it.readBytes() }
+        assertArrayEquals(byteArrayOf(72, 101, 121), bytes)
+    }
+
+    @Test fun writeTextCharset() {
+        file.writeText("Hey", charset = Charsets.UTF_16LE)
+
+        val bytes = file.openRead().use { it.readBytes() }
+        assertArrayEquals(byteArrayOf(72, 0, 101, 0, 121, 0), bytes)
+    }
+
+    @Test fun readBytes() {
+        val os = file.startWrite()
+        os.write(byteArrayOf(0, 1, 2))
+        file.finishWrite(os)
+
+        assertArrayEquals(byteArrayOf(0, 1, 2), file.readBytes())
+    }
+
+    @Test fun readText() {
+        val os = file.startWrite()
+        os.write(byteArrayOf(72, 101, 121))
+        file.finishWrite(os)
+
+        assertEquals("Hey", file.readText())
+    }
+
+    @Test fun readTextCharset() {
+        val os = file.startWrite()
+        os.write(byteArrayOf(72, 0, 101, 0, 121, 0))
+        file.finishWrite(os)
+
+        assertEquals("Hey", file.readText(charset = Charsets.UTF_16LE))
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/HalfTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/HalfTest.kt
new file mode 100644
index 0000000..b79e064
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/HalfTest.kt
@@ -0,0 +1,33 @@
+/*
+ * 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import android.util.Half
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 26)
+class HalfTest {
+    @Test fun shortToHalf() = assertEquals(Half(1.toShort()), 1.toShort().toHalf())
+
+    @Test fun floatToHalf() = assertEquals(Half(1f), 1f.toHalf())
+
+    @Test fun doubleToHalf() = assertEquals(Half(1.0), 1.0.toHalf())
+
+    @Test fun stringToHalf() = assertEquals(Half("1"), "1".toHalf())
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/LocaleTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/LocaleTest.kt
new file mode 100644
index 0000000..0a345f0
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/LocaleTest.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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.core.util
+
+import android.view.View
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import java.util.Locale
+
+class LocaleTest {
+    @Test fun layoutDirectionWithLTR() {
+        val ltrLocale = Locale.Builder().setLanguage("en").build()
+        assertEquals(View.LAYOUT_DIRECTION_LTR, ltrLocale.layoutDirection)
+    }
+
+    @Test fun layoutDirectionWithRTL() {
+        val rtlLocale = Locale.Builder().setLanguage("ar").build()
+        assertEquals(View.LAYOUT_DIRECTION_RTL, rtlLocale.layoutDirection)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/LongSparseArrayTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/LongSparseArrayTest.kt
new file mode 100644
index 0000000..c2289e6
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/LongSparseArrayTest.kt
@@ -0,0 +1,200 @@
+/*
+ * 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import android.util.LongSparseArray
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 16)
+class LongSparseArrayTest {
+    @Test fun sizeProperty() {
+        val array = LongSparseArray<String>()
+        assertEquals(0, array.size)
+        array.put(1L, "one")
+        assertEquals(1, array.size)
+    }
+
+    @Test fun containsOperator() {
+        val array = LongSparseArray<String>()
+        assertFalse(1L in array)
+        array.put(1L, "one")
+        assertTrue(1L in array)
+    }
+
+    @Test fun containsOperatorWithValue() {
+        val array = LongSparseArray<String>()
+
+        array.put(1L, "one")
+        assertFalse(2L in array)
+
+        array.put(2L, "two")
+        assertTrue(2L in array)
+    }
+
+    @Test fun setOperator() {
+        val array = LongSparseArray<String>()
+        array[1L] = "one"
+        assertEquals("one", array.get(1L))
+    }
+
+    @Test fun plusOperator() {
+        val first = LongSparseArray<String>().apply { put(1L, "one") }
+        val second = LongSparseArray<String>().apply { put(2L, "two") }
+        val combined = first + second
+        assertEquals(2, combined.size())
+        assertEquals(1L, combined.keyAt(0))
+        assertEquals("one", combined.valueAt(0))
+        assertEquals(2L, combined.keyAt(1))
+        assertEquals("two", combined.valueAt(1))
+    }
+
+    @Test fun containsKey() {
+        val array = LongSparseArray<String>()
+        assertFalse(array.containsKey(1L))
+        array.put(1L, "one")
+        assertTrue(array.containsKey(1L))
+    }
+
+    @Test fun containsKeyWithValue() {
+        val array = LongSparseArray<String>()
+
+        array.put(1L, "one")
+        assertFalse(array.containsKey(2L))
+
+        array.put(2L, "one")
+        assertTrue(array.containsKey(2L))
+    }
+
+    @Test fun containsValue() {
+        val array = LongSparseArray<String>()
+        assertFalse(array.containsValue("one"))
+        array.put(1L, "one")
+        assertTrue(array.containsValue("one"))
+    }
+
+    @Test fun getOrDefault() {
+        val array = LongSparseArray<Any>()
+        val default = Any()
+        assertSame(default, array.getOrDefault(1L, default))
+        array.put(1L, "one")
+        assertEquals("one", array.getOrDefault(1L, default))
+    }
+
+    @Test fun getOrElse() {
+        val array = LongSparseArray<Any>()
+        val default = Any()
+        assertSame(default, array.getOrElse(1L) { default })
+        array.put(1L, "one")
+        assertEquals("one", array.getOrElse(1L) { fail() })
+    }
+
+    @Test fun isEmpty() {
+        val array = LongSparseArray<String>()
+        assertTrue(array.isEmpty())
+        array.put(1L, "one")
+        assertFalse(array.isEmpty())
+    }
+
+    @Test fun isNotEmpty() {
+        val array = LongSparseArray<String>()
+        assertFalse(array.isNotEmpty())
+        array.put(1L, "one")
+        assertTrue(array.isNotEmpty())
+    }
+
+    @Test fun removeValue() {
+        val array = LongSparseArray<String>()
+        array.put(1L, "one")
+        assertFalse(array.remove(0L, "one"))
+        assertEquals(1, array.size())
+        assertFalse(array.remove(1L, "two"))
+        assertEquals(1, array.size())
+        assertTrue(array.remove(1L, "one"))
+        assertEquals(0, array.size())
+    }
+
+    @Test fun putAll() {
+        val dest = LongSparseArray<String>()
+        val source = LongSparseArray<String>()
+        source.put(1L, "one")
+
+        assertEquals(0, dest.size())
+        dest.putAll(source)
+        assertEquals(1, dest.size())
+    }
+
+    @Test fun forEach() {
+        val array = LongSparseArray<String>()
+        array.forEach { _, _ -> fail() }
+
+        array.put(1L, "one")
+        array.put(2L, "two")
+        array.put(6L, "six")
+
+        val keys = mutableListOf<Long>()
+        val values = mutableListOf<String>()
+        array.forEach { key, value ->
+            keys.add(key)
+            values.add(value)
+        }
+        assertThat(keys).containsExactly(1L, 2L, 6L)
+        assertThat(values).containsExactly("one", "two", "six")
+    }
+
+    @Test fun keyIterator() {
+        val array = LongSparseArray<String>()
+        assertFalse(array.keyIterator().hasNext())
+
+        array.put(1L, "one")
+        array.put(2L, "two")
+        array.put(6L, "six")
+
+        val iterator = array.keyIterator()
+        assertTrue(iterator.hasNext())
+        assertEquals(1L, iterator.nextLong())
+        assertTrue(iterator.hasNext())
+        assertEquals(2L, iterator.nextLong())
+        assertTrue(iterator.hasNext())
+        assertEquals(6L, iterator.nextLong())
+        assertFalse(iterator.hasNext())
+    }
+
+    @Test fun valueIterator() {
+        val array = LongSparseArray<String>()
+        assertFalse(array.valueIterator().hasNext())
+
+        array.put(1L, "one")
+        array.put(2L, "two")
+        array.put(6L, "six")
+
+        val iterator = array.valueIterator()
+        assertTrue(iterator.hasNext())
+        assertEquals("one", iterator.next())
+        assertTrue(iterator.hasNext())
+        assertEquals("two", iterator.next())
+        assertTrue(iterator.hasNext())
+        assertEquals("six", iterator.next())
+        assertFalse(iterator.hasNext())
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/LruCacheTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/LruCacheTest.kt
new file mode 100644
index 0000000..198e13d
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/LruCacheTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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.core.util
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class LruCacheTest {
+    private data class TestData(val x: String = "bla")
+
+    @Test fun size() {
+        val cache = lruCache<String, TestData>(200, { k, (x) -> k.length * x.length })
+        cache.put("long", TestData())
+        assertEquals(cache.size(), 12)
+    }
+
+    @Test fun create() {
+        val cache = lruCache<String, TestData>(200, create = { key -> TestData("$key foo") })
+        assertEquals(cache.get("kung"), TestData("kung foo"))
+    }
+
+    @Test fun onEntryRemoved() {
+        var wasCalled = false
+
+        val cache = lruCache<String, TestData>(200, onEntryRemoved = { _, _, _, _ ->
+            wasCalled = true
+        })
+        val initial = TestData()
+        cache.put("a", initial)
+        cache.remove("a")
+        assertTrue(wasCalled)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/PairTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/PairTest.kt
new file mode 100644
index 0000000..e3fac39
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/PairTest.kt
@@ -0,0 +1,50 @@
+/*
+ * 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 androidx.core.util
+
+import android.util.Pair
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertSame
+import org.junit.Test
+
+class PairTest {
+    @Test fun destructuringNonNull() {
+        val pair = Pair("one", "two")
+        val (first: String, second: String) = pair
+        assertSame(pair.first, first)
+        assertSame(pair.second, second)
+    }
+
+    @Test fun destructuringNullable() {
+        val pair = Pair("one", "two")
+        val (first: String?, second: String?) = pair
+        assertSame(pair.first, first)
+        assertSame(pair.second, second)
+    }
+
+    @Test fun toKotlin() {
+        val android = Pair("one", "two")
+        val kotlin = android.toKotlinPair()
+        assertEquals(android.first to android.second, kotlin)
+    }
+
+    @Test fun toAndroid() {
+        val kotlin = kotlin.Pair("one", "two")
+        val android = kotlin.toAndroidPair()
+        assertEquals(Pair(kotlin.first, kotlin.second), android)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/RangeTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/RangeTest.kt
new file mode 100644
index 0000000..ec5d238
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/RangeTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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.core.util
+
+import android.support.test.filters.SdkSuppress
+import android.util.Range
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 21)
+class RangeTest {
+    @Test fun infixFactory() {
+        val range: Range<String> = "a" rangeTo "c"
+        assertEquals("a", range.lower)
+        assertEquals("c", range.upper)
+    }
+
+    @Test fun extendValue() {
+        val range = ("a" rangeTo "c") + "e"
+        assertEquals("a", range.lower)
+        assertEquals("e", range.upper)
+    }
+
+    @Test fun extendRange() {
+        val range = ("a" rangeTo "c") + ("e" rangeTo "g")
+        assertEquals("a", range.lower)
+        assertEquals("g", range.upper)
+    }
+
+    @Test fun intersection() {
+        val range = ("a" rangeTo "e") and ("c" rangeTo "g")
+        assertEquals("c", range.lower)
+        assertEquals("e", range.upper)
+    }
+
+    @Test fun kotlinToAndroid() {
+        val range: Range<Int> = (1..3).toRange()
+        assertEquals(1, range.lower)
+        assertEquals(3, range.upper)
+    }
+
+    @Test fun androidToKotlin() {
+        val range: ClosedRange<String> = Range<String>("a", "c").toClosedRange()
+        assertEquals("a", range.start)
+        assertEquals("c", range.endInclusive)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/SizeTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/SizeTest.kt
new file mode 100644
index 0000000..b38c747
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/SizeTest.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 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.core.util
+
+import android.support.test.filters.SdkSuppress
+import android.util.Size
+import android.util.SizeF
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 21)
+class SizeTest {
+    @Test fun destructuringSize() {
+        val (w, h) = Size(320, 240)
+        assertEquals(320, w)
+        assertEquals(240, h)
+    }
+
+    @Test fun destructuringSizeF() {
+        val (w, h) = SizeF(1920.0f, 1080.0f)
+        assertEquals(1920.0f, w)
+        assertEquals(1080.0f, h)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/SparseArrayTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/SparseArrayTest.kt
new file mode 100644
index 0000000..4a49214
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/SparseArrayTest.kt
@@ -0,0 +1,188 @@
+/*
+ * 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 androidx.core.util
+
+import android.util.SparseArray
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class SparseArrayTest {
+    @Test fun sizeProperty() {
+        val array = SparseArray<String>()
+        assertEquals(0, array.size)
+        array.put(1, "one")
+        assertEquals(1, array.size)
+    }
+
+    @Test fun containsOperator() {
+        val array = SparseArray<String>()
+        assertFalse(1 in array)
+        array.put(1, "one")
+        assertTrue(1 in array)
+    }
+
+    @Test fun containsOperatorWithItem() {
+        val array = SparseArray<String>()
+
+        array.put(1, "one")
+        assertFalse(2 in array)
+
+        array.put(2, "two")
+        assertTrue(2 in array)
+    }
+
+    @Test fun setOperator() {
+        val array = SparseArray<String>()
+        array[1] = "one"
+        assertEquals("one", array.get(1))
+    }
+
+    @Test fun plusOperator() {
+        val first = SparseArray<String>().apply { put(1, "one") }
+        val second = SparseArray<String>().apply { put(2, "two") }
+        val combined = first + second
+        assertEquals(2, combined.size())
+        assertEquals(1, combined.keyAt(0))
+        assertEquals("one", combined.valueAt(0))
+        assertEquals(2, combined.keyAt(1))
+        assertEquals("two", combined.valueAt(1))
+    }
+
+    @Test fun containsKey() {
+        val array = SparseArray<String>()
+        assertFalse(array.containsKey(1))
+        array.put(1, "one")
+        assertTrue(array.containsKey(1))
+    }
+
+    @Test fun containsValue() {
+        val array = SparseArray<String>()
+        assertFalse(array.containsValue("one"))
+        array.put(1, "one")
+        assertTrue(array.containsValue("one"))
+    }
+
+    @Test fun getOrDefault() {
+        val array = SparseArray<Any>()
+        val default = Any()
+        assertSame(default, array.getOrDefault(1, default))
+        array.put(1, "one")
+        assertEquals("one", array.getOrDefault(1, default))
+    }
+
+    @Test fun getOrElse() {
+        val array = SparseArray<Any>()
+        val default = Any()
+        assertSame(default, array.getOrElse(1) { default })
+        array.put(1, "one")
+        assertEquals("one", array.getOrElse(1) { fail() })
+    }
+
+    @Test fun isEmpty() {
+        val array = SparseArray<String>()
+        assertTrue(array.isEmpty())
+        array.put(1, "one")
+        assertFalse(array.isEmpty())
+    }
+
+    @Test fun isNotEmpty() {
+        val array = SparseArray<String>()
+        assertFalse(array.isNotEmpty())
+        array.put(1, "one")
+        assertTrue(array.isNotEmpty())
+    }
+
+    @Test fun removeValue() {
+        val array = SparseArray<String>()
+        array.put(1, "one")
+        assertFalse(array.remove(0, "one"))
+        assertEquals(1, array.size())
+        assertFalse(array.remove(1, "two"))
+        assertEquals(1, array.size())
+        assertTrue(array.remove(1, "one"))
+        assertEquals(0, array.size())
+    }
+
+    @Test fun putAll() {
+        val dest = SparseArray<String>()
+        val source = SparseArray<String>()
+        source.put(1, "one")
+
+        assertEquals(0, dest.size())
+        dest.putAll(source)
+        assertEquals(1, dest.size())
+    }
+
+    @Test fun forEach() {
+        val array = SparseArray<String>()
+        array.forEach { _, _ -> fail() }
+
+        array.put(1, "one")
+        array.put(2, "two")
+        array.put(6, "six")
+
+        val keys = mutableListOf<Int>()
+        val values = mutableListOf<String>()
+        array.forEach { key, value ->
+            keys.add(key)
+            values.add(value)
+        }
+        assertThat(keys).containsExactly(1, 2, 6)
+        assertThat(values).containsExactly("one", "two", "six")
+    }
+
+    @Test fun keyIterator() {
+        val array = SparseArray<String>()
+        assertFalse(array.keyIterator().hasNext())
+
+        array.put(1, "one")
+        array.put(2, "two")
+        array.put(6, "six")
+
+        val iterator = array.keyIterator()
+        assertTrue(iterator.hasNext())
+        assertEquals(1, iterator.nextInt())
+        assertTrue(iterator.hasNext())
+        assertEquals(2, iterator.nextInt())
+        assertTrue(iterator.hasNext())
+        assertEquals(6, iterator.nextInt())
+        assertFalse(iterator.hasNext())
+    }
+
+    @Test fun valueIterator() {
+        val array = SparseArray<String>()
+        assertFalse(array.valueIterator().hasNext())
+
+        array.put(1, "one")
+        array.put(2, "two")
+        array.put(6, "six")
+
+        val iterator = array.valueIterator()
+        assertTrue(iterator.hasNext())
+        assertEquals("one", iterator.next())
+        assertTrue(iterator.hasNext())
+        assertEquals("two", iterator.next())
+        assertTrue(iterator.hasNext())
+        assertEquals("six", iterator.next())
+        assertFalse(iterator.hasNext())
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/SparseBooleanArrayTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/SparseBooleanArrayTest.kt
new file mode 100644
index 0000000..1d67493
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/SparseBooleanArrayTest.kt
@@ -0,0 +1,195 @@
+/*
+ * 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 androidx.core.util
+
+import android.util.SparseBooleanArray
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class SparseBooleanArrayTest {
+    @Test fun sizeProperty() {
+        val array = SparseBooleanArray()
+        assertEquals(0, array.size)
+        array.put(1, true)
+        assertEquals(1, array.size)
+    }
+
+    @Test fun containsOperator() {
+        val array = SparseBooleanArray()
+        assertFalse(1 in array)
+        array.put(1, true)
+        assertTrue(1 in array)
+    }
+
+    @Test fun containsOperatorWithValue() {
+        val array = SparseBooleanArray()
+
+        array.put(1, true)
+        assertFalse(2 in array)
+
+        array.put(2, true)
+        assertTrue(2 in array)
+    }
+
+    @Test fun setOperator() {
+        val array = SparseBooleanArray()
+        array[1] = true
+        assertTrue(array.get(1))
+    }
+
+    @Test fun plusOperator() {
+        val first = SparseBooleanArray().apply { put(1, true) }
+        val second = SparseBooleanArray().apply { put(2, false) }
+        val combined = first + second
+        assertEquals(2, combined.size())
+        assertEquals(1, combined.keyAt(0))
+        assertTrue(combined.valueAt(0))
+        assertEquals(2, combined.keyAt(1))
+        assertFalse(combined.valueAt(1))
+    }
+
+    @Test fun containsKey() {
+        val array = SparseBooleanArray()
+        assertFalse(array.containsKey(1))
+        array.put(1, true)
+        assertTrue(array.containsKey(1))
+    }
+
+    @Test fun containsKeyWithValue() {
+        val array = SparseBooleanArray()
+
+        array.put(1, true)
+        assertFalse(array.containsKey(2))
+
+        array.put(2, true)
+        assertTrue(array.containsKey(2))
+    }
+
+    @Test fun containsValue() {
+        val array = SparseBooleanArray()
+        assertFalse(array.containsValue(true))
+        array.put(1, true)
+        assertTrue(array.containsValue(true))
+    }
+
+    @Test fun getOrDefault() {
+        val array = SparseBooleanArray()
+        assertFalse(array.getOrDefault(1, false))
+        array.put(1, true)
+        assertTrue(array.getOrDefault(1, false))
+    }
+
+    @Test fun getOrElse() {
+        val array = SparseBooleanArray()
+        assertFalse(array.getOrElse(1) { false })
+        array.put(1, true)
+        assertTrue(array.getOrElse(1) { fail() })
+    }
+
+    @Test fun isEmpty() {
+        val array = SparseBooleanArray()
+        assertTrue(array.isEmpty())
+        array.put(1, true)
+        assertFalse(array.isEmpty())
+    }
+
+    @Test fun isNotEmpty() {
+        val array = SparseBooleanArray()
+        assertFalse(array.isNotEmpty())
+        array.put(1, true)
+        assertTrue(array.isNotEmpty())
+    }
+
+    @Test fun removeValue() {
+        val array = SparseBooleanArray()
+        array.put(1, true)
+        assertFalse(array.remove(0, true))
+        assertEquals(1, array.size())
+        assertFalse(array.remove(1, false))
+        assertEquals(1, array.size())
+        assertTrue(array.remove(1, true))
+        assertEquals(0, array.size())
+    }
+
+    @Test fun putAll() {
+        val dest = SparseBooleanArray()
+        val source = SparseBooleanArray()
+        source.put(1, true)
+
+        assertEquals(0, dest.size())
+        dest.putAll(source)
+        assertEquals(1, dest.size())
+    }
+
+    @Test fun forEach() {
+        val array = SparseBooleanArray()
+        array.forEach { _, _ -> fail() }
+
+        array.put(1, true)
+        array.put(2, false)
+        array.put(6, true)
+
+        val keys = mutableListOf<Int>()
+        val values = mutableListOf<Boolean>()
+        array.forEach { key, value ->
+            keys.add(key)
+            values.add(value)
+        }
+        assertThat(keys).containsExactly(1, 2, 6)
+        assertThat(values).containsExactly(true, false, true)
+    }
+
+    @Test fun keyIterator() {
+        val array = SparseBooleanArray()
+        assertFalse(array.keyIterator().hasNext())
+
+        array.put(1, true)
+        array.put(2, false)
+        array.put(6, true)
+
+        val iterator = array.keyIterator()
+        assertTrue(iterator.hasNext())
+        assertEquals(1, iterator.nextInt())
+        assertTrue(iterator.hasNext())
+        assertEquals(2, iterator.nextInt())
+        assertTrue(iterator.hasNext())
+        assertEquals(6, iterator.nextInt())
+        assertFalse(iterator.hasNext())
+    }
+
+    @Test fun valueIterator() {
+        val array = SparseBooleanArray()
+        assertFalse(array.valueIterator().hasNext())
+
+        array.put(1, true)
+        array.put(2, false)
+        array.put(6, true)
+
+        val iterator = array.valueIterator()
+        assertTrue(iterator.hasNext())
+        assertTrue(iterator.nextBoolean())
+        assertTrue(iterator.hasNext())
+        assertFalse(iterator.nextBoolean())
+        assertTrue(iterator.hasNext())
+        assertTrue(iterator.nextBoolean())
+        assertFalse(iterator.hasNext())
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/SparseIntArrayTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/SparseIntArrayTest.kt
new file mode 100644
index 0000000..40794c1
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/SparseIntArrayTest.kt
@@ -0,0 +1,195 @@
+/*
+ * 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 androidx.core.util
+
+import android.util.SparseIntArray
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class SparseIntArrayTest {
+    @Test fun sizeProperty() {
+        val array = SparseIntArray()
+        assertEquals(0, array.size)
+        array.put(1, 11)
+        assertEquals(1, array.size)
+    }
+
+    @Test fun containsOperator() {
+        val array = SparseIntArray()
+        assertFalse(1 in array)
+        array.put(1, 11)
+        assertTrue(1 in array)
+    }
+
+    @Test fun containsOperatorWithValue() {
+        val array = SparseIntArray()
+
+        array.put(1, 11)
+        assertFalse(2 in array)
+
+        array.put(2, 22)
+        assertTrue(2 in array)
+    }
+
+    @Test fun setOperator() {
+        val array = SparseIntArray()
+        array[1] = 11
+        assertEquals(11, array.get(1))
+    }
+
+    @Test fun plusOperator() {
+        val first = SparseIntArray().apply { put(1, 11) }
+        val second = SparseIntArray().apply { put(2, 22) }
+        val combined = first + second
+        assertEquals(2, combined.size())
+        assertEquals(1, combined.keyAt(0))
+        assertEquals(11, combined.valueAt(0))
+        assertEquals(2, combined.keyAt(1))
+        assertEquals(22, combined.valueAt(1))
+    }
+
+    @Test fun containsKey() {
+        val array = SparseIntArray()
+        assertFalse(array.containsKey(1))
+        array.put(1, 11)
+        assertTrue(array.containsKey(1))
+    }
+
+    @Test fun containsKeyWithValue() {
+        val array = SparseIntArray()
+
+        array.put(1, 11)
+        assertFalse(array.containsKey(2))
+
+        array.put(2, 22)
+        assertTrue(array.containsKey(2))
+    }
+
+    @Test fun containsValue() {
+        val array = SparseIntArray()
+        assertFalse(array.containsValue(11))
+        array.put(1, 11)
+        assertTrue(array.containsValue(11))
+    }
+
+    @Test fun getOrDefault() {
+        val array = SparseIntArray()
+        assertEquals(22, array.getOrDefault(1, 22))
+        array.put(1, 11)
+        assertEquals(11, array.getOrDefault(1, 22))
+    }
+
+    @Test fun getOrElse() {
+        val array = SparseIntArray()
+        assertEquals(22, array.getOrElse(1) { 22 })
+        array.put(1, 11)
+        assertEquals(11, array.getOrElse(1) { fail() })
+    }
+
+    @Test fun isEmpty() {
+        val array = SparseIntArray()
+        assertTrue(array.isEmpty())
+        array.put(1, 11)
+        assertFalse(array.isEmpty())
+    }
+
+    @Test fun isNotEmpty() {
+        val array = SparseIntArray()
+        assertFalse(array.isNotEmpty())
+        array.put(1, 11)
+        assertTrue(array.isNotEmpty())
+    }
+
+    @Test fun removeValue() {
+        val array = SparseIntArray()
+        array.put(1, 11)
+        assertFalse(array.remove(0, 11))
+        assertEquals(1, array.size())
+        assertFalse(array.remove(1, 22))
+        assertEquals(1, array.size())
+        assertTrue(array.remove(1, 11))
+        assertEquals(0, array.size())
+    }
+
+    @Test fun putAll() {
+        val dest = SparseIntArray()
+        val source = SparseIntArray()
+        source.put(1, 11)
+
+        assertEquals(0, dest.size())
+        dest.putAll(source)
+        assertEquals(1, dest.size())
+    }
+
+    @Test fun forEach() {
+        val array = SparseIntArray()
+        array.forEach { _, _ -> fail() }
+
+        array.put(1, 11)
+        array.put(2, 22)
+        array.put(6, 66)
+
+        val keys = mutableListOf<Int>()
+        val values = mutableListOf<Int>()
+        array.forEach { key, value ->
+            keys.add(key)
+            values.add(value)
+        }
+        assertThat(keys).containsExactly(1, 2, 6)
+        assertThat(values).containsExactly(11, 22, 66)
+    }
+
+    @Test fun keyIterator() {
+        val array = SparseIntArray()
+        assertFalse(array.keyIterator().hasNext())
+
+        array.put(1, 11)
+        array.put(2, 22)
+        array.put(6, 66)
+
+        val iterator = array.keyIterator()
+        assertTrue(iterator.hasNext())
+        assertEquals(1, iterator.nextInt())
+        assertTrue(iterator.hasNext())
+        assertEquals(2, iterator.nextInt())
+        assertTrue(iterator.hasNext())
+        assertEquals(6, iterator.nextInt())
+        assertFalse(iterator.hasNext())
+    }
+
+    @Test fun valueIterator() {
+        val array = SparseIntArray()
+        assertFalse(array.valueIterator().hasNext())
+
+        array.put(1, 11)
+        array.put(2, 22)
+        array.put(6, 66)
+
+        val iterator = array.valueIterator()
+        assertTrue(iterator.hasNext())
+        assertEquals(11, iterator.nextInt())
+        assertTrue(iterator.hasNext())
+        assertEquals(22, iterator.nextInt())
+        assertTrue(iterator.hasNext())
+        assertEquals(66, iterator.nextInt())
+        assertFalse(iterator.hasNext())
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/util/SparseLongArrayTest.kt b/core/ktx/src/androidTest/java/androidx/core/util/SparseLongArrayTest.kt
new file mode 100644
index 0000000..c65c2ad
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/util/SparseLongArrayTest.kt
@@ -0,0 +1,197 @@
+/*
+ * 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 androidx.core.util
+
+import android.support.test.filters.SdkSuppress
+import android.util.SparseLongArray
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 18)
+class SparseLongArrayTest {
+    @Test fun sizeProperty() {
+        val array = SparseLongArray()
+        assertEquals(0, array.size)
+        array.put(1, 11L)
+        assertEquals(1, array.size)
+    }
+
+    @Test fun containsOperator() {
+        val array = SparseLongArray()
+        assertFalse(1 in array)
+        array.put(1, 11L)
+        assertTrue(1 in array)
+    }
+
+    @Test fun containsOperatorWithValue() {
+        val array = SparseLongArray()
+
+        array.put(1, 11L)
+        assertFalse(2 in array)
+
+        array.put(2, 22L)
+        assertTrue(2 in array)
+    }
+
+    @Test fun setOperator() {
+        val array = SparseLongArray()
+        array[1] = 11L
+        assertEquals(11L, array.get(1))
+    }
+
+    @Test fun plusOperator() {
+        val first = SparseLongArray().apply { put(1, 11L) }
+        val second = SparseLongArray().apply { put(2, 22L) }
+        val combined = first + second
+        assertEquals(2, combined.size())
+        assertEquals(1, combined.keyAt(0))
+        assertEquals(11L, combined.valueAt(0))
+        assertEquals(2, combined.keyAt(1))
+        assertEquals(22L, combined.valueAt(1))
+    }
+
+    @Test fun containsKey() {
+        val array = SparseLongArray()
+        assertFalse(array.containsKey(1))
+        array.put(1, 11L)
+        assertTrue(array.containsKey(1))
+    }
+
+    @Test fun containsKeyWithValue() {
+        val array = SparseLongArray()
+
+        array.put(1, 11L)
+        assertFalse(array.containsKey(2))
+
+        array.put(2, 22L)
+        assertTrue(array.containsKey(2))
+    }
+
+    @Test fun containsValue() {
+        val array = SparseLongArray()
+        assertFalse(array.containsValue(11L))
+        array.put(1, 11L)
+        assertTrue(array.containsValue(11L))
+    }
+
+    @Test fun getOrDefault() {
+        val array = SparseLongArray()
+        assertEquals(22L, array.getOrDefault(1, 22L))
+        array.put(1, 11L)
+        assertEquals(11L, array.getOrDefault(1, 22L))
+    }
+
+    @Test fun getOrElse() {
+        val array = SparseLongArray()
+        assertEquals(22L, array.getOrElse(1) { 22L })
+        array.put(1, 11L)
+        assertEquals(11L, array.getOrElse(1) { fail() })
+    }
+
+    @Test fun isEmpty() {
+        val array = SparseLongArray()
+        assertTrue(array.isEmpty())
+        array.put(1, 11L)
+        assertFalse(array.isEmpty())
+    }
+
+    @Test fun isNotEmpty() {
+        val array = SparseLongArray()
+        assertFalse(array.isNotEmpty())
+        array.put(1, 11L)
+        assertTrue(array.isNotEmpty())
+    }
+
+    @Test fun removeValue() {
+        val array = SparseLongArray()
+        array.put(1, 11L)
+        assertFalse(array.remove(0, 11L))
+        assertEquals(1, array.size())
+        assertFalse(array.remove(1, 22L))
+        assertEquals(1, array.size())
+        assertTrue(array.remove(1, 11L))
+        assertEquals(0, array.size())
+    }
+
+    @Test fun putAll() {
+        val dest = SparseLongArray()
+        val source = SparseLongArray()
+        source.put(1, 11L)
+
+        assertEquals(0, dest.size())
+        dest.putAll(source)
+        assertEquals(1, dest.size())
+    }
+
+    @Test fun forEach() {
+        val array = SparseLongArray()
+        array.forEach { _, _ -> fail() }
+
+        array.put(1, 11L)
+        array.put(2, 22L)
+        array.put(6, 66L)
+
+        val keys = mutableListOf<Int>()
+        val values = mutableListOf<Long>()
+        array.forEach { key, value ->
+            keys.add(key)
+            values.add(value)
+        }
+        assertThat(keys).containsExactly(1, 2, 6)
+        assertThat(values).containsExactly(11L, 22L, 66L)
+    }
+
+    @Test fun keyIterator() {
+        val array = SparseLongArray()
+        assertFalse(array.keyIterator().hasNext())
+
+        array.put(1, 11L)
+        array.put(2, 22L)
+        array.put(6, 66L)
+
+        val iterator = array.keyIterator()
+        assertTrue(iterator.hasNext())
+        assertEquals(1, iterator.nextInt())
+        assertTrue(iterator.hasNext())
+        assertEquals(2, iterator.nextInt())
+        assertTrue(iterator.hasNext())
+        assertEquals(6, iterator.nextInt())
+        assertFalse(iterator.hasNext())
+    }
+
+    @Test fun valueIterator() {
+        val array = SparseLongArray()
+        assertFalse(array.valueIterator().hasNext())
+
+        array.put(1, 11L)
+        array.put(2, 22L)
+        array.put(6, 66L)
+
+        val iterator = array.valueIterator()
+        assertTrue(iterator.hasNext())
+        assertEquals(11, iterator.nextLong())
+        assertTrue(iterator.hasNext())
+        assertEquals(22, iterator.nextLong())
+        assertTrue(iterator.hasNext())
+        assertEquals(66, iterator.nextLong())
+        assertFalse(iterator.hasNext())
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/view/AccessibilityAnnouncementCapturingView.kt b/core/ktx/src/androidTest/java/androidx/core/view/AccessibilityAnnouncementCapturingView.kt
new file mode 100644
index 0000000..81996b8
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/view/AccessibilityAnnouncementCapturingView.kt
@@ -0,0 +1,14 @@
+package androidx.core.view
+
+import android.content.Context
+import android.view.View
+
+class AccessibilityAnnouncementCapturingView(context: Context?) : View(context) {
+
+    var announcement: CharSequence? = null
+
+    override fun announceForAccessibility(text: CharSequence?) {
+        super.announceForAccessibility(text)
+        announcement = text
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/view/MenuTest.kt b/core/ktx/src/androidTest/java/androidx/core/view/MenuTest.kt
new file mode 100644
index 0000000..18e1b56
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/view/MenuTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 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.core.view
+
+import android.support.test.InstrumentationRegistry
+import android.view.Menu.NONE
+import android.view.MenuItem
+import android.widget.Toolbar
+import androidx.testutils.assertThrows
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class MenuTest {
+    private val menu = Toolbar(InstrumentationRegistry.getContext()).menu
+
+    @Test fun get() {
+        val item = menu.add("")
+        assertSame(item, menu[0])
+    }
+
+    @Test fun contains() {
+        val item1 = menu.add("")
+        assertTrue(item1 in menu)
+
+        val item2 = menu.add("")
+        assertTrue(item2 in menu)
+    }
+
+    @Test fun size() {
+        assertEquals(0, menu.size)
+
+        menu.add("")
+        assertEquals(1, menu.size)
+
+        menu.add(NONE, 123, NONE, "")
+        assertEquals(2, menu.size)
+
+        menu.removeItem(123)
+        assertEquals(1, menu.size)
+    }
+
+    @Test fun isEmpty() {
+        assertTrue(menu.isEmpty())
+        menu.add("")
+        assertFalse(menu.isEmpty())
+    }
+
+    @Test fun isNotEmpty() {
+        assertFalse(menu.isNotEmpty())
+        menu.add("")
+        assertTrue(menu.isNotEmpty())
+    }
+
+    @Test fun forEach() {
+        menu.forEach {
+            fail("Empty menu should not invoke lambda")
+        }
+
+        val item1 = menu.add("")
+        val item2 = menu.add("")
+
+        val items = mutableListOf<MenuItem>()
+        menu.forEach {
+            items += it
+        }
+        assertThat(items).containsExactly(item1, item2)
+    }
+
+    @Test fun forEachIndexed() {
+        menu.forEachIndexed { _, _ ->
+            fail("Empty menu should not invoke lambda")
+        }
+
+        val item1 = menu.add("")
+        val item2 = menu.add("")
+
+        val items = mutableListOf<MenuItem>()
+        menu.forEachIndexed { index, item ->
+            assertEquals(index, items.size)
+            items += item
+        }
+        assertThat(items).containsExactly(item1, item2)
+    }
+
+    @Test fun iterator() {
+        val item1 = menu.add("")
+        val item2 = menu.add("")
+
+        val iterator = menu.iterator()
+        assertTrue(iterator.hasNext())
+        assertSame(item1, iterator.next())
+        assertTrue(iterator.hasNext())
+        assertSame(item2, iterator.next())
+        assertFalse(iterator.hasNext())
+        assertThrows<IndexOutOfBoundsException> {
+            iterator.next()
+        }
+    }
+
+    @Test fun iteratorRemoving() {
+        val item1 = menu.add("")
+        val item2 = menu.add("")
+
+        val iterator = menu.iterator()
+
+        assertSame(item1, iterator.next())
+        iterator.remove()
+        assertFalse(item1 in menu)
+        assertEquals(1, menu.size())
+
+        assertSame(item2, iterator.next())
+        iterator.remove()
+        assertFalse(item2 in menu)
+        assertEquals(0, menu.size())
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/view/ViewGroupTest.kt b/core/ktx/src/androidTest/java/androidx/core/view/ViewGroupTest.kt
new file mode 100644
index 0000000..7ec791d
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/view/ViewGroupTest.kt
@@ -0,0 +1,264 @@
+/*
+ * 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 androidx.core.view
+
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SdkSuppress
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import androidx.testutils.assertThrows
+import androidx.testutils.fail
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class ViewGroupTest {
+    private val context = InstrumentationRegistry.getContext()
+    private val viewGroup = LinearLayout(context)
+
+    @Test fun get() {
+        val view1 = View(context)
+        viewGroup.addView(view1)
+        val view2 = View(context)
+        viewGroup.addView(view2)
+
+        assertSame(view1, viewGroup[0])
+        assertSame(view2, viewGroup[1])
+
+        assertThrows<IndexOutOfBoundsException> {
+            viewGroup[-1]
+        }.hasMessageThat().isEqualTo("Index: -1, Size: 2")
+
+        assertThrows<IndexOutOfBoundsException> {
+            viewGroup[2]
+        }.hasMessageThat().isEqualTo("Index: 2, Size: 2")
+    }
+
+    @Test fun contains() {
+        val view1 = View(context)
+        viewGroup.addView(view1)
+        assertTrue(view1 in viewGroup)
+        assertFalse(view1 !in viewGroup)
+
+        val view2 = View(context)
+        assertFalse(view2 in viewGroup)
+        assertTrue(view2 !in viewGroup)
+    }
+
+    @Test fun plusAssign() {
+        assertEquals(0, viewGroup.childCount)
+
+        val view1 = View(context)
+        viewGroup += view1
+        assertEquals(1, viewGroup.childCount)
+        assertSame(view1, viewGroup.getChildAt(0))
+
+        val view2 = View(context)
+        viewGroup += view2
+        assertEquals(2, viewGroup.childCount)
+        assertSame(view2, viewGroup.getChildAt(1))
+    }
+
+    @Test fun minusAssign() {
+        val view1 = View(context)
+        viewGroup.addView(view1)
+        val view2 = View(context)
+        viewGroup.addView(view2)
+
+        assertEquals(2, viewGroup.childCount)
+
+        viewGroup -= view2
+        assertEquals(1, viewGroup.childCount)
+        assertSame(view1, viewGroup.getChildAt(0))
+
+        viewGroup -= view1
+        assertEquals(0, viewGroup.childCount)
+    }
+
+    @Test fun size() {
+        assertEquals(0, viewGroup.size)
+
+        viewGroup.addView(View(context))
+        assertEquals(1, viewGroup.size)
+
+        viewGroup.addView(View(context))
+        assertEquals(2, viewGroup.size)
+
+        viewGroup.removeViewAt(0)
+        assertEquals(1, viewGroup.size)
+    }
+
+    @Test fun isEmpty() {
+        assertTrue(viewGroup.isEmpty())
+        viewGroup.addView(View(context))
+        assertFalse(viewGroup.isEmpty())
+    }
+
+    @Test fun isNotEmpty() {
+        assertFalse(viewGroup.isNotEmpty())
+        viewGroup.addView(View(context))
+        assertTrue(viewGroup.isNotEmpty())
+    }
+
+    @Test fun forEach() {
+        viewGroup.forEach {
+            fail("Empty view group should not invoke lambda")
+        }
+
+        val view1 = View(context)
+        viewGroup.addView(view1)
+        val view2 = View(context)
+        viewGroup.addView(view2)
+
+        val views = mutableListOf<View>()
+        viewGroup.forEach {
+            views += it
+        }
+        assertThat(views).containsExactly(view1, view2)
+    }
+
+    @Test fun forEachIndexed() {
+        viewGroup.forEachIndexed { _, _ ->
+            fail("Empty view group should not invoke lambda")
+        }
+
+        val view1 = View(context)
+        viewGroup.addView(view1)
+        val view2 = View(context)
+        viewGroup.addView(view2)
+
+        val views = mutableListOf<View>()
+        viewGroup.forEachIndexed { index, view ->
+            assertEquals(index, views.size)
+            views += view
+        }
+        assertThat(views).containsExactly(view1, view2)
+    }
+
+    @Test fun iterator() {
+        val view1 = View(context)
+        viewGroup.addView(view1)
+        val view2 = View(context)
+        viewGroup.addView(view2)
+
+        val iterator = viewGroup.iterator()
+        assertTrue(iterator.hasNext())
+        assertSame(view1, iterator.next())
+        assertTrue(iterator.hasNext())
+        assertSame(view2, iterator.next())
+        assertFalse(iterator.hasNext())
+        assertThrows<IndexOutOfBoundsException> {
+            iterator.next()
+        }
+    }
+
+    @Test fun iteratorRemoving() {
+        val view1 = View(context)
+        viewGroup.addView(view1)
+        val view2 = View(context)
+        viewGroup.addView(view2)
+
+        val iterator = viewGroup.iterator()
+
+        assertSame(view1, iterator.next())
+        iterator.remove()
+        assertFalse(view1 in viewGroup)
+        assertEquals(1, viewGroup.childCount)
+
+        assertSame(view2, iterator.next())
+        iterator.remove()
+        assertFalse(view2 in viewGroup)
+        assertEquals(0, viewGroup.childCount)
+    }
+
+    @Test fun iteratorForEach() {
+        val views = listOf(View(context), View(context))
+        views.forEach(viewGroup::addView)
+
+        var index = 0
+        for (view in viewGroup) {
+            assertSame(views[index++], view)
+        }
+    }
+
+    @Test fun children() {
+        val views = listOf(View(context), View(context), View(context))
+        views.forEach { viewGroup.addView(it) }
+
+        viewGroup.children.forEachIndexed { index, child ->
+            assertSame(views[index], child)
+        }
+    }
+
+    @Test fun setMargins() {
+        val layoutParams = ViewGroup.MarginLayoutParams(100, 200)
+        layoutParams.setMargins(42)
+        assertEquals(42, layoutParams.leftMargin)
+        assertEquals(42, layoutParams.topMargin)
+        assertEquals(42, layoutParams.rightMargin)
+        assertEquals(42, layoutParams.bottomMargin)
+    }
+
+    @Test fun updateMargins() {
+        val layoutParams = ViewGroup.MarginLayoutParams(100, 200)
+        layoutParams.updateMargins(top = 10, right = 20)
+        assertEquals(0, layoutParams.leftMargin)
+        assertEquals(10, layoutParams.topMargin)
+        assertEquals(20, layoutParams.rightMargin)
+        assertEquals(0, layoutParams.bottomMargin)
+    }
+
+    @Test fun updateMarginsNoOp() {
+        val layoutParams = ViewGroup.MarginLayoutParams(100, 200)
+        layoutParams.setMargins(10, 20, 30, 40)
+        layoutParams.updateMargins()
+        assertEquals(10, layoutParams.leftMargin)
+        assertEquals(20, layoutParams.topMargin)
+        assertEquals(30, layoutParams.rightMargin)
+        assertEquals(40, layoutParams.bottomMargin)
+    }
+
+    @SdkSuppress(minSdkVersion = 17)
+    @Test fun updateMarginsRelative() {
+        val layoutParams = ViewGroup.MarginLayoutParams(100, 200)
+        layoutParams.updateMarginsRelative(start = 10, end = 20)
+        assertEquals(0, layoutParams.leftMargin)
+        assertEquals(0, layoutParams.topMargin)
+        assertEquals(0, layoutParams.rightMargin)
+        assertEquals(0, layoutParams.bottomMargin)
+        assertEquals(10, layoutParams.marginStart)
+        assertEquals(20, layoutParams.marginEnd)
+        assertTrue(layoutParams.isMarginRelative)
+    }
+
+    @SdkSuppress(minSdkVersion = 17)
+    @Test fun updateMarginsRelativeNoOp() {
+        val layoutParams = ViewGroup.MarginLayoutParams(100, 200)
+        layoutParams.setMargins(10, 20, 30, 40)
+        layoutParams.updateMarginsRelative()
+        assertEquals(10, layoutParams.leftMargin)
+        assertEquals(20, layoutParams.topMargin)
+        assertEquals(30, layoutParams.rightMargin)
+        assertEquals(40, layoutParams.bottomMargin)
+        assertEquals(10, layoutParams.marginStart)
+        assertEquals(30, layoutParams.marginEnd)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/view/ViewTest.kt b/core/ktx/src/androidTest/java/androidx/core/view/ViewTest.kt
new file mode 100644
index 0000000..0b7cfa2
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/view/ViewTest.kt
@@ -0,0 +1,248 @@
+/*
+ * 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 androidx.core.view
+
+import android.graphics.Bitmap
+import android.support.test.InstrumentationRegistry
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import android.widget.RelativeLayout
+import androidx.core.ktx.test.R
+import androidx.testutils.assertThrows
+import androidx.testutils.fail
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class ViewTest {
+    private val context = InstrumentationRegistry.getContext()
+    private val view = View(context)
+
+    @Test
+    fun doOnNextLayout() {
+        var calls = 0
+        view.doOnNextLayout {
+            calls++
+        }
+        view.layout(0, 0, 10, 10)
+        assertEquals(1, calls)
+
+        // Now layout again and make sure that the listener was removed
+        view.layout(0, 0, 10, 10)
+        assertEquals(1, calls)
+    }
+
+    @Test
+    fun doOnLayoutBeforeLayout() {
+        var called = false
+        view.doOnLayout {
+            called = true
+        }
+        view.layout(0, 0, 10, 10)
+        assertTrue(called)
+    }
+
+    @Test
+    fun doOnLayoutAfterLayout() {
+        view.layout(0, 0, 10, 10)
+
+        var called = false
+        view.doOnLayout {
+            called = true
+        }
+        assertTrue(called)
+    }
+
+    @Test
+    fun doOnLayoutWhileLayoutRequested() {
+        // First layout the view
+        view.layout(0, 0, 10, 10)
+        // Then later a layout is requested
+        view.requestLayout()
+
+        var called = false
+        view.doOnLayout {
+            called = true
+        }
+
+        // Assert that we haven't been called while the layout pass is pending
+        assertFalse(called)
+
+        // Now layout the view and assert that we're called
+        view.layout(0, 0, 20, 20)
+        assertTrue(called)
+    }
+
+    @Test
+    fun doOnPreDraw() {
+        var calls = 0
+        view.doOnPreDraw {
+            calls++
+        }
+        view.viewTreeObserver.dispatchOnPreDraw()
+        assertEquals(1, calls)
+
+        // Now dispatch again to make sure that the listener was removed
+        view.viewTreeObserver.dispatchOnPreDraw()
+        assertEquals(1, calls)
+    }
+
+    @Test
+    fun setPadding() {
+        view.setPadding(42)
+        assertEquals(42, view.paddingLeft)
+        assertEquals(42, view.paddingTop)
+        assertEquals(42, view.paddingRight)
+        assertEquals(42, view.paddingBottom)
+    }
+
+    @Test
+    fun updatePadding() {
+        view.updatePadding(top = 10, right = 20)
+        assertEquals(0, view.paddingLeft)
+        assertEquals(10, view.paddingTop)
+        assertEquals(20, view.paddingRight)
+        assertEquals(0, view.paddingBottom)
+    }
+
+    @Test
+    fun updatePaddingNoOp() {
+        view.setPadding(10, 20, 30, 40)
+        view.updatePadding()
+        assertEquals(10, view.paddingLeft)
+        assertEquals(20, view.paddingTop)
+        assertEquals(30, view.paddingRight)
+        assertEquals(40, view.paddingBottom)
+    }
+
+    @Test
+    fun updatePaddingRelative() {
+        view.updatePaddingRelative(start = 10, end = 20)
+        assertEquals(10, view.paddingStart)
+        assertEquals(0, view.paddingTop)
+        assertEquals(20, view.paddingEnd)
+        assertEquals(0, view.paddingBottom)
+    }
+
+    @Test
+    fun updatePaddingRelativeNoOp() {
+        view.setPaddingRelative(10, 20, 30, 40)
+        view.updatePaddingRelative()
+        assertEquals(10, view.paddingStart)
+        assertEquals(20, view.paddingTop)
+        assertEquals(30, view.paddingEnd)
+        assertEquals(40, view.paddingBottom)
+    }
+
+    @Test
+    fun toBitmapBeforeLayout() {
+        assertThrows<IllegalStateException> {
+            view.toBitmap()
+        }
+    }
+
+    @Test
+    fun toBitmap() {
+        view.layout(0, 0, 100, 100)
+        val bitmap = view.toBitmap()
+
+        assertEquals(100, bitmap.width)
+        assertEquals(100, bitmap.height)
+    }
+
+    @Test
+    fun toBitmapCustomConfig() {
+        view.layout(0, 0, 100, 100)
+        val bitmap = view.toBitmap(Bitmap.Config.RGB_565)
+
+        assertSame(Bitmap.Config.RGB_565, bitmap.config)
+    }
+
+    @Test fun isVisible() {
+        view.isVisible = true
+        assertTrue(view.isVisible)
+        assertEquals(View.VISIBLE, view.visibility)
+
+        view.isVisible = false
+        assertFalse(view.isVisible)
+        assertEquals(View.GONE, view.visibility)
+    }
+
+    @Test fun isInvisible() {
+        view.isInvisible = true
+        assertTrue(view.isInvisible)
+        assertEquals(View.INVISIBLE, view.visibility)
+
+        view.isInvisible = false
+        assertFalse(view.isInvisible)
+        assertEquals(View.VISIBLE, view.visibility)
+    }
+
+    @Test fun isGone() {
+        view.isGone = true
+        assertTrue(view.isGone)
+        assertEquals(View.GONE, view.visibility)
+
+        view.isGone = false
+        assertFalse(view.isGone)
+        assertEquals(View.VISIBLE, view.visibility)
+    }
+
+    @Test fun updateLayoutParams() {
+        view.layoutParams = ViewGroup.LayoutParams(0, 0)
+        view.updateLayoutParams {
+            assertSame(view.layoutParams, this)
+
+            width = 500
+            height = 1000
+        }
+
+        assertEquals(500, view.layoutParams.width)
+        assertEquals(1000, view.layoutParams.height)
+    }
+
+    @Test fun updateLayoutParamsAsType() {
+        view.layoutParams = LinearLayout.LayoutParams(0, 0)
+        view.updateLayoutParams<LinearLayout.LayoutParams> {
+            assertSame(view.layoutParams, this)
+
+            weight = 2f
+        }
+
+        assertEquals(2f, (view.layoutParams as LinearLayout.LayoutParams).weight)
+    }
+
+    @Test fun updateLayoutParamsWrongType() {
+        assertThrows<ClassCastException> {
+            view.updateLayoutParams<RelativeLayout.LayoutParams> {
+                fail()
+            }
+        }
+    }
+
+    @Test fun announceForAccessibility() {
+        val testView = AccessibilityAnnouncementCapturingView(context)
+
+        testView.announceForAccessibility(R.string.text)
+
+        val resolvedText = context.getText(R.string.text)
+        assertEquals(testView.announcement, resolvedText)
+    }
+}
diff --git a/core/ktx/src/androidTest/java/androidx/core/widget/ToastTest.kt b/core/ktx/src/androidTest/java/androidx/core/widget/ToastTest.kt
new file mode 100644
index 0000000..f8e9931
--- /dev/null
+++ b/core/ktx/src/androidTest/java/androidx/core/widget/ToastTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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.core.widget
+
+import android.support.test.InstrumentationRegistry
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import android.widget.Toast
+import androidx.core.ktx.test.R
+import androidx.core.view.forEach
+import androidx.core.view.isVisible
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class ToastTest {
+    private val context = InstrumentationRegistry.getContext()
+    private val instrumentation = InstrumentationRegistry.getInstrumentation()
+
+    @Test
+    fun createToastWithTextShort() = instrumentation.runOnMainSync {
+        val toast = context.toast("Short Toast")
+        assertEquals(Toast.LENGTH_SHORT, toast.duration)
+        assertTrue(containsText(toast.view, "Short Toast"))
+        assertTrue(toast.view.isVisible)
+    }
+
+    @Test
+    fun createToastWithTextLong() = instrumentation.runOnMainSync {
+        val toast = context.toast("Long Toast", Toast.LENGTH_LONG)
+        assertEquals(Toast.LENGTH_LONG, toast.duration)
+        assertTrue(containsText(toast.view, "Long Toast"))
+        assertTrue(toast.view.isVisible)
+    }
+
+    @Test
+    fun createToastWithResIdShort() = instrumentation.runOnMainSync {
+        val toast = context.toast(R.string.text)
+        assertEquals(Toast.LENGTH_SHORT, toast.duration)
+        assertTrue(containsText(toast.view, context.getString(R.string.text)))
+        assertTrue(toast.view.isVisible)
+    }
+
+    @Test
+    fun createToastWithResIdLong() = instrumentation.runOnMainSync {
+        val toast = context.toast(R.string.text, Toast.LENGTH_LONG)
+        assertEquals(Toast.LENGTH_LONG, toast.duration)
+        assertTrue(containsText(toast.view, context.getString(R.string.text)))
+        assertTrue(toast.view.isVisible)
+    }
+
+    private fun containsText(view: View, text: String): Boolean {
+        if (view is TextView && view.text == text) {
+            return true
+        }
+        if (view is ViewGroup) {
+            view.forEach {
+                if (containsText(it, text)) {
+                    return true
+                }
+            }
+        }
+        return false
+    }
+}
diff --git a/core/ktx/src/androidTest/res/drawable/box.xml b/core/ktx/src/androidTest/res/drawable/box.xml
new file mode 100644
index 0000000..3915878
--- /dev/null
+++ b/core/ktx/src/androidTest/res/drawable/box.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <size android:height="10px" android:width="10px"/>
+    <solid android:color="#fff"/>
+</shape>
diff --git a/core/ktx/src/androidTest/res/font/inconsolata_regular.ttf b/core/ktx/src/androidTest/res/font/inconsolata_regular.ttf
new file mode 100644
index 0000000..fc981ce
--- /dev/null
+++ b/core/ktx/src/androidTest/res/font/inconsolata_regular.ttf
Binary files differ
diff --git a/core/ktx/src/androidTest/res/layout/test_activity.xml b/core/ktx/src/androidTest/res/layout/test_activity.xml
new file mode 100644
index 0000000..a477350
--- /dev/null
+++ b/core/ktx/src/androidTest/res/layout/test_activity.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~       http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/root"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <ImageView android:id="@+id/image_view"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:src="@android:drawable/ic_media_play"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/ktx/src/androidTest/res/layout/test_attrs.xml b/core/ktx/src/androidTest/res/layout/test_attrs.xml
new file mode 100644
index 0000000..d5975c5
--- /dev/null
+++ b/core/ktx/src/androidTest/res/layout/test_attrs.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~       http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/root"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:sample="42" />
diff --git a/core/ktx/src/androidTest/res/layout/typed_array.xml b/core/ktx/src/androidTest/res/layout/typed_array.xml
new file mode 100644
index 0000000..e485ffd
--- /dev/null
+++ b/core/ktx/src/androidTest/res/layout/typed_array.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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.
+  -->
+
+<View 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="match_parent"
+      app:boolean_present="true"
+      app:color_present="#fff"
+      app:dimension_present="1px"
+      app:drawable_present="@drawable/box"
+      app:string_present="Hello"
+      app:float_present="0.1"
+      app:integer_present="1"
+      app:resource_present="@font/inconsolata_regular"
+      app:font_present="@font/inconsolata_regular"
+      app:text_array_present="@array/text_array"
+/>
diff --git a/core/ktx/src/androidTest/res/values/attrs.xml b/core/ktx/src/androidTest/res/values/attrs.xml
new file mode 100644
index 0000000..e3f9723
--- /dev/null
+++ b/core/ktx/src/androidTest/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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>
+    <declare-styleable name="SampleAttrs">
+        <attr name="sample" format="integer" />
+    </declare-styleable>
+</resources>
diff --git a/core/ktx/src/androidTest/res/values/strings.xml b/core/ktx/src/androidTest/res/values/strings.xml
new file mode 100644
index 0000000..20877e9
--- /dev/null
+++ b/core/ktx/src/androidTest/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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>
+    <string name="text">Hello World</string>
+    <string-array name="text_array">
+        <item>Hello</item>
+        <item>World</item>
+    </string-array>
+</resources>
diff --git a/core/ktx/src/androidTest/res/values/styles.xml b/core/ktx/src/androidTest/res/values/styles.xml
new file mode 100644
index 0000000..ea35e47
--- /dev/null
+++ b/core/ktx/src/androidTest/res/values/styles.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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>
+    <declare-styleable name="TypedArrayTypes">
+        <attr name="boolean_present" format="boolean"/>
+        <attr name="boolean_absent" format="boolean"/>
+
+        <attr name="color_present" format="color"/>
+        <attr name="color_absent" format="color"/>
+
+        <attr name="dimension_present" format="dimension"/>
+        <attr name="dimension_absent" format="dimension"/>
+
+        <attr name="drawable_present" format="reference"/>
+        <attr name="drawable_absent" format="reference"/>
+
+        <attr name="float_present" format="float"/>
+        <attr name="float_absent" format="float"/>
+
+        <attr name="font_present" format="reference"/>
+        <attr name="font_absent" format="reference"/>
+
+        <attr name="integer_present" format="integer"/>
+        <attr name="integer_absent" format="integer"/>
+
+        <attr name="resource_present" format="reference"/>
+        <attr name="resource_absent" format="reference"/>
+
+        <attr name="string_present" format="string"/>
+        <attr name="string_absent" format="string"/>
+
+        <attr name="text_array_present" format="reference"/>
+        <attr name="text_array_absent" format="reference"/>
+    </declare-styleable>
+</resources>
diff --git a/core/ktx/src/androidTest/res/xml/preferences.xml b/core/ktx/src/androidTest/res/xml/preferences.xml
new file mode 100644
index 0000000..33629e1
--- /dev/null
+++ b/core/ktx/src/androidTest/res/xml/preferences.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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.
+  -->
+
+<PreferenceScreen/>
\ No newline at end of file
diff --git a/core/ktx/src/main/AndroidManifest.xml b/core/ktx/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7f93e5e
--- /dev/null
+++ b/core/ktx/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+<manifest package="androidx.core.ktx"/>
diff --git a/core/ktx/src/main/java/androidx/core/animation/Animator.kt b/core/ktx/src/main/java/androidx/core/animation/Animator.kt
new file mode 100644
index 0000000..1438bcd
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/animation/Animator.kt
@@ -0,0 +1,119 @@
+/*
+ * 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 androidx.core.animation
+
+import android.animation.Animator
+import androidx.annotation.RequiresApi
+
+/**
+ * Add an action which will be invoked when the animation has ended.
+ *
+ * @return the [Animator.AnimatorListener] added to the Animator
+ * @see Animator.end
+ */
+fun Animator.doOnEnd(action: (animator: Animator) -> Unit) = addListener(onEnd = action)
+
+/**
+ * Add an action which will be invoked when the animation has started.
+ *
+ * @return the [Animator.AnimatorListener] added to the Animator
+ * @see Animator.start
+ */
+fun Animator.doOnStart(action: (animator: Animator) -> Unit) = addListener(onStart = action)
+
+/**
+ * Add an action which will be invoked when the animation has been cancelled.
+ *
+ * @return the [Animator.AnimatorListener] added to the Animator
+ * @see Animator.cancel
+ */
+fun Animator.doOnCancel(action: (animator: Animator) -> Unit) = addListener(onCancel = action)
+
+/**
+ * Add an action which will be invoked when the animation has repeated.
+ * @return the [Animator.AnimatorListener] added to the Animator
+ */
+fun Animator.doOnRepeat(action: (animator: Animator) -> Unit) = addListener(onRepeat = action)
+
+/**
+ * Add an action which will be invoked when the animation has resumed after a pause.
+ *
+ * @return the [Animator.AnimatorPauseListener] added to the Animator
+ * @see Animator.resume
+ */
+@RequiresApi(19)
+fun Animator.doOnResume(action: (animator: Animator) -> Unit) = addPauseListener(onResume = action)
+
+/**
+ * Add an action which will be invoked when the animation has been paused.
+ *
+ * @return the [Animator.AnimatorPauseListener] added to the Animator
+ * @see Animator.pause
+ */
+@RequiresApi(19)
+fun Animator.doOnPause(action: (animator: Animator) -> Unit) = addPauseListener(onPause = action)
+
+/**
+ * Add a listener to this Animator using the provided actions.
+ */
+fun Animator.addListener(
+    onEnd: ((animator: Animator) -> Unit)? = null,
+    onStart: ((animator: Animator) -> Unit)? = null,
+    onCancel: ((animator: Animator) -> Unit)? = null,
+    onRepeat: ((animator: Animator) -> Unit)? = null
+): Animator.AnimatorListener {
+    val listener = object : Animator.AnimatorListener {
+        override fun onAnimationRepeat(animator: Animator) {
+            onRepeat?.invoke(animator)
+        }
+
+        override fun onAnimationEnd(animator: Animator) {
+            onEnd?.invoke(animator)
+        }
+
+        override fun onAnimationCancel(animator: Animator) {
+            onCancel?.invoke(animator)
+        }
+
+        override fun onAnimationStart(animator: Animator) {
+            onStart?.invoke(animator)
+        }
+    }
+    addListener(listener)
+    return listener
+}
+
+/**
+ * Add a pause and resume listener to this Animator using the provided actions.
+ */
+@RequiresApi(19)
+fun Animator.addPauseListener(
+    onResume: ((animator: Animator) -> Unit)? = null,
+    onPause: ((animator: Animator) -> Unit)? = null
+): Animator.AnimatorPauseListener {
+    val listener = object : Animator.AnimatorPauseListener {
+        override fun onAnimationPause(animator: Animator) {
+            onPause?.invoke(animator)
+        }
+
+        override fun onAnimationResume(animator: Animator) {
+            onResume?.invoke(animator)
+        }
+    }
+    addPauseListener(listener)
+    return listener
+}
diff --git a/core/ktx/src/main/java/androidx/core/content/ContentValues.kt b/core/ktx/src/main/java/androidx/core/content/ContentValues.kt
new file mode 100644
index 0000000..e1a0d01
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/content/ContentValues.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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.core.content
+
+import android.content.ContentValues
+
+/**
+ * Returns a new [ContentValues] with the given key/value pairs as elements.
+ *
+ * @throws IllegalArgumentException When a value is not a supported type of [ContentValues].
+ */
+fun contentValuesOf(vararg pairs: Pair<String, Any?>) = ContentValues(pairs.size).apply {
+    for ((key, value) in pairs) {
+        when (value) {
+            null -> putNull(key)
+            is String -> put(key, value)
+            is Int -> put(key, value)
+            is Long -> put(key, value)
+            is Boolean -> put(key, value)
+            is Float -> put(key, value)
+            is Double -> put(key, value)
+            is ByteArray -> put(key, value)
+            is Byte -> put(key, value)
+            is Short -> put(key, value)
+            else -> {
+                val valueType = value.javaClass.canonicalName
+                throw IllegalArgumentException("Illegal value type $valueType for key \"$key\"")
+            }
+        }
+    }
+}
diff --git a/core/ktx/src/main/java/androidx/core/content/Context.kt b/core/ktx/src/main/java/androidx/core/content/Context.kt
new file mode 100644
index 0000000..2ffb17b
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/content/Context.kt
@@ -0,0 +1,93 @@
+/*
+ * 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 androidx.core.content
+
+import android.content.Context
+import android.content.res.TypedArray
+import android.util.AttributeSet
+import androidx.annotation.AttrRes
+import androidx.annotation.RequiresApi
+import androidx.annotation.StyleRes
+
+/**
+ * Return the handle to a system-level service by class.
+ *
+ * The return type of this function intentionally uses a
+ * [platform type](https://kotlinlang.org/docs/reference/java-interop.html#null-safety-and-platform-types)
+ * to allow callers to decide whether they require a service be present or can tolerate its absence.
+ *
+ * @see Context.getSystemService(Class)
+ */
+@RequiresApi(23)
+@Suppress("HasPlatformType") // Intentionally propagating platform type with unknown nullability.
+inline fun <reified T> Context.systemService() = getSystemService(T::class.java)
+
+/**
+ * Executes [block] on a [TypedArray] receiver. The [TypedArray] holds the attribute
+ * values in [set] that are listed in [attrs]. In addition, if the given [AttributeSet]
+ * specifies a style class (through the `style` attribute), that style will be applied
+ * on top of the base attributes it defines.
+ *
+ * @param set The base set of attribute values.
+ * @param attrs The desired attributes to be retrieved. These attribute IDs must be
+ *              sorted in ascending order.
+ * @param defStyleAttr An attribute in the current theme that contains a reference to
+ *                     a style resource that supplies defaults values for the [TypedArray].
+ *                     Can be 0 to not look for defaults.
+ * @param defStyleRes A resource identifier of a style resource that supplies default values
+ *                    for the [TypedArray], used only if [defStyleAttr] is 0 or can not be found
+ *                     in the theme. Can be 0 to not look for defaults.
+ *
+ * @see Context.obtainStyledAttributes
+ * @see android.content.res.Resources.Theme.obtainStyledAttributes
+ */
+inline fun Context.withStyledAttributes(
+    set: AttributeSet? = null,
+    attrs: IntArray,
+    @AttrRes defStyleAttr: Int = 0,
+    @StyleRes defStyleRes: Int = 0,
+    block: TypedArray.() -> Unit
+) {
+    val typedArray = obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes)
+    try {
+        typedArray.block()
+    } finally {
+        typedArray.recycle()
+    }
+}
+
+/**
+ * Executes [block] on a [TypedArray] receiver. The [TypedArray] holds the the values
+ * defined by the style resource [resourceId] which are listed in [attrs].
+ *
+ * @param attrs The desired attributes. These attribute IDs must be sorted in ascending order.
+ *
+ * @see Context.obtainStyledAttributes
+ * @see android.content.res.Resources.Theme.obtainStyledAttributes
+ */
+inline fun Context.withStyledAttributes(
+    @StyleRes resourceId: Int,
+    attrs: IntArray,
+    block: TypedArray.() -> Unit
+) {
+    val typedArray = obtainStyledAttributes(resourceId, attrs)
+    try {
+        typedArray.block()
+    } finally {
+        typedArray.recycle()
+    }
+}
diff --git a/core/ktx/src/main/java/androidx/core/content/SharedPreferences.kt b/core/ktx/src/main/java/androidx/core/content/SharedPreferences.kt
new file mode 100644
index 0000000..c4677ea
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/content/SharedPreferences.kt
@@ -0,0 +1,50 @@
+/*
+ * 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 androidx.core.content
+
+import android.annotation.SuppressLint
+import android.content.SharedPreferences
+
+/**
+ * Allows editing of this preference instance with a call to [apply][SharedPreferences.Editor.apply]
+ * or [commit][SharedPreferences.Editor.commit] to persist the changes.
+ * Default behaviour is [apply][SharedPreferences.Editor.apply].
+ * ```
+ * prefs.edit {
+ *     putString("key", value)
+ * }
+ * ```
+ * To [commit][SharedPreferences.Editor.commit] changes:
+ * ```
+ * prefs.edit(commit = true) {
+ *     putString("key", value)
+ * }
+ * ```
+ */
+@SuppressLint("ApplySharedPref")
+inline fun SharedPreferences.edit(
+    commit: Boolean = false,
+    action: SharedPreferences.Editor.() -> Unit
+) {
+    val editor = edit()
+    action(editor)
+    if (commit) {
+        editor.commit()
+    } else {
+        editor.apply()
+    }
+}
diff --git a/core/ktx/src/main/java/androidx/core/content/res/TypedArray.kt b/core/ktx/src/main/java/androidx/core/content/res/TypedArray.kt
new file mode 100644
index 0000000..a4f6fef
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/content/res/TypedArray.kt
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2018 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.core.content.res
+
+import android.content.res.ColorStateList
+import android.content.res.TypedArray
+import android.graphics.Typeface
+import android.graphics.drawable.Drawable
+import androidx.annotation.AnyRes
+import androidx.annotation.ColorInt
+import androidx.annotation.Dimension
+import androidx.annotation.RequiresApi
+import androidx.annotation.StyleableRes
+
+private fun TypedArray.checkAttribute(@StyleableRes index: Int) {
+    if (!hasValue(index)) {
+        throw IllegalArgumentException("Attribute not defined in set.")
+    }
+}
+
+/**
+ * Retrieve the boolean value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getBoolean
+ */
+fun TypedArray.getBooleanOrThrow(@StyleableRes index: Int): Boolean {
+    checkAttribute(index)
+    return getBoolean(index, false)
+}
+
+/**
+ * Retrieve the color value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getColor
+ */
+@ColorInt
+fun TypedArray.getColorOrThrow(@StyleableRes index: Int): Int {
+    checkAttribute(index)
+    return getColor(index, 0)
+}
+
+/**
+ * Retrieve the color state list value for the attribute at [index] or throws
+ * [IllegalArgumentException] if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getColorStateList
+ */
+fun TypedArray.getColorStateListOrThrow(@StyleableRes index: Int): ColorStateList {
+    checkAttribute(index)
+    return checkNotNull(getColorStateList(index)) {
+        "Attribute value was not a color or color state list."
+    }
+}
+
+/**
+ * Retrieve the dimension value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getDimension
+ */
+fun TypedArray.getDimensionOrThrow(@StyleableRes index: Int): Float {
+    checkAttribute(index)
+    return getDimension(index, 0f)
+}
+
+/**
+ * Retrieve the dimension pixel offset value for the attribute at [index] or throws
+ * [IllegalArgumentException] if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getDimensionPixelOffset
+ */
+@Dimension
+fun TypedArray.getDimensionPixelOffsetOrThrow(@StyleableRes index: Int): Int {
+    checkAttribute(index)
+    return getDimensionPixelOffset(index, 0)
+}
+
+/**
+ * Retrieve the dimension pixel size value for the attribute at [index] or throws
+ * [IllegalArgumentException] if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getDimensionPixelSize
+ */
+@Dimension
+fun TypedArray.getDimensionPixelSizeOrThrow(@StyleableRes index: Int): Int {
+    checkAttribute(index)
+    return getDimensionPixelSize(index, 0)
+}
+
+/**
+ * Retrieve the drawable value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getDrawable
+ */
+fun TypedArray.getDrawableOrThrow(@StyleableRes index: Int): Drawable {
+    checkAttribute(index)
+    return getDrawable(index)
+}
+
+/**
+ * Retrieve the float value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getFloat
+ */
+fun TypedArray.getFloatOrThrow(@StyleableRes index: Int): Float {
+    checkAttribute(index)
+    return getFloat(index, 0f)
+}
+
+/**
+ * Retrieve the font value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getFont
+ */
+@RequiresApi(26)
+fun TypedArray.getFontOrThrow(@StyleableRes index: Int): Typeface {
+    checkAttribute(index)
+    return getFont(index)
+}
+
+/**
+ * Retrieve the integer value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getInt
+ */
+fun TypedArray.getIntOrThrow(@StyleableRes index: Int): Int {
+    checkAttribute(index)
+    return getInt(index, 0)
+}
+
+/**
+ * Retrieve the integer value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getInteger
+ */
+fun TypedArray.getIntegerOrThrow(@StyleableRes index: Int): Int {
+    checkAttribute(index)
+    return getInteger(index, 0)
+}
+
+/**
+ * Retrieves the resource identifier for the attribute at [index] or throws
+ * [IllegalArgumentException] if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getResourceId
+ */
+@AnyRes
+fun TypedArray.getResourceIdOrThrow(@StyleableRes index: Int): Int {
+    checkAttribute(index)
+    return getResourceId(index, 0)
+}
+
+/**
+ * Retrieve the string value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getString
+ */
+fun TypedArray.getStringOrThrow(@StyleableRes index: Int): String {
+    checkAttribute(index)
+    return checkNotNull(getString(index)) {
+        "Attribute value could not be coerced to String."
+    }
+}
+
+/**
+ * Retrieve the text value for the attribute at [index] or throws [IllegalArgumentException]
+ * if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getText
+ */
+fun TypedArray.getTextOrThrow(@StyleableRes index: Int): CharSequence {
+    checkAttribute(index)
+    return checkNotNull(getText(index)) {
+        "Attribute value could not be coerced to CharSequence."
+    }
+}
+
+/**
+ * Retrieve the text array value for the attribute at [index] or throws
+ * [IllegalArgumentException] if not defined.
+ *
+ * @see TypedArray.hasValue
+ * @see TypedArray.getTextArray
+ */
+fun TypedArray.getTextArrayOrThrow(@StyleableRes index: Int): Array<CharSequence> {
+    checkAttribute(index)
+    return getTextArray(index)
+}
+
+/**
+ * Executes the given [block] function on this TypedArray and then recycles it.
+ *
+ * @see kotlin.io.use
+ */
+inline fun <R> TypedArray.use(block: (TypedArray) -> R): R {
+    return block(this).also {
+        recycle()
+    }
+}
diff --git a/core/ktx/src/main/java/androidx/core/database/Cursor.kt b/core/ktx/src/main/java/androidx/core/database/Cursor.kt
new file mode 100644
index 0000000..b63e719
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/database/Cursor.kt
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to other public API.
+
+package androidx.core.database
+
+import android.database.Cursor
+
+/**
+ * Returns the value of the requested column as a byte array.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not a blob type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getBlob
+ */
+inline fun Cursor.getBlob(columnName: String): ByteArray =
+    getBlob(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as a double.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not a floating-point type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getDouble
+ */
+inline fun Cursor.getDouble(columnName: String): Double =
+    getDouble(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as a float.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not a floating-point type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getFloat
+ */
+inline fun Cursor.getFloat(columnName: String): Float = getFloat(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as an integer.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not an integral type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getInt
+ */
+inline fun Cursor.getInt(columnName: String): Int = getInt(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as a long.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not an integral type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getLong
+ */
+inline fun Cursor.getLong(columnName: String): Long = getLong(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as a short.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not an integral type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getShort
+ */
+inline fun Cursor.getShort(columnName: String): Short = getShort(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as a string.
+ *
+ * The result and whether this method throws an exception when the column value is null or the
+ * column type is not a string type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.getString
+ */
+inline fun Cursor.getString(columnName: String): String =
+    getString(getColumnIndexOrThrow(columnName))
+
+/**
+ * Returns the value of the requested column as a nullable byte array.
+ *
+ * The result and whether this method throws an exception when the column type is not a blob type is
+ * implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getBlob
+ */
+inline fun Cursor.getBlobOrNull(index: Int) = if (isNull(index)) null else getBlob(index)
+
+/**
+ * Returns the value of the requested column as a nullable double.
+ *
+ * The result and whether this method throws an exception when the column type is not a
+ * floating-point type is implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getDouble
+ */
+inline fun Cursor.getDoubleOrNull(index: Int) = if (isNull(index)) null else getDouble(index)
+
+/**
+ * Returns the value of the requested column as a nullable float.
+ *
+ * The result and whether this method throws an exception when the column type is not a
+ * floating-point type is implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getFloat
+ */
+inline fun Cursor.getFloatOrNull(index: Int) = if (isNull(index)) null else getFloat(index)
+
+/**
+ * Returns the value of the requested column as a nullable integer.
+ *
+ * The result and whether this method throws an exception when the column type is not an integral
+ * type is implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getInt
+ */
+inline fun Cursor.getIntOrNull(index: Int) = if (isNull(index)) null else getInt(index)
+
+/**
+ * Returns the value of the requested column as a nullable long.
+ *
+ * The result and whether this method throws an exception when the column type is not an integral
+ * type is implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getLong
+ */
+inline fun Cursor.getLongOrNull(index: Int) = if (isNull(index)) null else getLong(index)
+
+/**
+ * Returns the value of the requested column as a nullable short.
+ *
+ * The result and whether this method throws an exception when the column type is not an integral
+ * type is implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getShort
+ */
+inline fun Cursor.getShortOrNull(index: Int) = if (isNull(index)) null else getShort(index)
+
+/**
+ * Returns the value of the requested column as a nullable string.
+ *
+ * The result and whether this method throws an exception when the column type is not a string type
+ * is implementation-defined.
+ *
+ * @see Cursor.isNull
+ * @see Cursor.getString
+ */
+inline fun Cursor.getStringOrNull(index: Int) = if (isNull(index)) null else getString(index)
+
+/**
+ * Returns the value of the requested column as a nullable byte array.
+ *
+ * The result and whether this method throws an exception when the column type is not a blob type is
+ * implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getBlob
+ */
+inline fun Cursor.getBlobOrNull(columnName: String) =
+    getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getBlob(it) }
+
+/**
+ * Returns the value of the requested column as a nullable double.
+ *
+ * The result and whether this method throws an exception when the column type is not a
+ * floating-point type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getDouble
+ */
+inline fun Cursor.getDoubleOrNull(columnName: String) =
+    getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getDouble(it) }
+
+/**
+ * Returns the value of the requested column as a nullable float.
+ *
+ * The result and whether this method throws an exception when the column type is not a
+ * floating-point type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getFloat
+ */
+inline fun Cursor.getFloatOrNull(columnName: String) =
+    getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getFloat(it) }
+
+/**
+ * Returns the value of the requested column as a nullable integer.
+ *
+ * The result and whether this method throws an exception when the column type is not an integral
+ * type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getInt
+ */
+inline fun Cursor.getIntOrNull(columnName: String) =
+    getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getInt(it) }
+
+/**
+ * Returns the value of the requested column as a nullable long.
+ *
+ * The result and whether this method throws an exception when the column type is not an integral
+ * type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getLong
+ */
+inline fun Cursor.getLongOrNull(columnName: String) =
+    getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getLong(it) }
+
+/**
+ * Returns the value of the requested column as a nullable short.
+ *
+ * The result and whether this method throws an exception when the column type is not an integral
+ * type is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getShort
+ */
+inline fun Cursor.getShortOrNull(columnName: String) =
+    getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getShort(it) }
+
+/**
+ * Returns the value of the requested column as a nullable string.
+ *
+ * The result and whether this method throws an exception when the column type is not a string type
+ * is implementation-defined.
+ *
+ * @see Cursor.getColumnIndexOrThrow
+ * @see Cursor.isNull
+ * @see Cursor.getString
+ */
+inline fun Cursor.getStringOrNull(columnName: String) =
+    getColumnIndexOrThrow(columnName).let { if (isNull(it)) null else getString(it) }
diff --git a/core/ktx/src/main/java/androidx/core/database/sqlite/SQLiteDatabase.kt b/core/ktx/src/main/java/androidx/core/database/sqlite/SQLiteDatabase.kt
new file mode 100644
index 0000000..bdbd94e
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/database/sqlite/SQLiteDatabase.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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.core.database.sqlite
+
+import android.database.sqlite.SQLiteDatabase
+
+/**
+ * Run [body] in a transaction marking it as successful if it completes without exception.
+ *
+ * @param exclusive Run in `EXCLUSIVE` mode when true, `IMMEDIATE` mode otherwise.
+ */
+inline fun <T> SQLiteDatabase.transaction(
+    exclusive: Boolean = true,
+    body: SQLiteDatabase.() -> T
+): T {
+    if (exclusive) {
+        beginTransaction()
+    } else {
+        beginTransactionNonExclusive()
+    }
+    try {
+        val result = body()
+        setTransactionSuccessful()
+        return result
+    } finally {
+        endTransaction()
+    }
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Bitmap.kt b/core/ktx/src/main/java/androidx/core/graphics/Bitmap.kt
new file mode 100644
index 0000000..45ab6ba
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Bitmap.kt
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.ColorSpace
+import androidx.annotation.ColorInt
+import androidx.annotation.RequiresApi
+
+/**
+ * Creates a new [Canvas] to draw on this bitmap and executes the specified
+ * [block] on the newly created canvas. Example:
+ *
+ * ```
+ * return Bitmap.createBitmap(…).applyCanvas {
+ *    drawLine(…)
+ *    translate(…)
+ *    drawRect(…)
+ * }
+ * ```
+ */
+inline fun Bitmap.applyCanvas(block: Canvas.() -> Unit): Bitmap {
+    val c = Canvas(this)
+    c.block()
+    return this
+}
+
+/**
+ * Returns the value of the pixel at the specified location. The returned value
+ * is a [color int][android.graphics.Color] in the sRGB color space.
+ */
+inline operator fun Bitmap.get(x: Int, y: Int) = getPixel(x, y)
+
+/**
+ * Writes the specified [color int][android.graphics.Color] into the bitmap
+ * (assuming it is mutable) at the specified `(x, y)` coordinate. The specified
+ * color is converted from sRGB to the bitmap's color space if needed.
+ */
+inline operator fun Bitmap.set(x: Int, y: Int, @ColorInt color: Int) = setPixel(x, y, color)
+
+/**
+ * Creates a new bitmap, scaled from this bitmap, when possible. If the specified
+ * [width] and [height] are the same as the current width and height of this bitmap,
+ * this bitmap is returned and no new bitmap is created.
+ *
+ * @param width The new bitmap's desired width
+ * @param height The new bitmap's desired height
+ * @param filter `true` if the source should be filtered (`true` by default)
+ *
+ * @return The new scaled bitmap or the source bitmap if no scaling is required.
+ */
+inline fun Bitmap.scale(width: Int, height: Int, filter: Boolean = true): Bitmap {
+    return Bitmap.createScaledBitmap(this, width, height, filter)
+}
+
+/**
+ * Returns a mutable bitmap with the specified [width] and [height]. A config
+ * can be optionally specified. If not, the default config is [Bitmap.Config.ARGB_8888].
+ *
+ * @param width The new bitmap's desired width
+ * @param height The new bitmap's desired height
+ * @param config The new bitmap's desired [config][Bitmap.Config]
+ *
+ * @return A new bitmap with the specified dimensions and config
+ */
+inline fun createBitmap(
+    width: Int,
+    height: Int,
+    config: Bitmap.Config = Bitmap.Config.ARGB_8888
+): Bitmap {
+    return Bitmap.createBitmap(width, height, config)
+}
+
+/**
+ * Returns a mutable bitmap with the specified [width] and [height]. The config,
+ * transparency and color space can optionally be specified. They respectively
+ * default to [Bitmap.Config.ARGB_8888], `true` and [sRGB][ColorSpace.Named.SRGB].
+ *
+ * @param width The new bitmap's desired width
+ * @param height The new bitmap's desired height
+ * @param config The new bitmap's desired [config][Bitmap.Config]
+ * @param hasAlpha Whether the new bitmap is opaque or not
+ * @param colorSpace The new bitmap's color space
+ *
+ * @return A new bitmap with the specified dimensions and config
+ */
+@RequiresApi(26)
+inline fun createBitmap(
+    width: Int,
+    height: Int,
+    config: Bitmap.Config = Bitmap.Config.ARGB_8888,
+    hasAlpha: Boolean = true,
+    colorSpace: ColorSpace = ColorSpace.get(ColorSpace.Named.SRGB)
+): Bitmap {
+    return Bitmap.createBitmap(width, height, config, hasAlpha, colorSpace)
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Canvas.kt b/core/ktx/src/main/java/androidx/core/graphics/Canvas.kt
new file mode 100644
index 0000000..71efc08
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Canvas.kt
@@ -0,0 +1,125 @@
+/*
+ * 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 androidx.core.graphics
+
+import android.graphics.Canvas
+import android.graphics.Matrix
+
+/**
+ * Wrap the specified [block] in calls to [Canvas.save]
+ * and [Canvas.restoreToCount].
+ */
+inline fun Canvas.withSave(block: Canvas.() -> Unit) {
+    val checkpoint = save()
+    try {
+        block()
+    } finally {
+        restoreToCount(checkpoint)
+    }
+}
+
+/**
+ * Wrap the specified [block] in calls to [Canvas.save]/[Canvas.translate]
+ * and [Canvas.restoreToCount].
+ */
+inline fun Canvas.withTranslation(
+    x: Float = 0.0f,
+    y: Float = 0.0f,
+    block: Canvas.() -> Unit
+) {
+    val checkpoint = save()
+    translate(x, y)
+    try {
+        block()
+    } finally {
+        restoreToCount(checkpoint)
+    }
+}
+
+/**
+ * Wrap the specified [block] in calls to [Canvas.save]/[Canvas.rotate]
+ * and [Canvas.restoreToCount].
+ */
+inline fun Canvas.withRotation(
+    degrees: Float = 0.0f,
+    pivotX: Float = 0.0f,
+    pivotY: Float = 0.0f,
+    block: Canvas.() -> Unit
+) {
+    val checkpoint = save()
+    rotate(degrees, pivotX, pivotY)
+    try {
+        block()
+    } finally {
+        restoreToCount(checkpoint)
+    }
+}
+
+/**
+ * Wrap the specified [block] in calls to [Canvas.save]/[Canvas.scale]
+ * and [Canvas.restoreToCount].
+ */
+inline fun Canvas.withScale(
+    x: Float = 1.0f,
+    y: Float = 1.0f,
+    pivotX: Float = 0.0f,
+    pivotY: Float = 0.0f,
+    block: Canvas.() -> Unit
+) {
+    val checkpoint = save()
+    scale(x, y, pivotX, pivotY)
+    try {
+        block()
+    } finally {
+        restoreToCount(checkpoint)
+    }
+}
+
+/**
+ * Wrap the specified [block] in calls to [Canvas.save]/[Canvas.skew]
+ * and [Canvas.restoreToCount].
+ */
+inline fun Canvas.withSkew(
+    x: Float = 0.0f,
+    y: Float = 0.0f,
+    block: Canvas.() -> Unit
+) {
+    val checkpoint = save()
+    skew(x, y)
+    try {
+        block()
+    } finally {
+        restoreToCount(checkpoint)
+    }
+}
+
+/**
+ * Wrap the specified [block] in calls to [Canvas.save]/[Canvas.concat]
+ * and [Canvas.restoreToCount].
+ */
+inline fun Canvas.withMatrix(
+    matrix: Matrix = Matrix(),
+    block: Canvas.() -> Unit
+) {
+    val checkpoint = save()
+    concat(matrix)
+    try {
+        block()
+    } finally {
+        restoreToCount(checkpoint)
+    }
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Color.kt b/core/ktx/src/main/java/androidx/core/graphics/Color.kt
new file mode 100644
index 0000000..f853422
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Color.kt
@@ -0,0 +1,364 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE", "WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE")
+
+package androidx.core.graphics
+
+import android.graphics.Color
+import android.graphics.ColorSpace
+import androidx.annotation.ColorInt
+import androidx.annotation.ColorLong
+import androidx.annotation.RequiresApi
+
+/**
+ * Returns the first component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the first component
+ * is "red".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue) = myColor
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun Color.component1() = getComponent(0)
+
+/**
+ * Returns the second component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the second component
+ * is "green".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue) = myColor
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun Color.component2() = getComponent(1)
+
+/**
+ * Returns the third component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the third component
+ * is "blue".
+= *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue) = myColor
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun Color.component3() = getComponent(2)
+
+/**
+ * Returns the fourth component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the fourth component
+ * is "alpha".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue, alpha) = myColor
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun Color.component4() = getComponent(3)
+
+/**
+ * Composites two translucent colors together. More specifically, adds two colors using
+ * the [source over][android.graphics.PorterDuff.Mode.SRC_OVER] blending mode. The colors
+ * must not be pre-multiplied and the result is a non pre-multiplied color.
+ *
+ * If the two colors have different color spaces, the color in the right-hand part
+ * of the expression is converted to the color space of the color in left-hand part
+ * of the expression.
+ *
+ * The following example creates a purple color by blending opaque blue with
+ * semi-translucent red:
+ *
+ * ```
+ * val purple = Color.valueOf(0f, 0f, 1f) + Color.valueOf(1f, 0f, 0f, 0.5f)
+ * ```
+ *
+ * @throws IllegalArgumentException if the [color models][android.graphics.Color.getModel]
+ *                                  of the colors do not match
+ */
+@RequiresApi(26)
+operator fun Color.plus(c: Color): Color = ColorUtils.compositeColors(c, this)
+
+/**
+ * Return the alpha component of a color int. This is equivalent to calling:
+ * ```
+ * Color.alpha(myInt)
+ * ```
+ */
+inline val @receiver:ColorInt Int.alpha get() = (this shr 24) and 0xff
+
+/**
+ * Return the red component of a color int. This is equivalent to calling:
+ * ```
+ * Color.red(myInt)
+ * ```
+ */
+inline val @receiver:ColorInt Int.red get() = (this shr 16) and 0xff
+
+/**
+ * Return the green component of a color int. This is equivalent to calling:
+ * ```
+ * Color.green(myInt)
+ * ```
+ */
+inline val @receiver:ColorInt Int.green get() = (this shr 8) and 0xff
+
+/**
+ * Return the blue component of a color int. This is equivalent to calling:
+ * ```
+ * Color.blue(myInt)
+ * ```
+ */
+inline val @receiver:ColorInt Int.blue get() = this and 0xff
+
+/**
+ * Return the alpha component of a color int. This is equivalent to calling:
+ * ```
+ * Color.alpha(myInt)
+ * ```
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (alpha, red, green, blue) = myColor
+ * ```
+ */
+inline operator fun @receiver:ColorInt Int.component1() = (this shr 24) and 0xff
+
+/**
+ * Return the red component of a color int. This is equivalent to calling:
+ * ```
+ * Color.red(myInt)
+ * ```
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (alpha, red, green, blue) = myColor
+ * ```
+ */
+inline operator fun @receiver:ColorInt Int.component2() = (this shr 16) and 0xff
+
+/**
+ * Return the green component of a color int. This is equivalent to calling:
+ * ```
+ * Color.green(myInt)
+ * ```
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (alpha, red, green, blue) = myColor
+ * ```
+ */
+inline operator fun @receiver:ColorInt Int.component3() = (this shr 8) and 0xff
+
+/**
+ * Return the blue component of a color int. This is equivalent to calling:
+ * ```
+ * Color.blue(myInt)
+ * ```
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (alpha, red, green, blue) = myColor
+ * ```
+ */
+inline operator fun @receiver:ColorInt Int.component4() = this and 0xff
+
+/**
+ * Returns the relative luminance of a color int, assuming sRGB encoding.
+ * Based on the formula for relative luminance defined in WCAG 2.0,
+ * W3C Recommendation 11 December 2008.
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorInt Int.luminance get() = Color.luminance(this)
+
+/**
+ * Creates a new [Color] instance from a color int. The resulting color
+ * is in the [sRGB][android.graphics.ColorSpace.Named.SRGB] color space.
+ */
+@RequiresApi(26)
+inline fun @receiver:ColorInt Int.toColor(): Color = Color.valueOf(this)
+
+/**
+ * Converts the specified ARGB [color int][Color] to an RGBA [color long][Color]
+ * in the [sRGB][android.graphics.ColorSpace.Named.SRGB] color space.
+ */
+@RequiresApi(26)
+@ColorLong
+inline fun @receiver:ColorInt Int.toColorLong() = Color.pack(this)
+
+/**
+ * Returns the first component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the first component
+ * is "red".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue, alpha) = myColorLong
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun @receiver:ColorLong Long.component1() = Color.red(this)
+
+/**
+ * Returns the second component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the second component
+ * is "green".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue, alpha) = myColorLong
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun @receiver:ColorLong Long.component2() = Color.green(this)
+
+/**
+ * Returns the third component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the third component
+ * is "blue".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue, alpha) = myColorLong
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun @receiver:ColorLong Long.component3() = Color.blue(this)
+
+/**
+ * Returns the fourth component of the color. For instance, when the color model
+ * of the color is [android.graphics.ColorSpace.Model.RGB], the fourth component
+ * is "alpha".
+ *
+ * This method allows to use destructuring declarations when working with colors,
+ * for example:
+ * ```
+ * val (red, green, blue, alpha) = myColorLong
+ * ```
+ */
+@RequiresApi(26)
+inline operator fun @receiver:ColorLong Long.component4() = Color.alpha(this)
+
+/**
+ * Return the alpha component of a color long. This is equivalent to calling:
+ * ```
+ * Color.alpha(myLong)
+ * ```
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.alpha get() = Color.alpha(this)
+
+/**
+ * Return the red component of a color long. This is equivalent to calling:
+ * ```
+ * Color.red(myLong)
+ * ```
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.red get() = Color.red(this)
+
+/**
+ * Return the green component of a color long. This is equivalent to calling:
+ * ```
+ * Color.green(myLong)
+ * ```
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.green get() = Color.green(this)
+
+/**
+ * Return the blue component of a color long. This is equivalent to calling:
+ * ```
+ * Color.blue(myLong)
+ * ```
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.blue get() = Color.blue(this)
+
+/**
+ * Returns the relative luminance of a color. Based on the formula for
+ * relative luminance defined in WCAG 2.0, W3C Recommendation 11 December 2008.
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.luminance get() = Color.luminance(this)
+
+/**
+ * Creates a new [Color] instance from a [color long][Color].
+ */
+@RequiresApi(26)
+inline fun @receiver:ColorLong Long.toColor(): Color = Color.valueOf(this)
+
+/**
+ * Converts the specified [color long][Color] to an ARGB [color int][Color].
+ */
+@RequiresApi(26)
+@ColorInt
+inline fun @receiver:ColorLong Long.toColorInt() = Color.toArgb(this)
+
+/**
+ * Indicates whether the color is in the [sRGB][android.graphics.ColorSpace.Named.SRGB]
+ * color space.
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.isSrgb get() = Color.isSrgb(this)
+
+/**
+ * Indicates whether the color is in a [wide-gamut][android.graphics.ColorSpace] color space.
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.isWideGamut get() = Color.isWideGamut(this)
+
+/**
+ * Returns the color space encoded in the specified color long.
+ */
+@get:RequiresApi(26)
+inline val @receiver:ColorLong Long.colorSpace: ColorSpace get() = Color.colorSpace(this)
+
+/**
+ * Return a corresponding [Int] color of this [String].
+ *
+ * Supported formats are:
+ * ```
+ * #RRGGBB
+ * #AARRGGBB
+ * ```
+ *
+ * The following names are also accepted: "red", "blue", "green", "black", "white",
+ * "gray", "cyan", "magenta", "yellow", "lightgray", "darkgray",
+ * "grey", "lightgrey", "darkgrey", "aqua", "fuchsia", "lime",
+ * "maroon", "navy", "olive", "purple", "silver", "teal".
+ *
+ * @throws IllegalArgumentException if this [String] cannot be parsed.
+ */
+@ColorInt
+inline fun String.toColorInt(): Int = Color.parseColor(this)
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Matrix.kt b/core/ktx/src/main/java/androidx/core/graphics/Matrix.kt
new file mode 100644
index 0000000..5cb0cdf
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Matrix.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.Matrix
+
+/**
+ * Multiplies this [Matrix] by another matrix and returns the result as
+ * a new matrix.
+ */
+inline operator fun Matrix.times(m: Matrix) = Matrix(this).apply { preConcat(m) }
+
+/**
+ * Returns the 9 values of this [Matrix] as a new array of floats.
+ */
+inline fun Matrix.values() = FloatArray(9).apply { getValues(this) }
+
+/**
+ * Creates a translation matrix with the translation amounts [tx] and [ty]
+ * respectively on the `x` and `y` axis.
+ */
+fun translationMatrix(tx: Float = 0.0f, ty: Float = 0.0f) = Matrix().apply { setTranslate(tx, ty) }
+
+/**
+ * Creates a scale matrix with the scale factor [sx] and [sy] respectively on the
+ * `x` and `y` axis.
+ */
+fun scaleMatrix(sx: Float = 1.0f, sy: Float = 1.0f) = Matrix().apply { setScale(sx, sy) }
+
+/**
+ * Creates a rotation matrix, defined by a rotation angle in degrees around the pivot
+ * point located at the coordinates ([px], [py]).
+ */
+fun rotationMatrix(degrees: Float, px: Float = 0.0f, py: Float = 0.0f) =
+    Matrix().apply { setRotate(degrees, px, py) }
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Path.kt b/core/ktx/src/main/java/androidx/core/graphics/Path.kt
new file mode 100644
index 0000000..ca70533
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Path.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.Path
+import androidx.annotation.RequiresApi
+
+/**
+ * Flattens (or approximate) the [Path] with a series of line segments.
+ *
+ * @param error The acceptable error for a line on the Path. Typically this would be
+ *              0.5 so that the error is less than half a pixel. This value must be
+ *              positive and is set to 0.5 by default.
+ *
+ * @see Path.approximate
+ */
+@RequiresApi(26)
+fun Path.flatten(error: Float = 0.5f): Iterable<PathSegment> = PathUtils.flatten(this, error)
+
+/**
+ * Returns the union of two paths as a new [Path].
+ */
+@RequiresApi(19)
+inline operator fun Path.plus(p: Path): Path {
+    return Path(this).apply {
+        op(p, Path.Op.UNION)
+    }
+}
+
+/**
+ * Returns the difference of two paths as a new [Path].
+ */
+@RequiresApi(19)
+inline operator fun Path.minus(p: Path): Path {
+    return Path(this).apply {
+        op(p, Path.Op.DIFFERENCE)
+    }
+}
+
+/**
+ * Returns the union of two paths as a new [Path].
+ */
+@RequiresApi(19)
+inline infix fun Path.and(p: Path) = this + p
+
+/**
+ * Returns the intersection of two paths as a new [Path].
+ * If the paths do not intersect, returns an empty path.
+ */
+@RequiresApi(19)
+inline infix fun Path.or(p: Path): Path {
+    return Path().apply {
+        op(this@or, p, Path.Op.INTERSECT)
+    }
+}
+
+/**
+ * Returns the union minus the intersection of two paths as a new [Path].
+ */
+@RequiresApi(19)
+inline infix fun Path.xor(p: Path): Path {
+    return Path(this).apply {
+        op(p, Path.Op.XOR)
+    }
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Picture.kt b/core/ktx/src/main/java/androidx/core/graphics/Picture.kt
new file mode 100644
index 0000000..d1e8c99
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Picture.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 20188 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.
+ */
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.Canvas
+import android.graphics.Picture
+
+/**
+ * Creates a new [Canvas] to record commands in this [Picture], executes the specified
+ * [block] on the newly created canvas and returns this [Picture]. Example:
+ *
+ * ```
+ * return myPicture.record(1280, 720) {
+ *    drawLine(…)
+ *    translate(…)
+ *    drawRect(…)
+ * }
+ * ```
+ */
+inline fun Picture.record(width: Int, height: Int, block: Canvas.() -> Unit): Picture {
+    val c = beginRecording(width, height)
+    try {
+        c.block()
+    } finally {
+        endRecording()
+    }
+    return this
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Point.kt b/core/ktx/src/main/java/androidx/core/graphics/Point.kt
new file mode 100644
index 0000000..b6e35e6
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Point.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.Point
+import android.graphics.PointF
+
+/**
+ * Returns the x coordinate of this point.
+ *
+ * This method allows to use destructuring declarations when working with points,
+ * for example:
+ * ```
+ * val (x, y) = myPoint
+ * ```
+ */
+inline operator fun Point.component1() = this.x
+
+/**
+ * Returns the y coordinate of this point.
+ *
+ * This method allows to use destructuring declarations when working with points,
+ * for example:
+ * ```
+ * val (x, y) = myPoint
+ * ```
+ */
+inline operator fun Point.component2() = this.y
+
+/**
+ * Returns the x coordinate of this point.
+ *
+ * This method allows to use destructuring declarations when working with points,
+ * for example:
+ * ```
+ * val (x, y) = myPoint
+ * ```
+ */
+inline operator fun PointF.component1() = this.x
+
+/**
+ * Returns the y coordinate of this point.
+ *
+ * This method allows to use destructuring declarations when working with points,
+ * for example:
+ * ```
+ * val (x, y) = myPoint
+ * ```
+ */
+inline operator fun PointF.component2() = this.y
+
+/**
+ * Offsets this point by the specified point and returns the result as a new point.
+ */
+inline operator fun Point.plus(p: Point): Point {
+    return Point(x, y).apply {
+        offset(p.x, p.y)
+    }
+}
+
+/**
+ * Offsets this point by the specified point and returns the result as a new point.
+ */
+inline operator fun PointF.plus(p: PointF): PointF {
+    return PointF(x, y).apply {
+        offset(p.x, p.y)
+    }
+}
+
+/**
+ * Offsets this point by the specified amount on both X and Y axis and returns the
+ * result as a new point.
+ */
+inline operator fun Point.plus(xy: Int): Point {
+    return Point(x, y).apply {
+        offset(xy, xy)
+    }
+}
+
+/**
+ * Offsets this point by the specified amount on both X and Y axis and returns the
+ * result as a new point.
+ */
+inline operator fun PointF.plus(xy: Float): PointF {
+    return PointF(x, y).apply {
+        offset(xy, xy)
+    }
+}
+
+/**
+ * Offsets this point by the negation of the specified point and returns the result
+ * as a new point.
+ */
+inline operator fun Point.minus(p: Point): Point {
+    return Point(x, y).apply {
+        offset(-p.x, -p.y)
+    }
+}
+
+/**
+ * Offsets this point by the negation of the specified point and returns the result
+ * as a new point.
+ */
+inline operator fun PointF.minus(p: PointF): PointF {
+    return PointF(x, y).apply {
+        offset(-p.x, -p.y)
+    }
+}
+
+/**
+ * Offsets this point by the negation of the specified amount on both X and Y axis and
+ * returns the result as a new point.
+ */
+inline operator fun Point.minus(xy: Int): Point {
+    return Point(x, y).apply {
+        offset(-xy, -xy)
+    }
+}
+
+/**
+ * Offsets this point by the negation of the specified amount on both X and Y axis and
+ * returns the result as a new point.
+ */
+inline operator fun PointF.minus(xy: Float): PointF {
+    return PointF(x, y).apply {
+        offset(-xy, -xy)
+    }
+}
+
+/**
+ * Returns a new point representing the negation of this point.
+ */
+inline operator fun Point.unaryMinus() = Point(-x, -y)
+
+/**
+ * Returns a new point representing the negation of this point.
+ */
+inline operator fun PointF.unaryMinus() = PointF(-x, -y)
+
+/**
+ * Returns a [PointF] representation of this point.
+ */
+inline fun Point.toPointF() = PointF(this)
+
+/**
+ * Returns a [Point] representation of this point.
+ */
+inline fun PointF.toPoint() = Point(x.toInt(), y.toInt())
diff --git a/core/ktx/src/main/java/androidx/core/graphics/PorterDuff.kt b/core/ktx/src/main/java/androidx/core/graphics/PorterDuff.kt
new file mode 100644
index 0000000..a34d6f9
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/PorterDuff.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.graphics.PorterDuffXfermode
+
+/**
+ * Creates a new [PorterDuffXfermode] that uses this [PorterDuff.Mode] as the
+ * alpha compositing or blending mode.
+ */
+inline fun PorterDuff.Mode.toXfermode() = PorterDuffXfermode(this)
+
+/**
+ * Creates a new [PorterDuffColorFilter] that uses this [PorterDuff.Mode] as the
+ * alpha compositing or blending mode, and the specified [color].
+ */
+inline fun PorterDuff.Mode.toColorFilter(color: Int) = PorterDuffColorFilter(color, this)
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Rect.kt b/core/ktx/src/main/java/androidx/core/graphics/Rect.kt
new file mode 100644
index 0000000..960c0c5
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Rect.kt
@@ -0,0 +1,335 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.annotation.SuppressLint
+import android.graphics.Matrix
+import android.graphics.Point
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.Region
+
+/**
+ * Returns "left", the first component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun Rect.component1() = this.left
+
+/**
+ * Returns "top", the second component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun Rect.component2() = this.top
+
+/**
+ * Returns "right", the third component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun Rect.component3() = this.right
+
+/**
+ * Returns "bottom", the fourth component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun Rect.component4() = this.bottom
+
+/**
+ * Returns "left", the first component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun RectF.component1() = this.left
+
+/**
+ * Returns "top", the second component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun RectF.component2() = this.top
+
+/**
+ * Returns "right", the third component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun RectF.component3() = this.right
+
+/**
+ * Returns "bottom", the fourth component of the rectangle.
+ *
+ * This method allows to use destructuring declarations when working with rectangles,
+ * for example:
+ * ```
+ * val (left, top, right, bottom) = myRectangle
+ * ```
+ */
+inline operator fun RectF.component4() = this.bottom
+
+/**
+ * Performs the union of this rectangle and the specified rectangle and returns
+ * the result as a new rectangle.
+ */
+inline operator fun Rect.plus(r: Rect): Rect {
+    return Rect(this).apply {
+        union(r)
+    }
+}
+
+/**
+ * Performs the union of this rectangle and the specified rectangle and returns
+ * the result as a new rectangle.
+ */
+inline operator fun RectF.plus(r: RectF): RectF {
+    return RectF(this).apply {
+        union(r)
+    }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the specified
+ * amount on both X and Y axis.
+ */
+inline operator fun Rect.plus(xy: Int): Rect {
+    return Rect(this).apply {
+        offset(xy, xy)
+    }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the specified
+ * amount on both X and Y axis.
+ */
+inline operator fun RectF.plus(xy: Float): RectF {
+    return RectF(this).apply {
+        offset(xy, xy)
+    }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the specified
+ * point.
+ */
+inline operator fun Rect.plus(xy: Point): Rect {
+    return Rect(this).apply {
+        offset(xy.x, xy.y)
+    }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the specified
+ * point.
+ */
+inline operator fun RectF.plus(xy: PointF): RectF {
+    return RectF(this).apply {
+        offset(xy.x, xy.y)
+    }
+}
+
+/**
+ * Returns the difference of this rectangle and the specified rectangle as a new region.
+ */
+inline operator fun Rect.minus(r: Rect): Region {
+    return Region(this).apply {
+        op(r, Region.Op.DIFFERENCE)
+    }
+}
+
+/**
+ * Returns the difference of this rectangle and the specified rectangle as a new region.
+ * This rectangle is first converted to a [Rect] using [RectF.toRect].
+ */
+inline operator fun RectF.minus(r: RectF): Region {
+    return Region(this.toRect()).apply {
+        op(r.toRect(), Region.Op.DIFFERENCE)
+    }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the negation
+ * of the specified amount on both X and Y axis.
+ */
+inline operator fun Rect.minus(xy: Int): Rect {
+    return Rect(this).apply {
+        offset(-xy, -xy)
+    }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the negation
+ * of the specified amount on both X and Y axis.
+ */
+inline operator fun RectF.minus(xy: Float): RectF {
+    return RectF(this).apply {
+        offset(-xy, -xy)
+    }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the negation of
+ * the specified point.
+ */
+inline operator fun Rect.minus(xy: Point): Rect {
+    return Rect(this).apply {
+        offset(-xy.x, -xy.y)
+    }
+}
+
+/**
+ * Returns a new rectangle representing this rectangle offset by the negation of
+ * the specified point.
+ */
+inline operator fun RectF.minus(xy: PointF): RectF {
+    return RectF(this).apply {
+        offset(-xy.x, -xy.y)
+    }
+}
+
+/**
+ * Returns the union of two rectangles as a new rectangle.
+ */
+inline infix fun Rect.and(r: Rect) = this + r
+
+/**
+ * Returns the union of two rectangles as a new rectangle.
+ */
+inline infix fun RectF.and(r: RectF) = this + r
+
+/**
+ * Returns the intersection of two rectangles as a new rectangle.
+ * If the rectangles do not intersect, returns a copy of the left hand side
+ * rectangle.
+ */
+@SuppressLint("CheckResult")
+inline infix fun Rect.or(r: Rect): Rect {
+    return Rect(this).apply {
+        intersect(r)
+    }
+}
+
+/**
+ * Returns the intersection of two rectangles as a new rectangle.
+ * If the rectangles do not intersect, returns a copy of the left hand side
+ * rectangle.
+ */
+@SuppressLint("CheckResult")
+inline infix fun RectF.or(r: RectF): RectF {
+    return RectF(this).apply {
+        intersect(r)
+    }
+}
+
+/**
+ * Returns the union minus the intersection of two rectangles as a new region.
+ */
+inline infix fun Rect.xor(r: Rect): Region {
+    return Region(this).apply {
+        op(r, Region.Op.XOR)
+    }
+}
+
+/**
+ * Returns the union minus the intersection of two rectangles as a new region.
+ * The two rectangles are first converted to [Rect] using [RectF.toRect].
+ */
+inline infix fun RectF.xor(r: RectF): Region {
+    return Region(this.toRect()).apply {
+        op(r.toRect(), Region.Op.XOR)
+    }
+}
+
+/**
+ * Returns true if the specified point is inside the rectangle.
+ * The left and top are considered to be inside, while the right and bottom are not.
+ * This means that for a point to be contained: left <= x < right and top <= y < bottom.
+ * An empty rectangle never contains any point.
+ */
+inline operator fun Rect.contains(p: Point) = contains(p.x, p.y)
+
+/**
+ * Returns true if the specified point is inside the rectangle.
+ * The left and top are considered to be inside, while the right and bottom are not.
+ * This means that for a point to be contained: left <= x < right and top <= y < bottom.
+ * An empty rectangle never contains any point.
+ */
+inline operator fun RectF.contains(p: PointF) = contains(p.x, p.y)
+
+/**
+ * Returns a [RectF] representation of this rectangle.
+ */
+inline fun Rect.toRectF(): RectF = RectF(this)
+
+/**
+ * Returns a [Rect] representation of this rectangle. The resulting rect will be sized such
+ * that this rect can fit within it.
+ */
+inline fun RectF.toRect(): Rect {
+    val r = Rect()
+    roundOut(r)
+    return r
+}
+
+/**
+ * Returns a [Region] representation of this rectangle.
+ */
+inline fun Rect.toRegion() = Region(this)
+
+/**
+ * Returns a [Region] representation of this rectangle. The resulting rect will be sized such
+ * that this rect can fit within it.
+ */
+inline fun RectF.toRegion() = Region(this.toRect())
+
+/**
+ * Transform this rectangle in place using the supplied [Matrix] and returns
+ * this rectangle.
+ */
+inline fun RectF.transform(m: Matrix) = apply { m.mapRect(this@transform) }
diff --git a/core/ktx/src/main/java/androidx/core/graphics/Region.kt b/core/ktx/src/main/java/androidx/core/graphics/Region.kt
new file mode 100644
index 0000000..4fc5179
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/Region.kt
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.graphics
+
+import android.graphics.Point
+import android.graphics.Rect
+import android.graphics.Region
+import android.graphics.RegionIterator
+
+/**
+ * Return true if the region contains the specified [Point].
+ */
+inline operator fun Region.contains(p: Point) = contains(p.x, p.y)
+
+/**
+ * Return the union of this region and the specified [Rect] as a new region.
+ */
+inline operator fun Region.plus(r: Rect): Region {
+    return Region(this).apply {
+        union(r)
+    }
+}
+
+/**
+ * Return the union of this region and the specified region as a new region.
+ */
+inline operator fun Region.plus(r: Region): Region {
+    return Region(this).apply {
+        op(r, Region.Op.UNION)
+    }
+}
+
+/**
+ * Return the difference of this region and the specified [Rect] as a new region.
+ */
+inline operator fun Region.minus(r: Rect): Region {
+    return Region(this).apply {
+        op(r, Region.Op.DIFFERENCE)
+    }
+}
+
+/**
+ * Return the difference of this region and the specified region as a new region.
+ */
+inline operator fun Region.minus(r: Region): Region {
+    return Region(this).apply {
+        op(r, Region.Op.DIFFERENCE)
+    }
+}
+
+/**
+ * Returns the negation of this region as a new region.
+ */
+inline operator fun Region.unaryMinus(): Region {
+    return Region(bounds).apply {
+        op(this@unaryMinus, Region.Op.DIFFERENCE)
+    }
+}
+
+/**
+ * Returns the negation of this region as a new region.
+ */
+inline operator fun Region.not() = -this
+
+/**
+ * Return the union of this region and the specified [Rect] as a new region.
+ */
+inline infix fun Region.and(r: Rect) = this + r
+
+/**
+ * Return the union of this region and the specified region as a new region.
+ */
+inline infix fun Region.and(r: Region) = this + r
+
+/**
+ * Return the intersection of this region and the specified [Rect] as a new region.
+ */
+inline infix fun Region.or(r: Rect): Region {
+    return Region(this).apply {
+        op(r, Region.Op.INTERSECT)
+    }
+}
+
+/**
+ * Return the intersection of this region and the specified region as a new region.
+ */
+inline infix fun Region.or(r: Region): Region {
+    return Region(this).apply {
+        op(r, Region.Op.INTERSECT)
+    }
+}
+
+/**
+ * Return the union minus the intersection of this region and the specified [Rect]
+ * as a new region.
+ */
+inline infix fun Region.xor(r: Rect): Region {
+    return Region(this).apply {
+        op(r, Region.Op.XOR)
+    }
+}
+
+/**
+ * Return the union minus the intersection of this region and the specified region
+ * as a new region.
+ */
+inline infix fun Region.xor(r: Region): Region {
+    return Region(this).apply {
+        op(r, Region.Op.XOR)
+    }
+}
+
+/** Performs the given action on each rect in this region. */
+inline fun Region.forEach(action: (rect: Rect) -> Unit) {
+    val iterator = RegionIterator(this)
+    while (true) {
+        val r = Rect()
+        if (!iterator.next(r)) {
+            break
+        }
+        action(r)
+    }
+}
+
+/** Returns an [Iterator] over the rects in this region. */
+operator fun Region.iterator() = object : Iterator<Rect> {
+    private val iterator = RegionIterator(this@iterator)
+    private val rect = Rect()
+    private var hasMore = iterator.next(rect)
+
+    override fun hasNext() = hasMore
+
+    override fun next(): Rect {
+        if (hasMore) {
+            val r = Rect(rect)
+            hasMore = iterator.next(rect)
+            return r
+        }
+        throw IndexOutOfBoundsException()
+    }
+}
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogConsumer.kt b/core/ktx/src/main/java/androidx/core/graphics/Shader.kt
similarity index 61%
copy from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogConsumer.kt
copy to core/ktx/src/main/java/androidx/core/graphics/Shader.kt
index ddebd25..0fb93cb 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogConsumer.kt
+++ b/core/ktx/src/main/java/androidx/core/graphics/Shader.kt
@@ -11,23 +11,20 @@
  * 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.
  */
 
-package android.support.tools.jetifier.core.utils
+package androidx.core.graphics
+
+import android.graphics.Matrix
+import android.graphics.Shader
 
 /**
- * Interface to plug custom logs consumers to [Log].
+ * Wrap the specified [block] in calls to [Shader.getLocalMatrix] and [Shader.setLocalMatrix].
  */
-interface LogConsumer {
-
-    fun error(message: String)
-
-    fun info(message: String)
-
-    fun verbose(message: String)
-
-    fun debug(message: String)
-
+inline fun Shader.transform(block: Matrix.() -> Unit) {
+    val matrix = Matrix()
+    getLocalMatrix(matrix)
+    block(matrix)
+    setLocalMatrix(matrix)
 }
-
diff --git a/core/ktx/src/main/java/androidx/core/graphics/drawable/BitmapDrawable.kt b/core/ktx/src/main/java/androidx/core/graphics/drawable/BitmapDrawable.kt
new file mode 100644
index 0000000..4a96aab
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/drawable/BitmapDrawable.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE", "WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE")
+
+package androidx.core.graphics.drawable
+
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.drawable.BitmapDrawable
+
+/** Create a [BitmapDrawable] from this [Bitmap]. */
+inline fun Bitmap.toDrawable(resources: Resources) = BitmapDrawable(resources, this)
diff --git a/core/ktx/src/main/java/androidx/core/graphics/drawable/ColorDrawable.kt b/core/ktx/src/main/java/androidx/core/graphics/drawable/ColorDrawable.kt
new file mode 100644
index 0000000..bef04df
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/drawable/ColorDrawable.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE", "WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE")
+
+package androidx.core.graphics.drawable
+
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import androidx.annotation.ColorInt
+import androidx.annotation.RequiresApi
+
+/** Create a [ColorDrawable] from this color value. */
+inline fun @receiver:ColorInt Int.toDrawable() = ColorDrawable(this)
+
+/** Create a [ColorDrawable] from this [Color] (via [Color.toArgb]). */
+@RequiresApi(26)
+inline fun Color.toDrawable() = ColorDrawable(toArgb())
diff --git a/core/ktx/src/main/java/androidx/core/graphics/drawable/Drawable.kt b/core/ktx/src/main/java/androidx/core/graphics/drawable/Drawable.kt
new file mode 100644
index 0000000..9e63bbd
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/drawable/Drawable.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 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.core.graphics.drawable
+
+import android.graphics.Bitmap
+import android.graphics.Bitmap.Config
+import android.graphics.Canvas
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import androidx.annotation.Px
+import androidx.core.graphics.component1
+import androidx.core.graphics.component2
+import androidx.core.graphics.component3
+import androidx.core.graphics.component4
+
+/**
+ * Return a [Bitmap] representation of this [Drawable].
+ *
+ * If this instance is a [BitmapDrawable] and the [width], [height], and [config] match, the
+ * underlying [Bitmap] instance will be returned directly. If any of those three properties differ
+ * then a new [Bitmap] is created. For all other [Drawable] types, a new [Bitmap] is created.
+ *
+ * @param width Width of the desired bitmap. Defaults to [Drawable.getIntrinsicWidth].
+ * @param height Height of the desired bitmap. Defaults to [Drawable.getIntrinsicHeight].
+ * @param config Bitmap config of the desired bitmap. Null attempts to use the native config, if
+ * any. Defaults to [Config.ARGB_8888] otherwise.
+ */
+fun Drawable.toBitmap(
+    @Px width: Int = intrinsicWidth,
+    @Px height: Int = intrinsicHeight,
+    config: Config? = null
+): Bitmap {
+    if (this is BitmapDrawable) {
+        if (config == null || bitmap.config == config) {
+            // Fast-path to return original. Bitmap.createScaledBitmap will do this check, but it
+            // involves allocation and two jumps into native code so we perform the check ourselves.
+            if (width == intrinsicWidth && height == intrinsicHeight) {
+                return bitmap
+            }
+            return Bitmap.createScaledBitmap(bitmap, width, height, true)
+        }
+    }
+
+    val (oldLeft, oldTop, oldRight, oldBottom) = bounds
+
+    val bitmap = Bitmap.createBitmap(width, height, config ?: Config.ARGB_8888)
+    setBounds(0, 0, width, height)
+    draw(Canvas(bitmap))
+
+    setBounds(oldLeft, oldTop, oldRight, oldBottom)
+    return bitmap
+}
+
+/**
+ * Updates this drawable's bounds. This version of the method allows using named parameters
+ * to just set one or more axes.
+ *
+ * @see Drawable.setBounds
+ */
+fun Drawable.updateBounds(
+    @Px left: Int = bounds.left,
+    @Px top: Int = bounds.top,
+    @Px right: Int = bounds.right,
+    @Px bottom: Int = bounds.bottom
+) {
+    setBounds(left, top, right, bottom)
+}
diff --git a/core/ktx/src/main/java/androidx/core/graphics/drawable/Icon.kt b/core/ktx/src/main/java/androidx/core/graphics/drawable/Icon.kt
new file mode 100644
index 0000000..a9077e2
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/graphics/drawable/Icon.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.graphics.drawable
+
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import android.net.Uri
+import androidx.annotation.RequiresApi
+
+/**
+ * Create an [Icon] from this adaptive [Bitmap].
+ *
+ * @see Icon.createWithAdaptiveBitmap
+ */
+@RequiresApi(26)
+inline fun Bitmap.toAdaptiveIcon(): Icon = Icon.createWithAdaptiveBitmap(this)
+
+/**
+ * Create an [Icon] from this [Bitmap].
+ *
+ * @see Icon.createWithBitmap
+ */
+@RequiresApi(26)
+inline fun Bitmap.toIcon(): Icon = Icon.createWithBitmap(this)
+
+/**
+ * Create an [Icon] from this [Uri].
+ *
+ * @see Icon.createWithContentUri
+ */
+@RequiresApi(26)
+inline fun Uri.toIcon(): Icon = Icon.createWithContentUri(this)
+
+/**
+ * Create an [Icon] from this [ByteArray].
+ *
+ * @see Icon.createWithData
+ */
+@RequiresApi(26)
+inline fun ByteArray.toIcon(): Icon = Icon.createWithData(this, 0, size)
diff --git a/core/ktx/src/main/java/androidx/core/net/Uri.kt b/core/ktx/src/main/java/androidx/core/net/Uri.kt
new file mode 100644
index 0000000..a5d9f1d
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/net/Uri.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.net
+
+import android.net.Uri
+import java.io.File
+
+/**
+ * Creates a Uri from the given encoded URI string.
+ *
+ * @see Uri.parse
+ */
+inline fun String.toUri(): Uri = Uri.parse(this)
+
+/**
+ * Creates a Uri from the given file.
+ *
+ * @see Uri.fromFile
+ */
+inline fun File.toUri(): Uri = Uri.fromFile(this)
+
+/** Creates a [File] from the given [Uri]. */
+inline fun Uri.toFile(): File = File(path)
diff --git a/core/ktx/src/main/java/androidx/core/os/Bundle.kt b/core/ktx/src/main/java/androidx/core/os/Bundle.kt
new file mode 100644
index 0000000..53da45d
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/os/Bundle.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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.core.os
+
+import android.os.Binder
+import android.os.Build
+import android.os.Bundle
+import android.os.Parcelable
+import android.util.Size
+import android.util.SizeF
+import java.io.Serializable
+
+/**
+ * Returns a new [Bundle] with the given key/value pairs as elements.
+ *
+ * @throws IllegalArgumentException When a value is not a supported type of [Bundle].
+ */
+fun bundleOf(vararg pairs: Pair<String, Any?>) = Bundle(pairs.size).apply {
+    for ((key, value) in pairs) {
+        when (value) {
+            null -> putString(key, null) // Any nullable type will suffice.
+
+            // Scalars
+            is Boolean -> putBoolean(key, value)
+            is Byte -> putByte(key, value)
+            is Char -> putChar(key, value)
+            is Double -> putDouble(key, value)
+            is Float -> putFloat(key, value)
+            is Int -> putInt(key, value)
+            is Long -> putLong(key, value)
+            is Short -> putShort(key, value)
+
+            // References
+            is Bundle -> putBundle(key, value)
+            is CharSequence -> putCharSequence(key, value)
+            is Parcelable -> putParcelable(key, value)
+
+            // Scalar arrays
+            is BooleanArray -> putBooleanArray(key, value)
+            is ByteArray -> putByteArray(key, value)
+            is CharArray -> putCharArray(key, value)
+            is DoubleArray -> putDoubleArray(key, value)
+            is FloatArray -> putFloatArray(key, value)
+            is IntArray -> putIntArray(key, value)
+            is LongArray -> putLongArray(key, value)
+            is ShortArray -> putShortArray(key, value)
+
+            // Reference arrays
+            is Array<*> -> {
+                val componentType = value::class.java.componentType
+                @Suppress("UNCHECKED_CAST") // Checked by reflection.
+                when {
+                    Parcelable::class.java.isAssignableFrom(componentType) -> {
+                        putParcelableArray(key, value as Array<Parcelable>)
+                    }
+                    String::class.java.isAssignableFrom(componentType) -> {
+                        putStringArray(key, value as Array<String>)
+                    }
+                    CharSequence::class.java.isAssignableFrom(componentType) -> {
+                        putCharSequenceArray(key, value as Array<CharSequence>)
+                    }
+                    Serializable::class.java.isAssignableFrom(componentType) -> {
+                        putSerializable(key, value)
+                    }
+                    else -> {
+                        val valueType = componentType.canonicalName
+                        throw IllegalArgumentException(
+                            "Illegal value array type $valueType for key \"$key\"")
+                    }
+                }
+            }
+
+            // Last resort. Also we must check this after Array<*> as all arrays are serializable.
+            is Serializable -> putSerializable(key, value)
+
+            else -> {
+                if (Build.VERSION.SDK_INT >= 18 && value is Binder) {
+                    putBinder(key, value)
+                } else if (Build.VERSION.SDK_INT >= 21 && value is Size) {
+                    putSize(key, value)
+                } else if (Build.VERSION.SDK_INT >= 21 && value is SizeF) {
+                    putSizeF(key, value)
+                } else {
+                    val valueType = value.javaClass.canonicalName
+                    throw IllegalArgumentException("Illegal value type $valueType for key \"$key\"")
+                }
+            }
+        }
+    }
+}
diff --git a/core/ktx/src/main/java/androidx/core/os/Handler.kt b/core/ktx/src/main/java/androidx/core/os/Handler.kt
new file mode 100644
index 0000000..de09a9b
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/os/Handler.kt
@@ -0,0 +1,68 @@
+/*
+ * 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 androidx.core.os
+
+import android.os.Handler
+
+/**
+ * Version of [Handler.postDelayed] which re-orders the parameters, allowing the action to be
+ * placed outside of parentheses.
+ *
+ * ```
+ * handler.postDelayed(200) {
+ *     doSomething()
+ * }
+ * ```
+ *
+ * @return the created Runnable
+ */
+inline fun Handler.postDelayed(
+    delayInMillis: Long,
+    token: Any? = null,
+    crossinline action: () -> Unit
+): Runnable {
+    val runnable = Runnable { action() }
+    if (token == null) {
+        postDelayed(runnable, delayInMillis)
+    } else {
+        HandlerCompat.postDelayed(this, runnable, token, delayInMillis)
+    }
+    return runnable
+}
+
+/**
+ * Version of [Handler.postAtTime] which re-orders the parameters, allowing the action to be
+ * placed outside of parentheses.
+ *
+ * ```
+ * handler.postAtTime(200) {
+ *     doSomething()
+ * }
+ * ```
+ *
+ * @param token An optional object with which the posted message will be associated.
+ * @return the created Runnable
+ */
+inline fun Handler.postAtTime(
+    uptimeMillis: Long,
+    token: Any? = null,
+    crossinline action: () -> Unit
+): Runnable {
+    val runnable = Runnable { action() }
+    postAtTime(runnable, token, uptimeMillis)
+    return runnable
+}
diff --git a/core/ktx/src/main/java/androidx/core/os/PersistableBundle.kt b/core/ktx/src/main/java/androidx/core/os/PersistableBundle.kt
new file mode 100644
index 0000000..261e72b
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/os/PersistableBundle.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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.core.os
+
+import android.os.Build
+import android.os.PersistableBundle
+import androidx.annotation.RequiresApi
+
+/**
+ * Returns a new [PersistableBundle] with the given key/value pairs as elements.
+ *
+ * @throws IllegalArgumentException When a value is not a supported type of [PersistableBundle].
+ */
+@RequiresApi(21)
+fun persistableBundleOf(vararg pairs: Pair<String, Any?>) = PersistableBundle(pairs.size).apply {
+    for ((key, value) in pairs) {
+        when (value) {
+            null -> putString(key, null) // Any nullable type will suffice.
+
+            // Scalars
+            is Boolean -> {
+                if (Build.VERSION.SDK_INT >= 22) {
+                    putBoolean(key, value)
+                } else {
+                    throw IllegalArgumentException("Illegal value type boolean for key \"$key\"")
+                }
+            }
+            is Double -> putDouble(key, value)
+            is Int -> putInt(key, value)
+            is Long -> putLong(key, value)
+
+            // References
+            is String -> putString(key, value)
+
+            // Scalar arrays
+            is BooleanArray -> {
+                if (Build.VERSION.SDK_INT >= 22) {
+                    putBooleanArray(key, value)
+                } else {
+                    throw IllegalArgumentException("Illegal value type boolean[] for key \"$key\"")
+                }
+            }
+            is DoubleArray -> putDoubleArray(key, value)
+            is IntArray -> putIntArray(key, value)
+            is LongArray -> putLongArray(key, value)
+
+            // Reference arrays
+            is Array<*> -> {
+                val componentType = value::class.java.componentType
+                @Suppress("UNCHECKED_CAST") // Checked by reflection.
+                when {
+                    String::class.java.isAssignableFrom(componentType) -> {
+                        putStringArray(key, value as Array<String>)
+                    }
+                    else -> {
+                        val valueType = componentType.canonicalName
+                        throw IllegalArgumentException(
+                            "Illegal value array type $valueType for key \"$key\"")
+                    }
+                }
+            }
+
+            else -> {
+                val valueType = value.javaClass.canonicalName
+                throw IllegalArgumentException("Illegal value type $valueType for key \"$key\"")
+            }
+        }
+    }
+}
diff --git a/core/ktx/src/main/java/androidx/core/os/Trace.kt b/core/ktx/src/main/java/androidx/core/os/Trace.kt
new file mode 100644
index 0000000..43bf85e
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/os/Trace.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.os
+
+import android.os.Trace
+
+/**
+ * Wrap the specified [block] in calls to [Trace.beginSection] (with the supplied [sectionName])
+ * and [Trace.endSection].
+ */
+inline fun <T> trace(sectionName: String, block: () -> T): T {
+    TraceCompat.beginSection(sectionName)
+    try {
+        return block()
+    } finally {
+        TraceCompat.endSection()
+    }
+}
diff --git a/core/ktx/src/main/java/androidx/core/preference/PreferenceGroup.kt b/core/ktx/src/main/java/androidx/core/preference/PreferenceGroup.kt
new file mode 100644
index 0000000..b4f267e
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/preference/PreferenceGroup.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.preference
+
+import android.preference.Preference
+import android.preference.PreferenceGroup
+
+/**
+ * Returns the preference with `key`.
+ *
+ * @throws NullPointerException if no preference is found with that key.
+ */
+inline operator fun PreferenceGroup.get(key: CharSequence): Preference = findPreference(key)
+
+/**
+ * Returns the preference at `index`.
+ *
+ * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the count.
+ */
+operator fun PreferenceGroup.get(index: Int): Preference = getPreference(index)
+        ?: throw IndexOutOfBoundsException("Index: $index, Size: $preferenceCount")
+
+/** Returns `true` if `preference` is found in this preference group. */
+operator fun PreferenceGroup.contains(preference: Preference): Boolean {
+    for (index in 0 until size) {
+        if (get(index) == preference) {
+            return true
+        }
+    }
+    return false
+}
+
+/** Adds `preference` to this preference group. */
+inline operator fun PreferenceGroup.plusAssign(preference: Preference) {
+    addPreference(preference)
+}
+
+/** Removes `preference` from this preference group. */
+inline operator fun PreferenceGroup.minusAssign(preference: Preference) {
+    removePreference(preference)
+}
+
+/** Returns the number of preferences in this preference group. */
+inline val PreferenceGroup.size: Int get() = preferenceCount
+
+/** Returns true if this preference group contains no preferences. */
+inline fun PreferenceGroup.isEmpty(): Boolean = size == 0
+
+/** Returns true if this preference group contains one or more preferences. */
+inline fun PreferenceGroup.isNotEmpty(): Boolean = size != 0
+
+/** Performs the given action on each preference in this preference group. */
+inline fun PreferenceGroup.forEach(action: (preference: Preference) -> Unit) {
+    for (index in 0 until size) {
+        action(get(index))
+    }
+}
+
+/** Performs the given action on each preference in this preference group, providing its sequential index. */
+inline fun PreferenceGroup.forEachIndexed(action: (index: Int, preference: Preference) -> Unit) {
+    for (index in 0 until size) {
+        action(index, get(index))
+    }
+}
+
+/** Returns a [MutableIterator] over the preferences in this preference group. */
+operator fun PreferenceGroup.iterator() = object : MutableIterator<Preference> {
+    private var index = 0
+    override fun hasNext() = index < size
+    override fun next() = getPreference(index++) ?: throw IndexOutOfBoundsException()
+    override fun remove() {
+        removePreference(getPreference(--index))
+    }
+}
diff --git a/core/ktx/src/main/java/androidx/core/text/CharSequence.kt b/core/ktx/src/main/java/androidx/core/text/CharSequence.kt
new file mode 100644
index 0000000..d41aed2
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/text/CharSequence.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.text
+
+import android.text.TextUtils
+
+/**
+ * Returns whether the given [CharSequence] contains only digits.
+ *
+ * @see TextUtils.isDigitsOnly
+ */
+inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)
+
+/**
+ * Returns the length that the specified [CharSequence] would have if spaces and ASCII control
+ * characters were trimmed from the start and end, as by [String.trim].
+ *
+ * @see TextUtils.getTrimmedLength
+ */
+inline fun CharSequence.trimmedLength() = TextUtils.getTrimmedLength(this)
diff --git a/core/ktx/src/main/java/androidx/core/text/Html.kt b/core/ktx/src/main/java/androidx/core/text/Html.kt
new file mode 100644
index 0000000..d2636ee
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/text/Html.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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.core.text
+
+import android.annotation.SuppressLint
+import android.text.Html
+import android.text.Html.FROM_HTML_MODE_LEGACY
+import android.text.Html.ImageGetter
+import android.text.Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE
+import android.text.Html.TagHandler
+import android.text.Spanned
+
+/**
+ * Returns a [Spanned] from parsing this string as HTML.
+ *
+ * @param flags Additional option to set the behavior of the HTML parsing. Default is set to
+ * [Html.FROM_HTML_MODE_LEGACY] which was introduced in API 24.
+ * @param imageGetter Returns displayable styled text from the provided HTML string.
+ * @param tagHandler Notified when HTML tags are encountered a tag the parser does
+ * not know how to interpret.
+ *
+ * @see Html.fromHtml
+ */
+fun String.parseAsHtml(
+    @SuppressLint("InlinedApi") flags: Int = FROM_HTML_MODE_LEGACY,
+    imageGetter: ImageGetter? = null,
+    tagHandler: TagHandler? = null
+): Spanned = HtmlCompat.fromHtml(this, flags, imageGetter, tagHandler)
+
+/**
+ * Returns a string of HTML from the spans in this [Spanned].
+ *
+ * @see Html.toHtml
+ */
+fun Spanned.toHtml(
+    @SuppressLint("InlinedApi") option: Int = TO_HTML_PARAGRAPH_LINES_CONSECUTIVE
+): String = HtmlCompat.toHtml(this, option)
diff --git a/core/ktx/src/main/java/androidx/core/text/SpannableString.kt b/core/ktx/src/main/java/androidx/core/text/SpannableString.kt
new file mode 100644
index 0000000..29aa9c2
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/text/SpannableString.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.text
+
+import android.text.Spannable
+import android.text.SpannableString
+import android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE
+
+/**
+ * Returns a new [Spannable] from [CharSequence],
+ * or the source itself if it is already an instance of [SpannableString].
+ */
+inline fun CharSequence.toSpannable(): Spannable = SpannableString.valueOf(this)
+
+/** Adds [span] to the entire text. */
+inline operator fun Spannable.plusAssign(span: Any) =
+    setSpan(span, 0, length, SPAN_INCLUSIVE_EXCLUSIVE)
+
+/** Removes [span] from this text. */
+inline operator fun Spannable.minusAssign(span: Any) = removeSpan(span)
+
+/** Clear all spans from this text. */
+inline fun Spannable.clearSpans() = getSpans<Any>().forEach { removeSpan(it) }
+
+/**
+ * Add [span] to the range [start]&hellip;[end] of the text.
+ *
+ * ```
+ * val s = "Hello, World!".toSpannable()
+ * s[0, 5] = UnderlineSpan()
+ * ```
+ *
+ * Note: The [end] value is exclusive.
+ *
+ * @see Spannable.setSpan
+ */
+inline operator fun Spannable.set(start: Int, end: Int, span: Any) {
+    setSpan(span, start, end, SPAN_INCLUSIVE_EXCLUSIVE)
+}
+
+/**
+ * Add [span] to the [range] of the text.
+ *
+ * ```
+ * val s = "Hello, World!".toSpannable()
+ * s[0..5] = UnderlineSpan()
+ * ```
+ *
+ * Note: The range end value is exclusive.
+ *
+ * @see Spannable.setSpan
+ */
+inline operator fun Spannable.set(range: IntRange, span: Any) {
+    // This looks weird, but endInclusive is just the exact upper value.
+    setSpan(span, range.start, range.endInclusive, SPAN_INCLUSIVE_EXCLUSIVE)
+}
diff --git a/core/ktx/src/main/java/androidx/core/text/SpannableStringBuilder.kt b/core/ktx/src/main/java/androidx/core/text/SpannableStringBuilder.kt
new file mode 100644
index 0000000..5834a34
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/text/SpannableStringBuilder.kt
@@ -0,0 +1,136 @@
+/*
+ * 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 androidx.core.text
+
+import android.graphics.Typeface.BOLD
+import android.graphics.Typeface.ITALIC
+import android.text.Spannable.SPAN_INCLUSIVE_EXCLUSIVE
+import android.text.SpannableStringBuilder
+import android.text.SpannedString
+import android.text.style.BackgroundColorSpan
+import android.text.style.ForegroundColorSpan
+import android.text.style.RelativeSizeSpan
+import android.text.style.StrikethroughSpan
+import android.text.style.StyleSpan
+import android.text.style.UnderlineSpan
+import androidx.annotation.ColorInt
+
+/**
+ * Builds new string by populating a newly created [SpannableStringBuilder] using the provided
+ * [builderAction] and then converting it to [SpannedString].
+ */
+inline fun buildSpannedString(builderAction: SpannableStringBuilder.() -> Unit): SpannedString {
+    val builder = SpannableStringBuilder()
+    builder.builderAction()
+    return SpannedString(builder)
+}
+
+/**
+ * Wrap appended text in [builderAction] in [spans].
+ *
+ * Note: the spans will only have the correct position if the [builderAction] only appends or
+ * replaces text. Inserting, deleting, or clearing the text will cause the span to be placed at
+ * an incorrect position.
+ */
+inline fun SpannableStringBuilder.inSpans(
+    vararg spans: Any,
+    builderAction: SpannableStringBuilder.() -> Unit
+): SpannableStringBuilder {
+    val start = length
+    builderAction()
+    for (span in spans) setSpan(span, start, length, SPAN_INCLUSIVE_EXCLUSIVE)
+    return this
+}
+
+/**
+ * Wrap appended text in [builderAction] in [span].
+ *
+ * Note: the span will only have the correct position if the `builderAction` only appends or
+ * replaces text. Inserting, deleting, or clearing the text will cause the span to be placed at
+ * an incorrect position.
+ */
+inline fun SpannableStringBuilder.inSpans(
+    span: Any,
+    builderAction: SpannableStringBuilder.() -> Unit
+): SpannableStringBuilder {
+    val start = length
+    builderAction()
+    setSpan(span, start, length, SPAN_INCLUSIVE_EXCLUSIVE)
+    return this
+}
+
+/**
+ * Wrap appended text in [builderAction] in a bold [StyleSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.bold(builderAction: SpannableStringBuilder.() -> Unit) =
+    inSpans(StyleSpan(BOLD), builderAction = builderAction)
+
+/**
+ * Wrap appended text in [builderAction] in an italic [StyleSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.italic(builderAction: SpannableStringBuilder.() -> Unit) =
+    inSpans(StyleSpan(ITALIC), builderAction = builderAction)
+
+/**
+ * Wrap appended text in [builderAction] in an [UnderlineSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.underline(builderAction: SpannableStringBuilder.() -> Unit) =
+    inSpans(UnderlineSpan(), builderAction = builderAction)
+
+/**
+ * Wrap appended text in [builderAction] in a [ForegroundColorSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.color(
+    @ColorInt color: Int,
+    builderAction: SpannableStringBuilder.() -> Unit
+) = inSpans(ForegroundColorSpan(color), builderAction = builderAction)
+
+/**
+ * Wrap appended text in [builderAction] in a [BackgroundColorSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.backgroundColor(
+    @ColorInt color: Int,
+    builderAction: SpannableStringBuilder.() -> Unit
+) = inSpans(BackgroundColorSpan(color), builderAction = builderAction)
+
+/**
+ * Wrap appended text in [builderAction] in a [StrikethroughSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.strikeThrough(builderAction: SpannableStringBuilder.() -> Unit) =
+    inSpans(StrikethroughSpan(), builderAction = builderAction)
+
+/**
+ * Wrap appended text in [builderAction] in a [RelativeSizeSpan].
+ *
+ * @see SpannableStringBuilder.inSpans
+ */
+inline fun SpannableStringBuilder.scale(
+    proportion: Float,
+    builderAction: SpannableStringBuilder.() -> Unit
+) = inSpans(RelativeSizeSpan(proportion), builderAction = builderAction)
diff --git a/core/ktx/src/main/java/androidx/core/text/SpannedString.kt b/core/ktx/src/main/java/androidx/core/text/SpannedString.kt
new file mode 100644
index 0000000..db27511
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/text/SpannedString.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.text
+
+import android.text.Spanned
+import android.text.SpannedString
+
+/**
+ * Returns a new [Spanned] from [CharSequence],
+ * or the source itself if it is already an instance of [SpannedString].
+ */
+inline fun CharSequence.toSpanned(): Spanned = SpannedString.valueOf(this)
+
+/** Get all spans that are instance of [T]. */
+inline fun <reified T : Any> Spanned.getSpans(start: Int = 0, end: Int = length): Array<out T> =
+    getSpans(start, end, T::class.java)
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt b/core/ktx/src/main/java/androidx/core/text/String.kt
similarity index 67%
copy from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
copy to core/ktx/src/main/java/androidx/core/text/String.kt
index 67f6d84..8bffdd0 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
+++ b/core/ktx/src/main/java/androidx/core/text/String.kt
@@ -11,14 +11,18 @@
  * 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.
  */
 
-package android.support.tools.jetifier.core
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
 
-import java.io.File
+package androidx.core.text
+
+import android.text.TextUtils
 
 /**
- * Represents a source file ([from]) to be mapped to a target file ([to]).
+ * Html-encode the string.
+ *
+ * @see TextUtils.htmlEncode
  */
-data class FileMapping(val from: File, val to: File)
\ No newline at end of file
+inline fun String.htmlEncode(): String = TextUtils.htmlEncode(this)
diff --git a/core/ktx/src/main/java/androidx/core/transition/Transition.kt b/core/ktx/src/main/java/androidx/core/transition/Transition.kt
new file mode 100644
index 0000000..b79f2cb
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/transition/Transition.kt
@@ -0,0 +1,94 @@
+/*
+ * 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 androidx.core.transition
+
+import android.transition.Transition
+import androidx.annotation.RequiresApi
+
+/**
+ * Add an action which will be invoked when this transition has ended.
+ */
+@RequiresApi(19)
+fun Transition.doOnEnd(action: (transition: Transition) -> Unit) {
+    addListener(onEnd = action)
+}
+
+/**
+ * Add an action which will be invoked when this transition has started.
+ */
+@RequiresApi(19)
+fun Transition.doOnStart(action: (transition: Transition) -> Unit) {
+    addListener(onStart = action)
+}
+
+/**
+ * Add an action which will be invoked when this transition has been cancelled.
+ */
+@RequiresApi(19)
+fun Transition.doOnCancel(action: (transition: Transition) -> Unit) {
+    addListener(onCancel = action)
+}
+
+/**
+ * Add an action which will be invoked when this transition has resumed after a pause.
+ */
+@RequiresApi(19)
+fun Transition.doOnResume(action: (transition: Transition) -> Unit) {
+    addListener(onResume = action)
+}
+
+/**
+ * Add an action which will be invoked when this transition has been paused.
+ */
+@RequiresApi(19)
+fun Transition.doOnPause(action: (transition: Transition) -> Unit) {
+    addListener(onPause = action)
+}
+
+/**
+ * Add a listener to this Transition using the provided actions.
+ */
+@RequiresApi(19)
+fun Transition.addListener(
+    onEnd: ((transition: Transition) -> Unit)? = null,
+    onStart: ((transition: Transition) -> Unit)? = null,
+    onCancel: ((transition: Transition) -> Unit)? = null,
+    onResume: ((transition: Transition) -> Unit)? = null,
+    onPause: ((transition: Transition) -> Unit)? = null
+) {
+    addListener(object : Transition.TransitionListener {
+        override fun onTransitionEnd(transition: Transition) {
+            onEnd?.invoke(transition)
+        }
+
+        override fun onTransitionResume(transition: Transition) {
+            onResume?.invoke(transition)
+        }
+
+        override fun onTransitionPause(transition: Transition) {
+            onPause?.invoke(transition)
+        }
+
+        override fun onTransitionCancel(transition: Transition) {
+            onCancel?.invoke(transition)
+        }
+
+        override fun onTransitionStart(transition: Transition) {
+            onStart?.invoke(transition)
+        }
+    })
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/ArrayMap.kt b/core/ktx/src/main/java/androidx/core/util/ArrayMap.kt
new file mode 100644
index 0000000..b676403
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/ArrayMap.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.ArrayMap
+import androidx.annotation.RequiresApi
+import kotlin.Pair
+
+/** Returns an empty new [ArrayMap]. */
+@RequiresApi(19)
+inline fun <K, V> arrayMapOf(): ArrayMap<K, V> = ArrayMap()
+
+/**
+ * Returns a new [ArrayMap] with the specified contents, given as a list of pairs where the first
+ * component is the key and the second component is the value.
+ *
+ * If multiple pairs have the same key, the resulting map will contain the value from the last of
+ * those pairs.
+ */
+@RequiresApi(19)
+fun <K, V> arrayMapOf(vararg pairs: Pair<K, V>): ArrayMap<K, V> {
+    val map = ArrayMap<K, V>(pairs.size)
+    for (pair in pairs) {
+        map[pair.first] = pair.second
+    }
+    return map
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/ArraySet.kt b/core/ktx/src/main/java/androidx/core/util/ArraySet.kt
new file mode 100644
index 0000000..6773d23
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/ArraySet.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.ArraySet
+import androidx.annotation.RequiresApi
+
+/** Returns an empty new [ArraySet]. */
+@RequiresApi(23)
+inline fun <T> arraySetOf(): ArraySet<T> = ArraySet()
+
+/** Returns a new [ArraySet] with the specified contents. */
+@RequiresApi(23)
+fun <T> arraySetOf(vararg values: T): ArraySet<T> {
+    val set = ArraySet<T>(values.size)
+    @Suppress("LoopToCallChain") // Causes needless copy to a list.
+    for (value in values) {
+        set.add(value)
+    }
+    return set
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/AtomicFile.kt b/core/ktx/src/main/java/androidx/core/util/AtomicFile.kt
new file mode 100644
index 0000000..b8fbba7
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/AtomicFile.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to other public API.
+
+package androidx.core.util
+
+import android.util.AtomicFile
+import androidx.annotation.RequiresApi
+import java.io.FileOutputStream
+import java.nio.charset.Charset
+
+/**
+ * Perform the write operations inside [block] on this file. If [block] throws an exception the
+ * write will be failed. Otherwise the write will be applied atomically to the file.
+ */
+@RequiresApi(17)
+inline fun AtomicFile.tryWrite(block: (out: FileOutputStream) -> Unit) {
+    val stream = startWrite()
+    var success = false
+    try {
+        block(stream)
+        success = true
+    } finally {
+        if (success) {
+            finishWrite(stream)
+        } else {
+            failWrite(stream)
+        }
+    }
+}
+
+/**
+ * Sets the content of this file as an [array] of bytes.
+ */
+@RequiresApi(17)
+fun AtomicFile.writeBytes(array: ByteArray) {
+    tryWrite {
+        it.write(array)
+    }
+}
+
+/**
+ * Sets the content of this file as [text] encoded using UTF-8 or specified [charset].
+ * If this file exists, it becomes overwritten.
+ */
+@RequiresApi(17)
+fun AtomicFile.writeText(text: String, charset: Charset = Charsets.UTF_8) {
+    writeBytes(text.toByteArray(charset))
+}
+
+/**
+ * Gets the entire content of this file as a byte array.
+ *
+ * This method is not recommended on huge files. It has an internal limitation of 2 GB file size.
+ */
+@RequiresApi(17)
+inline fun AtomicFile.readBytes(): ByteArray = readFully()
+
+/**
+ * Gets the entire content of this file as a String using UTF-8 or specified [charset].
+ *
+ * This method is not recommended on huge files. It has an internal limitation of 2 GB file size.
+ */
+@RequiresApi(17)
+fun AtomicFile.readText(charset: Charset = Charsets.UTF_8): String {
+    return readFully().toString(charset)
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/Half.kt b/core/ktx/src/main/java/androidx/core/util/Half.kt
new file mode 100644
index 0000000..409c01e
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/Half.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to other public API.
+
+package androidx.core.util
+
+import android.util.Half
+import androidx.annotation.HalfFloat
+import androidx.annotation.RequiresApi
+
+/**
+ * Returns a [Half] instance representing given [Short].
+ *
+ * @see Half.valueOf
+ */
+// TODO https://youtrack.jetbrains.com/issue/KT-21696
+@Suppress("WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE")
+@RequiresApi(26)
+inline fun @receiver:HalfFloat Short.toHalf(): Half = Half.valueOf(this)
+
+/**
+ * Returns a [Half] instance representing given [Float].
+ *
+ * @see Half.valueOf
+ */
+@RequiresApi(26)
+inline fun Float.toHalf(): Half = Half.valueOf(this)
+
+/**
+ * Returns a [Half] instance representing given [Double].
+ *
+ * @see Half.valueOf
+ */
+@RequiresApi(26)
+inline fun Double.toHalf(): Half = toFloat().toHalf()
+
+/**
+ * Returns a [Half] instance representing given [String].
+ *
+ * @see Half.valueOf
+ */
+@RequiresApi(26)
+inline fun String.toHalf(): Half = Half.valueOf(this)
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt b/core/ktx/src/main/java/androidx/core/util/Locale.kt
similarity index 61%
copy from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
copy to core/ktx/src/main/java/androidx/core/util/Locale.kt
index 67f6d84..2b4312e 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
+++ b/core/ktx/src/main/java/androidx/core/util/Locale.kt
@@ -11,14 +11,19 @@
  * 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.
  */
 
-package android.support.tools.jetifier.core
+package androidx.core.util
 
-import java.io.File
+import android.text.TextUtils
+import androidx.annotation.RequiresApi
+import java.util.Locale
 
 /**
- * Represents a source file ([from]) to be mapped to a target file ([to]).
+ * Returns layout direction for a given locale.
+ * @see TextUtils.getLayoutDirectionFromLocale
  */
-data class FileMapping(val from: File, val to: File)
\ No newline at end of file
+val Locale.layoutDirection: Int
+        @RequiresApi(17)
+        get() = TextUtils.getLayoutDirectionFromLocale(this)
diff --git a/core/ktx/src/main/java/androidx/core/util/LongSparseArray.kt b/core/ktx/src/main/java/androidx/core/util/LongSparseArray.kt
new file mode 100644
index 0000000..cd9eb4c
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/LongSparseArray.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.LongSparseArray
+import androidx.annotation.RequiresApi
+
+/** Returns the number of key/value pairs in the collection. */
+@get:RequiresApi(16)
+inline val <T> LongSparseArray<T>.size get() = size()
+
+/** Returns true if the collection contains [key]. */
+@RequiresApi(16)
+inline operator fun <T> LongSparseArray<T>.contains(key: Long) = indexOfKey(key) >= 0
+
+/** Allows the use of the index operator for storing values in the collection. */
+@RequiresApi(16)
+inline operator fun <T> LongSparseArray<T>.set(key: Long, value: T) = put(key, value)
+
+/** Creates a new collection by adding or replacing entries from [other]. */
+@RequiresApi(16)
+operator fun <T> LongSparseArray<T>.plus(other: LongSparseArray<T>): LongSparseArray<T> {
+    val new = LongSparseArray<T>(size() + other.size())
+    new.putAll(this)
+    new.putAll(other)
+    return new
+}
+
+/** Returns true if the collection contains [key]. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.containsKey(key: Long) = indexOfKey(key) >= 0
+
+/** Returns true if the collection contains [value]. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.containsValue(value: T) = indexOfValue(value) != -1
+
+/** Return the value corresponding to [key], or [defaultValue] when not present. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.getOrDefault(key: Long, defaultValue: T) =
+    get(key) ?: defaultValue
+
+/** Return the value corresponding to [key], or from [defaultValue] when not present. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.getOrElse(key: Long, defaultValue: () -> T) =
+    get(key) ?: defaultValue()
+
+/** Return true when the collection contains no elements. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.isEmpty() = size() == 0
+
+/** Return true when the collection contains elements. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.isNotEmpty() = size() != 0
+
+/** Removes the entry for [key] only if it is mapped to [value]. */
+@RequiresApi(16)
+fun <T> LongSparseArray<T>.remove(key: Long, value: T): Boolean {
+    val index = indexOfKey(key)
+    if (index != -1 && value == valueAt(index)) {
+        removeAt(index)
+        return true
+    }
+    return false
+}
+
+/** Update this collection by adding or replacing entries from [other]. */
+@RequiresApi(16)
+fun <T> LongSparseArray<T>.putAll(other: LongSparseArray<T>) = other.forEach(::put)
+
+/** Performs the given [action] for each key/value entry. */
+@RequiresApi(16)
+inline fun <T> LongSparseArray<T>.forEach(action: (key: Long, value: T) -> Unit) {
+    for (index in 0 until size()) {
+        action(keyAt(index), valueAt(index))
+    }
+}
+
+/** Return an iterator over the collection's keys. */
+@RequiresApi(16)
+fun <T> LongSparseArray<T>.keyIterator(): LongIterator = object : LongIterator() {
+    var index = 0
+    override fun hasNext() = index < size()
+    override fun nextLong() = keyAt(index++)
+}
+
+/** Return an iterator over the collection's values. */
+@RequiresApi(16)
+fun <T> LongSparseArray<T>.valueIterator(): Iterator<T> = object : Iterator<T> {
+    var index = 0
+    override fun hasNext() = index < size()
+    override fun next() = valueAt(index++)
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/LruCache.kt b/core/ktx/src/main/java/androidx/core/util/LruCache.kt
new file mode 100644
index 0000000..44c2f8d
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/LruCache.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 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.core.util
+
+import android.util.LruCache
+
+/**
+ * Creates an [LruCache] with the given parameters.
+ *
+ * @param maxSize for caches that do not specify [sizeOf], this is
+ * the maximum number of entries in the cache. For all other caches,
+ * this is the maximum sum of the sizes of the entries in this cache.
+ * @param sizeOf function that returns the size of the entry for key and value in
+ * user-defined units. The default implementation returns 1.
+ * @param create a create called after a cache miss to compute a value for the corresponding key.
+ * Returns the computed value or null if no value can be computed. The default implementation
+ * returns null.
+ * @param onEntryRemoved a function called for entries that have been evicted or removed.
+ *
+ * @see LruCache.sizeOf
+ * @see LruCache.create
+ * @see LruCache.entryRemoved
+ */
+inline fun <K : Any, V : Any> lruCache(
+    maxSize: Int,
+    crossinline sizeOf: (key: K, value: V) -> Int = { _, _ -> 1 },
+    @Suppress("USELESS_CAST") // https://youtrack.jetbrains.com/issue/KT-21946
+    crossinline create: (key: K) -> V? = { null as V? },
+    crossinline onEntryRemoved: (evicted: Boolean, key: K, oldValue: V, newValue: V?) -> Unit =
+        { _, _, _, _ -> }
+): LruCache<K, V> {
+    return object : LruCache<K, V>(maxSize) {
+        override fun sizeOf(key: K, value: V) = sizeOf(key, value)
+        override fun create(key: K) = create(key)
+        override fun entryRemoved(evicted: Boolean, key: K, oldValue: V, newValue: V?) {
+            onEntryRemoved(evicted, key, oldValue, newValue)
+        }
+    }
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/Pair.kt b/core/ktx/src/main/java/androidx/core/util/Pair.kt
new file mode 100644
index 0000000..cf12156
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/Pair.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.Pair
+
+/**
+ * Returns the first component of the pair.
+ *
+ * This method allows to use destructuring declarations when working with pairs, for example:
+ * ```
+ * val (first, second) = myPair
+ * ```
+ */
+@Suppress("HasPlatformType") // Intentionally propagating platform type with unknown nullability.
+inline operator fun <F, S> Pair<F, S>.component1() = first
+
+/**
+ * Returns the second component of the pair.
+ *
+ * This method allows to use destructuring declarations when working with pairs, for example:
+ * ```
+ * val (first, second) = myPair
+ * ```
+ */
+@Suppress("HasPlatformType") // Intentionally propagating platform type with unknown nullability.
+inline operator fun <F, S> Pair<F, S>.component2() = second
+
+/** Returns this [Pair] as a [kotlin.Pair]. */
+inline fun <F, S> Pair<F, S>.toKotlinPair() = kotlin.Pair(first, second)
+
+/** Returns this [kotlin.Pair] as an Android [Pair]. */
+// Note: the return type is explicitly specified here to prevent always seeing platform types.
+inline fun <F, S> kotlin.Pair<F, S>.toAndroidPair(): Pair<F, S> = Pair(first, second)
diff --git a/core/ktx/src/main/java/androidx/core/util/Range.kt b/core/ktx/src/main/java/androidx/core/util/Range.kt
new file mode 100644
index 0000000..b5f7eb6
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/Range.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.Range
+import androidx.annotation.RequiresApi
+
+/**
+ * Creates a range from this [Comparable] value to [that].
+ *
+ * @throws IllegalArgumentException if this value is comparatively smaller than [that].
+ */
+@RequiresApi(21)
+inline infix fun <T : Comparable<T>> T.rangeTo(that: T): Range<T> = Range(this, that)
+
+/** Return the smallest range that includes this and [value]. */
+@RequiresApi(21)
+inline operator fun <T : Comparable<T>> Range<T>.plus(value: T): Range<T> = extend(value)
+
+/** Return the smallest range that includes this and [other]. */
+@RequiresApi(21)
+inline operator fun <T : Comparable<T>> Range<T>.plus(other: Range<T>): Range<T> = extend(other)
+
+/**
+ * Return the intersection of this range and [other].
+ *
+ * @throws IllegalArgumentException if this is disjoint from [other].
+ */
+@RequiresApi(21)
+inline infix fun <T : Comparable<T>> Range<T>.and(other: Range<T>): Range<T> = intersect(other)
+
+/** Returns this [Range] as a [ClosedRange]. */
+@RequiresApi(21)
+fun <T : Comparable<T>> Range<T>.toClosedRange(): ClosedRange<T> = object : ClosedRange<T> {
+    override val endInclusive get() = upper
+    override val start get() = lower
+}
+
+/** Returns this [ClosedRange] as a [Range]. */
+@RequiresApi(21)
+fun <T : Comparable<T>> ClosedRange<T>.toRange(): Range<T> = Range(start, endInclusive)
diff --git a/core/ktx/src/main/java/androidx/core/util/Size.kt b/core/ktx/src/main/java/androidx/core/util/Size.kt
new file mode 100644
index 0000000..34f6854
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/Size.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.util
+
+import android.util.Size
+import android.util.SizeF
+import androidx.annotation.RequiresApi
+
+/**
+ * Returns "width", the first component of this [Size].
+ *
+ * This method allows to use destructuring declarations when working with
+ * sizes, for example:
+ * ```
+ * val (w, h) = mySize
+ * ```
+ */
+@RequiresApi(21)
+inline operator fun Size.component1() = width
+
+/**
+ * Returns "height", the second component of this [Size].
+ *
+ * This method allows to use destructuring declarations when working with
+ * sizes, for example:
+ * ```
+ * val (w, h) = mySize
+ * ```
+ */
+@RequiresApi(21)
+inline operator fun Size.component2() = height
+
+/**
+ * Returns "width", the first component of this [SizeF].
+ *
+ * This method allows to use destructuring declarations when working with
+ * sizes, for example:
+ * ```
+ * val (w, h) = mySize
+ * ```
+ */
+@RequiresApi(21)
+inline operator fun SizeF.component1() = width
+
+/**
+ * Returns "height", the second component of this [SizeF].
+ *
+ * This method allows to use destructuring declarations when working with
+ * sizes, for example:
+ * ```
+ * val (w, h) = mySize
+ * ```
+ */
+@RequiresApi(21)
+inline operator fun SizeF.component2() = height
diff --git a/core/ktx/src/main/java/androidx/core/util/SparseArray.kt b/core/ktx/src/main/java/androidx/core/util/SparseArray.kt
new file mode 100644
index 0000000..2b34a46
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/SparseArray.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.SparseArray
+
+/** Returns the number of key/value pairs in the collection. */
+inline val <T> SparseArray<T>.size get() = size()
+
+/** Returns true if the collection contains [key]. */
+inline operator fun <T> SparseArray<T>.contains(key: Int) = indexOfKey(key) >= 0
+
+/** Allows the use of the index operator for storing values in the collection. */
+inline operator fun <T> SparseArray<T>.set(key: Int, value: T) = put(key, value)
+
+/** Creates a new collection by adding or replacing entries from [other]. */
+operator fun <T> SparseArray<T>.plus(other: SparseArray<T>): SparseArray<T> {
+    val new = SparseArray<T>(size() + other.size())
+    new.putAll(this)
+    new.putAll(other)
+    return new
+}
+
+/** Returns true if the collection contains [key]. */
+inline fun <T> SparseArray<T>.containsKey(key: Int) = indexOfKey(key) >= 0
+
+/** Returns true if the collection contains [value]. */
+inline fun <T> SparseArray<T>.containsValue(value: T) = indexOfValue(value) != -1
+
+/** Return the value corresponding to [key], or [defaultValue] when not present. */
+inline fun <T> SparseArray<T>.getOrDefault(key: Int, defaultValue: T) = get(key) ?: defaultValue
+
+/** Return the value corresponding to [key], or from [defaultValue] when not present. */
+inline fun <T> SparseArray<T>.getOrElse(key: Int, defaultValue: () -> T) =
+    get(key) ?: defaultValue()
+
+/** Return true when the collection contains no elements. */
+inline fun <T> SparseArray<T>.isEmpty() = size() == 0
+
+/** Return true when the collection contains elements. */
+inline fun <T> SparseArray<T>.isNotEmpty() = size() != 0
+
+/** Removes the entry for [key] only if it is mapped to [value]. */
+fun <T> SparseArray<T>.remove(key: Int, value: T): Boolean {
+    val index = indexOfKey(key)
+    if (index != -1 && value == valueAt(index)) {
+        removeAt(index)
+        return true
+    }
+    return false
+}
+
+/** Update this collection by adding or replacing entries from [other]. */
+fun <T> SparseArray<T>.putAll(other: SparseArray<T>) = other.forEach(::put)
+
+/** Performs the given [action] for each key/value entry. */
+inline fun <T> SparseArray<T>.forEach(action: (key: Int, value: T) -> Unit) {
+    for (index in 0 until size()) {
+        action(keyAt(index), valueAt(index))
+    }
+}
+
+/** Return an iterator over the collection's keys. */
+fun <T> SparseArray<T>.keyIterator(): IntIterator = object : IntIterator() {
+    var index = 0
+    override fun hasNext() = index < size()
+    override fun nextInt() = keyAt(index++)
+}
+
+/** Return an iterator over the collection's values. */
+fun <T> SparseArray<T>.valueIterator(): Iterator<T> = object : Iterator<T> {
+    var index = 0
+    override fun hasNext() = index < size()
+    override fun next() = valueAt(index++)
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/SparseBooleanArray.kt b/core/ktx/src/main/java/androidx/core/util/SparseBooleanArray.kt
new file mode 100644
index 0000000..b349326
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/SparseBooleanArray.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.SparseBooleanArray
+
+/** Returns the number of key/value pairs in the collection. */
+inline val SparseBooleanArray.size get() = size()
+
+/** Returns true if the collection contains [key]. */
+inline operator fun SparseBooleanArray.contains(key: Int) = indexOfKey(key) >= 0
+
+/** Allows the use of the index operator for storing values in the collection. */
+inline operator fun SparseBooleanArray.set(key: Int, value: Boolean) = put(key, value)
+
+/** Creates a new collection by adding or replacing entries from [other]. */
+operator fun SparseBooleanArray.plus(other: SparseBooleanArray): SparseBooleanArray {
+    val new = SparseBooleanArray(size() + other.size())
+    new.putAll(this)
+    new.putAll(other)
+    return new
+}
+
+/** Returns true if the collection contains [key]. */
+inline fun SparseBooleanArray.containsKey(key: Int) = indexOfKey(key) >= 0
+
+/** Returns true if the collection contains [value]. */
+inline fun SparseBooleanArray.containsValue(value: Boolean) = indexOfValue(value) != -1
+
+/** Return the value corresponding to [key], or [defaultValue] when not present. */
+inline fun SparseBooleanArray.getOrDefault(key: Int, defaultValue: Boolean) = get(key, defaultValue)
+
+/** Return the value corresponding to [key], or from [defaultValue] when not present. */
+inline fun SparseBooleanArray.getOrElse(key: Int, defaultValue: () -> Boolean) =
+    indexOfKey(key).let { if (it != -1) valueAt(it) else defaultValue() }
+
+/** Return true when the collection contains no elements. */
+inline fun SparseBooleanArray.isEmpty() = size() == 0
+
+/** Return true when the collection contains elements. */
+inline fun SparseBooleanArray.isNotEmpty() = size() != 0
+
+/** Removes the entry for [key] only if it is mapped to [value]. */
+fun SparseBooleanArray.remove(key: Int, value: Boolean): Boolean {
+    val index = indexOfKey(key)
+    if (index != -1 && value == valueAt(index)) {
+        // Delete by key because of https://issuetracker.google.com/issues/70934959.
+        delete(key)
+        return true
+    }
+    return false
+}
+
+/** Update this collection by adding or replacing entries from [other]. */
+fun SparseBooleanArray.putAll(other: SparseBooleanArray) = other.forEach(::put)
+
+/** Performs the given [action] for each key/value entry. */
+inline fun SparseBooleanArray.forEach(action: (key: Int, value: Boolean) -> Unit) {
+    for (index in 0 until size()) {
+        action(keyAt(index), valueAt(index))
+    }
+}
+
+/** Return an iterator over the collection's keys. */
+fun SparseBooleanArray.keyIterator(): IntIterator = object : IntIterator() {
+    var index = 0
+    override fun hasNext() = index < size()
+    override fun nextInt() = keyAt(index++)
+}
+
+/** Return an iterator over the collection's values. */
+fun SparseBooleanArray.valueIterator(): BooleanIterator = object : BooleanIterator() {
+    var index = 0
+    override fun hasNext() = index < size()
+    override fun nextBoolean() = valueAt(index++)
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/SparseIntArray.kt b/core/ktx/src/main/java/androidx/core/util/SparseIntArray.kt
new file mode 100644
index 0000000..bba57c6
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/SparseIntArray.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.SparseIntArray
+
+/** Returns the number of key/value pairs in the collection. */
+inline val SparseIntArray.size get() = size()
+
+/** Returns true if the collection contains [key]. */
+inline operator fun SparseIntArray.contains(key: Int) = indexOfKey(key) >= 0
+
+/** Allows the use of the index operator for storing values in the collection. */
+inline operator fun SparseIntArray.set(key: Int, value: Int) = put(key, value)
+
+/** Creates a new collection by adding or replacing entries from [other]. */
+operator fun SparseIntArray.plus(other: SparseIntArray): SparseIntArray {
+    val new = SparseIntArray(size() + other.size())
+    new.putAll(this)
+    new.putAll(other)
+    return new
+}
+
+/** Returns true if the collection contains [key]. */
+inline fun SparseIntArray.containsKey(key: Int) = indexOfKey(key) >= 0
+
+/** Returns true if the collection contains [value]. */
+inline fun SparseIntArray.containsValue(value: Int) = indexOfValue(value) != -1
+
+/** Return the value corresponding to [key], or [defaultValue] when not present. */
+inline fun SparseIntArray.getOrDefault(key: Int, defaultValue: Int) = get(key, defaultValue)
+
+/** Return the value corresponding to [key], or from [defaultValue] when not present. */
+inline fun SparseIntArray.getOrElse(key: Int, defaultValue: () -> Int) =
+    indexOfKey(key).let { if (it != -1) valueAt(it) else defaultValue() }
+
+/** Return true when the collection contains no elements. */
+inline fun SparseIntArray.isEmpty() = size() == 0
+
+/** Return true when the collection contains elements. */
+inline fun SparseIntArray.isNotEmpty() = size() != 0
+
+/** Removes the entry for [key] only if it is mapped to [value]. */
+fun SparseIntArray.remove(key: Int, value: Int): Boolean {
+    val index = indexOfKey(key)
+    if (index != -1 && value == valueAt(index)) {
+        removeAt(index)
+        return true
+    }
+    return false
+}
+
+/** Update this collection by adding or replacing entries from [other]. */
+fun SparseIntArray.putAll(other: SparseIntArray) = other.forEach(::put)
+
+/** Performs the given [action] for each key/value entry. */
+inline fun SparseIntArray.forEach(action: (key: Int, value: Int) -> Unit) {
+    for (index in 0 until size()) {
+        action(keyAt(index), valueAt(index))
+    }
+}
+
+/** Return an iterator over the collection's keys. */
+fun SparseIntArray.keyIterator(): IntIterator = object : IntIterator() {
+    var index = 0
+    override fun hasNext() = index < size()
+    override fun nextInt() = keyAt(index++)
+}
+
+/** Return an iterator over the collection's values. */
+fun SparseIntArray.valueIterator(): IntIterator = object : IntIterator() {
+    var index = 0
+    override fun hasNext() = index < size()
+    override fun nextInt() = valueAt(index++)
+}
diff --git a/core/ktx/src/main/java/androidx/core/util/SparseLongArray.kt b/core/ktx/src/main/java/androidx/core/util/SparseLongArray.kt
new file mode 100644
index 0000000..68b4763
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/SparseLongArray.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
+
+package androidx.core.util
+
+import android.util.SparseLongArray
+import androidx.annotation.RequiresApi
+
+/** Returns the number of key/value entries in the collection. */
+@get:RequiresApi(18)
+inline val SparseLongArray.size get() = size()
+
+/** Returns true if the collection contains [key]. */
+@RequiresApi(18)
+inline operator fun SparseLongArray.contains(key: Int) = indexOfKey(key) >= 0
+
+/** Allows the use of the index operator for storing values in the collection. */
+@RequiresApi(18)
+inline operator fun SparseLongArray.set(key: Int, value: Long) = put(key, value)
+
+/** Creates a new collection by adding or replacing entries from [other]. */
+@RequiresApi(18)
+operator fun SparseLongArray.plus(other: SparseLongArray): SparseLongArray {
+    val new = SparseLongArray(size() + other.size())
+    new.putAll(this)
+    new.putAll(other)
+    return new
+}
+
+/** Returns true if the collection contains [key]. */
+@RequiresApi(18)
+inline fun SparseLongArray.containsKey(key: Int) = indexOfKey(key) >= 0
+
+/** Returns true if the collection contains [value]. */
+@RequiresApi(18)
+inline fun SparseLongArray.containsValue(value: Long) = indexOfValue(value) != -1
+
+/** Return the value corresponding to [key], or [defaultValue] when not present. */
+@RequiresApi(18)
+inline fun SparseLongArray.getOrDefault(key: Int, defaultValue: Long) = get(key, defaultValue)
+
+/** Return the value corresponding to [key], or from [defaultValue] when not present. */
+@RequiresApi(18)
+inline fun SparseLongArray.getOrElse(key: Int, defaultValue: () -> Long) =
+    indexOfKey(key).let { if (it != -1) valueAt(it) else defaultValue() }
+
+/** Return true when the collection contains no elements. */
+@RequiresApi(18)
+inline fun SparseLongArray.isEmpty() = size() == 0
+
+/** Return true when the collection contains elements. */
+@RequiresApi(18)
+inline fun SparseLongArray.isNotEmpty() = size() != 0
+
+/** Removes the entry for [key] only if it is set to [value]. */
+@RequiresApi(18)
+fun SparseLongArray.remove(key: Int, value: Long): Boolean {
+    val index = indexOfKey(key)
+    if (index != -1 && value == valueAt(index)) {
+        removeAt(index)
+        return true
+    }
+    return false
+}
+
+/** Update this collection by adding or replacing entries from [other]. */
+@RequiresApi(18)
+fun SparseLongArray.putAll(other: SparseLongArray) = other.forEach(::put)
+
+/** Performs the given [action] for each key/value entry. */
+@RequiresApi(18)
+inline fun SparseLongArray.forEach(action: (key: Int, value: Long) -> Unit) {
+    for (index in 0 until size()) {
+        action(keyAt(index), valueAt(index))
+    }
+}
+
+/** Return an iterator over the collection's keys. */
+@RequiresApi(18)
+fun SparseLongArray.keyIterator(): IntIterator = object : IntIterator() {
+    var index = 0
+    override fun hasNext() = index < size()
+    override fun nextInt() = keyAt(index++)
+}
+
+/** Return an iterator over the collection's values. */
+@RequiresApi(18)
+fun SparseLongArray.valueIterator(): LongIterator = object : LongIterator() {
+    var index = 0
+    override fun hasNext() = index < size()
+    override fun nextLong() = valueAt(index++)
+}
diff --git a/core/ktx/src/main/java/androidx/core/view/Menu.kt b/core/ktx/src/main/java/androidx/core/view/Menu.kt
new file mode 100644
index 0000000..7ab479f
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/view/Menu.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.view
+
+import android.view.Menu
+import android.view.MenuItem
+
+/**
+ * Returns the menu at [index].
+ *
+ * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the count.
+ */
+inline operator fun Menu.get(index: Int): MenuItem = getItem(index)
+
+/** Returns `true` if [item] is found in this menu. */
+operator fun Menu.contains(item: MenuItem): Boolean {
+    @Suppress("LoopToCallChain")
+    for (index in 0 until size) {
+        if (getItem(index) == item) {
+            return true
+        }
+    }
+    return false
+}
+
+/** Returns the number of items in this menu. */
+inline val Menu.size get() = size()
+
+/** Returns true if this menu contains no items. */
+inline fun Menu.isEmpty() = size() == 0
+
+/** Returns true if this menu contains one or more items. */
+inline fun Menu.isNotEmpty() = size() != 0
+
+/** Performs the given action on each item in this menu. */
+inline fun Menu.forEach(action: (item: MenuItem) -> Unit) {
+    for (index in 0 until size()) {
+        action(getItem(index))
+    }
+}
+
+/** Performs the given action on each item in this menu, providing its sequential index. */
+inline fun Menu.forEachIndexed(action: (index: Int, item: MenuItem) -> Unit) {
+    for (index in 0 until size()) {
+        action(index, getItem(index))
+    }
+}
+
+/** Returns a [MutableIterator] over the items in this menu. */
+operator fun Menu.iterator() = object : MutableIterator<MenuItem> {
+    private var index = 0
+    override fun hasNext() = index < size()
+    override fun next() = getItem(index++) ?: throw IndexOutOfBoundsException()
+    override fun remove() = removeItem(--index)
+}
diff --git a/core/ktx/src/main/java/androidx/core/view/View.kt b/core/ktx/src/main/java/androidx/core/view/View.kt
new file mode 100644
index 0000000..fcb0256
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/view/View.kt
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to other public API.
+
+package androidx.core.view
+
+import android.graphics.Bitmap
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewTreeObserver
+import android.view.accessibility.AccessibilityEvent
+import androidx.annotation.Px
+import androidx.annotation.RequiresApi
+import androidx.annotation.StringRes
+import androidx.core.graphics.applyCanvas
+
+/**
+ * Performs the given action when this view is next laid out.
+ *
+ * @see doOnLayout
+ */
+inline fun View.doOnNextLayout(crossinline action: (view: View) -> Unit) {
+    addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
+        override fun onLayoutChange(
+            view: View,
+            left: Int,
+            top: Int,
+            right: Int,
+            bottom: Int,
+            oldLeft: Int,
+            oldTop: Int,
+            oldRight: Int,
+            oldBottom: Int
+        ) {
+            view.removeOnLayoutChangeListener(this)
+            action(view)
+        }
+    })
+}
+
+/**
+ * Performs the given action when this view is laid out. If the view has been laid out and it
+ * has not requested a layout, the action will be performed straight away, otherwise the
+ * action will be performed after the view is next laid out.
+ *
+ * @see doOnNextLayout
+ */
+inline fun View.doOnLayout(crossinline action: (view: View) -> Unit) {
+    if (ViewCompat.isLaidOut(this) && !isLayoutRequested) {
+        action(this)
+    } else {
+        doOnNextLayout {
+            action(it)
+        }
+    }
+}
+
+/**
+ * Performs the given action when the view tree is about to be drawn.
+ */
+inline fun View.doOnPreDraw(crossinline action: (view: View) -> Unit) {
+    val vto = viewTreeObserver
+    vto.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
+        override fun onPreDraw(): Boolean {
+            action(this@doOnPreDraw)
+            when {
+                vto.isAlive -> vto.removeOnPreDrawListener(this)
+                else -> viewTreeObserver.removeOnPreDrawListener(this)
+            }
+            return true
+        }
+    })
+}
+
+/**
+ * Sends [AccessibilityEvent] of type [AccessibilityEvent.TYPE_ANNOUNCEMENT].
+ *
+ * @see View.announceForAccessibility
+ */
+@RequiresApi(16)
+inline fun View.announceForAccessibility(@StringRes resource: Int) {
+    val announcement = resources.getString(resource)
+    announceForAccessibility(announcement)
+}
+
+/**
+ * Updates this view's relative padding. This version of the method allows using named parameters
+ * to just set one or more axes.
+ *
+ * @see View.setPaddingRelative
+ */
+@RequiresApi(17)
+inline fun View.updatePaddingRelative(
+    @Px start: Int = paddingStart,
+    @Px top: Int = paddingTop,
+    @Px end: Int = paddingEnd,
+    @Px bottom: Int = paddingBottom
+) {
+    setPaddingRelative(start, top, end, bottom)
+}
+
+/**
+ * Updates this view's padding. This version of the method allows using named parameters
+ * to just set one or more axes.
+ *
+ * @see View.setPadding
+ */
+inline fun View.updatePadding(
+    @Px left: Int = paddingLeft,
+    @Px top: Int = paddingTop,
+    @Px right: Int = paddingRight,
+    @Px bottom: Int = paddingBottom
+) {
+    setPadding(left, top, right, bottom)
+}
+
+/**
+ * Sets the view's padding. This version of the method sets all axes to the provided size.
+ *
+ * @see View.setPadding
+ */
+inline fun View.setPadding(@Px size: Int) {
+    setPadding(size, size, size, size)
+}
+
+/**
+ * Version of [View.postDelayed] which re-orders the parameters, allowing the action to be placed
+ * outside of parentheses.
+ *
+ * ```
+ * view.postDelayed(200) {
+ *     doSomething()
+ * }
+ * ```
+ *
+ * @return the created Runnable
+ */
+inline fun View.postDelayed(delayInMillis: Long, crossinline action: () -> Unit): Runnable {
+    val runnable = Runnable { action() }
+    postDelayed(runnable, delayInMillis)
+    return runnable
+}
+
+/**
+ * Version of [View.postOnAnimationDelayed] which re-orders the parameters, allowing the action
+ * to be placed outside of parentheses.
+ *
+ * ```
+ * view.postOnAnimationDelayed(16) {
+ *     doSomething()
+ * }
+ * ```
+ *
+ * @return the created Runnable
+ */
+@RequiresApi(16)
+inline fun View.postOnAnimationDelayed(
+    delayInMillis: Long,
+    crossinline action: () -> Unit
+): Runnable {
+    val runnable = Runnable { action() }
+    postOnAnimationDelayed(runnable, delayInMillis)
+    return runnable
+}
+
+/**
+ * Return a [Bitmap] representation of this [View].
+ *
+ * The resulting bitmap will be the same width and height as this view's current layout
+ * dimensions. This does not take into account any transformations such as scale or translation.
+ *
+ * Note, this will use the software rendering pipeline to draw the view to the bitmap. This may
+ * result with different drawing to what is rendered on a hardware accelerated canvas (such as
+ * the device screen).
+ *
+ * If this view has not been laid out this method will throw a [IllegalStateException].
+ *
+ * @param config Bitmap config of the desired bitmap. Defaults to [Bitmap.Config.ARGB_8888].
+ */
+fun View.toBitmap(config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap {
+    if (!ViewCompat.isLaidOut(this)) {
+        throw IllegalStateException("View needs to be laid out before calling toBitmap()")
+    }
+    return Bitmap.createBitmap(width, height, config).applyCanvas(::draw)
+}
+
+/**
+ * Returns true when this view's visibility is [View.VISIBLE], false otherwise.
+ *
+ * ```
+ * if (view.isVisible) {
+ *     // Behavior...
+ * }
+ * ```
+ *
+ * Setting this property to true sets the visibility to [View.VISIBLE], false to [View.GONE].
+ *
+ * ```
+ * view.isVisible = true
+ * ```
+ */
+inline var View.isVisible: Boolean
+    get() = visibility == View.VISIBLE
+    set(value) {
+        visibility = if (value) View.VISIBLE else View.GONE
+    }
+
+/**
+ * Returns true when this view's visibility is [View.INVISIBLE], false otherwise.
+ *
+ * ```
+ * if (view.isInvisible) {
+ *     // Behavior...
+ * }
+ * ```
+ *
+ * Setting this property to true sets the visibility to [View.INVISIBLE], false to [View.VISIBLE].
+ *
+ * ```
+ * view.isInvisible = true
+ * ```
+ */
+inline var View.isInvisible: Boolean
+    get() = visibility == View.INVISIBLE
+    set(value) {
+        visibility = if (value) View.INVISIBLE else View.VISIBLE
+    }
+
+/**
+ * Returns true when this view's visibility is [View.GONE], false otherwise.
+ *
+ * ```
+ * if (view.isGone) {
+ *     // Behavior...
+ * }
+ * ```
+ *
+ * Setting this property to true sets the visibility to [View.GONE], false to [View.VISIBLE].
+ *
+ * ```
+ * view.isGone = true
+ * ```
+ */
+inline var View.isGone: Boolean
+    get() = visibility == View.GONE
+    set(value) {
+        visibility = if (value) View.GONE else View.VISIBLE
+    }
+
+/**
+ * Executes [block] with the View's layoutParams and reassigns the layoutParams with the
+ * updated version.
+ *
+ * @see View.getLayoutParams
+ * @see View.setLayoutParams
+ **/
+inline fun View.updateLayoutParams(block: ViewGroup.LayoutParams.() -> Unit) {
+    updateLayoutParams<ViewGroup.LayoutParams>(block)
+}
+
+/**
+ * Executes [block] with a typed version of the View's layoutParams and reassigns the
+ * layoutParams with the updated version.
+ *
+ * @see View.getLayoutParams
+ * @see View.setLayoutParams
+ **/
+@JvmName("updateLayoutParamsTyped")
+inline fun <reified T : ViewGroup.LayoutParams> View.updateLayoutParams(block: T.() -> Unit) {
+    val params = layoutParams as T
+    block(params)
+    layoutParams = params
+}
diff --git a/core/ktx/src/main/java/androidx/core/view/ViewGroup.kt b/core/ktx/src/main/java/androidx/core/view/ViewGroup.kt
new file mode 100644
index 0000000..8a7152a
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/view/ViewGroup.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to other public API.
+
+package androidx.core.view
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.Px
+import androidx.annotation.RequiresApi
+
+/**
+ * Returns the view at [index].
+ *
+ * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the count.
+ */
+operator fun ViewGroup.get(index: Int) =
+    getChildAt(index) ?: throw IndexOutOfBoundsException("Index: $index, Size: $childCount")
+
+/** Returns `true` if [view] is found in this view group. */
+inline operator fun ViewGroup.contains(view: View) = indexOfChild(view) != -1
+
+/** Adds [view] to this view group. */
+inline operator fun ViewGroup.plusAssign(view: View) = addView(view)
+
+/** Removes [view] from this view group. */
+inline operator fun ViewGroup.minusAssign(view: View) = removeView(view)
+
+/** Returns the number of views in this view group. */
+inline val ViewGroup.size get() = childCount
+
+/** Returns true if this view group contains no views. */
+inline fun ViewGroup.isEmpty() = childCount == 0
+
+/** Returns true if this view group contains one or more views. */
+inline fun ViewGroup.isNotEmpty() = childCount != 0
+
+/** Performs the given action on each view in this view group. */
+inline fun ViewGroup.forEach(action: (view: View) -> Unit) {
+    for (index in 0 until childCount) {
+        action(getChildAt(index))
+    }
+}
+
+/** Performs the given action on each view in this view group, providing its sequential index. */
+inline fun ViewGroup.forEachIndexed(action: (index: Int, view: View) -> Unit) {
+    for (index in 0 until childCount) {
+        action(index, getChildAt(index))
+    }
+}
+
+/** Returns a [MutableIterator] over the views in this view group. */
+operator fun ViewGroup.iterator() = object : MutableIterator<View> {
+    private var index = 0
+    override fun hasNext() = index < childCount
+    override fun next() = getChildAt(index++) ?: throw IndexOutOfBoundsException()
+    override fun remove() = removeViewAt(--index)
+}
+
+/** Returns a [Sequence] over the child views in this view group. */
+val ViewGroup.children: Sequence<View>
+    get() = object : Sequence<View> {
+        override fun iterator() = this@children.iterator()
+    }
+
+/**
+ * Sets the margins in the ViewGroup's MarginLayoutParams. This version of the method sets all axes
+ * to the provided size.
+ *
+ * @see ViewGroup.MarginLayoutParams.setMargins
+ */
+inline fun ViewGroup.MarginLayoutParams.setMargins(@Px size: Int) {
+    setMargins(size, size, size, size)
+}
+
+/**
+ * Updates the margins in the [ViewGroup]'s [ViewGroup.MarginLayoutParams].
+ * This version of the method allows using named parameters to just set one or more axes.
+ *
+ * @see ViewGroup.MarginLayoutParams.setMargins
+ */
+inline fun ViewGroup.MarginLayoutParams.updateMargins(
+    @Px left: Int = leftMargin,
+    @Px top: Int = topMargin,
+    @Px right: Int = rightMargin,
+    @Px bottom: Int = bottomMargin
+) {
+    setMargins(left, top, right, bottom)
+}
+
+/**
+ * Updates the relative margins in the ViewGroup's MarginLayoutParams.
+ * This version of the method allows using named parameters to just set one or more axes.
+ *
+ * @see ViewGroup.MarginLayoutParams.setMargins
+ */
+@RequiresApi(17)
+inline fun ViewGroup.MarginLayoutParams.updateMarginsRelative(
+    @Px start: Int = marginStart,
+    @Px top: Int = topMargin,
+    @Px end: Int = marginEnd,
+    @Px bottom: Int = bottomMargin
+) {
+    marginStart = start
+    topMargin = top
+    marginEnd = end
+    bottomMargin = bottom
+}
diff --git a/core/ktx/src/main/java/androidx/core/widget/Toast.kt b/core/ktx/src/main/java/androidx/core/widget/Toast.kt
new file mode 100644
index 0000000..b0ddfd1
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/widget/Toast.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.core.widget
+
+import android.content.Context
+import android.widget.Toast
+import androidx.annotation.StringRes
+
+/**
+ * Creates and shows a [Toast] with the given [text]
+ *
+ * @param duration Toast duration, defaults to [Toast.LENGTH_SHORT]
+ */
+inline fun Context.toast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT): Toast {
+    return Toast.makeText(this, text, duration).apply { show() }
+}
+
+/**
+ * Creates and shows a [Toast] with text from a resource
+ *
+ * @param resId Resource id of the string resource to use
+ * @param duration Toast duration, defaults to [Toast.LENGTH_SHORT]
+ */
+inline fun Context.toast(@StringRes resId: Int, duration: Int = Toast.LENGTH_SHORT): Toast {
+    return Toast.makeText(this, resId, duration).apply { show() }
+}
diff --git a/customview/src/main/java/androidx/customview/widget/FocusStrategy.java b/customview/src/main/java/androidx/customview/widget/FocusStrategy.java
index 6c3f860..02e6ea4 100644
--- a/customview/src/main/java/androidx/customview/widget/FocusStrategy.java
+++ b/customview/src/main/java/androidx/customview/widget/FocusStrategy.java
@@ -450,4 +450,7 @@
         V get(T collection, int index);
         int size(T collection);
     }
+
+    private FocusStrategy() {
+    }
 }
diff --git a/documentfile/build.gradle b/documentfile/build.gradle
index 81cabad..ab03322 100644
--- a/documentfile/build.gradle
+++ b/documentfile/build.gradle
@@ -1,3 +1,4 @@
+import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 
@@ -7,6 +8,8 @@
 
 dependencies {
     api(project(":annotation"))
+
+    annotationProcessor(NULLAWAY)
 }
 
 supportLibrary {
diff --git a/documentfile/src/main/java/androidx/documentfile/provider/DocumentFile.java b/documentfile/src/main/java/androidx/documentfile/provider/DocumentFile.java
index 6776257..43b5a18 100644
--- a/documentfile/src/main/java/androidx/documentfile/provider/DocumentFile.java
+++ b/documentfile/src/main/java/androidx/documentfile/provider/DocumentFile.java
@@ -23,6 +23,9 @@
 import android.os.Build;
 import android.provider.DocumentsContract;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import java.io.File;
 
 /**
@@ -79,9 +82,10 @@
 public abstract class DocumentFile {
     static final String TAG = "DocumentFile";
 
+    @Nullable
     private final DocumentFile mParent;
 
-    DocumentFile(DocumentFile parent) {
+    DocumentFile(@Nullable DocumentFile parent) {
         mParent = parent;
     }
 
@@ -93,7 +97,8 @@
      * {@link #getUri()} will return {@code file://} Uris for files explored
      * through this tree.
      */
-    public static DocumentFile fromFile(File file) {
+    @NonNull
+    public static DocumentFile fromFile(@NonNull File file) {
         return new RawDocumentFile(null, file);
     }
 
@@ -107,7 +112,8 @@
      *            {@link Intent#ACTION_OPEN_DOCUMENT} or
      *            {@link Intent#ACTION_CREATE_DOCUMENT} request.
      */
-    public static DocumentFile fromSingleUri(Context context, Uri singleUri) {
+    @Nullable
+    public static DocumentFile fromSingleUri(@NonNull Context context, @NonNull Uri singleUri) {
         if (Build.VERSION.SDK_INT >= 19) {
             return new SingleDocumentFile(null, context, singleUri);
         } else {
@@ -124,7 +130,8 @@
      * @param treeUri the {@link Intent#getData()} from a successful
      *            {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request.
      */
-    public static DocumentFile fromTreeUri(Context context, Uri treeUri) {
+    @Nullable
+    public static DocumentFile fromTreeUri(@NonNull Context context, @NonNull Uri treeUri) {
         if (Build.VERSION.SDK_INT >= 21) {
             return new TreeDocumentFile(null, context,
                     DocumentsContract.buildDocumentUriUsingTree(treeUri,
@@ -138,7 +145,7 @@
      * Test if given Uri is backed by a
      * {@link android.provider.DocumentsProvider}.
      */
-    public static boolean isDocumentUri(Context context, Uri uri) {
+    public static boolean isDocumentUri(@NonNull Context context, @Nullable Uri uri) {
         if (Build.VERSION.SDK_INT >= 19) {
             return DocumentsContract.isDocumentUri(context, uri);
         } else {
@@ -160,7 +167,8 @@
      * @see android.provider.DocumentsContract#createDocument(ContentResolver,
      *      Uri, String, String)
      */
-    public abstract DocumentFile createFile(String mimeType, String displayName);
+    @Nullable
+    public abstract DocumentFile createFile(@NonNull String mimeType, @NonNull String displayName);
 
     /**
      * Create a new directory as a direct child of this directory.
@@ -172,7 +180,8 @@
      * @see android.provider.DocumentsContract#createDocument(ContentResolver,
      *      Uri, String, String)
      */
-    public abstract DocumentFile createDirectory(String displayName);
+    @Nullable
+    public abstract DocumentFile createDirectory(@NonNull String displayName);
 
     /**
      * Return a Uri for the underlying document represented by this file. This
@@ -187,6 +196,7 @@
      * @see ContentResolver#openOutputStream(Uri)
      * @see ContentResolver#openFileDescriptor(Uri, String)
      */
+    @NonNull
     public abstract Uri getUri();
 
     /**
@@ -194,6 +204,7 @@
      *
      * @see android.provider.DocumentsContract.Document#COLUMN_DISPLAY_NAME
      */
+    @Nullable
     public abstract String getName();
 
     /**
@@ -201,6 +212,7 @@
      *
      * @see android.provider.DocumentsContract.Document#COLUMN_MIME_TYPE
      */
+    @Nullable
     public abstract String getType();
 
     /**
@@ -212,6 +224,7 @@
      * parent offered here is purely a convenience method, and it may be
      * incorrect if the underlying tree structure changes.
      */
+    @Nullable
     public DocumentFile getParentFile() {
         return mParent;
     }
@@ -309,6 +322,7 @@
      * @see android.provider.DocumentsContract#buildChildDocumentsUriUsingTree(Uri,
      *      String)
      */
+    @NonNull
     public abstract DocumentFile[] listFiles();
 
     /**
@@ -319,7 +333,8 @@
      * @throws UnsupportedOperationException when working with a single document
      *             created from {@link #fromSingleUri(Context, Uri)}.
      */
-    public DocumentFile findFile(String displayName) {
+    @Nullable
+    public DocumentFile findFile(@NonNull String displayName) {
         for (DocumentFile doc : listFiles()) {
             if (displayName.equals(doc.getName())) {
                 return doc;
@@ -348,5 +363,5 @@
      * @see android.provider.DocumentsContract#renameDocument(ContentResolver,
      *      Uri, String)
      */
-    public abstract boolean renameTo(String displayName);
+    public abstract boolean renameTo(@NonNull String displayName);
 }
diff --git a/documentfile/src/main/java/androidx/documentfile/provider/DocumentsContractApi19.java b/documentfile/src/main/java/androidx/documentfile/provider/DocumentsContractApi19.java
index 750958d..45338e5 100644
--- a/documentfile/src/main/java/androidx/documentfile/provider/DocumentsContractApi19.java
+++ b/documentfile/src/main/java/androidx/documentfile/provider/DocumentsContractApi19.java
@@ -26,6 +26,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
 @RequiresApi(19)
@@ -43,14 +44,17 @@
         return (getFlags(context, self) & FLAG_VIRTUAL_DOCUMENT) != 0;
     }
 
+    @Nullable
     public static String getName(Context context, Uri self) {
         return queryForString(context, self, DocumentsContract.Document.COLUMN_DISPLAY_NAME, null);
     }
 
+    @Nullable
     private static String getRawType(Context context, Uri self) {
         return queryForString(context, self, DocumentsContract.Document.COLUMN_MIME_TYPE, null);
     }
 
+    @Nullable
     public static String getType(Context context, Uri self) {
         final String rawType = getRawType(context, self);
         if (DocumentsContract.Document.MIME_TYPE_DIR.equals(rawType)) {
@@ -149,8 +153,9 @@
         }
     }
 
+    @Nullable
     private static String queryForString(Context context, Uri self, String column,
-            String defaultValue) {
+            @Nullable String defaultValue) {
         final ContentResolver resolver = context.getContentResolver();
 
         Cursor c = null;
@@ -194,7 +199,7 @@
         }
     }
 
-    private static void closeQuietly(AutoCloseable closeable) {
+    private static void closeQuietly(@Nullable AutoCloseable closeable) {
         if (closeable != null) {
             try {
                 closeable.close();
@@ -204,4 +209,7 @@
             }
         }
     }
+
+    private DocumentsContractApi19() {
+    }
 }
diff --git a/documentfile/src/main/java/androidx/documentfile/provider/RawDocumentFile.java b/documentfile/src/main/java/androidx/documentfile/provider/RawDocumentFile.java
index 722c7ae..0f5fa4e 100644
--- a/documentfile/src/main/java/androidx/documentfile/provider/RawDocumentFile.java
+++ b/documentfile/src/main/java/androidx/documentfile/provider/RawDocumentFile.java
@@ -20,6 +20,8 @@
 import android.util.Log;
 import android.webkit.MimeTypeMap;
 
+import androidx.annotation.Nullable;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -27,12 +29,13 @@
 class RawDocumentFile extends DocumentFile {
     private File mFile;
 
-    RawDocumentFile(DocumentFile parent, File file) {
+    RawDocumentFile(@Nullable DocumentFile parent, File file) {
         super(parent);
         mFile = file;
     }
 
     @Override
+    @Nullable
     public DocumentFile createFile(String mimeType, String displayName) {
         // Tack on extension when valid MIME type provided
         final String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
@@ -50,6 +53,7 @@
     }
 
     @Override
+    @Nullable
     public DocumentFile createDirectory(String displayName) {
         final File target = new File(mFile, displayName);
         if (target.isDirectory() || target.mkdir()) {
@@ -70,6 +74,7 @@
     }
 
     @Override
+    @Nullable
     public String getType() {
         if (mFile.isDirectory()) {
             return null;
diff --git a/documentfile/src/main/java/androidx/documentfile/provider/SingleDocumentFile.java b/documentfile/src/main/java/androidx/documentfile/provider/SingleDocumentFile.java
index 0b4e1d5..7cd4b8c 100644
--- a/documentfile/src/main/java/androidx/documentfile/provider/SingleDocumentFile.java
+++ b/documentfile/src/main/java/androidx/documentfile/provider/SingleDocumentFile.java
@@ -20,6 +20,7 @@
 import android.net.Uri;
 import android.provider.DocumentsContract;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
 @RequiresApi(19)
@@ -27,7 +28,7 @@
     private Context mContext;
     private Uri mUri;
 
-    SingleDocumentFile(DocumentFile parent, Context context, Uri uri) {
+    SingleDocumentFile(@Nullable DocumentFile parent, Context context, Uri uri) {
         super(parent);
         mContext = context;
         mUri = uri;
@@ -49,11 +50,13 @@
     }
 
     @Override
+    @Nullable
     public String getName() {
         return DocumentsContractApi19.getName(mContext, mUri);
     }
 
     @Override
+    @Nullable
     public String getType() {
         return DocumentsContractApi19.getType(mContext, mUri);
     }
diff --git a/documentfile/src/main/java/androidx/documentfile/provider/TreeDocumentFile.java b/documentfile/src/main/java/androidx/documentfile/provider/TreeDocumentFile.java
index d429008..9cc20d6 100644
--- a/documentfile/src/main/java/androidx/documentfile/provider/TreeDocumentFile.java
+++ b/documentfile/src/main/java/androidx/documentfile/provider/TreeDocumentFile.java
@@ -23,6 +23,7 @@
 import android.provider.DocumentsContract;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
 import java.util.ArrayList;
@@ -32,18 +33,20 @@
     private Context mContext;
     private Uri mUri;
 
-    TreeDocumentFile(DocumentFile parent, Context context, Uri uri) {
+    TreeDocumentFile(@Nullable DocumentFile parent, Context context, Uri uri) {
         super(parent);
         mContext = context;
         mUri = uri;
     }
 
     @Override
+    @Nullable
     public DocumentFile createFile(String mimeType, String displayName) {
         final Uri result = TreeDocumentFile.createFile(mContext, mUri, mimeType, displayName);
         return (result != null) ? new TreeDocumentFile(this, mContext, result) : null;
     }
 
+    @Nullable
     private static Uri createFile(Context context, Uri self, String mimeType,
             String displayName) {
         try {
@@ -55,6 +58,7 @@
     }
 
     @Override
+    @Nullable
     public DocumentFile createDirectory(String displayName) {
         final Uri result = TreeDocumentFile.createFile(
                 mContext, mUri, DocumentsContract.Document.MIME_TYPE_DIR, displayName);
@@ -67,11 +71,13 @@
     }
 
     @Override
+    @Nullable
     public String getName() {
         return DocumentsContractApi19.getName(mContext, mUri);
     }
 
     @Override
+    @Nullable
     public String getType() {
         return DocumentsContractApi19.getType(mContext, mUri);
     }
@@ -156,7 +162,7 @@
         return resultFiles;
     }
 
-    private static void closeQuietly(AutoCloseable closeable) {
+    private static void closeQuietly(@Nullable AutoCloseable closeable) {
         if (closeable != null) {
             try {
                 closeable.close();
diff --git a/emoji/core/src/main/java/androidx/emoji/text/MetadataListReader.java b/emoji/core/src/main/java/androidx/emoji/text/MetadataListReader.java
index 28a35f0..039bb6b 100644
--- a/emoji/core/src/main/java/androidx/emoji/text/MetadataListReader.java
+++ b/emoji/core/src/main/java/androidx/emoji/text/MetadataListReader.java
@@ -341,4 +341,7 @@
             return mByteBuffer.position();
         }
     }
+
+    private MetadataListReader() {
+    }
 }
diff --git a/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index 37c480d..f6fc2b8 100644
--- a/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -3812,6 +3812,9 @@
         public static final int Fragment_id = 1;
         public static final int Fragment_name = 0;
         public static final int Fragment_tag = 2;
+
+        private FragmentTag() {
+        }
     }
 
     /**
diff --git a/fragment/src/main/java/androidx/fragment/app/FragmentTransition.java b/fragment/src/main/java/androidx/fragment/app/FragmentTransition.java
index 8466bb9..f7b3cac 100644
--- a/fragment/src/main/java/androidx/fragment/app/FragmentTransition.java
+++ b/fragment/src/main/java/androidx/fragment/app/FragmentTransition.java
@@ -1254,4 +1254,7 @@
          */
         public BackStackRecord firstOutTransaction;
     }
+
+    private FragmentTransition() {
+    }
 }
diff --git a/gradle.properties b/gradle.properties
index 85bd83e..a21b997 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -3,4 +3,4 @@
 org.gradle.configureondemand=true
 org.gradle.parallel=true
 android.builder.sdkDownload=false
-android.enableD8=true
+
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f05ac0d..e686e68 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=../../../../tools/external/gradle/gradle-4.6-bin.zip
+distributionUrl=../../../../tools/external/gradle/gradle-4.7-20180329152519+0000-bin.zip
diff --git a/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java b/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java
index 2782eca..2110f3c 100644
--- a/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java
+++ b/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java
@@ -145,4 +145,7 @@
         }
         return interpolator;
     }
+
+    private AnimationUtilsCompat() {
+    }
 }
diff --git a/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat.java b/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat.java
index 62e87a0..8c71634 100644
--- a/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat.java
+++ b/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat.java
@@ -917,5 +917,8 @@
         return (type >= TypedValue.TYPE_FIRST_COLOR_INT) && (type
                 <= TypedValue.TYPE_LAST_COLOR_INT);
     }
+
+    private AnimatorInflaterCompat() {
+    }
 }
 
diff --git a/graphics/drawable/static/src/main/java/androidx/vectordrawable/graphics/drawable/AndroidResources.java b/graphics/drawable/static/src/main/java/androidx/vectordrawable/graphics/drawable/AndroidResources.java
index f63f569..409813f 100644
--- a/graphics/drawable/static/src/main/java/androidx/vectordrawable/graphics/drawable/AndroidResources.java
+++ b/graphics/drawable/static/src/main/java/androidx/vectordrawable/graphics/drawable/AndroidResources.java
@@ -146,4 +146,7 @@
     public static final int FAST_OUT_LINEAR_IN = 0x010c000f;
     public static final int FAST_OUT_SLOW_IN = 0x010c000d;
     public static final int LINEAR_OUT_SLOW_IN = 0x010c000e;
+
+    private AndroidResources() {
+    }
 }
diff --git a/heifwriter/api/current.txt b/heifwriter/api/current.txt
index c7311ba..3c39776 100644
--- a/heifwriter/api/current.txt
+++ b/heifwriter/api/current.txt
@@ -1,8 +1,6 @@
 package androidx.heifwriter {
 
   public final class HeifWriter implements java.lang.AutoCloseable {
-    ctor public HeifWriter(java.lang.String, int, int, boolean, int, int, int, int, android.os.Handler) throws java.io.IOException;
-    ctor public HeifWriter(java.io.FileDescriptor, int, int, boolean, int, int, int, int, android.os.Handler) throws java.io.IOException;
     method public void addBitmap(android.graphics.Bitmap);
     method public void addYuvBuffer(int, byte[]);
     method public void close();
@@ -15,5 +13,17 @@
     field public static final int INPUT_MODE_SURFACE = 1; // 0x1
   }
 
+  public static final class HeifWriter.Builder {
+    ctor public HeifWriter.Builder(java.lang.String, int, int, int);
+    ctor public HeifWriter.Builder(java.io.FileDescriptor, int, int, int);
+    method public androidx.heifwriter.HeifWriter build() throws java.io.IOException;
+    method public androidx.heifwriter.HeifWriter.Builder setGridEnabled(boolean);
+    method public androidx.heifwriter.HeifWriter.Builder setHandler(android.os.Handler);
+    method public androidx.heifwriter.HeifWriter.Builder setMaxImages(int);
+    method public androidx.heifwriter.HeifWriter.Builder setPrimaryIndex(int);
+    method public androidx.heifwriter.HeifWriter.Builder setQuality(int);
+    method public androidx.heifwriter.HeifWriter.Builder setRotation(int);
+  }
+
 }
 
diff --git a/heifwriter/build.gradle b/heifwriter/build.gradle
index 63e55ca..265708f 100644
--- a/heifwriter/build.gradle
+++ b/heifwriter/build.gradle
@@ -6,12 +6,6 @@
     id("SupportAndroidLibraryPlugin")
 }
 
-android {
-    defaultConfig {
-        targetSdkVersion 'P'
-    }
-}
-
 dependencies {
     api(project(":annotation"))
 
diff --git a/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java b/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java
index d5b339a..127ba1c 100644
--- a/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java
+++ b/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java
@@ -224,7 +224,11 @@
     }
 
     private void doTestForVariousNumberImages(TestConfig.Builder builder) throws Exception {
-        doTest(builder.setNumImages(4).build());
+        builder.setNumImages(4);
+        doTest(builder.setRotation(270).build());
+        doTest(builder.setRotation(180).build());
+        doTest(builder.setRotation(90).build());
+        doTest(builder.setRotation(0).build());
         doTest(builder.setNumImages(1).build());
         doTest(builder.setNumImages(8).build());
     }
@@ -252,102 +256,109 @@
     }
 
     private static class TestConfig {
-        final int inputMode;
-        final boolean useGrid;
-        final boolean useHandler;
-        final int maxNumImages;
-        final int numImages;
-        final int width;
-        final int height;
-        final int quality;
-        final String inputPath;
-        final String outputPath;
-        final Bitmap[] bitmaps;
+        final int mInputMode;
+        final boolean mUseGrid;
+        final boolean mUseHandler;
+        final int mMaxNumImages;
+        final int mNumImages;
+        final int mWidth;
+        final int mHeight;
+        final int mRotation;
+        final int mQuality;
+        final String mInputPath;
+        final String mOutputPath;
+        final Bitmap[] mBitmaps;
 
-        TestConfig(int _inputMode, boolean _useGrid, boolean _useHandler,
-                   int _maxNumImage, int _numImages, int _width, int _height, int _quality,
-                   String _inputPath, String _outputPath, Bitmap[] _bitmaps) {
-            inputMode = _inputMode;
-            useGrid = _useGrid;
-            useHandler = _useHandler;
-            maxNumImages = _maxNumImage;
-            numImages = _numImages;
-            width = _width;
-            height = _height;
-            quality = _quality;
-            inputPath = _inputPath;
-            outputPath = _outputPath;
-            bitmaps = _bitmaps;
+        TestConfig(int inputMode, boolean useGrid, boolean useHandler,
+                   int maxNumImages, int numImages, int width, int height,
+                   int rotation, int quality,
+                   String inputPath, String outputPath, Bitmap[] bitmaps) {
+            mInputMode = inputMode;
+            mUseGrid = useGrid;
+            mUseHandler = useHandler;
+            mMaxNumImages = maxNumImages;
+            mNumImages = numImages;
+            mWidth = width;
+            mHeight = height;
+            mRotation = rotation;
+            mQuality = quality;
+            mInputPath = inputPath;
+            mOutputPath = outputPath;
+            mBitmaps = bitmaps;
         }
 
         static class Builder {
-            final int inputMode;
-            final boolean useGrid;
-            final boolean useHandler;
-            int maxNumImages;
-            int numImages;
-            int width;
-            int height;
-            int quality;
-            String inputPath;
-            final String outputPath;
-            Bitmap[] bitmaps;
-
-            boolean numImagesSetExplicitly;
+            final int mInputMode;
+            final boolean mUseGrid;
+            final boolean mUseHandler;
+            int mMaxNumImages;
+            int mNumImages;
+            int mWidth;
+            int mHeight;
+            int mRotation;
+            final int mQuality;
+            String mInputPath;
+            final String mOutputPath;
+            Bitmap[] mBitmaps;
+            boolean mNumImagesSetExplicitly;
 
 
-            Builder(int _inputMode, boolean _useGrids, boolean _useHandler) {
-                inputMode = _inputMode;
-                useGrid = _useGrids;
-                useHandler = _useHandler;
-                maxNumImages = numImages = 4;
-                width = 1920;
-                height = 1080;
-                quality = 100;
-                outputPath = new File(Environment.getExternalStorageDirectory(),
+            Builder(int inputMode, boolean useGrids, boolean useHandler) {
+                mInputMode = inputMode;
+                mUseGrid = useGrids;
+                mUseHandler = useHandler;
+                mMaxNumImages = mNumImages = 4;
+                mWidth = 1920;
+                mHeight = 1080;
+                mRotation = 0;
+                mQuality = 100;
+                mOutputPath = new File(Environment.getExternalStorageDirectory(),
                         OUTPUT_FILENAME).getAbsolutePath();
             }
 
-            Builder setInputPath(String _inputPath) {
-                inputPath = (inputMode == INPUT_MODE_BITMAP) ? _inputPath : null;
+            Builder setInputPath(String inputPath) {
+                mInputPath = (mInputMode == INPUT_MODE_BITMAP) ? inputPath : null;
                 return this;
             }
 
-            Builder setNumImages(int _numImages) {
-                numImagesSetExplicitly = true;
-                numImages = _numImages;
+            Builder setNumImages(int numImages) {
+                mNumImagesSetExplicitly = true;
+                mNumImages = numImages;
+                return this;
+            }
+
+            Builder setRotation(int rotation) {
+                mRotation = rotation;
                 return this;
             }
 
             private void loadBitmapInputs() {
-                if (inputMode != INPUT_MODE_BITMAP) {
+                if (mInputMode != INPUT_MODE_BITMAP) {
                     return;
                 }
                 MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-                retriever.setDataSource(inputPath);
+                retriever.setDataSource(mInputPath);
                 String hasImage = retriever.extractMetadata(
                         MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
                 if (!"yes".equals(hasImage)) {
                     throw new IllegalArgumentException("no bitmap found!");
                 }
-                width = Integer.parseInt(retriever.extractMetadata(
-                        MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH));
-                height = Integer.parseInt(retriever.extractMetadata(
-                        MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT));
-                maxNumImages = Math.min(maxNumImages, Integer.parseInt(retriever.extractMetadata(
+                mMaxNumImages = Math.min(mMaxNumImages, Integer.parseInt(retriever.extractMetadata(
                         MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
-                if (!numImagesSetExplicitly) {
-                    numImages = maxNumImages;
+                if (!mNumImagesSetExplicitly) {
+                    mNumImages = mMaxNumImages;
                 }
-                bitmaps = new Bitmap[maxNumImages];
-                for (int i = 0; i < bitmaps.length; i++) {
-                    bitmaps[i] = retriever.getImageAtIndex(i);
+                mBitmaps = new Bitmap[mMaxNumImages];
+                for (int i = 0; i < mBitmaps.length; i++) {
+                    mBitmaps[i] = retriever.getImageAtIndex(i);
                 }
+                mWidth = mBitmaps[0].getWidth();
+                mHeight = mBitmaps[0].getHeight();
                 retriever.release();
             }
 
             private void cleanupStaleOutputs() {
-                File outputFile = new File(outputPath);
+                File outputFile = new File(mOutputPath);
                 if (outputFile.exists()) {
                     outputFile.delete();
                 }
@@ -357,58 +368,61 @@
                 cleanupStaleOutputs();
                 loadBitmapInputs();
 
-                return new TestConfig(inputMode, useGrid, useHandler, maxNumImages, numImages,
-                        width, height, quality, inputPath, outputPath, bitmaps);
+                return new TestConfig(mInputMode, mUseGrid, mUseHandler, mMaxNumImages, mNumImages,
+                        mWidth, mHeight, mRotation, mQuality, mInputPath, mOutputPath, mBitmaps);
             }
         }
 
         @Override
         public String toString() {
-            return "TestConfig" +
-                    ": inputMode " + inputMode +
-                    ", useGrid " + useGrid +
-                    ", useHandler " + useHandler +
-                    ", maxNumImages " + maxNumImages +
-                    ", numImages " + numImages +
-                    ", width " + width +
-                    ", height " + height +
-                    ", quality " + quality +
-                    ", inputPath " + inputPath +
-                    ", outputPath " + outputPath;
+            return "TestConfig"
+                    + ": mInputMode " + mInputMode
+                    + ", mUseGrid " + mUseGrid
+                    + ", mUseHandler " + mUseHandler
+                    + ", mMaxNumImages " + mMaxNumImages
+                    + ", mNumImages " + mNumImages
+                    + ", mWidth " + mWidth
+                    + ", mHeight " + mHeight
+                    + ", mRotation " + mRotation
+                    + ", mQuality " + mQuality
+                    + ", mInputPath " + mInputPath
+                    + ", mOutputPath " + mOutputPath;
         }
     }
 
-    private void doTest(TestConfig testConfig) throws Exception {
-        int width = testConfig.width;
-        int height = testConfig.height;
-        int numImages = testConfig.numImages;
+    private void doTest(TestConfig config) throws Exception {
+        int width = config.mWidth;
+        int height = config.mHeight;
+        int numImages = config.mNumImages;
 
         mInputIndex = 0;
         HeifWriter heifWriter = null;
         FileInputStream inputStream = null;
         FileOutputStream outputStream = null;
         try {
-            if (DEBUG) Log.d(TAG, "started: " + testConfig);
+            if (DEBUG) Log.d(TAG, "started: " + config);
 
-            heifWriter = new HeifWriter(testConfig.outputPath, width, height,
-                    testConfig.useGrid,
-                    testConfig.quality,
-                    testConfig.maxNumImages,
-                    testConfig.maxNumImages - 1,
-                    testConfig.inputMode,
-                    testConfig.useHandler ? mHandler : null);
+            heifWriter = new HeifWriter.Builder(
+                    config.mOutputPath, width, height, config.mInputMode)
+                    .setRotation(config.mRotation)
+                    .setGridEnabled(config.mUseGrid)
+                    .setMaxImages(config.mMaxNumImages)
+                    .setQuality(config.mQuality)
+                    .setPrimaryIndex(config.mMaxNumImages - 1)
+                    .setHandler(config.mUseHandler ? mHandler : null)
+                    .build();
 
-            if (testConfig.inputMode == INPUT_MODE_SURFACE) {
+            if (config.mInputMode == INPUT_MODE_SURFACE) {
                 mInputEglSurface = new EglWindowSurface(heifWriter.getInputSurface());
             }
 
             heifWriter.start();
 
-            if (testConfig.inputMode == INPUT_MODE_BUFFER) {
+            if (config.mInputMode == INPUT_MODE_BUFFER) {
                 byte[] data = new byte[width * height * 3 / 2];
 
-                if (testConfig.inputPath != null) {
-                    inputStream = new FileInputStream(testConfig.inputPath);
+                if (config.mInputPath != null) {
+                    inputStream = new FileInputStream(config.mInputPath);
                 }
 
                 if (DUMP_YUV_INPUT) {
@@ -426,7 +440,7 @@
                     }
                     heifWriter.addYuvBuffer(ImageFormat.YUV_420_888, data);
                 }
-            } else if (testConfig.inputMode == INPUT_MODE_SURFACE) {
+            } else if (config.mInputMode == INPUT_MODE_SURFACE) {
                 // The input surface is a surface texture using single buffer mode, draws will be
                 // blocked until onFrameAvailable is done with the buffer, which is dependant on
                 // how fast MediaCodec processes them, which is further dependent on how fast the
@@ -438,8 +452,8 @@
                 }
                 heifWriter.setInputEndOfStreamTimestamp(
                         1000 * computePresentationTime(numImages - 1));
-            } else if (testConfig.inputMode == INPUT_MODE_BITMAP) {
-                Bitmap[] bitmaps = testConfig.bitmaps;
+            } else if (config.mInputMode == INPUT_MODE_BITMAP) {
+                Bitmap[] bitmaps = config.mBitmaps;
                 for (int i = 0; i < Math.min(bitmaps.length, numImages); i++) {
                     if (DEBUG) Log.d(TAG, "addBitmap: " + i);
                     heifWriter.addBitmap(bitmaps[i]);
@@ -448,8 +462,8 @@
             }
 
             heifWriter.stop(3000);
-            verifyResult(testConfig.outputPath, width, height, testConfig.useGrid,
-                    Math.min(numImages, testConfig.maxNumImages));
+            verifyResult(config.mOutputPath, width, height, config.mRotation, config.mUseGrid,
+                    Math.min(numImages, config.mMaxNumImages));
             if (DEBUG) Log.d(TAG, "finished: PASS");
         } finally {
             try {
@@ -532,7 +546,7 @@
     }
 
     private void verifyResult(
-            String filename, int width, int height, boolean useGrid, int numImages)
+            String filename, int width, int height, int rotation, boolean useGrid, int numImages)
             throws Exception {
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
         retriever.setDataSource(filename);
@@ -542,23 +556,26 @@
         }
         assertEquals("Wrong image count", numImages,
                 Integer.parseInt(retriever.extractMetadata(
-                MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
+                    MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
         assertEquals("Wrong width", width,
                 Integer.parseInt(retriever.extractMetadata(
-                MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH)));
+                    MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH)));
         assertEquals("Wrong height", height,
                 Integer.parseInt(retriever.extractMetadata(
-                MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT)));
+                    MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT)));
+        assertEquals("Wrong rotation", rotation,
+                Integer.parseInt(retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION)));
         retriever.release();
 
         if (useGrid) {
             MediaExtractor extractor = new MediaExtractor();
             extractor.setDataSource(filename);
             MediaFormat format = extractor.getTrackFormat(0);
-            int gridWidth = format.getInteger(MediaFormat.KEY_GRID_WIDTH);
-            int gridHeight = format.getInteger(MediaFormat.KEY_GRID_HEIGHT);
+            int gridWidth = format.getInteger(MediaFormat.KEY_TILE_WIDTH);
+            int gridHeight = format.getInteger(MediaFormat.KEY_TILE_HEIGHT);
             int gridRows = format.getInteger(MediaFormat.KEY_GRID_ROWS);
-            int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLS);
+            int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLUMNS);
             assertTrue("Wrong grid width or cols",
                     ((width + gridWidth - 1) / gridWidth) == gridCols);
             assertTrue("Wrong grid height or rows",
diff --git a/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java b/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java
index 3020b96..70b1a2f 100644
--- a/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java
+++ b/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java
@@ -213,7 +213,14 @@
 
         int gridWidth, gridHeight, gridRows, gridCols;
 
-        useGrid = useGrid && (width > GRID_WIDTH || height > GRID_HEIGHT);
+        MediaCodecInfo.CodecCapabilities caps =
+                mEncoder.getCodecInfo().getCapabilitiesForType(useHeicEncoder
+                        ? MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC
+                        : MediaFormat.MIMETYPE_VIDEO_HEVC);
+
+        useGrid &= (width > GRID_WIDTH || height > GRID_HEIGHT);
+        // Always enable grid if the size is too large for the HEVC encoder
+        useGrid |= !caps.getVideoCapabilities().isSizeSupported(width, height);
 
         if (useGrid) {
             gridWidth = GRID_WIDTH;
@@ -237,9 +244,9 @@
         }
 
         if (useGrid) {
-            codecFormat.setInteger(MediaFormat.KEY_GRID_WIDTH, gridWidth);
-            codecFormat.setInteger(MediaFormat.KEY_GRID_HEIGHT, gridHeight);
-            codecFormat.setInteger(MediaFormat.KEY_GRID_COLS, gridCols);
+            codecFormat.setInteger(MediaFormat.KEY_TILE_WIDTH, gridWidth);
+            codecFormat.setInteger(MediaFormat.KEY_TILE_HEIGHT, gridHeight);
+            codecFormat.setInteger(MediaFormat.KEY_GRID_COLUMNS, gridCols);
             codecFormat.setInteger(MediaFormat.KEY_GRID_ROWS, gridRows);
         }
 
@@ -258,15 +265,11 @@
 
         codecFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0);
         codecFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
-
-        MediaCodecInfo.CodecCapabilities caps =
-                mEncoder.getCodecInfo().getCapabilitiesForType(useHeicEncoder
-                        ? MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC
-                        : MediaFormat.MIMETYPE_VIDEO_HEVC);
-        MediaCodecInfo.EncoderCapabilities encoderCaps = caps.getEncoderCapabilities();
-
         codecFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mNumTiles);
         codecFormat.setInteger(MediaFormat.KEY_CAPTURE_RATE, mNumTiles * 30);
+
+        MediaCodecInfo.EncoderCapabilities encoderCaps = caps.getEncoderCapabilities();
+
         if (encoderCaps.isBitrateModeSupported(
                 MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ)) {
             Log.d(TAG, "Setting bitrate mode to constant quality");
@@ -278,14 +281,14 @@
                             (qualityRange.getUpper() - qualityRange.getLower()) * quality / 100.0));
         } else {
             if (encoderCaps.isBitrateModeSupported(
-                    MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR)) {
-                Log.d(TAG, "Setting bitrate mode to variable bitrate");
-                codecFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
-                        MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
-            } else { // assume CBR
+                    MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR)) {
                 Log.d(TAG, "Setting bitrate mode to constant bitrate");
                 codecFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
                         MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR);
+            } else { // assume VBR
+                Log.d(TAG, "Setting bitrate mode to variable bitrate");
+                codecFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
+                        MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
             }
             // Calculate the bitrate based on image dimension, max compression ratio and quality.
             // Note that we set the frame rate to the number of tiles, so the bitrate would be the
@@ -806,10 +809,10 @@
                 format.setInteger(MediaFormat.KEY_HEIGHT, mHeight);
 
                 if (mNumTiles > 1) {
-                    format.setInteger(MediaFormat.KEY_GRID_WIDTH, mGridWidth);
-                    format.setInteger(MediaFormat.KEY_GRID_HEIGHT, mGridHeight);
+                    format.setInteger(MediaFormat.KEY_TILE_WIDTH, mGridWidth);
+                    format.setInteger(MediaFormat.KEY_TILE_HEIGHT, mGridHeight);
                     format.setInteger(MediaFormat.KEY_GRID_ROWS, mGridRows);
-                    format.setInteger(MediaFormat.KEY_GRID_COLS, mGridCols);
+                    format.setInteger(MediaFormat.KEY_GRID_COLUMNS, mGridCols);
                 }
             }
 
diff --git a/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java b/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java
index be7dffb..bc657c4 100644
--- a/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java
+++ b/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java
@@ -16,6 +16,8 @@
 
 package androidx.heifwriter;
 
+import static android.media.MediaMuxer.OutputFormat.MUXER_OUTPUT_HEIF;
+
 import android.annotation.SuppressLint;
 import android.graphics.Bitmap;
 import android.media.MediaCodec;
@@ -25,11 +27,12 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Process;
+import android.util.Log;
+import android.view.Surface;
+
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import android.util.Log;
-import android.view.Surface;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -38,8 +41,6 @@
 import java.nio.ByteBuffer;
 import java.util.concurrent.TimeoutException;
 
-import static android.media.MediaMuxer.OutputFormat.MUXER_OUTPUT_HEIF;
-
 /**
  * This class writes one or more still images (of the same dimensions) into
  * a heif file.
@@ -79,7 +80,8 @@
     private final HandlerThread mHandlerThread;
     private final Handler mHandler;
     private int mNumTiles;
-    private final int mNumImages;
+    private final int mRotation;
+    private final int mMaxImages;
     private final int mPrimaryIndex;
     private final ResultWaiter mResultWaiter = new ResultWaiter();
 
@@ -119,96 +121,200 @@
     public @interface InputMode {}
 
     /**
-     * Construct a heif writer that writes to a file specified by its path.
-     *
-     * @param path Path of the file to be written.
-     * @param width Width of the image.
-     * @param height Height of the image.
-     * @param useGrid Whether to encode image into tiles. If enabled, the tile size will be
-     *                automatically chosen.
-     * @param quality A number between 0 and 100 (inclusive), with 100 indicating the best quality
-     *                supported by this implementation (which often results in larger file size).
-     * @param numImages Max number of images to write. Frames exceeding this number will not be
-     *                  written to file. The writing can be stopped earlier before this number of
-     *                  images are written by {@link #stop(long)}, except for the input mode of
-     *                  {@link #INPUT_MODE_SURFACE}, where the EOS timestamp must be specified (via
-     *                 {@link #setInputEndOfStreamTimestamp(long)} and reached.
-     * @param primaryIndex Index of the image that should be marked as primary, must be within range
-     *                     [0, numImages - 1] inclusive.
-     * @param inputMode Input mode for this writer, must be one of {@link #INPUT_MODE_BUFFER},
-     *                  {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
-     * @param handler If not null, client will receive all callbacks on the handler's looper.
-     *                Otherwise, client will receive callbacks on a looper created by the writer.
-     *
-     * @throws IOException if failed to construct MediaMuxer or HeifEncoder.
+     * Builder class for constructing a HeifWriter object from specified parameters.
      */
-    @SuppressLint("WrongConstant")
-    public HeifWriter(@NonNull String path,
-                      int width, int height, boolean useGrid,
-                      int quality, int numImages, int primaryIndex,
-                      @InputMode int inputMode,
-                      @Nullable Handler handler) throws IOException {
-        this(width, height, useGrid, quality, numImages, primaryIndex, inputMode, handler,
-                new MediaMuxer(path, MUXER_OUTPUT_HEIF));
+    public static final class Builder {
+        private final String mPath;
+        private final FileDescriptor mFd;
+        private final int mWidth;
+        private final int mHeight;
+        private final @InputMode int mInputMode;
+        private boolean mGridEnabled = true;
+        private int mQuality = 100;
+        private int mMaxImages = 1;
+        private int mPrimaryIndex = 0;
+        private int mRotation = 0;
+        private Handler mHandler;
+
+        /**
+         * Construct a Builder with output specified by its path.
+         *
+         * @param path Path of the file to be written.
+         * @param width Width of the image.
+         * @param height Height of the image.
+         * @param inputMode Input mode for this writer, must be one of {@link #INPUT_MODE_BUFFER},
+         *                  {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
+         */
+        public Builder(@NonNull String path,
+                       int width, int height, @InputMode int inputMode) {
+            this(path, null, width, height, inputMode);
+        }
+
+        /**
+         * Construct a Builder with output specified by its file descriptor.
+         *
+         * @param fd File descriptor of the file to be written.
+         * @param width Width of the image.
+         * @param height Height of the image.
+         * @param inputMode Input mode for this writer, must be one of {@link #INPUT_MODE_BUFFER},
+         *                  {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
+         */
+        public Builder(@NonNull FileDescriptor fd,
+                       int width, int height, @InputMode int inputMode) {
+            this(null, fd, width, height, inputMode);
+        }
+
+        private Builder(String path, FileDescriptor fd,
+                        int width, int height, @InputMode int inputMode) {
+            if (width <= 0 || height <= 0) {
+                throw new IllegalArgumentException("Invalid image size: " + width + "x" + height);
+            }
+            mPath = path;
+            mFd = fd;
+            mWidth = width;
+            mHeight = height;
+            mInputMode = inputMode;
+        }
+
+        /**
+         * Set the image rotation in degrees.
+         *
+         * @param rotation Rotation angle (clockwise) of the image, must be 0, 90, 180 or 270.
+         *                 Default is 0.
+         * @return this Builder object.
+         */
+        public Builder setRotation(int rotation) {
+            if (rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270) {
+                throw new IllegalArgumentException("Invalid rotation angle: " + rotation);
+            }
+            mRotation = rotation;
+            return this;
+        }
+
+        /**
+         * Set whether to enable grid option.
+         *
+         * @param gridEnabled Whether to enable grid option. If enabled, the tile size will be
+         *                    automatically chosen. Default is to enable.
+         * @return this Builder object.
+         */
+        public Builder setGridEnabled(boolean gridEnabled) {
+            mGridEnabled = gridEnabled;
+            return this;
+        }
+
+        /**
+         * Set the quality for encoding images.
+         *
+         * @param quality A number between 0 and 100 (inclusive), with 100 indicating the best
+         *                quality supported by this implementation. Default is 100.
+         * @return this Builder object.
+         */
+        public Builder setQuality(int quality) {
+            if (quality < 0 || quality > 100) {
+                throw new IllegalArgumentException("Invalid quality: " + quality);
+            }
+            mQuality = quality;
+            return this;
+        }
+
+        /**
+         * Set the maximum number of images to write.
+         *
+         * @param maxImages Max number of images to write. Frames exceeding this number will not be
+         *                  written to file. The writing can be stopped earlier before this number
+         *                  of images are written by {@link #stop(long)}, except for the input mode
+         *                  of {@link #INPUT_MODE_SURFACE}, where the EOS timestamp must be
+         *                  specified (via {@link #setInputEndOfStreamTimestamp(long)} and reached.
+         *                  Default is 1.
+         * @return this Builder object.
+         */
+        public Builder setMaxImages(int maxImages) {
+            if (maxImages <= 0) {
+                throw new IllegalArgumentException("Invalid maxImage: " + maxImages);
+            }
+            mMaxImages = maxImages;
+            return this;
+        }
+
+        /**
+         * Set the primary image index.
+         *
+         * @param primaryIndex Index of the image that should be marked as primary, must be within
+         *                     range [0, maxImages - 1] inclusive. Default is 0.
+         * @return this Builder object.
+         */
+        public Builder setPrimaryIndex(int primaryIndex) {
+            if (primaryIndex < 0) {
+                throw new IllegalArgumentException("Invalid primaryIndex: " + primaryIndex);
+            }
+            mPrimaryIndex = primaryIndex;
+            return this;
+        }
+
+        /**
+         * Provide a handler for the HeifWriter to use.
+         *
+         * @param handler If not null, client will receive all callbacks on the handler's looper.
+         *                Otherwise, client will receive callbacks on a looper created by the
+         *                writer. Default is null.
+         * @return this Builder object.
+         */
+        public Builder setHandler(@Nullable Handler handler) {
+            mHandler = handler;
+            return this;
+        }
+
+        /**
+         * Build a HeifWriter object.
+         *
+         * @return a HeifWriter object built according to the specifications.
+         * @throws IOException if failed to create the writer, possibly due to failure to create
+         *                     {@link android.media.MediaMuxer} or {@link android.media.MediaCodec}.
+         */
+        public HeifWriter build() throws IOException {
+            return new HeifWriter(mPath, mFd, mWidth, mHeight, mRotation, mGridEnabled, mQuality,
+                    mMaxImages, mPrimaryIndex, mInputMode, mHandler);
+        }
     }
 
-    /**
-     * Construct a heif writer that writes to a file specified by file descriptor.
-     *
-     * @param fd File descriptor of the file to be written.
-     * @param width Width of the image.
-     * @param height Height of the image.
-     * @param useGrid Whether to encode image into tiles. If enabled, the tile size will be
-     *                automatically chosen.
-     * @param quality A number between 0 and 100 (inclusive), with 100 indicating the best quality
-     *                supported by this implementation (which often results in larger file size).
-     * @param numImages Max number of images to write. Frames exceeding this number will not be
-     *                  written to file. The writing can be stopped earlier before this number of
-     *                  images are written by {@link #stop(long)}, except for the input mode of
-     *                  {@link #INPUT_MODE_SURFACE}, where the EOS timestamp must be specified (via
-     *                 {@link #setInputEndOfStreamTimestamp(long)} and reached.
-     * @param primaryIndex Index of the image that should be marked as primary, must be within range
-     *                     [0, numImages - 1] inclusive.
-     * @param inputMode Input mode for this writer, must be one of {@link #INPUT_MODE_BUFFER},
-     *                  {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
-     * @param handler If not null, client will receive all callbacks on the handler's looper.
-     *                Otherwise, client will receive callbacks on a looper created by the writer.
-     *
-     * @throws IOException if failed to construct MediaMuxer or HeifEncoder.
-     */
     @SuppressLint("WrongConstant")
-    public HeifWriter(@NonNull FileDescriptor fd,
-                      int width, int height, boolean useGrid,
-                      int quality, int numImages, int primaryIndex,
-                      @InputMode int inputMode,
-                      @Nullable Handler handler) throws IOException {
-        this(width, height, useGrid, quality, numImages, primaryIndex, inputMode, handler,
-                new MediaMuxer(fd, MUXER_OUTPUT_HEIF));
-    }
-
-    private HeifWriter(int width, int height, boolean useGrid,
-                       int quality, int numImages, int primaryIndex,
+    private HeifWriter(@NonNull String path,
+                       @NonNull FileDescriptor fd,
+                       int width,
+                       int height,
+                       int rotation,
+                       boolean gridEnabled,
+                       int quality,
+                       int maxImages,
+                       int primaryIndex,
                        @InputMode int inputMode,
-                       @Nullable Handler handler,
-                       @NonNull MediaMuxer muxer) throws IOException {
-        if (numImages <= 0 || primaryIndex < 0 || primaryIndex >= numImages) {
+                       @Nullable Handler handler) throws IOException {
+        if (primaryIndex >= maxImages) {
             throw new IllegalArgumentException(
-                    "Invalid numImages (" + numImages + ") or primaryIndex (" + primaryIndex + ")");
+                    "Invalid maxImages (" + maxImages + ") or primaryIndex (" + primaryIndex + ")");
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "width: " + width
+                    + ", height: " + height
+                    + ", rotation: " + rotation
+                    + ", gridEnabled: " + gridEnabled
+                    + ", quality: " + quality
+                    + ", maxImages: " + maxImages
+                    + ", primaryIndex: " + primaryIndex
+                    + ", inputMode: " + inputMode);
         }
 
         MediaFormat format = MediaFormat.createVideoFormat(
                 MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC, width, height);
 
-        if (DEBUG) {
-            Log.d(TAG, "format: " + format + ", inputMode: " + inputMode +
-                    ", numImage: " + numImages + ", primaryIndex: " + primaryIndex);
-        }
-
         // set to 1 initially, and wait for output format to know for sure
         mNumTiles = 1;
 
+        mRotation = rotation;
         mInputMode = inputMode;
-        mNumImages = numImages;
+        mMaxImages = maxImages;
         mPrimaryIndex = primaryIndex;
 
         Looper looper = (handler != null) ? handler.getLooper() : null;
@@ -222,9 +328,10 @@
         }
         mHandler = new Handler(looper);
 
-        mMuxer = muxer;
+        mMuxer = (path != null) ? new MediaMuxer(path, MUXER_OUTPUT_HEIF)
+                                : new MediaMuxer(fd, MUXER_OUTPUT_HEIF);
 
-        mHeifEncoder = new HeifEncoder(width, height, useGrid, quality,
+        mHeifEncoder = new HeifEncoder(width, height, gridEnabled, quality,
                 mInputMode, mHandler, new HeifCallback());
     }
 
@@ -400,19 +507,23 @@
 
             try {
                 int gridRows = format.getInteger(MediaFormat.KEY_GRID_ROWS);
-                int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLS);
+                int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLUMNS);
                 mNumTiles = gridRows * gridCols;
             } catch (NullPointerException | ClassCastException  e) {
                 mNumTiles = 1;
             }
 
-            // add mNumImages image tracks of the same format
-            mTrackIndexArray = new int[mNumImages];
+            // add mMaxImages image tracks of the same format
+            mTrackIndexArray = new int[mMaxImages];
+
+            // set rotation angle
+            if (mRotation > 0) {
+                Log.d(TAG, "setting rotation: " + mRotation);
+                mMuxer.setOrientationHint(mRotation);
+            }
             for (int i = 0; i < mTrackIndexArray.length; i++) {
                 // mark primary
-                if (i == mPrimaryIndex) {
-                    format.setInteger(MediaFormat.KEY_IS_DEFAULT, 1);
-                }
+                format.setInteger(MediaFormat.KEY_IS_DEFAULT, (i == mPrimaryIndex) ? 1 : 0);
                 mTrackIndexArray[i] = mMuxer.addTrack(format);
             }
             mMuxer.start();
@@ -436,7 +547,7 @@
                 return;
             }
 
-            if (mOutputIndex < mNumImages * mNumTiles) {
+            if (mOutputIndex < mMaxImages * mNumTiles) {
                 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
                 info.set(byteBuffer.position(), byteBuffer.remaining(), 0, 0);
                 mMuxer.writeSampleData(
@@ -446,7 +557,7 @@
             mOutputIndex++;
 
             // post EOS if reached max number of images allowed.
-            if (mOutputIndex == mNumImages * mNumTiles) {
+            if (mOutputIndex == mMaxImages * mNumTiles) {
                 stopAndNotify(null);
             }
         }
diff --git a/jetifier/jetifier/core/build.gradle b/jetifier/jetifier/core/build.gradle
index 315b41e..68f3a00 100644
--- a/jetifier/jetifier/core/build.gradle
+++ b/jetifier/jetifier/core/build.gradle
@@ -14,20 +14,18 @@
  * limitations under the License
  */
 
-import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
 
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 
+import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
+
 plugins {
     id("SupportKotlinLibraryPlugin")
 }
 
 dependencies {
-    compile("org.ow2.asm:asm:5.2")
-    compile("org.ow2.asm:asm-commons:5.2")
     compile("com.google.code.gson:gson:2.8.0")
-    compile("org.jdom:jdom2:2.0.6")
     compile(KOTLIN_STDLIB)
     testCompile("junit:junit:4.12")
     testCompile("com.google.truth:truth:0.34")
@@ -35,7 +33,7 @@
 
 supportLibrary {
     name = "Android Jetifier Core"
-    publish = false
+    publish = true
     mavenVersion = LibraryVersions.JETIFIER
     mavenGroup = LibraryGroups.JETIFIER
     generateDocs = false
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/MapGeneratorRemapper.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/MapGeneratorRemapper.kt
deleted file mode 100644
index 97574e7..0000000
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/MapGeneratorRemapper.kt
+++ /dev/null
@@ -1,89 +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.tools.jetifier.core.map
-
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.transform.bytecode.CoreRemapper
-import android.support.tools.jetifier.core.transform.bytecode.asm.CustomRemapper
-import android.support.tools.jetifier.core.utils.Log
-import org.objectweb.asm.ClassVisitor
-import org.objectweb.asm.commons.ClassRemapper
-
-/**
- * Hooks to asm remapping to collect data for [TypesMap] by applying all the [RewriteRule]s from the
- * given [config] on all discovered and eligible types.
- */
-class MapGeneratorRemapper(private val config: Config) : CoreRemapper {
-
-    companion object {
-        private const val TAG: String = "MapGeneratorRemapper"
-    }
-
-    private val typesRewritesMap = mutableMapOf<JavaType, JavaType>()
-
-    var isMapNotComplete = false
-        private set
-
-    fun createClassRemapper(visitor: ClassVisitor): ClassRemapper {
-        return ClassRemapper(visitor, CustomRemapper(this))
-    }
-
-    override fun rewriteType(typeToMap: JavaType): JavaType {
-        if (!isTypeSupported(typeToMap)) {
-            return typeToMap
-        }
-
-        if (typesRewritesMap.contains(typeToMap)) {
-            return typeToMap
-        }
-
-        // Try to find a rule
-        val type = typeToMap.getRootType()
-        for (rule in config.rewriteRules) {
-            val typeRewriteResult = rule.apply(type)
-            if (typeRewriteResult.isIgnored) {
-                Log.i(TAG, "Ignoring: " + type)
-                return type
-            }
-            if (typeRewriteResult.result == null) {
-                continue
-            }
-            typesRewritesMap.put(type, typeRewriteResult.result)
-            Log.i(TAG, "  map: %s -> %s", type, typeRewriteResult.result)
-            return typeRewriteResult.result
-        }
-
-        isMapNotComplete = true
-        Log.e(TAG, "No rule for: " + type)
-        typesRewritesMap.put(type, type) // Identity
-        return type
-    }
-
-    override fun rewriteString(value: String): String {
-        // We don't build map from strings
-        return value
-    }
-
-    fun createTypesMap(): TypesMap {
-        return TypesMap(typesRewritesMap)
-    }
-
-    private fun isTypeSupported(type: JavaType): Boolean {
-        return config.restrictToPackagePrefixes.any { type.fullName.startsWith(it) }
-    }
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/TransformationContext.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/TransformationContext.kt
deleted file mode 100644
index d6705af..0000000
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/TransformationContext.kt
+++ /dev/null
@@ -1,135 +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.tools.jetifier.core.transform
-
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule.TypeRewriteResult
-import android.support.tools.jetifier.core.transform.proguard.ProGuardType
-import java.util.regex.Pattern
-
-/**
- * Context to share the transformation state between individual [Transformer]s.
- */
-class TransformationContext(
-    val config: Config,
-    val rewritingSupportLib: Boolean = false,
-    val isInReversedMode: Boolean = false,
-    /**
-     * Whether to use identity if type in our scope is missing instead of throwing an exception.
-     */
-    val useIdentityIfTypeIsMissing: Boolean = true
-) {
-
-    // Merges all packages prefixes into one regEx pattern
-    private val packagePrefixPattern = Pattern.compile(
-        "^(" + config.restrictToPackagePrefixes.map { "($it)" }.joinToString("|") + ").*$")
-
-    /**
-     * Whether to skip verification of dependency version match in pom files.
-     */
-    val ignorePomVersionCheck = rewritingSupportLib || isInReversedMode
-
-    /** Counter for [reportNoMappingFoundFailure] calls. */
-    var mappingNotFoundFailuresCount = 0
-        private set
-
-    /** Counter for [reportNoProGuardMappingFoundFailure] calls. */
-    var proGuardMappingNotFoundFailuresCount = 0
-        private set
-
-    /** Counter for [reportNoPackageMappingFoundFailure] calls. */
-    var packageMappingNotFoundFailuresCounts = 0
-
-    var libraryName: String = ""
-
-    private var runtimeIgnoreRules =
-        (
-            if (rewritingSupportLib) {
-                config.slRules
-            } else {
-                config.rewriteRules
-            }
-        )
-        .filter { it.isRuntimeIgnoreRule() }
-        .toTypedArray()
-
-    /** Total amount of errors found during the transformation process */
-    fun errorsTotal() = mappingNotFoundFailuresCount + proGuardMappingNotFoundFailuresCount +
-        packageMappingNotFoundFailuresCounts
-
-    /**
-     * Returns whether the given type is eligible for rewrite.
-     *
-     * If not, the transformers should ignore it.
-     */
-    fun isEligibleForRewrite(type: JavaType): Boolean {
-        if (!isEligibleForRewriteInternal(type.fullName)) {
-            return false
-        }
-
-        val isIgnored = runtimeIgnoreRules.any { it.apply(type) == TypeRewriteResult.IGNORED }
-        return !isIgnored
-    }
-
-    /**
-     * Returns whether the given ProGuard type reference is eligible for rewrite.
-     *
-     * Keep in mind that his has limited capabilities - mainly when * is used as a prefix. Rules
-     * like *.v7 are not matched by prefix support.v7. So don't rely on it and use
-     * the [ProGuardTypesMap] as first.
-     */
-    fun isEligibleForRewrite(type: ProGuardType): Boolean {
-        if (!isEligibleForRewriteInternal(type.value)) {
-            return false
-        }
-
-        val isIgnored = runtimeIgnoreRules.any { it.doesThisIgnoreProGuard(type) }
-        return !isIgnored
-    }
-
-    private fun isEligibleForRewriteInternal(type: String): Boolean {
-        if (config.restrictToPackagePrefixes.isEmpty()) {
-            return false
-        }
-        return packagePrefixPattern.matcher(type).matches()
-    }
-
-    /**
-     * Reports that there was a reference found that satisfies [isEligibleForRewrite] but no
-     * mapping was found to rewrite it.
-     */
-    fun reportNoMappingFoundFailure() {
-        mappingNotFoundFailuresCount++
-    }
-
-    /**
-     * Reports that there was a reference found in a ProGuard file that satisfies
-     * [isEligibleForRewrite] but no mapping was found to rewrite it.
-     */
-    fun reportNoProGuardMappingFoundFailure() {
-        proGuardMappingNotFoundFailuresCount++
-    }
-
-    /**
-     * Reports that there was a package reference found in a manifest file during a support library
-     * artifact rewrite but no mapping was found for it.
-     */
-    fun reportNoPackageMappingFoundFailure() {
-        packageMappingNotFoundFailuresCounts++
-    }
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomDependency.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomDependency.kt
deleted file mode 100644
index 8e480d0..0000000
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomDependency.kt
+++ /dev/null
@@ -1,148 +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.tools.jetifier.core.transform.pom
-
-import com.google.gson.annotations.SerializedName
-import org.jdom2.Document
-import org.jdom2.Element
-
-/**
- * Represents a '<dependency>' XML node of a POM file.
- *
- * See documentation of the content at https://maven.apache.org/pom.html#Dependencies
- */
-data class PomDependency(
-        @SerializedName("groupId")
-        val groupId: String?,
-
-        @SerializedName("artifactId")
-        val artifactId: String?,
-
-        @SerializedName("version")
-        var version: String? = null,
-
-        @SerializedName("classifier")
-        val classifier: String? = null,
-
-        @SerializedName("type")
-        val type: String? = null,
-
-        @SerializedName("scope")
-        val scope: String? = null,
-
-        @SerializedName("systemPath")
-        val systemPath: String? = null,
-
-        @SerializedName("optional")
-        val optional: String? = null) {
-
-    companion object {
-
-        /**
-         * Creates a new [PomDependency] from the given XML [Element].
-         */
-        fun fromXmlElement(node: Element, properties: Map<String, String>): PomDependency {
-            var groupId: String? = null
-            var artifactId: String? = null
-            var version: String? = null
-            var classifier: String? = null
-            var type: String? = null
-            var scope: String? = null
-            var systemPath: String? = null
-            var optional: String? = null
-
-            for (childNode in node.children) {
-                when (childNode.name) {
-                    "groupId" -> groupId = XmlUtils.resolveValue(childNode.value, properties)
-                    "artifactId" -> artifactId = XmlUtils.resolveValue(childNode.value, properties)
-                    "version" -> version = XmlUtils.resolveValue(childNode.value, properties)
-                    "classifier" -> classifier = XmlUtils.resolveValue(childNode.value, properties)
-                    "type" -> type = XmlUtils.resolveValue(childNode.value, properties)
-                    "scope" -> scope = XmlUtils.resolveValue(childNode.value, properties)
-                    "systemPath" -> systemPath = XmlUtils.resolveValue(childNode.value, properties)
-                    "optional" -> optional = XmlUtils.resolveValue(childNode.value, properties)
-                }
-            }
-
-            return PomDependency(
-                    groupId = groupId,
-                    artifactId = artifactId,
-                    version = version,
-                    classifier = classifier,
-                    type = type,
-                    scope = scope,
-                    systemPath = systemPath,
-                    optional = optional)
-        }
-    }
-
-    init {
-        if (version != null) {
-            version = version!!.toLowerCase()
-        }
-    }
-
-    /**
-     * Whether this dependency should be skipped from the rewriting process
-     */
-    fun shouldSkipRewrite(): Boolean {
-        return scope != null && scope.toLowerCase() == "test"
-    }
-
-    /**
-     * Returns a new dependency created by taking all the items from the [input] dependency and then
-     * overwriting these with all of its non-null items.
-     */
-    fun rewrite(input: PomDependency): PomDependency {
-        return PomDependency(
-            groupId = groupId ?: input.groupId,
-            artifactId = artifactId ?: input.artifactId,
-            version = version ?: input.version,
-            classifier = classifier ?: input.classifier,
-            type = type ?: input.type,
-            scope = scope ?: input.scope,
-            systemPath = systemPath ?: input.systemPath,
-            optional = optional ?: input.optional
-        )
-    }
-
-    /**
-     * Transforms the current data into XML '<dependency>' node.
-     */
-    fun toXmlElement(document: Document): Element {
-        val node = Element("dependency")
-        node.namespace = document.rootElement.namespace
-
-        XmlUtils.addStringNodeToNode(node, "groupId", groupId)
-        XmlUtils.addStringNodeToNode(node, "artifactId", artifactId)
-        XmlUtils.addStringNodeToNode(node, "version", version)
-        XmlUtils.addStringNodeToNode(node, "classifier", classifier)
-        XmlUtils.addStringNodeToNode(node, "type", type)
-        XmlUtils.addStringNodeToNode(node, "scope", scope)
-        XmlUtils.addStringNodeToNode(node, "systemPath", systemPath)
-        XmlUtils.addStringNodeToNode(node, "optional", optional)
-
-        return node
-    }
-
-    /**
-     * Returns the dependency in format "groupId:artifactId:version".
-     */
-    fun toStringNotation(): String {
-        return "$groupId:$artifactId:$version"
-    }
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/XmlUtils.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/XmlUtils.kt
deleted file mode 100644
index 67b7a3d..0000000
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/XmlUtils.kt
+++ /dev/null
@@ -1,91 +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.tools.jetifier.core.transform.pom
-
-import android.support.tools.jetifier.core.utils.Log
-import org.jdom2.Document
-import org.jdom2.Element
-import org.jdom2.input.SAXBuilder
-import org.jdom2.output.Format
-import org.jdom2.output.XMLOutputter
-import java.io.ByteArrayOutputStream
-import java.util.regex.Pattern
-
-/**
- * Utilities for handling XML documents.
- */
-class XmlUtils {
-
-    companion object {
-
-        private val variablePattern = Pattern.compile("\\$\\{([^}]*)}")
-
-        /** Saves the given [Document] to a new byte array */
-        fun convertDocumentToByteArray(document : Document) : ByteArray {
-            val xmlOutput = XMLOutputter()
-            ByteArrayOutputStream().use {
-                xmlOutput.format = Format.getPrettyFormat()
-                xmlOutput.output(document, it)
-                return it.toByteArray()
-            }
-        }
-
-        /** Creates a new [Document] from the given [ByteArray] */
-        fun createDocumentFromByteArray(data: ByteArray) : Document {
-            val builder = SAXBuilder()
-            data.inputStream().use {
-                return builder.build(it)
-            }
-        }
-
-        /**
-         * Creates a new XML element with the given [id] and text given in [value] and puts it under
-         * the given [parent]. Nothing is created if the [value] argument is null or empty.
-         */
-        fun addStringNodeToNode(parent: Element, id: String, value: String?) {
-            if (value.isNullOrEmpty()) {
-                return
-            }
-
-            val element = Element(id)
-            element.text = value
-            element.namespace = parent.namespace
-            parent.children.add(element)
-        }
-
-
-        fun resolveValue(value: String?, properties: Map<String, String>) : String? {
-            if (value == null) {
-                return null
-            }
-
-            val matcher = variablePattern.matcher(value)
-            if (matcher.matches()) {
-                val variableName = matcher.group(1)
-                val varValue = properties[variableName]
-                if (varValue == null) {
-                    Log.e("TAG", "Failed to resolve variable '%s'", value)
-                    return value
-                }
-                return varValue
-            }
-
-            return value
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTransformer.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTransformer.kt
deleted file mode 100644
index b7536c7..0000000
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTransformer.kt
+++ /dev/null
@@ -1,52 +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.tools.jetifier.core.transform.proguard
-
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.Transformer
-import android.support.tools.jetifier.core.transform.proguard.patterns.ReplacersRunner
-import java.nio.charset.StandardCharsets
-
-/**
- * The [Transformer] responsible for ProGuard files refactoring.
- */
-class ProGuardTransformer internal constructor(context: TransformationContext) : Transformer {
-
-    private val mapper = ProGuardTypesMapper(context)
-
-    val replacer = ReplacersRunner(listOf(
-        ProGuardClassSpecParser(mapper).replacer,
-        ProGuardClassFilterParser(mapper).replacer
-    ))
-
-    override fun canTransform(file: ArchiveFile): Boolean {
-        return file.isProGuardFile()
-    }
-
-    override fun runTransform(file: ArchiveFile) {
-        val content = StringBuilder(file.data.toString(StandardCharsets.UTF_8)).toString()
-        val result = replacer.applyReplacers(content)
-
-        if (result == content) {
-            return
-        }
-
-        file.setNewData(result.toByteArray())
-    }
-}
-
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/PackageMap.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/PackageMap.kt
similarity index 95%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/PackageMap.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/PackageMap.kt
index 23888d3..7a1e300 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/PackageMap.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/PackageMap.kt
@@ -1,22 +1,22 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform
+package com.android.tools.build.jetifier.core
 
-import android.support.tools.jetifier.core.rules.PackageName
+import com.android.tools.build.jetifier.core.type.PackageName
 
 /**
  * Package map to be used to rewrite packages. The rewrite rules allow duplicities where the
@@ -304,6 +304,14 @@
             PackageRule(
                 from = "android/arch/lifecycle/reactivestreams",
                 to = "androidx/lifecycle/reactivestreams"
+            ),
+            PackageRule(
+                from = "android/support/multidex/instrumentation",
+                to = "androidx/multidex/instrumentation"
+            ),
+            PackageRule(
+                from = "android/support/multidex",
+                to = "androidx/multidex"
             )
         )
 
diff --git a/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/TypeRewriter.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/TypeRewriter.kt
new file mode 100644
index 0000000..9f85d38
--- /dev/null
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/TypeRewriter.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2018 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.android.tools.build.jetifier.core
+
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardType
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.utils.Log
+import java.util.regex.Pattern
+
+/**
+ * Wraps capabilities of [TypesMap] and [RewriteRulesMap] into one place.
+ */
+class TypeRewriter(private val config: Config, private val useFallback: Boolean) {
+
+    companion object {
+        private const val TAG = "TypeRewriter"
+    }
+
+    // Merges all packages prefixes into one regEx pattern
+    private val packagePrefixPattern = Pattern.compile(
+            "^(" + config.restrictToPackagePrefixes.map { "($it)" }.joinToString("|") + ").*$")
+
+    fun rewriteType(type: JavaType): JavaType? {
+        if (!isEligibleForRewrite(type)) {
+            return type
+        }
+
+        val result = config.typesMap.mapType(type)
+        if (result != null) {
+            Log.i(TAG, "Map: %s -> %s", type, result)
+            return result
+        }
+
+        if (!useFallback) {
+            Log.e(TAG, "No mapping for: " + type)
+            return null
+        }
+
+        val rulesResult = config.rulesMap.rewriteType(type)
+        if (rulesResult != null) {
+            Log.i(TAG, "Using fallback: %s -> %s", type, rulesResult)
+            return rulesResult
+        }
+
+        Log.e(TAG, "No mapping for: " + type)
+        return null
+    }
+
+    /**
+     * Returns whether the given type is eligible for rewrite.
+     *
+     * If not, the transformers should ignore it.
+     */
+    fun isEligibleForRewrite(type: JavaType): Boolean {
+        if (!isEligibleForRewriteInternal(type.fullName)) {
+            return false
+        }
+
+        val isIgnored = config.rulesMap.runtimeIgnoreRules
+            .any { it.apply(type) == RewriteRule.TypeRewriteResult.IGNORED }
+        return !isIgnored
+    }
+
+    /**
+    * Returns whether the given ProGuard type reference is eligible for rewrite.
+    *
+    * Keep in mind that his has limited capabilities - mainly when * is used as a prefix. Rules
+    * like *.v7 are not matched by prefix support.v7. So don't rely on it and use
+    * the [ProGuardTypesMap] as first.
+    */
+    fun isEligibleForRewrite(type: ProGuardType): Boolean {
+        if (!isEligibleForRewriteInternal(type.value)) {
+            return false
+        }
+
+        val isIgnored = config.rulesMap.runtimeIgnoreRules.any { it.doesThisIgnoreProGuard(type) }
+        return !isIgnored
+    }
+
+    private fun isEligibleForRewriteInternal(type: String): Boolean {
+        if (config.restrictToPackagePrefixes.isEmpty()) {
+            return false
+        }
+        return packagePrefixPattern.matcher(type).matches()
+    }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/config/Config.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/config/Config.kt
similarity index 69%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/config/Config.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/config/Config.kt
index 673fbf6..e9414bb 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/config/Config.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/config/Config.kt
@@ -1,33 +1,34 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.config
+package com.android.tools.build.jetifier.core.config
 
-import android.support.tools.jetifier.core.rules.RewriteRule
-import android.support.tools.jetifier.core.transform.pom.PomRewriteRule
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.transform.PackageMap
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.TypesMap
 import com.google.gson.annotations.SerializedName
 
 /**
  * The main and only one configuration that is used by the tool and all its transformers.
  *
  * [restrictToPackagePrefixes] Package prefixes that limit the scope of the rewriting
- * [rewriteRules] Rules to scan support libraries to generate [TypesMap]
+ * [rulesMap] Rules to scan support libraries to generate [TypesMap]
  * [slRules] List of rules used when rewriting the support library itself in the reversed mode to
  * ignore packages that don't need rewriting anymore.
  * [pomRewriteRules] Rules to rewrite POM files
@@ -36,9 +37,9 @@
  * rewrite.
  */
 data class Config(
-    val restrictToPackagePrefixes: List<String>,
-    val rewriteRules: List<RewriteRule>,
-    val slRules: List<RewriteRule>,
+    val restrictToPackagePrefixes: Set<String>,
+    val rulesMap: RewriteRulesMap,
+    val slRules: Set<RewriteRule>,
     val pomRewriteRules: Set<PomRewriteRule>,
     val typesMap: TypesMap,
     val proGuardMap: ProGuardTypesMap,
@@ -62,18 +63,28 @@
     companion object {
         /** Path to the default config file located within the jar file. */
         const val DEFAULT_CONFIG_RES_PATH = "/default.generated.config"
+
+        val EMPTY = Config(
+            restrictToPackagePrefixes = emptySet(),
+            rulesMap = RewriteRulesMap.EMPTY,
+            slRules = emptySet(),
+            pomRewriteRules = emptySet(),
+            typesMap = TypesMap.EMPTY,
+            proGuardMap = ProGuardTypesMap.EMPTY,
+            packageMap = PackageMap.EMPTY
+        )
     }
 
     fun setNewMap(mappings: TypesMap): Config {
-        return Config(restrictToPackagePrefixes, rewriteRules, slRules, pomRewriteRules,
+        return Config(restrictToPackagePrefixes, rulesMap, slRules, pomRewriteRules,
             mappings, proGuardMap)
     }
 
     /** Returns JSON data model of this class */
     fun toJson(): JsonData {
         return JsonData(
-            restrictToPackagePrefixes,
-            rewriteRules.map { it.toJson() }.toList(),
+            restrictToPackagePrefixes.toList(),
+            rulesMap.toJson().rules.toList(),
             slRules.map { it.toJson() }.toList(),
             pomRewriteRules.map { it.toJson() }.toList(),
             typesMap.toJson(),
@@ -89,7 +100,7 @@
             val restrictToPackages: List<String?>,
 
             @SerializedName("rules")
-            val rules: List<RewriteRule.JsonData?>,
+            val rules: List<RewriteRule.JsonData?>?,
 
             @SerializedName("slRules")
             val slRules: List<RewriteRule.JsonData?>?,
@@ -107,9 +118,10 @@
         fun toConfig(): Config {
 
             return Config(
-                restrictToPackagePrefixes = restrictToPackages.filterNotNull(),
-                rewriteRules = rules.filterNotNull().map { it.toRule() },
-                slRules = slRules?.filterNotNull()?.map { it.toRule() } ?: listOf(),
+                restrictToPackagePrefixes = restrictToPackages.filterNotNull().toSet(),
+                rulesMap = RewriteRulesMap(
+                    rules?.filterNotNull()?.map { it.toRule() }?.toSet() ?: emptySet()),
+                slRules = slRules?.filterNotNull()?.map { it.toRule() }?.toSet() ?: emptySet(),
                 pomRewriteRules = pomRules.filterNotNull().map { it.toRule() }.toSet(),
                 typesMap = mappings?.toMappings() ?: TypesMap.EMPTY,
                 proGuardMap = proGuardMap?.toMappings() ?: ProGuardTypesMap.EMPTY
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/config/ConfigParser.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/config/ConfigParser.kt
similarity index 78%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/config/ConfigParser.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/config/ConfigParser.kt
index 50d510c..74fd3d1 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/config/ConfigParser.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/config/ConfigParser.kt
@@ -1,22 +1,22 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.config
+package com.android.tools.build.jetifier.core.config
 
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
 import com.google.gson.GsonBuilder
 import java.io.FileNotFoundException
 import java.io.FileWriter
@@ -25,11 +25,11 @@
 
 object ConfigParser {
 
-    private const val TAG : String = "Config"
+    private const val TAG: String = "Config"
 
     private val gson = GsonBuilder().setPrettyPrinting().create()
 
-    fun writeToString(config: Config) : String {
+    fun writeToString(config: Config): String {
         return gson.toJson(config.toJson())
     }
 
@@ -39,22 +39,22 @@
         }
     }
 
-    fun parseFromString(inputText: String) : Config? {
+    fun parseFromString(inputText: String): Config? {
         return gson.fromJson(inputText, Config.JsonData::class.java).toConfig()
     }
 
-    fun loadFromFile(configPath: Path) : Config? {
+    fun loadFromFile(configPath: Path): Config? {
         return loadConfigFileInternal(configPath)
     }
 
-    fun loadDefaultConfig() : Config? {
+    fun loadDefaultConfig(): Config? {
         Log.v(TAG, "Using the default config '%s'", Config.DEFAULT_CONFIG_RES_PATH)
 
         val inputStream = javaClass.getResourceAsStream(Config.DEFAULT_CONFIG_RES_PATH)
         return parseFromString(inputStream.reader().readText())
     }
 
-    fun loadConfigOrFail(configPath: Path?) : Config {
+    fun loadConfigOrFail(configPath: Path?): Config {
         if (configPath != null) {
             val config = loadConfigFileInternal(configPath)
             if (config != null) {
@@ -70,7 +70,7 @@
         throw AssertionError("The default config could not be found!")
     }
 
-    private fun loadConfigFileInternal(configPath: Path) : Config? {
+    private fun loadConfigFileInternal(configPath: Path): Config? {
         if (!Files.isReadable(configPath)) {
             Log.e(TAG, "Cannot access the config file: '%s'", configPath)
             return null
diff --git a/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/pom/PomDependency.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/pom/PomDependency.kt
new file mode 100644
index 0000000..6109064
--- /dev/null
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/pom/PomDependency.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2018 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.android.tools.build.jetifier.core.pom
+
+import com.google.gson.annotations.SerializedName
+
+/**
+ * Represents a '<dependency>' XML node of a POM file.
+ *
+ * See documentation of the content at https://maven.apache.org/pom.html#Dependencies
+ */
+data class PomDependency(
+    @SerializedName("groupId")
+    val groupId: String?,
+
+    @SerializedName("artifactId")
+    val artifactId: String?,
+
+    @SerializedName("version")
+    var version: String? = null,
+
+    @SerializedName("classifier")
+    val classifier: String? = null,
+
+    @SerializedName("type")
+    val type: String? = null,
+
+    @SerializedName("scope")
+    val scope: String? = null,
+
+    @SerializedName("systemPath")
+    val systemPath: String? = null,
+
+    @SerializedName("optional")
+    val optional: String? = null) {
+
+    init {
+        if (version != null) {
+            version = version!!.toLowerCase()
+        }
+    }
+
+    /**
+     * Whether this dependency should be skipped from the rewriting process
+     */
+    fun shouldSkipRewrite(): Boolean {
+        return scope != null && scope.toLowerCase() == "test"
+    }
+
+    /**
+     * Returns a new dependency created by taking all the items from the [input] dependency and then
+     * overwriting these with all of its non-null items.
+     */
+    fun rewrite(input: PomDependency): PomDependency {
+        return PomDependency(
+            groupId = groupId ?: input.groupId,
+            artifactId = artifactId ?: input.artifactId,
+            version = version ?: input.version,
+            classifier = classifier ?: input.classifier,
+            type = type ?: input.type,
+            scope = scope ?: input.scope,
+            systemPath = systemPath ?: input.systemPath,
+            optional = optional ?: input.optional
+        )
+    }
+
+    /**
+     * Returns the dependency in format "groupId:artifactId:version".
+     */
+    fun toStringNotation(): String {
+        return "$groupId:$artifactId:$version"
+    }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteRule.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/pom/PomRewriteRule.kt
similarity index 85%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteRule.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/pom/PomRewriteRule.kt
index 49bf8de..326b323 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteRule.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/pom/PomRewriteRule.kt
@@ -1,23 +1,24 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.pom
+package com.android.tools.build.jetifier.core.pom
 
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
 import com.google.gson.annotations.SerializedName
+import java.nio.file.Path
 
 /**
  * Rule that defines how to rewrite a dependency element in a POM file.
@@ -61,7 +62,7 @@
     /**
      * Validates that the given [input] dependency has a valid version.
      */
-    fun validateVersion(input: PomDependency, document: PomDocument? = null): Boolean {
+    fun validateVersion(input: PomDependency, pomPath: Path? = null): Boolean {
         if (from.version == null || input.version == null) {
             return true
         }
@@ -72,8 +73,8 @@
 
         if (!areVersionsMatching(from.version!!, input.version!!)) {
             Log.e(TAG, "Version mismatch! Expected version '%s' but found version '%s' for " +
-                    "'%s:%s' in '%s' file.", from.version, input.version, input.groupId,
-                    input.artifactId, document?.file?.relativePath)
+                "'%s:%s' in '%s' file.", from.version, input.version, input.groupId,
+                input.artifactId, pomPath.toString())
             return false
         }
 
@@ -107,8 +108,8 @@
     }
 
     /** Returns JSON data model of this class */
-    fun toJson(): PomRewriteRule.JsonData {
-        return PomRewriteRule.JsonData(from, to)
+    fun toJson(): JsonData {
+        return JsonData(from, to)
     }
 
     /**
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardType.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/proguard/ProGuardType.kt
similarity index 78%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardType.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/proguard/ProGuardType.kt
index be15fbf..fd45004 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardType.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/proguard/ProGuardType.kt
@@ -1,22 +1,22 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.core.proguard
 
-import android.support.tools.jetifier.core.rules.JavaType
+import com.android.tools.build.jetifier.core.type.JavaType
 
 /**
  * Represents a type reference in ProGuard file. This type is similar to the regular java type but
@@ -32,7 +32,7 @@
 
     companion object {
         /** Creates the type reference from notation where packages are separated using '.' */
-        fun fromDotNotation(type: String) : ProGuardType {
+        fun fromDotNotation(type: String): ProGuardType {
             return ProGuardType(type.replace('.', '/'))
         }
     }
@@ -42,7 +42,7 @@
      */
     fun isTrivial() = value == "*" || value == "**" || value == "***" || value == "%"
 
-    fun toJavaType() : JavaType? {
+    fun toJavaType(): JavaType? {
         if (value.contains('*') || value.contains('?')) {
             return null
         }
@@ -50,7 +50,7 @@
     }
 
     /** Returns the type reference as a string where packages are separated using '.' */
-    fun toDotNotation() : String {
+    fun toDotNotation(): String {
         return value.replace('/', '.')
     }
 }
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMap.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/proguard/ProGuardTypesMap.kt
similarity index 89%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMap.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/proguard/ProGuardTypesMap.kt
index f232a35..1aa81fe 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMap.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/proguard/ProGuardTypesMap.kt
@@ -1,22 +1,22 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.core.proguard
 
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
 
 /**
  * Contains custom mappings to map support library types referenced in ProGuard to new ones.
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/RewriteRule.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRule.kt
similarity index 85%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/RewriteRule.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRule.kt
index 0a7fbde..8846080 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/RewriteRule.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRule.kt
@@ -1,22 +1,23 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.rules
+package com.android.tools.build.jetifier.core.rule
 
-import android.support.tools.jetifier.core.transform.proguard.ProGuardType
+import com.android.tools.build.jetifier.core.proguard.ProGuardType
+import com.android.tools.build.jetifier.core.type.JavaType
 import com.google.gson.annotations.SerializedName
 import java.util.regex.Pattern
 
@@ -30,9 +31,7 @@
  * @param to A string to be used as a replacement if the 'from' pattern is matched. It can also
  * apply groups matched from the original pattern using {x} annotation, e.g. {0}.
  */
-class RewriteRule(
-        private val from: String,
-        private val to: String) {
+class RewriteRule(private val from: String, private val to: String) {
 
     companion object {
         const val IGNORE_RUNTIME = "ignore"
@@ -86,6 +85,12 @@
         return TypeRewriteResult(JavaType(result))
     }
 
+    fun reverse(): RewriteRule {
+        val newFrom = to.replace("{0}", "(.*)")
+        val newTo = from.replace("(.*)", "{0}")
+        return RewriteRule(newFrom, newTo)
+    }
+
     /*
      * Returns whether this rule is an ignore rule and applies to the given proGuard type.
      */
@@ -111,11 +116,11 @@
      * JSON data model for [RewriteRule].
      */
     data class JsonData(
-            @SerializedName("from")
-            val from: String,
+        @SerializedName("from")
+        val from: String,
 
-            @SerializedName("to")
-            val to: String) {
+        @SerializedName("to")
+        val to: String) {
 
         /** Creates instance of [RewriteRule] */
         fun toRule(): RewriteRule {
diff --git a/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRulesMap.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRulesMap.kt
new file mode 100644
index 0000000..daf5b97
--- /dev/null
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRulesMap.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018 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.android.tools.build.jetifier.core.rule
+
+import com.android.tools.build.jetifier.core.type.JavaType
+
+/**
+ * Contains all [RewriteRule]s.
+ */
+class RewriteRulesMap(val rewriteRules: Set<RewriteRule>) {
+
+    companion object {
+        private const val TAG = "RewriteRulesMap"
+
+        val EMPTY = RewriteRulesMap(emptySet())
+    }
+
+    constructor(vararg rules: RewriteRule) : this(rules.toSet())
+
+    val runtimeIgnoreRules = rewriteRules.filter { it.isRuntimeIgnoreRule() }.toSet()
+
+    /**
+     * Tries to rewrite the given given type using the rules. If
+     */
+    fun rewriteType(type: JavaType): JavaType? {
+        // Try to find a rule
+        for (rule in rewriteRules) {
+            if (rule.isIgnoreRule()) {
+                continue
+            }
+            val typeRewriteResult = rule.apply(type)
+            if (typeRewriteResult.result == null) {
+                continue
+            }
+            return typeRewriteResult.result!!
+        }
+
+        return null
+    }
+
+    fun reverse(): RewriteRulesMap {
+        return RewriteRulesMap(rewriteRules
+            .filter { !it.isIgnoreRule() }
+            .map { it.reverse() }
+            .toSet())
+    }
+
+    fun appendRules(rules: Set<RewriteRule>): RewriteRulesMap {
+        return RewriteRulesMap(rewriteRules + rules)
+    }
+
+    fun toJson(): JsonData {
+        return JsonData(rewriteRules.map { it.toJson() }.toSet())
+    }
+
+    /**
+     * JSON data model for [RewriteRulesMap].
+     */
+    data class JsonData(val rules: Set<RewriteRule.JsonData>)
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/JavaType.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/JavaType.kt
similarity index 92%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/JavaType.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/JavaType.kt
index 734f46d..66a67cc 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/JavaType.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/JavaType.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.rules
+package com.android.tools.build.jetifier.core.type
 
 /**
  * Wrapper for Java type declaration.
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/PackageName.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/PackageName.kt
similarity index 86%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/PackageName.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/PackageName.kt
index c42f995..b89738e 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/PackageName.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/PackageName.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.rules
+package com.android.tools.build.jetifier.core.type
 
 /**
  * Wrapper for Java package name declaration.
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/TypesMap.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/TypesMap.kt
similarity index 88%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/TypesMap.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/TypesMap.kt
index 8e497f5..e592a71 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/TypesMap.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/TypesMap.kt
@@ -1,24 +1,22 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.map
+package com.android.tools.build.jetifier.core.type
 
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
 
 /**
  * Contains all the mappings needed to rewrite java types.
@@ -41,6 +39,8 @@
         }
     }
 
+    constructor(vararg types: Pair<JavaType, JavaType>) : this(types.toMap())
+
     /** Returns JSON data model of this class */
     fun toJson(): JsonData {
         return JsonData(types.map { it.key.fullName to it.value.fullName }.toMap())
@@ -66,7 +66,7 @@
                 "See the log for more details.")
         }
 
-        return TypesMap(types= typesReversed)
+        return TypesMap(types = typesReversed)
     }
 
     /** Maps the given type using this map. */
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/Log.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
similarity index 82%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/Log.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
index b1bb0af..fa558c5 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/Log.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
@@ -1,33 +1,34 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.utils
+package com.android.tools.build.jetifier.core.utils
 
 object Log {
 
-    var currentLevel: LogLevel = LogLevel.INFO
+    var currentLevel: LogLevel = LogLevel.ERROR
 
     var logConsumer: LogConsumer = StdOutLogConsumer()
 
     fun setLevel(level: String?) {
         currentLevel = when (level) {
+            "info" -> LogLevel.INFO
             "error" -> LogLevel.ERROR
             "debug" -> LogLevel.DEBUG
             "verbose" -> LogLevel.VERBOSE
-            else -> LogLevel.INFO
+            else -> LogLevel.ERROR
         }
     }
 
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogConsumer.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/LogConsumer.kt
similarity index 78%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogConsumer.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/LogConsumer.kt
index ddebd25..0489274 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogConsumer.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/LogConsumer.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.utils
+package com.android.tools.build.jetifier.core.utils
 
 /**
  * Interface to plug custom logs consumers to [Log].
@@ -28,6 +28,5 @@
     fun verbose(message: String)
 
     fun debug(message: String)
-
 }
 
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogLevel.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/LogLevel.kt
similarity index 70%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogLevel.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/LogLevel.kt
index f46b8f6..3017304 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogLevel.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/LogLevel.kt
@@ -1,22 +1,22 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.utils
+package com.android.tools.build.jetifier.core.utils
 
-enum class LogLevel(val priority : Int) {
+enum class LogLevel(val priority: Int) {
     ERROR(0),
     INFO(1),
     VERBOSE(2),
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/StdOutLogConsumer.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/StdOutLogConsumer.kt
similarity index 82%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/StdOutLogConsumer.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/StdOutLogConsumer.kt
index 7cfd25e..f63cb32 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/StdOutLogConsumer.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/StdOutLogConsumer.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.utils
+package com.android.tools.build.jetifier.core.utils
 
 /**
  * Prints logs to the standard output.
diff --git a/jetifier/jetifier/core/src/main/resources/default.config b/jetifier/jetifier/core/src/main/resources/default.config
index 4dc5fda..bec56c3 100644
--- a/jetifier/jetifier/core/src/main/resources/default.config
+++ b/jetifier/jetifier/core/src/main/resources/default.config
@@ -665,6 +665,12 @@
             "from": "android/databinding/(.*)",
             "to": "androidx/databinding/{0}"
         },
+
+        #Multidex
+        {
+            "from": "android/support/multidex/(.*)",
+            "to": "androidx/multidex/{0}"
+        }
     ],
     "slRules": [
         # Ignore
@@ -699,7 +705,7 @@
         {
             "from": "androidx/textclassifier/(.*)",
             "to": "ignore"
-        },
+        }
     ],
     "pomRules": [
         {
@@ -739,12 +745,12 @@
             "to": [{ "groupId": "androidx.mediarouter", "artifactId": "mediarouter", "version": "1.0.0" }]
         },
         {
-            "from": { "groupId": "com.android.support", "artifactId": "multidex", "version": "28.0.0" },
-            "to": [{ "groupId": "androidx.multidex", "artifactId": "multidex", "version": "1.0.0" }]
+            "from": { "groupId": "com.android.support", "artifactId": "multidex", "version": "1.0.3" },
+            "to": [{ "groupId": "androidx.multidex", "artifactId": "multidex", "version": "2.0.0" }]
         },
         {
-            "from": { "groupId": "com.android.support", "artifactId": "multidex-instrumentation", "version": "28.0.0" },
-            "to": [{ "groupId": "androidx.multidex", "artifactId": "multidex-instrumentation", "version": "1.0.0" }]
+            "from": { "groupId": "com.android.support", "artifactId": "multidex-instrumentation", "version": "1.0.3" },
+            "to": [{ "groupId": "androidx.multidex", "artifactId": "multidex-instrumentation", "version": "2.0.0" }]
         },
         {
             "from": { "groupId": "com.android.support", "artifactId": "palette-v7", "version": "28.0.0" },
@@ -1108,16 +1114,44 @@
         },
         # Keep it same
         {
-            "from": { "groupId": "androidx.slice", "artifactId": "slices-core", "version": "1.0.0" },
+            "from": { "groupId": "com.android.support", "artifactId": "slices-core", "version": "28.0.0" },
             "to": [{ "groupId": "androidx.slice", "artifactId": "slices-core", "version": "1.0.0" }]
         },
         {
-            "from": { "groupId": "androidx.slice", "artifactId": "slices-builders", "version": "1.0.0" },
+            "from": { "groupId": "com.android.support", "artifactId": "slices-builders", "version": "28.0.0" },
             "to": [{ "groupId": "androidx.slice", "artifactId": "slices-builders", "version": "1.0.0" }]
         },
         {
-            "from": { "groupId": "androidx.slice", "artifactId": "slices-view", "version": "1.0.0" },
+            "from": { "groupId": "com.android.support", "artifactId": "slices-view", "version": "28.0.0" },
             "to": [{ "groupId": "androidx.slice", "artifactId": "slices-view", "version": "1.0.0" }]
+        },
+        {
+            "from": { "groupId": "com.android.support", "artifactId": "heifwriter", "version": "28.0.0" },
+            "to": [{ "groupId": "androidx.heifwriter", "artifactId": "heifwriter", "version": "1.0.0" }]
+        },
+        {
+            "from": { "groupId": "com.android.support", "artifactId": "recyclerview-selection", "version": "28.0.0" },
+            "to": [{ "groupId": "androidx.recyclerview", "artifactId": "recyclerview-selection", "version": "1.0.0" }]
+        },
+        {
+            "from": { "groupId": "com.android.support", "artifactId": "webkit", "version": "28.0.0" },
+            "to": [{ "groupId": "androidx.webkit", "artifactId": "webkit", "version": "1.0.0" }]
+        },
+        {
+            "from": { "groupId": "com.android.support", "artifactId": "palette-ktx", "version": "28.0.0" },
+            "to": [{ "groupId": "androidx.palette", "artifactId": "palette-ktx", "version": "1.0.0" }]
+        },
+        {
+            "from": { "groupId": "com.android.support", "artifactId": "fragment-ktx", "version": "28.0.0" },
+            "to": [{ "groupId": "androidx.fragment", "artifactId": "fragment-ktx", "version": "1.0.0" }]
+        },
+        {
+            "from": { "groupId": "com.android.support", "artifactId": "core-ktx", "version": "28.0.0" },
+            "to": [{ "groupId": "androidx.core", "artifactId": "core-ktx", "version": "1.0.0" }]
+        },
+        {
+            "from": { "groupId": "com.android.support", "artifactId": "collection-ktx", "version": "28.0.0" },
+            "to": [{ "groupId": "androidx.collection", "artifactId": "collection-ktx", "version": "1.0.0" }]
         }
     ],
     "map": {
diff --git a/jetifier/jetifier/core/src/main/resources/default.generated.config b/jetifier/jetifier/core/src/main/resources/default.generated.config
index ca86505..57590dd 100644
--- a/jetifier/jetifier/core/src/main/resources/default.generated.config
+++ b/jetifier/jetifier/core/src/main/resources/default.generated.config
@@ -646,6 +646,10 @@
     {
       "from": "android/databinding/(.*)",
       "to": "androidx/databinding/{0}"
+    },
+    {
+      "from": "android/support/multidex/(.*)",
+      "to": "androidx/multidex/{0}"
     }
   ],
   "slRules": [
@@ -813,13 +817,13 @@
       "from": {
         "groupId": "com.android.support",
         "artifactId": "multidex",
-        "version": "28.0.0"
+        "version": "1.0.3"
       },
       "to": [
         {
           "groupId": "androidx.multidex",
           "artifactId": "multidex",
-          "version": "1.0.0"
+          "version": "2.0.0"
         }
       ]
     },
@@ -827,13 +831,13 @@
       "from": {
         "groupId": "com.android.support",
         "artifactId": "multidex-instrumentation",
-        "version": "28.0.0"
+        "version": "1.0.3"
       },
       "to": [
         {
           "groupId": "androidx.multidex",
           "artifactId": "multidex-instrumentation",
-          "version": "1.0.0"
+          "version": "2.0.0"
         }
       ]
     },
@@ -2099,9 +2103,9 @@
     },
     {
       "from": {
-        "groupId": "androidx.slice",
+        "groupId": "com.android.support",
         "artifactId": "slices-core",
-        "version": "1.0.0"
+        "version": "28.0.0"
       },
       "to": [
         {
@@ -2113,9 +2117,9 @@
     },
     {
       "from": {
-        "groupId": "androidx.slice",
+        "groupId": "com.android.support",
         "artifactId": "slices-builders",
-        "version": "1.0.0"
+        "version": "28.0.0"
       },
       "to": [
         {
@@ -2127,9 +2131,9 @@
     },
     {
       "from": {
-        "groupId": "androidx.slice",
+        "groupId": "com.android.support",
         "artifactId": "slices-view",
-        "version": "1.0.0"
+        "version": "28.0.0"
       },
       "to": [
         {
@@ -2138,6 +2142,104 @@
           "version": "1.0.0"
         }
       ]
+    },
+    {
+      "from": {
+        "groupId": "com.android.support",
+        "artifactId": "heifwriter",
+        "version": "28.0.0"
+      },
+      "to": [
+        {
+          "groupId": "androidx.heifwriter",
+          "artifactId": "heifwriter",
+          "version": "1.0.0"
+        }
+      ]
+    },
+    {
+      "from": {
+        "groupId": "com.android.support",
+        "artifactId": "recyclerview-selection",
+        "version": "28.0.0"
+      },
+      "to": [
+        {
+          "groupId": "androidx.recyclerview",
+          "artifactId": "recyclerview-selection",
+          "version": "1.0.0"
+        }
+      ]
+    },
+    {
+      "from": {
+        "groupId": "com.android.support",
+        "artifactId": "webkit",
+        "version": "28.0.0"
+      },
+      "to": [
+        {
+          "groupId": "androidx.webkit",
+          "artifactId": "webkit",
+          "version": "1.0.0"
+        }
+      ]
+    },
+    {
+      "from": {
+        "groupId": "com.android.support",
+        "artifactId": "palette-ktx",
+        "version": "28.0.0"
+      },
+      "to": [
+        {
+          "groupId": "androidx.palette",
+          "artifactId": "palette-ktx",
+          "version": "1.0.0"
+        }
+      ]
+    },
+    {
+      "from": {
+        "groupId": "com.android.support",
+        "artifactId": "fragment-ktx",
+        "version": "28.0.0"
+      },
+      "to": [
+        {
+          "groupId": "androidx.fragment",
+          "artifactId": "fragment-ktx",
+          "version": "1.0.0"
+        }
+      ]
+    },
+    {
+      "from": {
+        "groupId": "com.android.support",
+        "artifactId": "core-ktx",
+        "version": "28.0.0"
+      },
+      "to": [
+        {
+          "groupId": "androidx.core",
+          "artifactId": "core-ktx",
+          "version": "1.0.0"
+        }
+      ]
+    },
+    {
+      "from": {
+        "groupId": "com.android.support",
+        "artifactId": "collection-ktx",
+        "version": "28.0.0"
+      },
+      "to": [
+        {
+          "groupId": "androidx.collection",
+          "artifactId": "collection-ktx",
+          "version": "1.0.0"
+        }
+      ]
     }
   ],
   "map": {
@@ -2313,6 +2415,10 @@
       "android/support/v4/content/res/ResourcesCompat": "androidx/core/content/res/ResourcesCompat",
       "android/arch/lifecycle/DefaultLifecycleObserver": "androidx/lifecycle/DefaultLifecycleObserver",
       "android/arch/lifecycle/FullLifecycleObserver": "androidx/lifecycle/FullLifecycleObserver",
+      "android/support/multidex/ZipUtil": "androidx/multidex/ZipUtil",
+      "android/support/multidex/MultiDexExtractor": "androidx/multidex/MultiDexExtractor",
+      "android/support/multidex/MultiDex": "androidx/multidex/MultiDex",
+      "android/support/multidex/MultiDexApplication": "androidx/multidex/MultiDexApplication",
       "android/support/v7/widget/CardView": "androidx/cardview/widget/CardView",
       "android/support/v7/widget/CardViewDelegate": "androidx/cardview/widget/CardViewDelegate",
       "android/support/v7/cardview/R": "androidx/cardview/R",
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/DependencyMappingTest.kt b/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/DependencyMappingTest.kt
deleted file mode 100644
index 3499fef..0000000
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/DependencyMappingTest.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package android.support.tools.jetifier.core.transform
-
-import android.support.tools.jetifier.core.Processor
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.transform.pom.PomDependency
-import android.support.tools.jetifier.core.transform.pom.PomRewriteRule
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
-import com.google.common.truth.Truth
-import org.junit.Test
-
-class DependencyMappingTest {
-
-    @Test
-    fun mapTest_oneToOne_shouldMap() {
-        MappingTester
-            .testRewrite(
-                from = "hello:world:1.0.0",
-                to = setOf("hi:all:2.0.0"),
-                rules = setOf(
-                    PomRewriteRule(
-                        from = PomDependency(groupId = "hello", artifactId = "world"),
-                        to = setOf(
-                            PomDependency(groupId = "hi", artifactId = "all", version = "2.0.0")
-                        )
-                    ))
-            )
-    }
-
-    @Test
-    fun mapTest_oneToTwo_shouldMap() {
-        MappingTester
-            .testRewrite(
-                from = "hello:world:1.0.0",
-                to = setOf("hi:all:2.0.0", "hey:all:3.0.0"),
-                rules = setOf(
-                    PomRewriteRule(
-                        from = PomDependency(groupId = "hello", artifactId = "world"),
-                        to = setOf(
-                            PomDependency(groupId = "hi", artifactId = "all", version = "2.0.0"),
-                            PomDependency(groupId = "hey", artifactId = "all", version = "3.0.0")
-                        )
-                    ))
-            )
-    }
-
-    @Test
-    fun mapTest_oneToNone_shouldMapToEmpty() {
-        MappingTester
-            .testRewrite(
-                from = "hello:world:1.0.0",
-                to = setOf(),
-                rules = setOf(
-                    PomRewriteRule(
-                        from = PomDependency(groupId = "hello", artifactId = "world"),
-                        to = setOf()
-                    ))
-            )
-    }
-
-    @Test
-    fun mapTest_oneToNull_ruleNotFound_returnNull() {
-        MappingTester
-            .testRewrite(
-                from = "hello:world:1.0.0",
-                to = null,
-                rules = setOf(
-                    PomRewriteRule(
-                        from = PomDependency(groupId = "hello", artifactId = "me", version = "1.0"),
-                        to = setOf()
-                    ))
-            )
-    }
-
-    object MappingTester {
-
-        fun testRewrite(
-            from: String,
-            to: Set<String>?,
-            rules: Set<PomRewriteRule>
-        ) {
-            val config = Config(
-                restrictToPackagePrefixes = emptyList(),
-                rewriteRules = emptyList(),
-                slRules = emptyList(),
-                pomRewriteRules = rules,
-                typesMap = TypesMap.EMPTY,
-                proGuardMap = ProGuardTypesMap.EMPTY,
-                packageMap = PackageMap.EMPTY
-            )
-
-            val processor = Processor.createProcessor(config)
-            val result = processor.mapDependency(from)
-
-            if (to == null) {
-                Truth.assertThat(result).isNull()
-            } else {
-                Truth.assertThat(result).containsExactlyElementsIn(to)
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapperImplTest.kt b/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapperImplTest.kt
deleted file mode 100644
index 4dcbd39..0000000
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapperImplTest.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2018 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.tools.jetifier.core.transform.bytecode
-
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.transform.PackageMap
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
-import com.google.common.truth.Truth
-import org.junit.Test
-import org.objectweb.asm.ClassWriter
-
-class CoreRemapperImplTest {
-
-    @Test
-    fun remapString_shouldUseFallbackForField() {
-        val remapper = prepareRemapper(
-            TypesMap(mapOf(
-                JavaType.fromDotVersion("androidx.test.InputConnectionCompat")
-                    to JavaType.fromDotVersion("android.support.test.InputConnectionCompat")
-            )),
-            "androidx/")
-
-        val given = "androidx.test.InputConnectionCompat.CONTENT_URI"
-        val expected = "android.support.test.InputConnectionCompat.CONTENT_URI"
-
-        Truth.assertThat(remapper.rewriteString(given)).isEqualTo(expected)
-    }
-
-    private fun prepareRemapper(
-            typesMap: TypesMap,
-            restrictToPackagePrefix: String? = null
-    ): CoreRemapperImpl {
-        val prefixes = if (restrictToPackagePrefix == null) {
-            emptyList()
-        } else {
-            listOf(restrictToPackagePrefix)
-        }
-
-        val config = Config(
-                restrictToPackagePrefixes = prefixes,
-                rewriteRules = emptyList(),
-                typesMap = typesMap,
-                slRules = emptyList(),
-                pomRewriteRules = emptySet(),
-                proGuardMap = ProGuardTypesMap.EMPTY,
-                packageMap = PackageMap.EMPTY)
-
-        val context = TransformationContext(config,
-                isInReversedMode = true)
-
-        val writer = ClassWriter(0 /* flags */)
-        return CoreRemapperImpl(context, writer)
-    }
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/TypeRewriterTest.kt b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/TypeRewriterTest.kt
new file mode 100644
index 0000000..72556a9
--- /dev/null
+++ b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/TypeRewriterTest.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2018 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.android.tools.build.jetifier.core
+
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.google.common.truth.Truth
+import org.junit.Test
+
+class TypeRewriterTest {
+
+    @Test fun simpleRewrite_typesMap() {
+        testRewrite(
+            from = "test.sample.Class",
+            to = "test.sample2.Class2",
+            typesMap = TypesMap(
+                JavaType.fromDotVersion("test.sample.Class")
+                        to JavaType.fromDotVersion("test.sample2.Class2")
+            )
+        )
+    }
+
+    @Test fun prefixNotAllowedForRewrite() {
+        testRewrite(
+            from = "test.sample.Class",
+            to = "test.sample.Class",
+            packagePrefix = "notTest/",
+            typesMap = TypesMap(
+                JavaType.fromDotVersion("test.sample.Class")
+                        to JavaType.fromDotVersion("test.sample2.Class2")
+            )
+        )
+    }
+
+    @Test fun typeMissingInMap_returnNull() {
+        testRewrite(
+            from = "test.sample.Class",
+            to = null
+        )
+    }
+
+    @Test fun typeMissingInMap_useFallback_shouldRewrite() {
+        testRewrite(
+            from = "test.sample.Class",
+            to = "test.sample2.Class2",
+            rewriteRulesMap = RewriteRulesMap(RewriteRule(
+                "test/sample/Cl(.*)",
+                "test/sample2/Cl{0}2"
+            )),
+            useFallback = true
+        )
+    }
+
+    @Test fun typeMissingInMap_useFallback_innerClass_shouldRewrite() {
+        testRewrite(
+            from = "test.sample.Class\$Inner",
+            to = "test.sample2.Class2\$Inner",
+            rewriteRulesMap = RewriteRulesMap(RewriteRule(
+                    "test/sample/Class(.*)",
+                    "test/sample2/Class2{0}"
+            )),
+            useFallback = true
+        )
+    }
+
+    @Test fun typeMissingInMap_useFallback_reversedMap_shouldRewrite() {
+        testRewrite(
+            from = "test.sample.Class",
+            to = "test.sample2.Class2",
+            rewriteRulesMap = RewriteRulesMap(RewriteRule(
+                "test/sample2/Cl(.*)2",
+                "test/sample/Cl{0}"
+            )).reverse(),
+            useFallback = true
+        )
+    }
+
+    @Test fun useBothMaps_typesMapHasPriority() {
+        testRewrite(
+            from = "test.sample.Class",
+            to = "test.sample2.Class2",
+            typesMap = TypesMap(
+                JavaType.fromDotVersion("test.sample.Class")
+                        to JavaType.fromDotVersion("test.sample2.Class2")
+            ),
+            rewriteRulesMap = RewriteRulesMap(RewriteRule(
+                "test/sample/Cl(.*)",
+                "test/sample3/Cl{0}3"
+            )),
+            useFallback = true
+        )
+    }
+
+    @Test fun ignoreRule_shouldNotRewrite() {
+        testRewrite(
+            from = "test.sample.Class",
+            to = "test.sample2.Class2",
+            typesMap = TypesMap(
+                JavaType.fromDotVersion("test.sample.Class")
+                        to JavaType.fromDotVersion("test.sample2.Class2")
+            ),
+            rewriteRulesMap = RewriteRulesMap(RewriteRule(
+                "test/sample/Cl(.*)",
+                "ignoreInRuntime"
+            ))
+        )
+    }
+
+    fun testRewrite(
+            from: String,
+            to: String?,
+            packagePrefix: String = "test/",
+            typesMap: TypesMap = TypesMap.EMPTY,
+            rewriteRulesMap: RewriteRulesMap = RewriteRulesMap.EMPTY,
+            useFallback: Boolean = false) {
+        val config = Config(
+            restrictToPackagePrefixes = setOf(packagePrefix),
+            rulesMap = rewriteRulesMap,
+            slRules = emptySet(),
+            pomRewriteRules = emptySet(),
+            typesMap = typesMap,
+            proGuardMap = ProGuardTypesMap.EMPTY,
+            packageMap = PackageMap.EMPTY
+        )
+
+        val rewriter = TypeRewriter(config, useFallback)
+        val result = rewriter.rewriteType(JavaType.fromDotVersion(from))
+
+        if (to == null) {
+            Truth.assertThat(result).isNull()
+        } else {
+            Truth.assertThat(result).isEqualTo(JavaType.fromDotVersion(to))
+        }
+    }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/config/ConfigParserTest.kt b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/config/ConfigParserTest.kt
similarity index 92%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/config/ConfigParserTest.kt
rename to jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/config/ConfigParserTest.kt
index 49ef6f8..794b342 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/config/ConfigParserTest.kt
+++ b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/config/ConfigParserTest.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.config
+package com.android.tools.build.jetifier.core.config
 
 import com.google.common.truth.Truth
 import org.junit.Test
@@ -55,8 +55,8 @@
         val config = ConfigParser.parseFromString(confStr)
 
         Truth.assertThat(config).isNotNull()
-        Truth.assertThat(config!!.restrictToPackagePrefixes[0]).isEqualTo("android/support/")
-        Truth.assertThat(config.rewriteRules.size).isEqualTo(2)
+        Truth.assertThat(config!!.restrictToPackagePrefixes.first()).isEqualTo("android/support/")
+        Truth.assertThat(config.rulesMap.rewriteRules.size).isEqualTo(2)
         Truth.assertThat(config.proGuardMap.rules.size).isEqualTo(1)
     }
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/RewriteRuleTest.kt b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRuleTest.kt
similarity index 81%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/RewriteRuleTest.kt
rename to jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRuleTest.kt
index ebb48dd..404b0bd 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/RewriteRuleTest.kt
+++ b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRuleTest.kt
@@ -1,24 +1,23 @@
-
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform
+package android.support.tools.jetifier.processor.transform
 
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.type.JavaType
 import com.google.common.truth.Truth
 import org.junit.Test
 
@@ -79,13 +78,14 @@
 
         class RuleTesterStep1(val from: String, val to: String) {
 
-            fun rewritesType(inputType: String)
-                    = RuleTesterFinalTypeStep(from, to, inputType)
+            fun rewritesType(inputType: String) = RuleTesterFinalTypeStep(from, to, inputType)
         }
 
-        class RuleTesterFinalTypeStep(val fromType: String,
-                                      val toType: String,
-                                      val inputType: String) {
+        class RuleTesterFinalTypeStep(
+            val fromType: String,
+            val toType: String,
+            val inputType: String
+        ) {
 
             fun into(expectedResult: String) {
                 val fieldRule = RewriteRule(fromType, toType)
@@ -103,7 +103,6 @@
                 Truth.assertThat(result.isIgnored).isTrue()
             }
         }
-
     }
 }
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/rules/JavaTypeTest.kt b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/type/JavaTypeTest.kt
similarity index 96%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/rules/JavaTypeTest.kt
rename to jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/type/JavaTypeTest.kt
index a5c0e89..2a73c21 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/rules/JavaTypeTest.kt
+++ b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/type/JavaTypeTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.rules
+package com.android.tools.build.jetifier.core.type
 
 import com.google.common.truth.Truth
 import org.junit.Test
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/map/TypesMapTest.kt b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/type/TypesMapTest.kt
similarity index 94%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/map/TypesMapTest.kt
rename to jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/type/TypesMapTest.kt
index 7aa60df..49fae50 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/map/TypesMapTest.kt
+++ b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/type/TypesMapTest.kt
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.map
+package com.android.tools.build.jetifier.core.type
 
-import android.support.tools.jetifier.core.rules.JavaType
 import com.google.common.truth.Truth
 import org.junit.Test
 
diff --git a/jetifier/jetifier/gradle-plugin/build.gradle b/jetifier/jetifier/gradle-plugin/build.gradle
index deeb722..526901b 100644
--- a/jetifier/jetifier/gradle-plugin/build.gradle
+++ b/jetifier/jetifier/gradle-plugin/build.gradle
@@ -14,17 +14,18 @@
  * limitations under the License
  */
 
-import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
 
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 
+import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
+
 plugins {
   id("SupportKotlinLibraryPlugin")
 }
 
 dependencies {
-    compile project(':jetifier-core')
+    compile project(':jetifier-processor')
     compile(KOTLIN_STDLIB)
     compileOnly gradleApi()
 }
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierExtension.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierExtension.kt
similarity index 95%
rename from jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierExtension.kt
rename to jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierExtension.kt
index e0eb60c..4270e22 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierExtension.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierExtension.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.plugin.gradle
+package com.android.tools.build.jetifier.plugin.gradle
 
 import groovy.lang.Closure
 import org.gradle.api.Project
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierLoggerAdapter.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierLoggerAdapter.kt
similarity index 79%
rename from jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierLoggerAdapter.kt
rename to jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierLoggerAdapter.kt
index 9d85851..a3ee44b 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierLoggerAdapter.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierLoggerAdapter.kt
@@ -1,22 +1,22 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.plugin.gradle
+package com.android.tools.build.jetifier.plugin.gradle
 
-import android.support.tools.jetifier.core.utils.LogConsumer
+import com.android.tools.build.jetifier.core.utils.LogConsumer
 import org.gradle.api.logging.Logger
 
 /**
@@ -39,5 +39,4 @@
     override fun debug(message: String) {
         gradleLogger.debug(message)
     }
-
 }
\ No newline at end of file
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierPlugin.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierPlugin.kt
similarity index 87%
rename from jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierPlugin.kt
rename to jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierPlugin.kt
index 4c26406..5680875 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierPlugin.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierPlugin.kt
@@ -1,22 +1,22 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.plugin.gradle
+package com.android.tools.build.jetifier.plugin.gradle
 
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifyGlobalTask.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifyGlobalTask.kt
similarity index 84%
rename from jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifyGlobalTask.kt
rename to jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifyGlobalTask.kt
index 62e7e21..b562299 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifyGlobalTask.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifyGlobalTask.kt
@@ -1,7 +1,23 @@
-package android.support.tools.jetifier.plugin.gradle
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
-import android.support.tools.jetifier.core.FileMapping
-import android.support.tools.jetifier.core.config.ConfigParser
+package com.android.tools.build.jetifier.plugin.gradle
+
+import com.android.tools.build.jetifier.core.config.ConfigParser
+import com.android.tools.build.jetifier.processor.FileMapping
 import org.gradle.api.DefaultTask
 import org.gradle.api.Project
 import org.gradle.api.artifacts.Configuration
@@ -117,10 +133,10 @@
 
         // Process the files using Jetifier
         val result = TasksCommon.processFiles(config,
-                dependenciesMap.keys.map {
-                    FileMapping(it, File(outputDir, it.name))
-                }.toSet(),
-                project.logger)
+            dependenciesMap.keys.map {
+                FileMapping(it, File(outputDir, it.name))
+            }.toSet(),
+            project.logger)
 
         configurationsToProcess.forEach { conf ->
             // Remove files that we don't need anymore
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifyLibsTask.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifyLibsTask.kt
similarity index 91%
rename from jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifyLibsTask.kt
rename to jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifyLibsTask.kt
index 34230c0..d4fd941 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifyLibsTask.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifyLibsTask.kt
@@ -1,23 +1,23 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.plugin.gradle
+package com.android.tools.build.jetifier.plugin.gradle
 
-import android.support.tools.jetifier.core.FileMapping
-import android.support.tools.jetifier.core.config.ConfigParser
+import com.android.tools.build.jetifier.core.config.ConfigParser
+import com.android.tools.build.jetifier.processor.FileMapping
 import org.gradle.api.DefaultTask
 import org.gradle.api.Project
 import org.gradle.api.file.FileCollection
@@ -52,7 +52,6 @@
             }
             return project.tasks.create(TASK_NAME, JetifyLibsTask::class.java)
         }
-
     }
 
     private val outputDir = File(project.buildDir, OUTPUT_DIR_APPENDIX)
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/TasksCommon.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/TasksCommon.kt
similarity index 78%
rename from jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/TasksCommon.kt
rename to jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/TasksCommon.kt
index de75cbb..6364870 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/TasksCommon.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/TasksCommon.kt
@@ -1,25 +1,25 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.plugin.gradle
+package com.android.tools.build.jetifier.plugin.gradle
 
-import android.support.tools.jetifier.core.FileMapping
-import android.support.tools.jetifier.core.Processor
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.FileMapping
+import com.android.tools.build.jetifier.processor.Processor
 import org.gradle.api.logging.LogLevel
 import org.gradle.api.logging.Logger
 import java.io.File
diff --git a/jetifier/jetifier/gradle-plugin/src/main/resources/META-INF/gradle-plugins/androidx.tools.jetifier.properties b/jetifier/jetifier/gradle-plugin/src/main/resources/META-INF/gradle-plugins/androidx.tools.jetifier.properties
index 7ee7e73..555bd14 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/resources/META-INF/gradle-plugins/androidx.tools.jetifier.properties
+++ b/jetifier/jetifier/gradle-plugin/src/main/resources/META-INF/gradle-plugins/androidx.tools.jetifier.properties
@@ -14,4 +14,4 @@
 # limitations under the License
 #
 
-implementation-class=android.support.tools.jetifier.plugin.gradle.JetifierPlugin
\ No newline at end of file
+implementation-class=com.android.tools.build.jetifier.plugin.gradle.JetifierPlugin
\ No newline at end of file
diff --git a/jetifier/jetifier/preprocessor/build.gradle b/jetifier/jetifier/preprocessor/build.gradle
index 893b763..24fbb69 100644
--- a/jetifier/jetifier/preprocessor/build.gradle
+++ b/jetifier/jetifier/preprocessor/build.gradle
@@ -19,9 +19,9 @@
     id("application")
 }
 
-mainClassName = "android.support.tools.jetifier.preprocessor.MainKt"
+mainClassName = "com.android.tools.build.jetifier.preprocessor.MainKt"
 
 dependencies {
-    compile project(':jetifier-core')
+    compile project(':jetifier-processor')
     compile group: 'commons-cli', name: 'commons-cli', version: '1.3.1'
 }
diff --git a/jetifier/jetifier/preprocessor/scripts/processDefaultConfig.sh b/jetifier/jetifier/preprocessor/scripts/processDefaultConfig.sh
index 19dcc7e..aa9c264 100755
--- a/jetifier/jetifier/preprocessor/scripts/processDefaultConfig.sh
+++ b/jetifier/jetifier/preprocessor/scripts/processDefaultConfig.sh
@@ -87,6 +87,8 @@
 	unzip -oj "$SUPPORT_LIBS_DOWNLOADED/support-lib.zip" -d "$SUPPORT_LIBS_UNPACKED"
 	unzip -oj "$SUPPORT_LIBS_DOWNLOADED/arch.zip" -d "$SUPPORT_LIBS_UNPACKED"
 	find "$CHECKOUT_DIR/prebuilts/maven_repo/android/com/android/support/" -type f -name "*design-*28.0.0*.aar" -exec cp '{}' -t "$SUPPORT_LIBS_UNPACKED" \;
+	cp "$CHECKOUT_DIR/prebuilts/maven_repo/android/com/android/support/multidex/1.0.3/multidex-1.0.3.aar" "$SUPPORT_LIBS_UNPACKED/multidex.aar"
+	cp "$CHECKOUT_DIR/prebuilts/maven_repo/android/com/android/support/multidex-instrumentation/1.0.3/multidex-instrumentation-1.0.3.aar" "$SUPPORT_LIBS_UNPACKED/multidex-instrumentation.aar"
 	find "$SUPPORT_LIBS_UNPACKED" -type f -name "jetifier*" -exec rm -f {} \;
 }
 
diff --git a/jetifier/jetifier/preprocessor/src/main/kotlin/android/support/tools/jetifier/preprocessor/ConfigGenerator.kt b/jetifier/jetifier/preprocessor/src/main/kotlin/com/android/tools/build/jetifier/preprocessor/ConfigGenerator.kt
similarity index 86%
rename from jetifier/jetifier/preprocessor/src/main/kotlin/android/support/tools/jetifier/preprocessor/ConfigGenerator.kt
rename to jetifier/jetifier/preprocessor/src/main/kotlin/com/android/tools/build/jetifier/preprocessor/ConfigGenerator.kt
index 0e9f63a..1444203 100644
--- a/jetifier/jetifier/preprocessor/src/main/kotlin/android/support/tools/jetifier/preprocessor/ConfigGenerator.kt
+++ b/jetifier/jetifier/preprocessor/src/main/kotlin/com/android/tools/build/jetifier/preprocessor/ConfigGenerator.kt
@@ -1,25 +1,25 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.preprocessor
+package com.android.tools.build.jetifier.preprocessor
 
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.config.ConfigParser
-import android.support.tools.jetifier.core.map.LibraryMapGenerator
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.config.ConfigParser
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.type.LibraryMapGenerator
 import java.io.File
 import java.nio.file.Path
 
diff --git a/jetifier/jetifier/preprocessor/src/main/kotlin/android/support/tools/jetifier/preprocessor/Main.kt b/jetifier/jetifier/preprocessor/src/main/kotlin/com/android/tools/build/jetifier/preprocessor/Main.kt
similarity index 78%
rename from jetifier/jetifier/preprocessor/src/main/kotlin/android/support/tools/jetifier/preprocessor/Main.kt
rename to jetifier/jetifier/preprocessor/src/main/kotlin/com/android/tools/build/jetifier/preprocessor/Main.kt
index 58cb20f..ac35ab7 100644
--- a/jetifier/jetifier/preprocessor/src/main/kotlin/android/support/tools/jetifier/preprocessor/Main.kt
+++ b/jetifier/jetifier/preprocessor/src/main/kotlin/com/android/tools/build/jetifier/preprocessor/Main.kt
@@ -1,23 +1,23 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.preprocessor
+package com.android.tools.build.jetifier.preprocessor
 
-import android.support.tools.jetifier.core.config.ConfigParser
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.config.ConfigParser
+import com.android.tools.build.jetifier.core.utils.Log
 import org.apache.commons.cli.CommandLine
 import org.apache.commons.cli.DefaultParser
 import org.apache.commons.cli.HelpFormatter
@@ -40,10 +40,12 @@
         val OPTION_LOG_LEVEL = createOption("l", "Logging level. debug, verbose, default",
             isRequired = false)
 
-        private fun createOption(argName: String,
-                                 desc: String,
-                                 isRequired: Boolean = true,
-                                 multiple: Boolean = false) : Option {
+        private fun createOption(
+            argName: String,
+            desc: String,
+            isRequired: Boolean = true,
+            multiple: Boolean = false
+        ): Option {
             val op = Option(argName, true, desc)
             op.isRequired = isRequired
             if (multiple) {
@@ -54,7 +56,7 @@
         }
     }
 
-    fun run(args : Array<String>) {
+    fun run(args: Array<String>) {
         val cmd = parseCmdLine(args)
         if (cmd == null) {
             System.exit(1)
@@ -77,7 +79,7 @@
         generator.generateMapping(config, inputLibraries, outputConfigPath)
     }
 
-    private fun parseCmdLine(args : Array<String>) : CommandLine? {
+    private fun parseCmdLine(args: Array<String>): CommandLine? {
         try {
             return DefaultParser().parse(OPTIONS, args)
         } catch (e: ParseException) {
@@ -86,10 +88,8 @@
         }
         return null
     }
-
 }
 
-
-fun main(args : Array<String>) {
+fun main(args: Array<String>) {
     Main().run(args)
 }
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/build.gradle b/jetifier/jetifier/processor/build.gradle
new file mode 100644
index 0000000..dbcc0ac
--- /dev/null
+++ b/jetifier/jetifier/processor/build.gradle
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+
+import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
+
+plugins {
+    id("SupportKotlinLibraryPlugin")
+}
+
+dependencies {
+    compile project(':jetifier-core')
+    compile("org.ow2.asm:asm:5.2")
+    compile("org.ow2.asm:asm-commons:5.2")
+    compile("org.jdom:jdom2:2.0.6")
+    compile(KOTLIN_STDLIB)
+    testCompile("junit:junit:4.12")
+    testCompile("com.google.truth:truth:0.34")
+}
+
+supportLibrary {
+    name = "Android Jetifier Processor"
+    publish = true
+    mavenVersion = LibraryVersions.JETIFIER
+    mavenGroup = LibraryGroups.JETIFIER
+    generateDocs = false
+    inceptionYear = "2018"
+    description = "Android Jetifier Processor"
+}
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/FileMapping.kt
similarity index 77%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/FileMapping.kt
index 67f6d84..d807aeb 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/FileMapping.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core
+package com.android.tools.build.jetifier.processor
 
 import java.io.File
 
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
similarity index 81%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
index 61415da..c9eaf25 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
@@ -1,35 +1,35 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core
+package com.android.tools.build.jetifier.processor
 
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.archive.ArchiveItemVisitor
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.Transformer
-import android.support.tools.jetifier.core.transform.bytecode.ByteCodeTransformer
-import android.support.tools.jetifier.core.transform.metainf.MetaInfTransformer
-import android.support.tools.jetifier.core.transform.pom.PomDependency
-import android.support.tools.jetifier.core.transform.pom.PomDocument
-import android.support.tools.jetifier.core.transform.pom.PomScanner
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTransformer
-import android.support.tools.jetifier.core.transform.resource.XmlResourcesTransformer
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.Transformer
+import com.android.tools.build.jetifier.processor.transform.bytecode.ByteCodeTransformer
+import com.android.tools.build.jetifier.processor.transform.metainf.MetaInfTransformer
+import com.android.tools.build.jetifier.processor.transform.pom.PomDocument
+import com.android.tools.build.jetifier.processor.transform.pom.PomScanner
+import com.android.tools.build.jetifier.processor.transform.proguard.ProGuardTransformer
+import com.android.tools.build.jetifier.processor.transform.resource.XmlResourcesTransformer
 import java.io.File
 import java.io.FileNotFoundException
 
@@ -38,7 +38,7 @@
  * the registered [Transformer]s over the set and creates new archives that will contain the
  * transformed files.
  */
-class Processor private constructor (
+class Processor private constructor(
     private val context: TransformationContext,
     private val transformers: List<Transformer>
 ) : ArchiveItemVisitor {
@@ -49,7 +49,7 @@
         /**
          * Value of "restrictToPackagePrefixes" config for reversed jetification.
          */
-        private val REVERSE_RESTRICT_TO_PACKAGE = listOf(
+        private val REVERSE_RESTRICT_TO_PACKAGE = setOf(
             "androidx/",
             "com/google/android/material/"
         )
@@ -93,7 +93,7 @@
             if (reversedMode) {
                 newConfig = Config(
                     restrictToPackagePrefixes = REVERSE_RESTRICT_TO_PACKAGE,
-                    rewriteRules = config.rewriteRules,
+                    rulesMap = config.rulesMap.reverse().appendRules(config.slRules),
                     slRules = config.slRules,
                     pomRewriteRules = config.pomRewriteRules.map { it.getReversed() }.toSet(),
                     typesMap = config.typesMap.reverseMapOrDie(),
@@ -106,7 +106,7 @@
                 config = newConfig,
                 rewritingSupportLib = rewritingSupportLib,
                 isInReversedMode = reversedMode,
-                useIdentityIfTypeIsMissing = useIdentityIfTypeIsMissing)
+                useFallbackIfTypeIsMissing = useIdentityIfTypeIsMissing)
             val transformers = if (rewritingSupportLib) {
                 createSLTransformers(context)
             } else {
@@ -149,7 +149,7 @@
 
         if (context.errorsTotal() > 0) {
             throw IllegalArgumentException("There were ${context.errorsTotal()}" +
-                    " errors found during the remapping. Check the logs for more details.")
+                " errors found during the remapping. Check the logs for more details.")
         }
 
         // TODO: Here we might need to modify the POM files if they point at a library that we have
@@ -160,11 +160,11 @@
 
         // 5) Repackage the libraries back to archive files
         val generatedLibraries = libraries
-                .filter { copyUnmodifiedLibsAlso || it.wasChanged }
-                .map {
-                    it.writeSelf()
-                }
-                .toSet()
+            .filter { copyUnmodifiedLibsAlso || it.wasChanged }
+            .map {
+                it.writeSelf()
+            }
+            .toSet()
 
         if (copyUnmodifiedLibsAlso) {
             return generatedLibraries
@@ -172,9 +172,9 @@
 
         // 6) Create a set of files that should be removed (because they've been changed).
         val filesToRemove = libraries
-                .filter { it.wasChanged }
-                .map { it.relativePath.toFile() }
-                .toSet()
+            .filter { it.wasChanged }
+            .map { it.relativePath.toFile() }
+            .toSet()
 
         return inputLibraries.minus(filesToRemove).plus(generatedLibraries)
     }
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/TransformationResult.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/TransformationResult.kt
similarity index 81%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/TransformationResult.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/TransformationResult.kt
index 3e90483..cc7f422 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/TransformationResult.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/TransformationResult.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core
+package com.android.tools.build.jetifier.processor
 
 import java.io.File
 
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/Archive.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/Archive.kt
similarity index 91%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/Archive.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/Archive.kt
index 3564ff3..448eb4d 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/Archive.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/Archive.kt
@@ -1,22 +1,22 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.archive
+package com.android.tools.build.jetifier.processor.archive
 
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
 import java.io.BufferedOutputStream
 import java.io.File
 import java.io.FileInputStream
@@ -35,9 +35,9 @@
  * Represents an archive (zip, jar, aar ...)
  */
 class Archive(
-        override val relativePath: Path,
-        val files: List<ArchiveItem>)
-    : ArchiveItem {
+    override val relativePath: Path,
+    val files: List<ArchiveItem>
+) : ArchiveItem {
 
     companion object {
         /** Defines file extensions that are recognized as archives */
@@ -126,9 +126,9 @@
 
         @Throws(IOException::class)
         private fun extractArchive(
-                inputStream: InputStream,
-                relativePath: Path,
-                recursive: Boolean
+            inputStream: InputStream,
+            relativePath: Path,
+            recursive: Boolean
         ): Archive {
             val zipIn = ZipInputStream(inputStream)
             val files = mutableListOf<ArchiveItem>()
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveFile.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveFile.kt
similarity index 60%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveFile.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveFile.kt
index 81c166e..06e332c 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveFile.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveFile.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.archive
+package com.android.tools.build.jetifier.processor.archive
 
 import java.io.IOException
 import java.io.OutputStream
@@ -55,8 +55,25 @@
         fileName = relativePath.fileName.toString()
     }
 
+    /**
+     * Sets new data while also marking this file as changed. This will result into the parent
+     * archive also being considered as changed thus marking it as dependent on the Support library.
+     */
     fun setNewData(newData: ByteArray) {
         data = newData
         wasChanged = true
     }
+
+    /**
+     * Sets a potentially new data without triggering a change. Useful in cases the change is not
+     * significant for the refactoring because it occurred due to some optimization or
+     * formatting change.
+     *
+     * If there was at least one genuine change in any file of the parent archive this won't prevent
+     * this file from being updated. However this will prevent the change to propagate to
+     * the parent archive which would otherwise mark it as dependent on the Support Library.
+     */
+    fun setNewDataSilently(newData: ByteArray) {
+        data = newData
+    }
 }
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItem.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveItem.kt
similarity index 84%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItem.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveItem.kt
index 16a8afa..9e188ca 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItem.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveItem.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.archive
+package com.android.tools.build.jetifier.processor.archive
 
 import java.io.OutputStream
 import java.nio.file.Path
@@ -39,7 +39,9 @@
     val fileName: String
 
     /**
-     * Whether the item's content or its children were changed by Jetifier.
+     * Whether the item's content or its children were changed by Jetifier. This determines
+     * whether the parent archive is going to be marked as changed thus having a dependency on
+     * support.
      */
     val wasChanged: Boolean
 
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItemVisitor.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveItemVisitor.kt
similarity index 76%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItemVisitor.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveItemVisitor.kt
index 7c99fd9..9e6a6ce 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItemVisitor.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveItemVisitor.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.archive
+package com.android.tools.build.jetifier.processor.archive
 
 /**
  * Visitor for [ArchiveItem]
@@ -24,5 +24,4 @@
     fun visit(archive: Archive)
 
     fun visit(archiveFile: ArchiveFile)
-
 }
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt
new file mode 100644
index 0000000..5c53e88
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2018 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.android.tools.build.jetifier.processor.transform
+
+import com.android.tools.build.jetifier.core.TypeRewriter
+import com.android.tools.build.jetifier.core.config.Config
+import java.util.regex.Pattern
+
+/**
+ * Context to share the transformation state between individual [Transformer]s.
+ */
+class TransformationContext(
+    val config: Config,
+    val rewritingSupportLib: Boolean = false,
+    val isInReversedMode: Boolean = false,
+    /**
+     * Whether to use fallback if type in our scope is missing instead of throwing an exception.
+     */
+    val useFallbackIfTypeIsMissing: Boolean = true
+) {
+
+    // Merges all packages prefixes into one regEx pattern
+    private val packagePrefixPattern = Pattern.compile(
+        "^(" + config.restrictToPackagePrefixes.map { "($it)" }.joinToString("|") + ").*$")
+
+    val typeRewriter: TypeRewriter = TypeRewriter(config, useFallbackIfTypeIsMissing)
+
+    /**
+     * Whether to skip verification of dependency version match in pom files.
+     */
+    val ignorePomVersionCheck = rewritingSupportLib || isInReversedMode
+
+    /** Counter for [reportNoMappingFoundFailure] calls. */
+    var mappingNotFoundFailuresCount = 0
+        private set
+
+    /** Counter for [reportNoProGuardMappingFoundFailure] calls. */
+    var proGuardMappingNotFoundFailuresCount = 0
+        private set
+
+    /** Counter for [reportNoPackageMappingFoundFailure] calls. */
+    var packageMappingNotFoundFailuresCounts = 0
+
+    var libraryName: String = ""
+
+    /** Total amount of errors found during the transformation process */
+    fun errorsTotal() = mappingNotFoundFailuresCount + proGuardMappingNotFoundFailuresCount +
+        packageMappingNotFoundFailuresCounts
+
+    /**
+     * Reports that there was a reference found that satisfies [isEligibleForRewrite] but no
+     * mapping was found to rewrite it.
+     */
+    fun reportNoMappingFoundFailure() {
+        mappingNotFoundFailuresCount++
+    }
+
+    /**
+     * Reports that there was a reference found in a ProGuard file that satisfies
+     * [isEligibleForRewrite] but no mapping was found to rewrite it.
+     */
+    fun reportNoProGuardMappingFoundFailure() {
+        proGuardMappingNotFoundFailuresCount++
+    }
+
+    /**
+     * Reports that there was a package reference found in a manifest file during a support library
+     * artifact rewrite but no mapping was found for it.
+     */
+    fun reportNoPackageMappingFoundFailure() {
+        packageMappingNotFoundFailuresCounts++
+    }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/Transformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/Transformer.kt
similarity index 75%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/Transformer.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/Transformer.kt
index 51335e2..3e9c460 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/Transformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/Transformer.kt
@@ -1,22 +1,22 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform
+package com.android.tools.build.jetifier.processor.transform
 
-import android.support.tools.jetifier.core.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
 
 /**
  * Interface to be implemented by any class that wants process files.
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/ByteCodeTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ByteCodeTransformer.kt
similarity index 72%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/ByteCodeTransformer.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ByteCodeTransformer.kt
index 9024ff1..553ac05 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/ByteCodeTransformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ByteCodeTransformer.kt
@@ -1,24 +1,24 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.bytecode
+package com.android.tools.build.jetifier.processor.transform.bytecode
 
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.Transformer
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.Transformer
 import org.objectweb.asm.ClassReader
 import org.objectweb.asm.ClassWriter
 
@@ -39,6 +39,7 @@
         reader.accept(remapper.classRemapper, 0 /* flags */)
 
         if (!remapper.changesDone) {
+            file.setNewDataSilently(writer.toByteArray())
             return
         }
 
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapper.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapper.kt
similarity index 72%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapper.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapper.kt
index 1b91088..9f39381 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapper.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapper.kt
@@ -1,22 +1,22 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.bytecode
+package com.android.tools.build.jetifier.processor.transform.bytecode
 
-import android.support.tools.jetifier.core.rules.JavaType
+import com.android.tools.build.jetifier.core.type.JavaType
 
 /**
  * High-level re-mapping interface to provide only the refactorings needed by jetifier.
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapperImpl.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt
similarity index 67%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapperImpl.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt
index dc400e3..aa769dd 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapperImpl.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt
@@ -1,26 +1,26 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.bytecode
+package com.android.tools.build.jetifier.processor.transform.bytecode
 
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.bytecode.asm.CustomRemapper
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.bytecode.asm.CustomRemapper
 import org.objectweb.asm.ClassVisitor
 import org.objectweb.asm.commons.ClassRemapper
 import java.nio.file.Path
@@ -45,22 +45,14 @@
     val classRemapper = ClassRemapper(visitor, CustomRemapper(this))
 
     override fun rewriteType(type: JavaType): JavaType {
-        if (!context.isEligibleForRewrite(type)) {
-            return type
-        }
-
-        val result = typesMap.mapType(type)
+        val result = context.typeRewriter.rewriteType(type)
         if (result != null) {
             changesDone = changesDone || result != type
-            Log.i(TAG, "  map: %s -> %s", type, result)
             return result
         }
 
-        if (context.useIdentityIfTypeIsMissing) {
-            Log.i(TAG, "No mapping for %s - using identity", type)
-        } else {
+        if (!context.useFallbackIfTypeIsMissing) {
             context.reportNoMappingFoundFailure()
-            Log.e(TAG, "No mapping for: " + type)
         }
         return type
     }
@@ -69,14 +61,14 @@
         val startsWithAndroidX = context.isInReversedMode && value.startsWith("androidx")
 
         val type = JavaType.fromDotVersion(value)
-        if (!context.isEligibleForRewrite(type)) {
+        if (!context.typeRewriter.isEligibleForRewrite(type)) {
             if (startsWithAndroidX) {
-                Log.i(TAG, "Found string '%s' but failed to rewrite", value)
+                Log.i(TAG, "Found & ignored string '%s'", value)
             }
             return value
         }
 
-        val result = typesMap.mapType(type)
+        val result = context.config.typesMap.mapType(type)
         if (result != null) {
             changesDone = changesDone || result != type
             Log.i(TAG, "Map string: '%s' -> '%s'", type, result)
@@ -86,14 +78,23 @@
         // We might be working with an internal type or field reference, e.g.
         // AccessibilityNodeInfoCompat.PANE_TITLE_KEY. So we try to remove last segment to help it.
         if (value.contains(".")) {
-            val subTypeResult = typesMap.mapType(type.getParentType())
+            val subTypeResult = context.config.typesMap.mapType(type.getParentType())
             if (subTypeResult != null) {
                 val result = subTypeResult.toDotNotation() + '.' + value.substringAfterLast('.')
-                Log.i(TAG, "Map string: '%s' -> '%s' via fallback", value, result)
+                Log.i(TAG, "Map string: '%s' -> '%s' via type fallback", value, result)
                 return result
             }
         }
 
+        // Try rewrite rules
+        if (context.useFallbackIfTypeIsMissing) {
+            val result = context.config.rulesMap.rewriteType(type)
+            if (result != null) {
+                Log.i(TAG, "Map string: '%s' -> '%s' via fallback", value, result)
+                return result.toDotNotation()
+            }
+        }
+
         // We do not treat string content mismatches as errors
         Log.i(TAG, "Found string '%s' but failed to rewrite", value)
         return value
@@ -106,23 +107,16 @@
 
         val owner = path.toFile().path.replace('\\', '/').removeSuffix(".class")
         val type = JavaType(owner)
-        if (!context.isEligibleForRewrite(type)) {
-            return path
-        }
-
         val result = rewriteType(type)
         if (result != type) {
             changesDone = true
             return path.fileSystem.getPath(result.fullName + ".class")
         }
 
-        if (context.useIdentityIfTypeIsMissing) {
-            Log.i(TAG, "No mapping for: %s", type)
-            return path
+        if (!context.useFallbackIfTypeIsMissing) {
+            context.reportNoMappingFoundFailure()
         }
 
-        context.reportNoMappingFoundFailure()
-        Log.e(TAG, "No mapping for: %s", type)
         return path
     }
 }
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/asm/CustomRemapper.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt
similarity index 74%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/asm/CustomRemapper.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt
index a54e8bd..b3f096b 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/asm/CustomRemapper.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt
@@ -1,23 +1,23 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.bytecode.asm
+package com.android.tools.build.jetifier.processor.transform.bytecode.asm
 
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.transform.bytecode.CoreRemapper
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.processor.transform.bytecode.CoreRemapper
 import org.objectweb.asm.commons.Remapper
 
 /**
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
similarity index 80%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformer.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
index 333d776..146daf2 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
@@ -1,24 +1,24 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.metainf
+package com.android.tools.build.jetifier.processor.transform.metainf
 
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.Transformer
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.Transformer
 import java.nio.charset.StandardCharsets
 
 /**
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDependencyExtensions.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDependencyExtensions.kt
new file mode 100644
index 0000000..b56d896
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDependencyExtensions.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018 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.android.tools.build.jetifier.processor.transform.pom
+
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import org.jdom2.Document
+import org.jdom2.Element
+
+/**
+ * Transforms the current data into XML '<dependency>' node.
+ */
+fun PomDependency.toXmlElement(document: Document): Element {
+    val node = Element("dependency")
+    node.namespace = document.rootElement.namespace
+
+    XmlUtils.addStringNodeToNode(node, "groupId", groupId)
+    XmlUtils.addStringNodeToNode(node, "artifactId", artifactId)
+    XmlUtils.addStringNodeToNode(node, "version", version)
+    XmlUtils.addStringNodeToNode(node, "classifier", classifier)
+    XmlUtils.addStringNodeToNode(node, "type", type)
+    XmlUtils.addStringNodeToNode(node, "scope", scope)
+    XmlUtils.addStringNodeToNode(node, "systemPath", systemPath)
+    XmlUtils.addStringNodeToNode(node, "optional", optional)
+    return node
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomDocument.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDocument.kt
similarity index 90%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomDocument.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDocument.kt
index 1c10e02..ae9a296 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomDocument.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDocument.kt
@@ -1,24 +1,26 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.pom
+package com.android.tools.build.jetifier.processor.transform.pom
 
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
 import org.jdom2.Document
 import org.jdom2.Element
 
@@ -55,7 +57,7 @@
         dependenciesGroup = document.rootElement
                 .getChild("dependencies", document.rootElement.namespace) ?: return
         dependenciesGroup!!.children.mapTo(dependencies) {
-            PomDependency.fromXmlElement(it, properties)
+            XmlUtils.createDependencyFrom(it, properties)
         }
     }
 
@@ -156,7 +158,7 @@
         }
 
         if (matchesPrefix) {
-            if (context.useIdentityIfTypeIsMissing) {
+            if (context.useFallbackIfTypeIsMissing) {
                 Log.i(TAG, "No mapping found for '%s' - using identity",
                         dependency.toStringNotation())
             } else {
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomScanner.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomScanner.kt
similarity index 77%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomScanner.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomScanner.kt
index 45b695c..ac15488 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomScanner.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomScanner.kt
@@ -1,26 +1,26 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.pom
+package com.android.tools.build.jetifier.processor.transform.pom
 
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.archive.ArchiveItemVisitor
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
 
 /**
  * Helper to scan [Archive]s to find their POM files.
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/XmlUtils.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/XmlUtils.kt
new file mode 100644
index 0000000..f6283b7
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/XmlUtils.kt
@@ -0,0 +1,127 @@
+/*
+ * 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.android.tools.build.jetifier.processor.transform.pom
+
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.utils.Log
+import org.jdom2.Document
+import org.jdom2.Element
+import org.jdom2.input.SAXBuilder
+import org.jdom2.output.Format
+import org.jdom2.output.XMLOutputter
+import java.io.ByteArrayOutputStream
+import java.util.regex.Pattern
+
+/**
+ * Utilities for handling XML documents.
+ */
+class XmlUtils {
+
+    companion object {
+
+        private val variablePattern = Pattern.compile("\\$\\{([^}]*)}")
+
+        /** Saves the given [Document] to a new byte array */
+        fun convertDocumentToByteArray(document: Document): ByteArray {
+            val xmlOutput = XMLOutputter()
+            ByteArrayOutputStream().use {
+                xmlOutput.format = Format.getPrettyFormat()
+                xmlOutput.output(document, it)
+                return it.toByteArray()
+            }
+        }
+
+        /** Creates a new [Document] from the given [ByteArray] */
+        fun createDocumentFromByteArray(data: ByteArray): Document {
+            val builder = SAXBuilder()
+            data.inputStream().use {
+                return builder.build(it)
+            }
+        }
+
+        /**
+         * Creates a new XML element with the given [id] and text given in [value] and puts it under
+         * the given [parent]. Nothing is created if the [value] argument is null or empty.
+         */
+        fun addStringNodeToNode(parent: Element, id: String, value: String?) {
+            if (value.isNullOrEmpty()) {
+                return
+            }
+
+            val element = Element(id)
+            element.text = value
+            element.namespace = parent.namespace
+            parent.children.add(element)
+        }
+
+        fun resolveValue(value: String?, properties: Map<String, String>): String? {
+            if (value == null) {
+                return null
+            }
+
+            val matcher = variablePattern.matcher(value)
+            if (matcher.matches()) {
+                val variableName = matcher.group(1)
+                val varValue = properties[variableName]
+                if (varValue == null) {
+                    Log.e("TAG", "Failed to resolve variable '%s'", value)
+                    return value
+                }
+                return varValue
+            }
+
+            return value
+        }
+
+        /**
+         * Creates a new [PomDependency] from the given XML [Element].
+         */
+        fun createDependencyFrom(node: Element, properties: Map<String, String>): PomDependency {
+            var groupId: String? = null
+            var artifactId: String? = null
+            var version: String? = null
+            var classifier: String? = null
+            var type: String? = null
+            var scope: String? = null
+            var systemPath: String? = null
+            var optional: String? = null
+
+            for (childNode in node.children) {
+                when (childNode.name) {
+                    "groupId" -> groupId = resolveValue(childNode.value, properties)
+                    "artifactId" -> artifactId = resolveValue(childNode.value, properties)
+                    "version" -> version = resolveValue(childNode.value, properties)
+                    "classifier" -> classifier = resolveValue(childNode.value, properties)
+                    "type" -> type = resolveValue(childNode.value, properties)
+                    "scope" -> scope = resolveValue(childNode.value, properties)
+                    "systemPath" -> systemPath = resolveValue(childNode.value, properties)
+                    "optional" -> optional = resolveValue(childNode.value, properties)
+                }
+            }
+
+            return PomDependency(
+                groupId = groupId,
+                artifactId = artifactId,
+                version = version,
+                classifier = classifier,
+                type = type,
+                scope = scope,
+                systemPath = systemPath,
+                optional = optional)
+        }
+    }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardClassFilterParser.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardClassFilterParser.kt
similarity index 68%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardClassFilterParser.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardClassFilterParser.kt
index c431572..ba67b29 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardClassFilterParser.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardClassFilterParser.kt
@@ -1,30 +1,30 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
-import android.support.tools.jetifier.core.transform.proguard.patterns.GroupsReplacer
-import android.support.tools.jetifier.core.transform.proguard.patterns.PatternHelper
+import com.android.tools.build.jetifier.processor.transform.proguard.patterns.GroupsReplacer
+import com.android.tools.build.jetifier.processor.transform.proguard.patterns.PatternHelper
 import java.util.regex.Pattern
 
 /**
  * Parses and rewrites ProGuard rules that contain class filters. See ProGuard documentation
  * https://www.guardsquare.com/en/proguard/manual/usage#filters
  */
-class ProGuardClassFilterParser(private val mapper : ProGuardTypesMapper) {
+class ProGuardClassFilterParser(private val mapper: ProGuardTypesMapper) {
 
     companion object {
         private const val RULES = "(adaptclassstrings|dontnote|dontwarn)"
@@ -33,11 +33,11 @@
     val replacer = GroupsReplacer(
         pattern = PatternHelper.build("^ *-$RULES ⦅[^-]+⦆ *$", Pattern.MULTILINE),
         groupsMap = listOf(
-            { filter : String -> rewriteClassFilter(filter) }
+            { filter: String -> rewriteClassFilter(filter) }
         )
     )
 
-    private fun rewriteClassFilter(classFilter: String) : String {
+    private fun rewriteClassFilter(classFilter: String): String {
         return classFilter
             .splitToSequence(",")
             .filterNotNull()
@@ -47,7 +47,7 @@
             .joinToString(separator = ", ")
     }
 
-    private fun replaceTypeInClassFilter(type: String) : String {
+    private fun replaceTypeInClassFilter(type: String): String {
         if (!type.startsWith('!')) {
             return mapper.replaceType(type)
         }
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardClassSpecParser.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardClassSpecParser.kt
similarity index 69%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardClassSpecParser.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardClassSpecParser.kt
index 933ff08..b0487fb 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardClassSpecParser.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardClassSpecParser.kt
@@ -1,29 +1,29 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
-import android.support.tools.jetifier.core.transform.proguard.patterns.GroupsReplacer
-import android.support.tools.jetifier.core.transform.proguard.patterns.PatternHelper
+import com.android.tools.build.jetifier.processor.transform.proguard.patterns.GroupsReplacer
+import com.android.tools.build.jetifier.processor.transform.proguard.patterns.PatternHelper
 
 /**
  * Parses and rewrites ProGuard rules that contain class specification. See ProGuard documentation
  * https://www.guardsquare.com/en/proguard/manual/usage#classspecification
  */
-class ProGuardClassSpecParser(private val mapper : ProGuardTypesMapper) {
+class ProGuardClassSpecParser(private val mapper: ProGuardTypesMapper) {
 
     companion object {
         private const val RULES = "(keep[a-z]*|whyareyoukeeping|assumenosideeffects)"
@@ -53,10 +53,10 @@
             "-$RULES ($RULES_MODIFIERS )*(@⦅$ANNOTATION_TYPE⦆ )?($CLASS_MODIFIERS )*$CLASS_TYPES " +
             "⦅$CLASS_NAME⦆( (extends|implements) ⦅$CLASS_NAME⦆)?+ *( *\\{⦅[^}]*⦆\\} *)?+"),
         groupsMap = listOf(
-            { annotation : String -> mapper.replaceType(annotation) },
-            { className : String -> mapper.replaceType(className) },
-            { className2 : String -> mapper.replaceType(className2) },
-            { bodyGroup : String -> rewriteBodyGroup(bodyGroup) }
+            { annotation: String -> mapper.replaceType(annotation) },
+            { className: String -> mapper.replaceType(className) },
+            { className2: String -> mapper.replaceType(className2) },
+            { bodyGroup: String -> rewriteBodyGroup(bodyGroup) }
         )
     )
 
@@ -66,7 +66,7 @@
             pattern = PatternHelper.build(
                 "^ *(@⦅$ANNOTATION_TYPE⦆ )?($FIELD_MODIFIERS )*<fields> *$"),
             groupsMap = listOf(
-                { annotation : String -> mapper.replaceType(annotation) }
+                { annotation: String -> mapper.replaceType(annotation) }
         )),
 
         // [@annotation] [[!]public|private|etc...] fieldType fieldName;
@@ -74,8 +74,8 @@
             pattern = PatternHelper.build(
                 "^ *(@⦅$ANNOTATION_TYPE⦆ )?($FIELD_MODIFIERS )*(⦅$FIELD_TYPE⦆ $FIELD_NAME) *$"),
             groupsMap = listOf(
-                { annotation : String -> mapper.replaceType(annotation) },
-                { fieldType : String -> mapper.replaceType(fieldType) }
+                { annotation: String -> mapper.replaceType(annotation) },
+                { fieldType: String -> mapper.replaceType(fieldType) }
         )),
 
         // [@annotation] [[!]public|private|etc...] <methods>;
@@ -83,7 +83,7 @@
             pattern = PatternHelper.build(
                 "^ *(@⦅$ANNOTATION_TYPE⦆ )?($METHOD_MODIFIERS )*<methods> *$"),
             groupsMap = listOf(
-                { annotation : String -> mapper.replaceType(annotation) }
+                { annotation: String -> mapper.replaceType(annotation) }
         )),
 
         // [@annotation] [[!]public|private|etc...] className(argumentType,...));
@@ -91,33 +91,33 @@
             pattern = PatternHelper.build(
                 "^ *(@⦅$ANNOTATION_TYPE⦆ )?($METHOD_MODIFIERS )*⦅$CLASS_NAME⦆ *\\(⦅$ARGS⦆\\) *$"),
             groupsMap = listOf(
-                { annotation : String -> mapper.replaceType(annotation) },
-                { className : String -> mapper.replaceType(className) },
-                { argsType : String -> mapper.replaceMethodArgs(argsType) }
-            )
-        ),
+                { annotation: String -> mapper.replaceType(annotation) },
+                { className: String -> mapper.replaceType(className) },
+                { argsType: String -> mapper.replaceMethodArgs(argsType) }
+        )),
 
         // [@annotation] [[!]public|private|etc...] <init>(argumentType,...));
         GroupsReplacer(
             pattern = PatternHelper.build(
                 "^ *(@⦅$ANNOTATION_TYPE⦆ )?($METHOD_MODIFIERS )*<init> *\\(⦅$ARGS⦆\\) *$"),
             groupsMap = listOf(
-                { annotation : String -> mapper.replaceType(annotation) },
-                { argsType : String -> mapper.replaceMethodArgs(argsType) }
+                { annotation: String -> mapper.replaceType(annotation) },
+                { argsType: String -> mapper.replaceMethodArgs(argsType) }
         )),
 
         // [@annotation] [[!]public|private|etc...] returnType methodName(argumentType,...));
         GroupsReplacer(
-            pattern = PatternHelper.build("^ *(@⦅$ANNOTATION_TYPE⦆ )?($METHOD_MODIFIERS )*" +
+            pattern = PatternHelper.build(
+                "^ *(@⦅$ANNOTATION_TYPE⦆ )?($METHOD_MODIFIERS )*" +
                 "⦅$RETURN_TYPE_NAME⦆ $METHOD_NAME *\\(⦅$ARGS⦆\\) *$"),
             groupsMap = listOf(
-                { annotation : String -> mapper.replaceType(annotation) },
-                { returnType : String -> mapper.replaceType(returnType) },
-                { argsType : String -> mapper.replaceMethodArgs(argsType) }
+                { annotation: String -> mapper.replaceType(annotation) },
+                { returnType: String -> mapper.replaceType(returnType) },
+                { argsType: String -> mapper.replaceMethodArgs(argsType) }
         ))
     )
 
-    private fun rewriteBodyGroup(bodyGroup: String) : String {
+    private fun rewriteBodyGroup(bodyGroup: String): String {
         if (bodyGroup == "*" || bodyGroup == "**") {
             return bodyGroup
         }
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTransformer.kt
new file mode 100644
index 0000000..129df0b
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTransformer.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.android.tools.build.jetifier.processor.transform.proguard
+
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.Transformer
+import com.android.tools.build.jetifier.processor.transform.proguard.patterns.ReplacersRunner
+import java.nio.charset.StandardCharsets
+
+/**
+ * The [Transformer] responsible for ProGuard files refactoring.
+ */
+class ProGuardTransformer internal constructor(context: TransformationContext) : Transformer {
+
+    private val mapper = ProGuardTypesMapper(
+        context)
+
+    val replacer = ReplacersRunner(
+        listOf(
+            ProGuardClassSpecParser(mapper).replacer,
+            ProGuardClassFilterParser(mapper).replacer
+        ))
+
+    override fun canTransform(file: ArchiveFile): Boolean {
+        return file.isProGuardFile()
+    }
+
+    override fun runTransform(file: ArchiveFile) {
+        val content = StringBuilder(file.data.toString(StandardCharsets.UTF_8)).toString()
+        val result = replacer.applyReplacers(content)
+
+        if (result == content) {
+            return
+        }
+
+        file.setNewData(result.toByteArray())
+    }
+}
+
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMapper.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTypesMapper.kt
similarity index 74%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMapper.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTypesMapper.kt
index 779384e..9cc261b 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMapper.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTypesMapper.kt
@@ -1,23 +1,24 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.proguard.ProGuardType
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
 
 /**
  * Maps ProGuard types using [TypesMap] and [ProGuardTypesMap].
@@ -42,22 +43,11 @@
 
         val javaType = type.toJavaType()
         if (javaType != null) {
-            // We are dealing with an explicit type definition
-            if (!context.isEligibleForRewrite(javaType)) {
-                return typeToReplace
-            }
-
-            val result = config.typesMap.mapType(javaType)
+            val result = context.typeRewriter.rewriteType(javaType)
             if (result != null) {
-                Log.i(TAG, "  map: %s -> %s", type, result)
                 return result.toDotNotation()
             }
 
-            if (context.useIdentityIfTypeIsMissing) {
-                Log.i(TAG, "No mapping for: %s - using identity")
-                return typeToReplace
-            }
-
             context.reportNoProGuardMappingFoundFailure()
             Log.e(TAG, "No mapping for: %s", type)
             return typeToReplace
@@ -71,7 +61,7 @@
         }
 
         // Report error only when we are sure
-        if (context.isEligibleForRewrite(type)) {
+        if (context.typeRewriter.isEligibleForRewrite(type)) {
             context.reportNoProGuardMappingFoundFailure()
             Log.e(TAG, "No mapping for: " + type)
         }
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/GroupsReplacer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/GroupsReplacer.kt
similarity index 74%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/GroupsReplacer.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/GroupsReplacer.kt
index 6213a55..a76f104 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/GroupsReplacer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/GroupsReplacer.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard.patterns
+package com.android.tools.build.jetifier.processor.transform.proguard.patterns
 
 import java.util.regex.Matcher
 import java.util.regex.Pattern
@@ -23,21 +23,23 @@
  * Applies replacements on a matched string using the given [pattern] and its groups. Each group is
  * mapped using a lambda from [groupsMap].
  */
-class GroupsReplacer(val pattern: Pattern,
-                     private val groupsMap: List<(String) -> String>) {
+class GroupsReplacer(
+    val pattern: Pattern,
+    private val groupsMap: List<(String) -> String>
+) {
 
     /**
      * Takes the given [matcher] and replace its matched groups using mapping functions given in
      * [groupsMap].
      */
-    fun runReplacements(matcher: Matcher) : String {
+    fun runReplacements(matcher: Matcher): String {
         var result = matcher.group(0)
 
         // We go intentionally backwards to replace using indexes
         for (i in groupsMap.size - 1 downTo 0) {
             val groupVal = matcher.group(i + 1) ?: continue
             val localStart = matcher.start(i + 1) - matcher.start()
-            val localEnd =  matcher.end(i + 1) - matcher.start()
+            val localEnd = matcher.end(i + 1) - matcher.start()
 
             result = result.replaceRange(
                 startIndex = localStart,
@@ -46,5 +48,4 @@
         }
         return result
     }
-
 }
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/PatternHelper.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/PatternHelper.kt
similarity index 85%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/PatternHelper.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/PatternHelper.kt
index 3171185..faa46ab 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/PatternHelper.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/PatternHelper.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard.patterns
+package com.android.tools.build.jetifier.processor.transform.proguard.patterns
 
 import java.util.regex.Pattern
 
@@ -42,7 +42,7 @@
      * Transforms the given [toReplace] according to the rules defined in documentation of this
      * class and compiles it to a [Pattern].
      */
-    fun build(toReplace: String, flags : Int = 0) : Pattern {
+    fun build(toReplace: String, flags: Int = 0): Pattern {
         var result = toReplace
         result = result.replace("(?<!\\\\)\\(".toRegex(), "(?:")
         rewrites.forEach { result = result.replace(it.first, it.second) }
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/ReplacersRunner.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/ReplacersRunner.kt
similarity index 89%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/ReplacersRunner.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/ReplacersRunner.kt
index bd2fc12..431ccbb 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/ReplacersRunner.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/ReplacersRunner.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard.patterns
+package com.android.tools.build.jetifier.processor.transform.proguard.patterns
 
 /**
  * Runs multiple [GroupsReplacer]s on given strings.
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/resource/XmlResourcesTransformer.kt
similarity index 83%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformer.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/resource/XmlResourcesTransformer.kt
index c7c6b1d..e78a11d 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/resource/XmlResourcesTransformer.kt
@@ -1,28 +1,28 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.resource
+package com.android.tools.build.jetifier.processor.transform.resource
 
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.PackageName
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.Transformer
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.PackageName
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.Transformer
 import java.nio.charset.Charset
 import java.nio.charset.StandardCharsets
 import java.util.regex.Pattern
@@ -72,8 +72,6 @@
         Pattern.compile("<manifest[^>]*package=\"([a-zA-Z0-9._]+)\"[^>]*>")
     )
 
-    private val typesMap = context.config.typesMap
-
     override fun canTransform(file: ArchiveFile) = file.isXmlFile() && !file.isPomFile()
 
     override fun runTransform(file: ArchiveFile) {
@@ -157,23 +155,15 @@
 
     private fun rewriteType(typeName: String): String {
         val type = JavaType.fromDotVersion(typeName)
-        if (!context.isEligibleForRewrite(type)) {
-            return typeName
-        }
-
-        val result = typesMap.mapType(type)
+        val result = context.typeRewriter.rewriteType(type)
         if (result != null) {
-            Log.i(TAG, "  map: %s -> %s", type, result)
             return result.toDotNotation()
         }
 
-        if (context.useIdentityIfTypeIsMissing) {
-            Log.i(TAG, "No mapping for: %s - using identity", type)
-            return typeName
+        if (!context.useFallbackIfTypeIsMissing) {
+            context.reportNoMappingFoundFailure()
         }
 
-        context.reportNoMappingFoundFailure()
-        Log.e(TAG, "No mapping for: %s", type)
         return typeName
     }
 
@@ -183,7 +173,7 @@
         if (result != null) {
             return result.toDotNotation()
         }
-        if (context.useIdentityIfTypeIsMissing) {
+        if (context.useFallbackIfTypeIsMissing) {
             Log.i(TAG, "No mapping for package: '%s' in artifact: '%s' - using identity",
                     pckg, archiveName)
             return packageName
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/LibraryMapGenerator.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/type/LibraryMapGenerator.kt
similarity index 76%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/LibraryMapGenerator.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/type/LibraryMapGenerator.kt
index c9d8f9f..3ad3cf0 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/LibraryMapGenerator.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/type/LibraryMapGenerator.kt
@@ -1,25 +1,26 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.map
+package com.android.tools.build.jetifier.processor.type
 
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.archive.ArchiveItemVisitor
-import android.support.tools.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
 import org.objectweb.asm.ClassReader
 import org.objectweb.asm.ClassWriter
 
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/type/MapGeneratorRemapper.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/type/MapGeneratorRemapper.kt
new file mode 100644
index 0000000..aa6f077
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/type/MapGeneratorRemapper.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2018 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.android.tools.build.jetifier.processor.type
+
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.transform.bytecode.CoreRemapper
+import com.android.tools.build.jetifier.processor.transform.bytecode.asm.CustomRemapper
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.commons.ClassRemapper
+
+/**
+ * Hooks to asm remapping to collect data for [TypesMap] by applying all the [RewriteRule]s from the
+ * given [config] on all discovered and eligible types.
+ */
+class MapGeneratorRemapper(private val config: Config) : CoreRemapper {
+
+    companion object {
+        private const val TAG: String = "MapGeneratorRemapper"
+    }
+
+    private val typesRewritesMap = mutableMapOf<JavaType, JavaType>()
+
+    var isMapNotComplete = false
+        private set
+
+    fun createClassRemapper(visitor: ClassVisitor): ClassRemapper {
+        return ClassRemapper(visitor, CustomRemapper(this))
+    }
+
+    override fun rewriteType(type: JavaType): JavaType {
+        if (!isTypeSupported(type)) {
+            return type
+        }
+
+        if (typesRewritesMap.contains(type)) {
+            return type
+        }
+
+        // Try to find a rule
+        val typeToMap = type.getRootType()
+        for (rule in config.rulesMap.rewriteRules) {
+            val typeRewriteResult = rule.apply(typeToMap)
+            if (typeRewriteResult.isIgnored) {
+                Log.i(TAG, "Ignoring: " + typeToMap)
+                return typeToMap
+            }
+            if (typeRewriteResult.result == null) {
+                continue
+            }
+            typesRewritesMap.put(typeToMap, typeRewriteResult.result!!)
+            Log.i(TAG, "  map: %s -> %s", typeToMap, typeRewriteResult.result)
+            return typeRewriteResult.result!!
+        }
+
+        isMapNotComplete = true
+        Log.e(TAG, "No rule for: " + typeToMap)
+        typesRewritesMap.put(typeToMap, typeToMap) // Identity
+        return typeToMap
+    }
+
+    override fun rewriteString(value: String): String {
+        // We don't build map from strings
+        return value
+    }
+
+    fun createTypesMap(): TypesMap {
+        return TypesMap(typesRewritesMap)
+    }
+
+    private fun isTypeSupported(type: JavaType): Boolean {
+        return config.restrictToPackagePrefixes.any { type.fullName.startsWith(it) }
+    }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/ChangeDetectionTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/ChangeDetectionTest.kt
similarity index 81%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/ChangeDetectionTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/ChangeDetectionTest.kt
index fb7a92d..92cb371 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/ChangeDetectionTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/ChangeDetectionTest.kt
@@ -1,31 +1,32 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core
+package com.android.tools.build.jetifier.processor
 
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule
-import android.support.tools.jetifier.core.transform.PackageMap
-import android.support.tools.jetifier.core.transform.pom.PomDependency
-import android.support.tools.jetifier.core.transform.pom.PomRewriteRule
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
 import com.google.common.truth.Truth
 import org.junit.Test
 import java.io.File
@@ -37,24 +38,15 @@
  * was something to rewrite or not.
  */
 class ChangeDetectionTest {
-    private val emptyConfig = Config(
-        restrictToPackagePrefixes = emptyList(),
-        rewriteRules = emptyList(),
-        slRules = emptyList(),
-        pomRewriteRules = emptySet(),
-        typesMap = TypesMap.EMPTY,
-        proGuardMap = ProGuardTypesMap.EMPTY,
-        packageMap = PackageMap.EMPTY
-    )
 
     private val prefRewriteConfig = Config(
-        restrictToPackagePrefixes = listOf("android/support/v7/preference"),
-        rewriteRules =
-        listOf(
+        restrictToPackagePrefixes = setOf("android/support/v7/preference"),
+        rulesMap =
+        RewriteRulesMap(
             RewriteRule(from = "android/support/v7/preference/Preference(.+)", to = "ignore"),
             RewriteRule(from = "(.*)/R(.*)", to = "ignore")
         ),
-        slRules = emptyList(),
+        slRules = setOf(),
         pomRewriteRules = setOf(
             PomRewriteRule(
                 PomDependency(
@@ -63,11 +55,11 @@
                     PomDependency(
                         groupId = "testGroup", artifactId = "testArtifact", version = "1.0")
                 )
-        )),
-        typesMap = TypesMap(mapOf(
+            )),
+        typesMap = TypesMap(
             JavaType("android/support/v7/preference/Preference")
-                to JavaType("android/test/pref/Preference")
-        )),
+                    to JavaType("android/test/pref/Preference")
+        ),
         proGuardMap = ProGuardTypesMap.EMPTY,
         packageMap = PackageMap.EMPTY
     )
@@ -87,7 +79,7 @@
     @Test
     fun xmlRewrite_archiveNotChanged() {
         testChange(
-            config = emptyConfig,
+            config = Config.EMPTY,
             fileContent =
                 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                 "<android.support.v7.preference.Preference/>",
@@ -112,7 +104,7 @@
     @Test
     fun proGuard_archiveNotChanged() {
         testChange(
-            config = emptyConfig,
+            config = Config.EMPTY,
             fileContent =
                 "-keep public class * extends android.support.v7.preference.Preference { \n" +
                 "  <fields>; \n" +
@@ -148,7 +140,7 @@
     @Test
     fun pom_archiveNotChanged() {
         testChange(
-            config = emptyConfig,
+            config = Config.EMPTY,
             fileContent =
                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                 "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" " +
@@ -186,7 +178,7 @@
         val inputFile = File(javaClass.getResource(inputClassPath).file)
 
         testChange(
-            config = emptyConfig,
+            config = Config.EMPTY,
             file = ArchiveFile(Paths.get("/", "preference.class"), inputFile.readBytes()),
             areChangesExpected = false
         )
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/map/MapGenerationTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/map/MapGenerationTest.kt
similarity index 85%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/map/MapGenerationTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/map/MapGenerationTest.kt
index 9f56676..653f9f6 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/map/MapGenerationTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/map/MapGenerationTest.kt
@@ -1,25 +1,28 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.map
+package com.android.tools.build.jetifier.processor.map
 
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.type.MapGeneratorRemapper
 import com.google.common.truth.Truth
 import org.junit.Test
 
@@ -30,8 +33,7 @@
             .testThatRules(
                 RewriteRule("android/support/v7/(.*)", "android/test/{0}")
             )
-            .withAllowedPrefixes(
-                "android/support/"
+            .withAllowedPrefixes("android/support/"
             )
             .forGivenTypes(
                 JavaType("android/support/v7/pref/Preference")
@@ -137,31 +139,27 @@
 
     object ScanTester {
 
-        fun testThatRules(vararg rules: RewriteRule) = Step1(rules.toList())
+        fun testThatRules(vararg rules: RewriteRule) = Step1(rules.toSet())
 
+        class Step1(private val rules: Set<RewriteRule>) {
 
-        class Step1(private val rules: List<RewriteRule>) {
+            fun withAllowedPrefixes(vararg prefixes: String) = Step2(rules, prefixes.toSet())
 
-            fun withAllowedPrefixes(vararg prefixes: String) = Step2(rules, prefixes.toList())
-
-
-            class Step2(private val rules: List<RewriteRule>, private val prefixes: List<String>) {
+            class Step2(private val rules: Set<RewriteRule>, private val prefixes: Set<String>) {
 
                 private val allTypes: MutableList<JavaType> = mutableListOf()
                 private var wasMapIncomplete = false
 
-
                 fun forGivenTypes(vararg types: JavaType): Step2 {
                     allTypes.addAll(types)
                     return this
                 }
 
-
                 fun mapInto(types: Map<String, String>): Step2 {
                     val config = Config(
                         restrictToPackagePrefixes = prefixes,
-                        rewriteRules = rules,
-                        slRules = emptyList(),
+                        rulesMap = RewriteRulesMap(rules),
+                        slRules = emptySet(),
                         pomRewriteRules = emptySet(),
                         typesMap = TypesMap.EMPTY,
                         proGuardMap = ProGuardTypesMap.EMPTY)
@@ -184,9 +182,7 @@
                     Truth.assertThat(wasMapIncomplete).isFalse()
                 }
             }
-
         }
-
     }
 }
 
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/DependencyMappingTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/DependencyMappingTest.kt
new file mode 100644
index 0000000..bc70508
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/DependencyMappingTest.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2018 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.android.tools.build.jetifier.processor.transform
+
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.Processor
+import com.google.common.truth.Truth
+import org.junit.Test
+
+class DependencyMappingTest {
+
+    @Test fun mapTest_oneToOne_shouldMap() {
+        MappingTester.testRewrite(
+            from = "hello:world:1.0.0",
+            to = setOf("hi:all:2.0.0"),
+            rules = setOf(
+                PomRewriteRule(
+                    from = PomDependency(groupId = "hello", artifactId = "world"),
+                    to = setOf(
+                        PomDependency(groupId = "hi", artifactId = "all", version = "2.0.0")
+                    )
+                ))
+        )
+    }
+
+    @Test fun mapTest_oneToTwo_shouldMap() {
+        MappingTester.testRewrite(
+            from = "hello:world:1.0.0",
+            to = setOf("hi:all:2.0.0", "hey:all:3.0.0"),
+            rules = setOf(
+                PomRewriteRule(
+                    from = PomDependency(groupId = "hello", artifactId = "world"),
+                    to = setOf(
+                        PomDependency(groupId = "hi", artifactId = "all", version = "2.0.0"),
+                        PomDependency(groupId = "hey", artifactId = "all", version = "3.0.0")
+                    )
+                ))
+        )
+    }
+
+    @Test fun mapTest_oneToNone_shouldMapToEmpty() {
+        MappingTester.testRewrite(
+            from = "hello:world:1.0.0",
+            to = setOf(),
+            rules = setOf(
+                PomRewriteRule(
+                    from = PomDependency(groupId = "hello", artifactId = "world"),
+                    to = setOf()
+                ))
+        )
+    }
+
+    @Test fun mapTest_oneToNull_ruleNotFound_returnNull() {
+        MappingTester.testRewrite(
+            from = "hello:world:1.0.0",
+            to = null,
+            rules = setOf(
+                PomRewriteRule(
+                    from = PomDependency(groupId = "hello", artifactId = "me", version = "1.0"),
+                    to = setOf()
+                ))
+        )
+    }
+
+    object MappingTester {
+
+        fun testRewrite(from: String, to: Set<String>?, rules: Set<PomRewriteRule>) {
+            val config = Config(
+                restrictToPackagePrefixes = emptySet(),
+                rulesMap = RewriteRulesMap.EMPTY,
+                slRules = emptySet(),
+                pomRewriteRules = rules,
+                typesMap = TypesMap.EMPTY,
+                proGuardMap = ProGuardTypesMap.EMPTY,
+                packageMap = PackageMap.EMPTY
+            )
+
+            val processor = Processor.createProcessor(config)
+            val result = processor.mapDependency(from)
+
+            if (to == null) {
+                Truth.assertThat(result).isNull()
+            } else {
+                Truth.assertThat(result).containsExactlyElementsIn(to)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/ClassFilesMoveTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ClassFilesMoveTest.kt
similarity index 75%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/ClassFilesMoveTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ClassFilesMoveTest.kt
index 1bebfc4..63a7d1e 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/ClassFilesMoveTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ClassFilesMoveTest.kt
@@ -1,31 +1,32 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.bytecode
+package com.android.tools.build.jetifier.processor.transform.bytecode
 
-import android.support.tools.jetifier.core.FileMapping
-import android.support.tools.jetifier.core.Processor
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.archive.ArchiveItemVisitor
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.FileMapping
+import com.android.tools.build.jetifier.processor.Processor
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
 import com.google.common.truth.Truth
 import org.junit.Test
 import java.io.File
@@ -37,13 +38,13 @@
 
     companion object {
         private val TEST_CONFIG = Config(
-            restrictToPackagePrefixes = listOf("android/support"),
-            rewriteRules = listOf(
+            restrictToPackagePrefixes = setOf("android/support"),
+            rulesMap = RewriteRulesMap(
                 RewriteRule("android/support/annotation/(.*)", "ignore"),
                 RewriteRule("android/support/v7/preference/R(.*)", "ignore"),
                 RewriteRule("android/support/v4/(.*)", "ignore")
             ),
-            slRules = listOf(
+            slRules = setOf(
                 RewriteRule("android/support/annotation/(.*)", "ignore"),
                 RewriteRule("android/support/v7/preference/R(.*)", "ignore"),
                 RewriteRule("android/support/v4/(.*)", "ignore")
@@ -51,19 +52,19 @@
             pomRewriteRules = emptySet(),
             typesMap = TypesMap(mapOf(
                 "android/support/v7/preference/Preference"
-                    to "androidx/support/preference/Preference",
+                        to "androidx/support/preference/Preference",
                 "android/support/v7/preference/TwoStatePreference"
-                    to "androidx/support/preference/TwoStatePreference",
+                        to "androidx/support/preference/TwoStatePreference",
                 "android/support/v7/preference/PreferenceGroup"
-                    to "androidx/support/preference/PreferenceGroup",
+                        to "androidx/support/preference/PreferenceGroup",
                 "android/support/v7/preference/PreferenceViewHolder"
-                    to "androidx/support/preference/PreferenceViewHolder",
+                        to "androidx/support/preference/PreferenceViewHolder",
                 "android/support/v7/preference/PreferenceManager"
-                    to "androidx/support/preference/PreferenceManager",
+                        to "androidx/support/preference/PreferenceManager",
                 "android/support/v14/preference/SwitchPreference"
-                    to "androidx/support/preference/SwitchPreference",
+                        to "androidx/support/preference/SwitchPreference",
                 "android/support/v7/preference/PreferenceDataStore"
-                    to "androidx/support/preference/PreferenceDataStore"
+                        to "androidx/support/preference/PreferenceDataStore"
             ).map { JavaType(it.key) to JavaType(it.value) }.toMap()),
             proGuardMap = ProGuardTypesMap.EMPTY
         )
@@ -91,8 +92,7 @@
         val resultFiles = processor.transform(setOf(FileMapping(inputFile, expectedFile)))
 
         Truth.assertThat(resultFiles).hasSize(1)
-        testArchivesAreSame(resultFiles.first(),
-            File(javaClass.getResource(expectedZipPath).file))
+        testArchivesAreSame(resultFiles.first(), File(javaClass.getResource(expectedZipPath).file))
 
         tempDir.delete()
     }
@@ -114,8 +114,7 @@
         val resultFiles = processor.transform(setOf(FileMapping(inputFile, expectedFile)))
 
         Truth.assertThat(resultFiles).hasSize(1)
-        testArchivesAreSame(resultFiles.first(),
-            File(javaClass.getResource(expectedZipPath).file))
+        testArchivesAreSame(resultFiles.first(), File(javaClass.getResource(expectedZipPath).file))
 
         tempDir.delete()
     }
@@ -130,8 +129,7 @@
         val inputZipPath = "/fileRenameTest/inputTestLib.zip"
 
         // Transform forward
-        val processor = Processor.createProcessor(TEST_CONFIG,
-            rewritingSupportLib = true)
+        val processor = Processor.createProcessor(TEST_CONFIG, rewritingSupportLib = true)
         val inputFile = File(javaClass.getResource(inputZipPath).file)
 
         val tempDir = createTempDir()
@@ -140,15 +138,15 @@
         val resultFiles = processor.transform(setOf(FileMapping(inputFile, expectedFile)))
 
         // Take previous result & reverse it
-        val processor2 = Processor.createProcessor(TEST_CONFIG,
+        val processor2 = Processor.createProcessor(
+            TEST_CONFIG,
             rewritingSupportLib = true,
             reversedMode = true)
         val expectedFile2 = File(createTempDir(), "test2.zip")
         val resultFiles2 = processor2.transform(setOf(
-                FileMapping(resultFiles.first(), expectedFile2)))
+            FileMapping(resultFiles.first(), expectedFile2)))
 
-        testArchivesAreSame(resultFiles2.first(),
-            File(javaClass.getResource(inputZipPath).file))
+        testArchivesAreSame(resultFiles2.first(), File(javaClass.getResource(inputZipPath).file))
 
         tempDir.delete()
     }
@@ -169,8 +167,7 @@
         val resultFiles = processor.transform(setOf(FileMapping(inputFile, expectedFile)))
 
         Truth.assertThat(resultFiles).hasSize(1)
-        testArchivesAreSame(resultFiles.first(),
-            File(javaClass.getResource(inputZipPath).file))
+        testArchivesAreSame(resultFiles.first(), File(javaClass.getResource(inputZipPath).file))
 
         tempDir.delete()
     }
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImplTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImplTest.kt
new file mode 100644
index 0000000..1c23991
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImplTest.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.android.tools.build.jetifier.processor.transform.bytecode
+
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.objectweb.asm.ClassWriter
+
+class CoreRemapperImplTest {
+
+    @Test
+    fun remapString_shouldUseFallbackForField() {
+        val remapper = prepareRemapper(
+            TypesMap(mapOf(
+                JavaType.fromDotVersion("androidx.test.InputConnectionCompat")
+                    to JavaType.fromDotVersion(
+                    "android.support.test.InputConnectionCompat")
+            )),
+            "androidx/")
+
+        val given = "androidx.test.InputConnectionCompat.CONTENT_URI"
+        val expected = "android.support.test.InputConnectionCompat.CONTENT_URI"
+
+        Truth.assertThat(remapper.rewriteString(given)).isEqualTo(expected)
+    }
+
+    private fun prepareRemapper(
+        typesMap: TypesMap,
+        restrictToPackagePrefix: String? = null
+    ): CoreRemapperImpl {
+        val prefixes = if (restrictToPackagePrefix == null) {
+            emptySet()
+        } else {
+            setOf(restrictToPackagePrefix)
+        }
+
+        val config = Config(
+            restrictToPackagePrefixes = prefixes,
+            rulesMap = RewriteRulesMap.EMPTY,
+            typesMap = typesMap,
+            slRules = emptySet(),
+            pomRewriteRules = emptySet(),
+            proGuardMap = ProGuardTypesMap.EMPTY,
+            packageMap = PackageMap.EMPTY)
+
+        val context = TransformationContext(config, isInReversedMode = true)
+
+        val writer = ClassWriter(0 /* flags */)
+        return CoreRemapperImpl(context, writer)
+    }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformerTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformerTest.kt
similarity index 76%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformerTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformerTest.kt
index c88c08d..4db1a6e 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformerTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformerTest.kt
@@ -1,27 +1,24 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.metainf
+package com.android.tools.build.jetifier.processor.transform.metainf
 
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.transform.PackageMap
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
 import com.google.common.truth.Truth
 import org.junit.Test
 import java.nio.charset.Charset
@@ -99,16 +96,7 @@
         expectedCanTransform: Boolean = true,
         rewritingSupportLib: Boolean = true
     ) {
-        val config = Config(
-            restrictToPackagePrefixes = emptyList(),
-            rewriteRules = emptyList(),
-            slRules = emptyList(),
-            pomRewriteRules = emptySet(),
-            packageMap = PackageMap.EMPTY,
-            typesMap = TypesMap.EMPTY,
-            proGuardMap = ProGuardTypesMap.EMPTY
-        )
-        val context = TransformationContext(config,
+        val context = TransformationContext(Config.EMPTY,
             rewritingSupportLib = rewritingSupportLib,
             isInReversedMode = reverseMode)
         val transformer = MetaInfTransformer(context)
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomDocumentTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDocumentTest.kt
similarity index 88%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomDocumentTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDocumentTest.kt
index 567097d..a805f56 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomDocumentTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDocumentTest.kt
@@ -1,28 +1,30 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.pom
+package com.android.tools.build.jetifier.processor.transform.pom
 
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.config.ConfigParser
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.transform.PackageMap
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
 import com.google.common.truth.Truth
 import org.junit.Test
 import java.nio.charset.StandardCharsets
@@ -75,7 +77,7 @@
                 PomRewriteRule(
                     PomDependency(
                         groupId = "supportGroup", artifactId = "supportArtifact",
-                        version =  "4.0"),
+                        version = "4.0"),
                     setOf(
                         PomDependency(
                             groupId = "testGroup", artifactId = "testArtifact",
@@ -101,7 +103,7 @@
                 PomRewriteRule(
                     PomDependency(
                         groupId = "supportGroup", artifactId = "supportArtifact",
-                        version =  "4.0"),
+                        version = "4.0"),
                     setOf(
                         PomDependency(
                             groupId = "testGroup", artifactId = "testArtifact",
@@ -126,7 +128,7 @@
                 PomRewriteRule(
                     PomDependency(
                         groupId = "supportGroup", artifactId = "supportArtifact2",
-                        version =  "4.0"),
+                        version = "4.0"),
                     setOf(
                         PomDependency(
                             groupId = "testGroup", artifactId = "testArtifact",
@@ -173,7 +175,7 @@
                 PomRewriteRule(
                     PomDependency(
                         groupId = "supportGroup", artifactId = "supportArtifact",
-                        version =  "4.0"),
+                        version = "4.0"),
                     setOf(
                         PomDependency(
                             groupId = "testGroup", artifactId = "testArtifact",
@@ -211,7 +213,7 @@
                 PomRewriteRule(
                     PomDependency(
                         groupId = "supportGroup", artifactId = "supportArtifact",
-                        version =  "4.0"),
+                        version = "4.0"),
                     setOf(
                         PomDependency(
                             groupId = "testGroup", artifactId = "testArtifact",
@@ -256,7 +258,7 @@
                 PomRewriteRule(
                     PomDependency(
                         groupId = "supportGroup", artifactId = "supportArtifact",
-                        version =  "4.0"),
+                        version = "4.0"),
                     setOf(
                         PomDependency(
                             groupId = "testGroup", artifactId = "testArtifact",
@@ -269,7 +271,7 @@
                 PomRewriteRule(
                     PomDependency(
                         groupId = "supportGroup", artifactId = "supportArtifact2",
-                        version =  "4.0"),
+                        version = "4.0"),
                     setOf(
                         PomDependency(
                             groupId = "testGroup", artifactId = "testArtifact",
@@ -314,7 +316,7 @@
                 PomRewriteRule(
                     PomDependency(
                         groupId = "supportGroup", artifactId = "supportArtifact",
-                        version =  "4.0"),
+                        version = "4.0"),
                     setOf(
                         PomDependency(
                             groupId = "testGroup", artifactId = "testArtifact",
@@ -362,17 +364,17 @@
         Truth.assertThat(dependency.artifactId).isEqualTo("supportArtifact")
     }
 
-
     private fun testRewriteToTheSame(givenAndExpectedXml: String, rules: Set<PomRewriteRule>) {
         testRewrite(givenAndExpectedXml, givenAndExpectedXml, rules)
     }
 
-    private fun testRewrite(givenXml: String, expectedXml : String, rules: Set<PomRewriteRule>) {
+    private fun testRewrite(givenXml: String, expectedXml: String, rules: Set<PomRewriteRule>) {
         val given =
             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
             "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" " +
-                "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
-                "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
+            "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
+            "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 " +
+            "http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
             "  <!-- Some comment -->\n" +
             "  <groupId>test.group</groupId>\n" +
             "  <artifactId>test.artifact.id</artifactId>\n" +
@@ -383,8 +385,9 @@
         var expected =
             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
             "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" " +
-                "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
-                "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
+            "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
+            "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 " +
+            "http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
             "  <!-- Some comment -->\n" +
             "  <groupId>test.group</groupId>\n" +
             "  <artifactId>test.artifact.id</artifactId>\n" +
@@ -395,13 +398,13 @@
         val file = ArchiveFile(Paths.get("pom.xml"), given.toByteArray())
         val pomDocument = PomDocument.loadFrom(file)
         val config = Config(
-                restrictToPackagePrefixes = emptyList(),
-                rewriteRules = emptyList(),
-                typesMap = TypesMap.EMPTY,
-                slRules = emptyList(),
-                pomRewriteRules = rules,
-                proGuardMap = ProGuardTypesMap.EMPTY,
-                packageMap = PackageMap.EMPTY);
+            restrictToPackagePrefixes = emptySet(),
+            rulesMap = RewriteRulesMap.EMPTY,
+            typesMap = TypesMap.EMPTY,
+            slRules = emptySet(),
+            pomRewriteRules = rules,
+            proGuardMap = ProGuardTypesMap.EMPTY,
+            packageMap = PackageMap.EMPTY)
         val context = TransformationContext(config)
         pomDocument.applyRules(context)
         pomDocument.saveBackToFileIfNeeded()
@@ -420,12 +423,13 @@
         Truth.assertThat(strResult).isEqualTo(expected)
     }
 
-    private fun loadDocument(givenXml : String) : PomDocument {
+    private fun loadDocument(givenXml: String): PomDocument {
         val given =
             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
             "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" " +
             "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
-            "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
+            "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 " +
+            "http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
             "  <!-- Some comment -->\n" +
             "  <groupId>test.group</groupId>\n" +
             "  <artifactId>test.artifact.id</artifactId>\n" +
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteInZipTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteInZipTest.kt
similarity index 78%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteInZipTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteInZipTest.kt
index 77dc196..ac95d1c 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteInZipTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteInZipTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * 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.
@@ -14,15 +14,18 @@
  * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.pom
+package com.android.tools.build.jetifier.processor.transform.pom
 
-import android.support.tools.jetifier.core.FileMapping
-import android.support.tools.jetifier.core.Processor
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.FileMapping
+import com.android.tools.build.jetifier.processor.Processor
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
 import com.google.common.truth.Truth
 import org.junit.Test
 import java.io.File
@@ -34,16 +37,16 @@
 
     companion object {
         private val TEST_CONFIG = Config(
-            restrictToPackagePrefixes = listOf("com/sample"),
-            rewriteRules = listOf(),
-            slRules = listOf(),
+            restrictToPackagePrefixes = setOf("com/sample"),
+            rulesMap = RewriteRulesMap.EMPTY,
+            slRules = setOf(),
             pomRewriteRules = setOf(
                 PomRewriteRule(
                     from = PomDependency(
                         groupId = "old.group",
                         artifactId = "myOldArtifact",
                         version = "0.1.0"),
-                to = setOf(
+                    to = setOf(
                         PomDependency(
                             groupId = "com.sample.my.group",
                             artifactId = "myArtifact",
@@ -94,9 +97,9 @@
         val inputZipPath = "/pomRefactorTest/pomTest.zip"
 
         val processor = Processor.createProcessor(
-                TEST_CONFIG,
-                rewritingSupportLib = false,
-                reversedMode = true)
+            TEST_CONFIG,
+            rewritingSupportLib = false,
+            reversedMode = true)
 
         val inputFile = File(javaClass.getResource(inputZipPath).file)
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteRuleTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteRuleTest.kt
similarity index 91%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteRuleTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteRuleTest.kt
index 94ed6e3..43cc1a7 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteRuleTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteRuleTest.kt
@@ -1,21 +1,23 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.pom
+package com.android.tools.build.jetifier.processor.transform.pom
 
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
 import com.google.common.truth.Truth
 import org.junit.Test
 
@@ -138,5 +140,4 @@
 
         Truth.assertThat(rule.validateVersion(pom)).isFalse()
     }
-
 }
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassFilterTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassFilterTest.kt
similarity index 92%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassFilterTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassFilterTest.kt
index 86b1599..25c4af6 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassFilterTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassFilterTest.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
 import org.junit.Test
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest.kt
similarity index 95%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest.kt
index dc3e470..45eaa91 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
 import org.junit.Test
 
@@ -85,8 +85,10 @@
                 "-keep allowshrinking class support.Activity \n" +
                 "-keep allowoptimization class support.Activity \n" +
                 "-keep allowobfuscation class support.Activity \n" +
-                "-keep allowshrinking allowoptimization allowobfuscation class support.Activity \n" +
-                "-keep allowshrinking   allowoptimization   allowobfuscation  class support.Activity"
+                "-keep allowshrinking allowoptimization allowobfuscation class support.Activity" +
+                " \n" +
+                "-keep allowshrinking   allowoptimization   allowobfuscation  class " +
+                "support.Activity"
             )
             .rewritesTo(
                 "-keep includedescriptorclasses class test.Activity \n" +
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_FieldTypeSelector.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_FieldTypeSelector.kt
similarity index 95%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_FieldTypeSelector.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_FieldTypeSelector.kt
index d08bbbe..f388e88 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_FieldTypeSelector.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_FieldTypeSelector.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
 import org.junit.Test
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_FieldsSelector.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_FieldsSelector.kt
similarity index 93%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_FieldsSelector.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_FieldsSelector.kt
index 2a0f752..e637308 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_FieldsSelector.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_FieldsSelector.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
 import org.junit.Test
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_MethodInitSelector.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_MethodInitSelector.kt
similarity index 97%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_MethodInitSelector.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_MethodInitSelector.kt
index d681bda..ee58fd1 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_MethodInitSelector.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_MethodInitSelector.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
 import org.junit.Test
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_MethodSelectorWithReturnType.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_MethodSelectorWithReturnType.kt
similarity index 97%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_MethodSelectorWithReturnType.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_MethodSelectorWithReturnType.kt
index 1cee765..dde9ad2 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_MethodSelectorWithReturnType.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_MethodSelectorWithReturnType.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
 import org.junit.Test
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_NamedCtorSelector.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_NamedCtorSelector.kt
similarity index 96%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_NamedCtorSelector.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_NamedCtorSelector.kt
index 671be61..825a027 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_NamedCtorSelector.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_NamedCtorSelector.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
 import org.junit.Test
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTester.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTester.kt
similarity index 75%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTester.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTester.kt
index 7d13826..a4b38b5 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTester.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTester.kt
@@ -1,27 +1,30 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule
-import android.support.tools.jetifier.core.transform.TransformationContext
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardType
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
 import com.google.common.truth.Truth
 import java.nio.charset.StandardCharsets
 import java.nio.file.Paths
@@ -34,10 +37,10 @@
     private var javaTypes = emptyList<Pair<String, String>>()
     private var rewriteRules = emptyList<Pair<String, String>>()
     private var proGuardTypes = emptyList<Pair<ProGuardType, ProGuardType>>()
-    private var prefixes = emptyList<String>()
+    private var prefixes = emptySet<String>()
 
     fun forGivenPrefixes(vararg prefixes: String): ProGuardTester {
-        this.prefixes = prefixes.toList()
+        this.prefixes = prefixes.toSet()
         return this
     }
 
@@ -53,8 +56,8 @@
 
     fun forGivenProGuardMap(vararg rules: Pair<String, String>): ProGuardTester {
         this.proGuardTypes = rules.map {
-            ProGuardType.fromDotNotation(it.first) to ProGuardType.fromDotNotation(it.second) }
-            .toList()
+            ProGuardType.fromDotNotation(it.first) to ProGuardType.fromDotNotation(it.second)
+        }.toList()
         return this
     }
 
@@ -73,8 +76,10 @@
     private fun createConfig(): Config {
         return Config(
             restrictToPackagePrefixes = prefixes,
-            rewriteRules = rewriteRules.map { RewriteRule(it.first, it.second) },
-            slRules = emptyList(),
+            rulesMap = RewriteRulesMap(rewriteRules
+                .map { RewriteRule(it.first, it.second) }
+                .toSet()),
+            slRules = emptySet(),
             pomRewriteRules = emptySet(),
             typesMap = TypesMap(
                 types = javaTypes.map { JavaType(it.first) to JavaType(it.second) }.toMap()
@@ -100,7 +105,7 @@
     class ProGuardTesterForType(private val config: Config, private val given: String) {
 
         fun getsRewrittenTo(expectedType: String) {
-            val context = TransformationContext(config, useIdentityIfTypeIsMissing = false)
+            val context = TransformationContext(config, useFallbackIfTypeIsMissing = false)
             val mapper = ProGuardTypesMapper(context)
             val result = mapper.replaceType(given)
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMapperTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTypesMapperTest.kt
similarity index 96%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMapperTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTypesMapperTest.kt
index 3249d0a..1d1ffd9 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMapperTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTypesMapperTest.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
 import org.junit.Test
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProguardSamplesTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProguardSamplesTest.kt
similarity index 97%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProguardSamplesTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProguardSamplesTest.kt
index f9554f1..1bbcb7b 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProguardSamplesTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProguardSamplesTest.kt
@@ -1,20 +1,20 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
 
 import org.junit.Test
 
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformerTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/resource/XmlResourcesTransformerTest.kt
similarity index 86%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformerTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/resource/XmlResourcesTransformerTest.kt
index 3549559..36718a9 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformerTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/resource/XmlResourcesTransformerTest.kt
@@ -1,28 +1,29 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.core.transform.resource
+package com.android.tools.build.jetifier.processor.transform.resource
 
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.transform.PackageMap
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
 import com.google.common.truth.Truth
 import org.junit.Test
 import java.nio.charset.Charset
@@ -35,7 +36,7 @@
             givenAndExpectedXml =
                 "<android.support.v7.preference.Preference>\n" +
                 "</android.support.v7.preference.Preference>",
-            prefixes = listOf(),
+            prefixes = setOf(),
             map = mapOf(
                 "android/support/v7/preference/Preference" to "android/test/pref/Preference"
             )
@@ -47,7 +48,7 @@
             givenAndExpectedXml =
                 "<android.support.v7.preference.Preference>\n" +
                 "</android.support.v7.preference.Preference>",
-            prefixes = listOf("android/support/v7/"),
+            prefixes = setOf("android/support/v7/"),
             map = mapOf(),
             errorsExpected = true
         )
@@ -58,7 +59,7 @@
             givenAndExpectedXml =
                 "<android.support.v7.preference.Preference>\n" +
                 "</android.support.v7.preference.Preference>",
-            prefixes = listOf("android/support/v14/"),
+            prefixes = setOf("android/support/v14/"),
             map = mapOf(
                 "android/support/v7/preference/Preference" to "android/test/pref/Preference"
             )
@@ -70,7 +71,7 @@
             givenAndExpectedXml =
                 "<my.android.support.v7.preference.Preference>\n" +
                 "</my.android.support.v7.preference.Preference>",
-            prefixes = listOf("android/support/v7/"),
+            prefixes = setOf("android/support/v7/"),
             map = mapOf(
                 "android/support/v7/preference/Preference" to "android/test/pref/Preference"
             )
@@ -82,7 +83,7 @@
             givenAndExpectedXml =
                 "<android.support.v7.preference.Preference>\n" +
                 "</android.support.v7.preference.Preference>",
-            prefixes = listOf("android/support/"),
+            prefixes = setOf("android/support/"),
             map = mapOf(
                 "android/support2/v7/preference/Preference" to "android/test/pref/Preference"
             ),
@@ -96,7 +97,7 @@
                 "<android.support.v7.preference.Preference/>",
             expectedXml =
                 "<android.test.pref.Preference/>",
-            prefixes = listOf("android/support/"),
+            prefixes = setOf("android/support/"),
             typesMap = mapOf(
                 "android/support/v7/preference/Preference" to "android/test/pref/Preference"
             )
@@ -111,7 +112,7 @@
             expectedXml =
                 "<android.test.pref.Preference \n" +
                 "    someAttribute=\"android.support.v7.preference.Preference\"/>",
-            prefixes = listOf("android/support/"),
+            prefixes = setOf("android/support/"),
             typesMap = mapOf(
                 "android/support/v7/preference/Preference" to "android/test/pref/Preference"
             )
@@ -126,7 +127,7 @@
             expectedXml =
                 "<android.test.pref.Preference>\n" +
                 "</android.test.pref.Preference>",
-            prefixes = listOf("android/support/"),
+            prefixes = setOf("android/support/"),
             typesMap = mapOf(
                 "android/support/v7/preference/Preference" to "android/test/pref/Preference"
             )
@@ -139,7 +140,7 @@
                 "<view class=\"android.support.v7.preference.Preference\">",
             expectedXml =
                 "<view class=\"android.test.pref.Preference\">",
-            prefixes = listOf("android/support/"),
+            prefixes = setOf("android/support/"),
             typesMap = mapOf(
                 "android/support/v7/preference/Preference" to "android/test/pref/Preference"
             )
@@ -156,7 +157,7 @@
                 "<view notRelated=\"true\" " +
                 "      class=\"android.test.pref.Preference\"" +
                 "      ignoreMe=\"android.support.v7.preference.Preference\">",
-            prefixes = listOf("android/support/"),
+            prefixes = setOf("android/support/"),
             typesMap = mapOf(
                 "android/support/v7/preference/Preference" to "android/test/pref/Preference"
             )
@@ -167,7 +168,7 @@
         testRewriteToTheSame(
             givenAndExpectedXml =
                 "<test attribute=\"view\" class=\"android.support.v7.preference.Preference\">",
-            prefixes = listOf("android/support/"),
+            prefixes = setOf("android/support/"),
             map = mapOf(
                 "android/support/v7/preference/Preference" to "android/test/pref/Preference"
             )
@@ -182,7 +183,7 @@
             expectedXml =
                 "<android.support.v7.preference.Preference>\n" +
                 "</android.support.v7.preference.Preference>",
-            prefixes = listOf("android/support/"),
+            prefixes = setOf("android/support/"),
             typesMap = mapOf(
                 "android/support/v7/preference/Preference"
                     to "android/support/v7/preference/Preference"
@@ -216,7 +217,7 @@
                 "</android.test.pref.Preference>\n" +
                 "\n" +
                 "<android.test.pref.ListPref/>",
-            prefixes = listOf(
+            prefixes = setOf(
                 "android/support/v7/",
                 "android/support/v14/"
             ),
@@ -245,7 +246,7 @@
                 "          package=\"androidx.preference\">\n" +
                 "    <uses-sdk android:minSdkVersion=\"14\"/>\n" +
                 "</manifest>",
-            prefixes = listOf(
+            prefixes = setOf(
             ),
             typesMap = mapOf(
             ),
@@ -262,16 +263,16 @@
     @Test fun manifestFile_packageRewrite_chooseBasedOnArtifactName() {
         testRewrite(
             givenXml =
-            "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
                 "          package=\"androidx.preference\">\n" +
                 "    <uses-sdk android:minSdkVersion=\"14\"/>\n" +
                 "</manifest>",
             expectedXml =
-            "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
                 "          package=\"android.support.v14.preference\">\n" +
                 "    <uses-sdk android:minSdkVersion=\"14\"/>\n" +
                 "</manifest>",
-            prefixes = listOf(
+            prefixes = setOf(
             ),
             typesMap = mapOf(
             ),
@@ -293,7 +294,7 @@
 
     private fun testRewriteToTheSame(
         givenAndExpectedXml: String,
-        prefixes: List<String>,
+        prefixes: Set<String>,
         map: Map<String, String>,
         errorsExpected: Boolean = false
     ) {
@@ -304,7 +305,7 @@
     private fun testRewrite(
         givenXml: String,
         expectedXml: String,
-        prefixes: List<String>,
+        prefixes: Set<String>,
         typesMap: Map<String, String>,
         packageMap: PackageMap = PackageMap.EMPTY,
         rewritingSupportLib: Boolean = false,
@@ -323,8 +324,8 @@
         val typeMap = TypesMap(typesMap.map { JavaType(it.key) to JavaType(it.value) }.toMap())
         val config = Config(
             restrictToPackagePrefixes = prefixes,
-            rewriteRules = emptyList(),
-            slRules = emptyList(),
+            rulesMap = RewriteRulesMap.EMPTY,
+            slRules = emptySet(),
             pomRewriteRules = emptySet(),
             typesMap = typeMap,
             proGuardMap = ProGuardTypesMap.EMPTY,
@@ -333,7 +334,7 @@
         val context = TransformationContext(
             config,
             rewritingSupportLib = rewritingSupportLib,
-            useIdentityIfTypeIsMissing = false)
+            useFallbackIfTypeIsMissing = false)
         context.libraryName = libraryName
         val processor = XmlResourcesTransformer(context)
         val fileName = if (isManifestFile) {
diff --git a/jetifier/jetifier/core/src/test/resources/changeDetectionTest/testPreference.class b/jetifier/jetifier/processor/src/test/resources/changeDetectionTest/testPreference.class
similarity index 100%
rename from jetifier/jetifier/core/src/test/resources/changeDetectionTest/testPreference.class
rename to jetifier/jetifier/processor/src/test/resources/changeDetectionTest/testPreference.class
Binary files differ
diff --git a/jetifier/jetifier/core/src/test/resources/fileRenameTest/expectedTestLib.zip b/jetifier/jetifier/processor/src/test/resources/fileRenameTest/expectedTestLib.zip
similarity index 100%
rename from jetifier/jetifier/core/src/test/resources/fileRenameTest/expectedTestLib.zip
rename to jetifier/jetifier/processor/src/test/resources/fileRenameTest/expectedTestLib.zip
Binary files differ
diff --git a/jetifier/jetifier/core/src/test/resources/fileRenameTest/expectedTestLibNested.zip b/jetifier/jetifier/processor/src/test/resources/fileRenameTest/expectedTestLibNested.zip
similarity index 100%
rename from jetifier/jetifier/core/src/test/resources/fileRenameTest/expectedTestLibNested.zip
rename to jetifier/jetifier/processor/src/test/resources/fileRenameTest/expectedTestLibNested.zip
Binary files differ
diff --git a/jetifier/jetifier/core/src/test/resources/fileRenameTest/inputTestLib.zip b/jetifier/jetifier/processor/src/test/resources/fileRenameTest/inputTestLib.zip
similarity index 100%
rename from jetifier/jetifier/core/src/test/resources/fileRenameTest/inputTestLib.zip
rename to jetifier/jetifier/processor/src/test/resources/fileRenameTest/inputTestLib.zip
Binary files differ
diff --git a/jetifier/jetifier/core/src/test/resources/fileRenameTest/inputTestLibNested.zip b/jetifier/jetifier/processor/src/test/resources/fileRenameTest/inputTestLibNested.zip
similarity index 100%
rename from jetifier/jetifier/core/src/test/resources/fileRenameTest/inputTestLibNested.zip
rename to jetifier/jetifier/processor/src/test/resources/fileRenameTest/inputTestLibNested.zip
Binary files differ
diff --git a/jetifier/jetifier/core/src/test/resources/pomRefactorTest/pomTest.zip b/jetifier/jetifier/processor/src/test/resources/pomRefactorTest/pomTest.zip
similarity index 100%
rename from jetifier/jetifier/core/src/test/resources/pomRefactorTest/pomTest.zip
rename to jetifier/jetifier/processor/src/test/resources/pomRefactorTest/pomTest.zip
Binary files differ
diff --git a/jetifier/jetifier/source-transformer/rewriteAll.sh b/jetifier/jetifier/source-transformer/rewriteAll.sh
new file mode 100755
index 0000000..c402d5e
--- /dev/null
+++ b/jetifier/jetifier/source-transformer/rewriteAll.sh
@@ -0,0 +1,7 @@
+set -e
+work_path="$PWD"
+
+script_path="$(cd $(dirname $0) && pwd)"
+
+"$script_path/rewriteMake.py"
+"$script_path/rewritePackageNames.py"
diff --git a/jetifier/jetifier/source-transformer/rewriteMake.py b/jetifier/jetifier/source-transformer/rewriteMake.py
new file mode 100755
index 0000000..5954a2f
--- /dev/null
+++ b/jetifier/jetifier/source-transformer/rewriteMake.py
@@ -0,0 +1,112 @@
+#!/usr/bin/python
+
+import csv, subprocess
+
+class StringBuilder(object):
+  def __init__(self, item=None):
+    self.items = []
+    if item is not None:
+      self.add(item)
+
+  def add(self, item):
+    self.items.append(str(item))
+    return self
+
+  def __str__(self):
+    return "".join(self.items)
+
+target_map = """android-slices-builders,androidx.slice_slice-builders
+android-slices-core,androidx.slice_slice-core
+android-slices-view,androidx.slice_slice-view
+android-support-animatedvectordrawable,androidx.vectordrawable_vectordrawable-animated
+android-support-annotations,androidx.annotation_annotation
+android-support-asynclayoutinflater,androidx.asynclayoutinflater_asynclayoutinflater
+android-support-car,androidx.car_car
+android-support-collections,androidx.collection_collection
+android-support-compat,androidx.core_core
+android-support-contentpaging,androidx.contentpaging_contentpaging
+android-support-coordinatorlayout,androidx.coordinatorlayout_coordinatorlayout
+android-support-core-ui,androidx.legacy_legacy-support-core-ui
+android-support-core-utils,androidx.legacy_legacy-support-core-utils
+android-support-cursoradapter,androidx.cursoradapter_cursoradapter
+android-support-customtabs,androidx.browser_browser
+android-support-customview,androidx.customview_customview
+android-support-documentfile,androidx.documentfile_documentfile
+android-support-drawerlayout,androidx.drawerlayout_drawerlayout
+android-support-dynamic-animation,androidx.dynamicanimation_dynamicanimation
+android-support-emoji,androidx.emoji_emoji
+android-support-emoji-appcompat,androidx.emoji_emoji-appcompat
+android-support-emoji-bundled,androidx.emoji_emoji-bundled
+android-support-exifinterface,androidx.exifinterface_exifinterface
+android-support-fragment,androidx.fragment_fragment
+android-support-heifwriter,androidx.heifwriter_heifwriter
+android-support-interpolator,androidx.interpolator_interpolator
+android-support-loader,androidx.loader_loader
+android-support-localbroadcastmanager,androidx.localbroadcastmanager_localbroadcastmanager
+android-support-media-compat,androidx.media_media
+android-support-percent,androidx.percentlayout_percentlayout
+android-support-print,androidx.print_print
+android-support-recommendation,androidx.recommendation_recommendation
+android-support-recyclerview-selection,androidx.recyclerview_recyclerview-selection
+android-support-slidingpanelayout,androidx.slidingpanelayout_slidingpanelayout
+android-support-swiperefreshlayout,androidx.swiperefreshlayout_swiperefreshlayout
+android-support-textclassifier,androidx.textclassifier_textclassifier
+android-support-transition,androidx.transition_transition
+android-support-tv-provider,androidx.tvprovider_tvprovider
+android-support-v13,androidx.legacy_legacy-support-v13
+android-support-v14-preference,androidx.legacy_legacy-preference-v14
+android-support-v17-leanback,androidx.leanback_leanback
+android-support-v17-preference-leanback,androidx.leanback_leanback-preference
+android-support-v4,androidx.legacy_legacy-support-v4
+android-support-v7-appcompat,androidx.appcompat_appcompat
+android-support-v7-cardview,androidx.cardview_cardview
+android-support-v7-gridlayout,androidx.gridlayout_gridlayout
+android-support-v7-mediarouter,androidx.mediarouter_mediarouter
+android-support-v7-palette,androidx.palette_palette
+android-support-v7-preference,androidx.preference_preference
+android-support-v7-recyclerview,androidx.recyclerview_recyclerview
+android-support-vectordrawable,androidx.vectordrawable_vectordrawable
+android-support-viewpager,androidx.viewpager_viewpager
+android-support-wear,androidx.wear_wear
+android-support-webkit,androidx.webkit_webkit
+android-arch-core-common,androidx.arch.core_core-common
+android-arch-core-runtime,androidx.arch.core_core-runtime
+android-arch-lifecycle-common,androidx.lifecycle_lifecycle-common
+android-arch-lifecycle-common-java8,androidx.lifecycle_lifecycle-common-java8
+android-arch-lifecycle-extensions,androidx.lifecycle_lifecycle-extensions
+android-arch-lifecycle-livedata,androidx.lifecycle_lifecycle-livedata
+android-arch-lifecycle-livedata-core,androidx.lifecycle_lifecycle-livedata-core
+android-arch-lifecycle-runtime,androidx.lifecycle_lifecycle-runtime
+android-arch-lifecycle-viewmodel,androidx.lifecycle_lifecycle-viewmodel
+android-arch-paging-common,androidx.paging_paging-common
+android-arch-paging-runtime,androidx.paging_paging-runtime
+android-arch-persistence-db,androidx.sqlite_sqlite
+android-arch-persistence-db-framework,androidx.sqlite_sqlite-framework
+android-arch-room-common,androidx.room_room-common
+android-arch-room-migration,androidx.room_room-migration
+android-arch-room-runtime,androidx.room_room-runtime
+android-arch-room-testing,androidx.room_room-testing
+$(ANDROID_SUPPORT_DESIGN_TARGETS),androidx.design_design"""
+
+reader = csv.reader(target_map.split('\n'), delimiter=',')
+
+rewriterTextBuilder = StringBuilder()
+for row in reader:
+  rewriterTextBuilder.add("s|").add(row[0]).add("|").add(row[1]).add("|g\n")
+scriptPath = "/tmp/jetifier-make-sed-script.txt"
+print("Writing " + scriptPath)
+with open(scriptPath, 'w') as scriptFile:
+  scriptFile.write(str(rewriterTextBuilder))
+
+rewriteCommand = "time find . -name out -prune -o -name .git -prune -o -name .repo -prune -o -iregex '.*\.mk' -print | xargs -n 1 --no-run-if-empty -P 64 sed -i -f /tmp/jetifier-make-sed-script.txt"
+
+print("""
+Will run command:
+
+""" + rewriteCommand + """
+
+""")
+response = raw_input("Ok? [y/n]")
+if response == "y":
+  subprocess.check_output(rewriteCommand, shell=True)
+
diff --git a/jetifier/jetifier/source-transformer/rewritePackageNames.py b/jetifier/jetifier/source-transformer/rewritePackageNames.py
new file mode 100755
index 0000000..5cfea14
--- /dev/null
+++ b/jetifier/jetifier/source-transformer/rewritePackageNames.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+
+# This script does package renamings on source code (and .xml files and build files, etc), as part of migrating code to Jetpack.
+# For example, this script may replace the text
+#   import android.support.annotation.RequiresApi;
+# with
+#   import androidx.annotation.RequiresApi;
+
+# See also b/74074903
+import json
+import os.path
+import subprocess
+
+class StringBuilder(object):
+  def __init__(self, item=None):
+    self.items = []
+    if item is not None:
+      self.add(item)
+
+  def add(self, item):
+    self.items.append(str(item))
+    return self
+
+  def __str__(self):
+    return "".join(self.items)
+
+class ExecutionConfig(object):
+  """Stores configuration about this execution of the package renaming.
+     For example, file paths of the source code.
+     This config could potentially be affected by command-line arguments.
+  """
+  def __init__(self, jetifierConfig, sourceRoots, excludeDirs):
+    self.jetifierConfig = jetifierConfig
+    self.sourceRoots = sourceRoots
+    self.excludeDirs = excludeDirs
+
+
+class SourceRewriteRule(object):
+  def __init__(self, fromName, toName):
+    self.fromName = fromName
+    self.toName = toName
+
+  def serialize(self):
+    return self.fromName + ":" + self.toName
+
+class JetifierConfig(object):
+  """Stores configuration about the renaming itself, such as package rename rules.
+     This config isn't supposed to be affected by command-line arguments.
+  """
+  @staticmethod
+  def parse(filePath):
+    with open(filePath) as f:
+      lines = f.readlines()
+      nonCommentLines = [line for line in lines if not line.strip().startswith("#")]
+      parsed = json.loads("".join(nonCommentLines))
+      return JetifierConfig(parsed)
+    
+  def __init__(self, parsedJson):
+    self.json = parsedJson
+
+  def getTypesMap(self):
+    rules = []
+    for rule in self.json["rules"]:
+      fromName = rule["from"].replace("/", ".").replace("(.*)", "")
+      toName = rule["to"].replace("/", ".").replace("{0}", "")
+      if not toName.startswith("ignore"):
+        rules.append(SourceRewriteRule(fromName, toName))
+
+    return rules
+
+
+def createRewriteCommand(executionConfig):
+  # create command to find source files
+  finderTextBuilder = StringBuilder("find")
+  for sourceRoot in executionConfig.sourceRoots:
+    finderTextBuilder.add(" ").add(sourceRoot)
+  for exclusion in executionConfig.excludeDirs:
+    finderTextBuilder.add(" -name ").add(exclusion).add(" -prune -o")
+  finderTextBuilder.add(" -iregex '.*\.java\|.*\.xml' -print")
+
+  # create command to rewrite one source
+  print("Building sed instructions")
+  rewriterTextBuilder = StringBuilder()
+  rewriteRules = executionConfig.jetifierConfig.getTypesMap()
+  for rule in rewriteRules:
+    rewriterTextBuilder.add("s|").add(rule.fromName).add("|").add(rule.toName).add("|g\n")
+  scriptPath = "/tmp/jetifier-sed-script.txt"
+  print("Writing " + scriptPath)
+  with open(scriptPath, 'w') as scriptFile:
+    scriptFile.write(str(rewriterTextBuilder))
+  
+  # create the command to do the rewrites
+  fullCommand = "time " + str(finderTextBuilder) + " | xargs -n 1 --no-run-if-empty -P 64 sed -i -f " + scriptPath
+
+  return fullCommand  
+
+def processConfig(executionConfig):
+  print("Building rewrite command")
+  rewriteCommand = createRewriteCommand(executionConfig)
+  commandLength = len(rewriteCommand)
+  print("""
+Will run command:
+
+""" + rewriteCommand + """
+
+""")
+  response = raw_input("Ok? [y/n]")
+  if response == "y":
+    subprocess.check_output(rewriteCommand, shell=True)  
+
+
+def main():
+  pathOfThisFile = os.path.realpath(__file__)
+  jetifierPath = os.path.abspath(os.path.join(pathOfThisFile, "..", ".."))
+
+  jetifierConfigPath = os.path.join(jetifierPath, "core", "src", "main", "resources", "default.config")
+  print("Parsing " + jetifierConfigPath)
+  jetifierConfig = JetifierConfig.parse(jetifierConfigPath)
+
+  sourceRoot = os.getcwd()
+  excludeDirs = ["out", ".git", ".repo"]
+
+  executionConfig = ExecutionConfig(jetifierConfig, [sourceRoot], excludeDirs)
+
+  processConfig(executionConfig)
+
+main()
+
+
diff --git a/jetifier/jetifier/standalone/build.gradle b/jetifier/jetifier/standalone/build.gradle
index eed727c..ded5432 100644
--- a/jetifier/jetifier/standalone/build.gradle
+++ b/jetifier/jetifier/standalone/build.gradle
@@ -19,10 +19,10 @@
     id("application")
 }
 
-mainClassName = "android.support.tools.jetifier.standalone.MainKt"
+mainClassName = "com.android.tools.build.jetifier.standalone.Main"
 
 dependencies {
-    compile project(':jetifier-core')
+    compile project(':jetifier-processor')
     compile group: 'commons-cli', name: 'commons-cli', version: '1.3.1'
 }
 
diff --git a/jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/Main.kt b/jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/Main.kt
similarity index 82%
rename from jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/Main.kt
rename to jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/Main.kt
index e426621..132b90c 100644
--- a/jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/Main.kt
+++ b/jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/Main.kt
@@ -1,26 +1,26 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 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
+ *      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
+ * limitations under the License.
  */
 
-package android.support.tools.jetifier.standalone
+package com.android.tools.build.jetifier.standalone
 
-import android.support.tools.jetifier.core.FileMapping
-import android.support.tools.jetifier.core.Processor
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.config.ConfigParser
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.config.ConfigParser
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.FileMapping
+import com.android.tools.build.jetifier.processor.Processor
 import org.apache.commons.cli.CommandLine
 import org.apache.commons.cli.DefaultParser
 import org.apache.commons.cli.HelpFormatter
@@ -39,21 +39,21 @@
         val OPTIONS = Options()
         val OPTION_INPUT = createOption("i", "Input libraries paths", multiple = true)
         val OPTION_OUTPUT_DIR = createOption("outputdir", "Output directory path",
-                isRequired = false)
+            isRequired = false)
         val OPTION_OUTPUT_FILE = createOption("outputfile", "Output file", isRequired = false)
         val OPTION_CONFIG = createOption("c", "Input config path", isRequired = false)
         val OPTION_LOG_LEVEL = createOption("l", "Logging level. debug, verbose, error, info " +
-                "(default)", isRequired = false)
+            "(default)", isRequired = false)
         val OPTION_REVERSED = createOption("r", "Run reversed process", hasArgs = false,
-                isRequired = false)
+            isRequired = false)
         val OPTION_REWRITE_SUPPORT_LIB = createOption("s", "If set, all libraries being rewritten" +
-                " are assumed to be part of Support Library. Otherwise only general dependencies " +
-                " are expected.", hasArgs = false, isRequired = false)
+            " are assumed to be part of Support Library. Otherwise only general dependencies " +
+            " are expected.", hasArgs = false, isRequired = false)
         val OPTION_STRICT = createOption("strict",
-                "Don't fallback in case rules are missing", hasArgs = false, isRequired = false)
+            "Don't fallback in case rules are missing", hasArgs = false, isRequired = false)
         val OPTION_REBUILD_TOP_OF_TREE = createOption("rebuildTopOfTree",
-                "Rebuild the zip of maven distribution according to the generated pom file",
-                hasArgs = false, isRequired = false)
+            "Rebuild the zip of maven distribution according to the generated pom file",
+            hasArgs = false, isRequired = false)
 
         private fun createOption(
             argName: String,
@@ -70,6 +70,10 @@
             OPTIONS.addOption(op)
             return op
         }
+
+        @JvmStatic fun main(args: Array<String>) {
+            Main().run(args)
+        }
     }
 
     fun run(args: Array<String>) {
@@ -156,6 +160,3 @@
     }
 }
 
-fun main(args: Array<String>) {
-    Main().run(args)
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/TopOfTreeBuilder.kt b/jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/TopOfTreeBuilder.kt
similarity index 76%
rename from jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/TopOfTreeBuilder.kt
rename to jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/TopOfTreeBuilder.kt
index 893ee65..aac3fc2 100644
--- a/jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/TopOfTreeBuilder.kt
+++ b/jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/TopOfTreeBuilder.kt
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.support.tools.jetifier.standalone
+package com.android.tools.build.jetifier.standalone
 
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.archive.ArchiveItemVisitor
-import android.support.tools.jetifier.core.transform.pom.PomDocument
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
+import com.android.tools.build.jetifier.processor.transform.pom.PomDocument
 import java.io.File
 import java.nio.file.Paths
 import java.security.MessageDigest
@@ -44,8 +44,8 @@
         // Find archives
         val archivesFilter = FileFilter({
             return@FileFilter it.fileName.endsWith(".aar")
-                    || (it.fileName.endsWith("jar")
-                    && !it.fileName.contains("sources") && !it.fileName.contains("javadoc"))
+                || (it.fileName.endsWith("jar")
+                && !it.fileName.contains("sources") && !it.fileName.contains("javadoc"))
         })
         archive.accept(archivesFilter)
         val libFiles = archivesFilter.files
@@ -54,10 +54,15 @@
         val newFiles = mutableSetOf<ArchiveFile>()
         pomFiles.forEach {
             pomFile -> run {
-            val name = pomFile.relativePath.toFile().nameWithoutExtension
-            val artifactFile = libFiles.first { it.relativePath.toString().contains(name) }
-            process(pomFile, artifactFile, newFiles)
-        }
+                val name = pomFile.relativePath.toFile().nameWithoutExtension
+                val nameAar = name + ".aar"
+                val nameJar = name + ".jar"
+                val artifactFile = libFiles.first {
+                    it.fileName == nameAar || it.fileName == nameJar
+                }
+
+                process(pomFile, artifactFile, newFiles)
+            }
         }
 
         // Write the result
@@ -78,7 +83,7 @@
         val baseFileName = "${pomDep.artifactId}-${pomDep.version}"
 
         val artifactDir = Paths.get(
-                DIR_PREFIX, groupAsPath, pomDep.artifactId, pomDep.version!!.toUpperCase())
+            DIR_PREFIX, groupAsPath, pomDep.artifactId, pomDep.version!!.toUpperCase())
         val newLibFilePath = Paths.get(artifactDir.toString(), "$baseFileName.$packaging")
         val newPomFilePath = Paths.get(artifactDir.toString(), "$baseFileName.pom")
 
@@ -98,7 +103,7 @@
         val md = MessageDigest.getInstance(hashType)
         val result = md.digest(file.data)
         return ArchiveFile(Paths.get(
-                file.relativePath.toString() + "." + hashType.toLowerCase()), result)
+            file.relativePath.toString() + "." + hashType.toLowerCase()), result)
     }
 
     private class FileFilter(private val filter: (ArchiveFile) -> Boolean) : ArchiveItemVisitor {
diff --git a/leanback-preference/api21/androidx/leanback/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java b/leanback-preference/api21/androidx/leanback/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
index ebcad93..0f74626 100644
--- a/leanback-preference/api21/androidx/leanback/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
+++ b/leanback-preference/api21/androidx/leanback/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
@@ -44,4 +44,6 @@
     }
 
 
+    private LeanbackPreferenceFragmentTransitionHelperApi21() {
+    }
 }
diff --git a/leanback/api/current.txt b/leanback/api/current.txt
index 138509e..dd8202e 100644
--- a/leanback/api/current.txt
+++ b/leanback/api/current.txt
@@ -1703,7 +1703,7 @@
   }
 
   public class FocusHighlightHelper {
-    ctor public FocusHighlightHelper();
+    ctor public deprecated FocusHighlightHelper();
     method public static void setupBrowseItemFocusHighlight(androidx.leanback.widget.ItemBridgeAdapter, int, boolean);
     method public static deprecated void setupHeaderItemFocusHighlight(androidx.leanback.widget.VerticalGridView);
     method public static deprecated void setupHeaderItemFocusHighlight(androidx.leanback.widget.VerticalGridView, boolean);
@@ -2113,6 +2113,7 @@
     method protected void onCreate(androidx.leanback.widget.ItemBridgeAdapter.ViewHolder);
     method public final androidx.recyclerview.widget.RecyclerView.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
     method protected void onDetachedFromWindow(androidx.leanback.widget.ItemBridgeAdapter.ViewHolder);
+    method public final boolean onFailedToRecycleView(androidx.recyclerview.widget.RecyclerView.ViewHolder);
     method protected void onUnbind(androidx.leanback.widget.ItemBridgeAdapter.ViewHolder);
     method public final void onViewAttachedToWindow(androidx.recyclerview.widget.RecyclerView.ViewHolder);
     method public final void onViewDetachedFromWindow(androidx.recyclerview.widget.RecyclerView.ViewHolder);
diff --git a/leanback/api21/androidx/leanback/transition/TranslationAnimationCreator.java b/leanback/api21/androidx/leanback/transition/TranslationAnimationCreator.java
index 7ad7faa..455a60d 100644
--- a/leanback/api21/androidx/leanback/transition/TranslationAnimationCreator.java
+++ b/leanback/api21/androidx/leanback/transition/TranslationAnimationCreator.java
@@ -154,4 +154,6 @@
         }
     }
 
+    private TranslationAnimationCreator() {
+    }
 }
diff --git a/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java b/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
index d4a751f..99e6fc7 100644
--- a/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
+++ b/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
@@ -71,6 +71,7 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
@@ -4648,6 +4649,58 @@
     }
 
     @Test
+    public void testAccessibilityBug77292190() throws Throwable {
+        Intent intent = new Intent();
+        final int numItems = 1000;
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
+        intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.item_full_width);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS,  1000);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        initActivity(intent);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 1;
+
+        setSelectedPosition(0);
+
+        final RecyclerViewAccessibilityDelegate delegateCompat = mGridView
+                .getCompatAccessibilityDelegate();
+        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
+            }
+        });
+        if (Build.VERSION.SDK_INT >= 21) {
+            assertFalse(hasAction(info,
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP));
+            assertTrue(hasAction(info,
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN));
+        } else {
+            assertFalse(hasAction(info, AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD));
+            assertTrue(hasAction(info, AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD));
+        }
+
+        setSelectedPosition(numItems - 1);
+        final AccessibilityNodeInfoCompat info2 = AccessibilityNodeInfoCompat.obtain();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info2);
+            }
+        });
+        if (Build.VERSION.SDK_INT >= 21) {
+            assertTrue(hasAction(info2,
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP));
+            assertFalse(hasAction(info2,
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN));
+        } else {
+            assertTrue(hasAction(info2, AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD));
+            assertFalse(hasAction(info2, AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD));
+        }
+    }
+
+    @Test
     public void testAccessibilityWhenScrollDisabled() throws Throwable {
         Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
@@ -4681,12 +4734,18 @@
         });
         assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState());
     }
-
     private boolean hasAction(AccessibilityNodeInfoCompat info, Object action) {
         if (Build.VERSION.SDK_INT >= 21) {
             AccessibilityNodeInfoCompat.AccessibilityActionCompat convertedAction =
                     (AccessibilityNodeInfoCompat.AccessibilityActionCompat) action;
-            return ((info.getActions() & convertedAction.getId()) != 0);
+            List<AccessibilityNodeInfoCompat.AccessibilityActionCompat> actions =
+                    info.getActionList();
+            for (int i = 0; i < actions.size(); i++) {
+                if (actions.get(i).getId() == convertedAction.getId()) {
+                    return true;
+                }
+            }
+            return false;
         } else {
             int convertedAction = (int) action;
             return ((info.getActions() & convertedAction) != 0);
diff --git a/leanback/src/androidTest/java/androidx/leanback/widget/ItemBridgeAdapterTest.java b/leanback/src/androidTest/java/androidx/leanback/widget/ItemBridgeAdapterTest.java
new file mode 100644
index 0000000..40c741c
--- /dev/null
+++ b/leanback/src/androidTest/java/androidx/leanback/widget/ItemBridgeAdapterTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2018 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.leanback.widget;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class ItemBridgeAdapterTest {
+    private static final int sViewWidth = 100;
+    private static final int sViewHeight = 100;
+
+    public static class BasePresenter extends Presenter {
+
+        @Override
+        public ViewHolder onCreateViewHolder(ViewGroup parent) {
+            View view = new View(parent.getContext());
+            view.setLayoutParams(new ViewGroup.LayoutParams(sViewWidth, sViewHeight));
+            return new ViewHolder(view);
+        }
+
+        @Override
+        public void onBindViewHolder(ViewHolder viewHolder, Object item) {
+        }
+
+        @Override
+        public void onUnbindViewHolder(ViewHolder viewHolder) {
+
+        }
+    }
+
+    private ItemBridgeAdapter mItemBridgeAdapter;
+
+    private ArrayObjectAdapter mAdapter;
+
+    private RecyclerView mRecyclerView;
+
+    private Presenter mPresenter;
+
+    @Before
+    public void setup() {
+        mPresenter = spy(BasePresenter.class);
+
+        mItemBridgeAdapter = new ItemBridgeAdapter();
+        mAdapter = new ArrayObjectAdapter(mPresenter);
+        mAdapter.setItems(populateData(), null);
+        mItemBridgeAdapter.setAdapter(mAdapter);
+
+        Context context = InstrumentationRegistry.getTargetContext();
+        mRecyclerView = new RecyclerView(context);
+        mRecyclerView.setItemViewCacheSize(0);
+        mRecyclerView.setLayoutManager(new LinearLayoutManager(context,
+                LinearLayoutManager.VERTICAL, false));
+        mRecyclerView.setHasFixedSize(false); // force layout items in layout pass
+        mRecyclerView.setAdapter(mItemBridgeAdapter);
+        measureAndLayoutRecycleView(mRecyclerView);
+    }
+
+    List populateData() {
+        List data = new ArrayList();
+        for (int i = 0; i < 10000; i++) {
+            data.add(i);
+        }
+        return data;
+    }
+
+    static void measureAndLayoutRecycleView(RecyclerView recyclerView) {
+        measureAndLayout(recyclerView, 1920, 1080);
+    }
+
+    static void measureAndLayout(View view, int expectedWidth, int expectedHeight) {
+        view.measure(View.MeasureSpec.makeMeasureSpec(expectedWidth, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(expectedHeight, View.MeasureSpec.EXACTLY));
+        view.layout(0, 0, expectedWidth, expectedHeight);
+    }
+
+    @Test
+    public void onCreateAndOnBind() {
+        int childCount = mRecyclerView.getChildCount();
+        assertTrue(childCount > 0);
+        // Assert number of invokes of onCreateViewHolder and onBindViewHolder.
+        Mockito.verify(mPresenter, times(childCount)).onCreateViewHolder(any(ViewGroup.class));
+        Mockito.verify(mPresenter, times(childCount))
+                .onBindViewHolder(any(Presenter.ViewHolder.class), any());
+    }
+    @Test
+    public void onUnbind() {
+        assertTrue(mRecyclerView.getChildCount() > 0);
+        // When scroll one item offscreen, assert onUnbindViewHolder called once.
+        mRecyclerView.scrollBy(0, mRecyclerView.getChildAt(0).getHeight());
+        Mockito.verify(mPresenter, times(1))
+                .onUnbindViewHolder(any(Presenter.ViewHolder.class));
+    }
+
+    @Test
+    public void onUnbindWithTransientState() {
+        assertTrue(mRecyclerView.getChildCount() > 0);
+        // Set TransientState to true to simulate the view is running custom ViewPropertyAnimation.
+        mRecyclerView.getChildAt(0).setHasTransientState(true);
+        // When scroll one item with custom animation offscreen, assert onUnbindViewHolder
+        // was called once when ViewHolder is still having transient state.
+        mRecyclerView.scrollBy(0, mRecyclerView.getChildAt(0).getHeight());
+        Mockito.verify(mPresenter, times(1))
+                .onUnbindViewHolder(any(Presenter.ViewHolder.class));
+    }
+}
diff --git a/leanback/src/androidTest/res/layout/item_full_width.xml b/leanback/src/androidTest/res/layout/item_full_width.xml
new file mode 100644
index 0000000..75bf9b2
--- /dev/null
+++ b/leanback/src/androidTest/res/layout/item_full_width.xml
@@ -0,0 +1,32 @@
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="400dp"
+    >
+    <TextView
+        android:layout_alignParentTop="true"
+        android:text="unfocusable text"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"/>
+    <Button
+        android:layout_alignParentBottom="true"
+        android:text="button"
+        android:focusable="true"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"/>
+</RelativeLayout>
diff --git a/leanback/src/main/java/androidx/leanback/app/FragmentUtil.java b/leanback/src/main/java/androidx/leanback/app/FragmentUtil.java
index 8d042e8..9ff380e 100644
--- a/leanback/src/main/java/androidx/leanback/app/FragmentUtil.java
+++ b/leanback/src/main/java/androidx/leanback/app/FragmentUtil.java
@@ -26,4 +26,7 @@
         }
         return fragment.getActivity();
     }
+
+    private FragmentUtil() {
+    }
 }
diff --git a/leanback/src/main/java/androidx/leanback/app/PermissionHelper.java b/leanback/src/main/java/androidx/leanback/app/PermissionHelper.java
index 73d1a05..a7e56bb 100644
--- a/leanback/src/main/java/androidx/leanback/app/PermissionHelper.java
+++ b/leanback/src/main/java/androidx/leanback/app/PermissionHelper.java
@@ -32,4 +32,6 @@
         }
     }
 
+    private PermissionHelper() {
+    }
 }
diff --git a/leanback/src/main/java/androidx/leanback/transition/LeanbackTransitionHelper.java b/leanback/src/main/java/androidx/leanback/transition/LeanbackTransitionHelper.java
index a259050..9693013 100644
--- a/leanback/src/main/java/androidx/leanback/transition/LeanbackTransitionHelper.java
+++ b/leanback/src/main/java/androidx/leanback/transition/LeanbackTransitionHelper.java
@@ -55,4 +55,7 @@
         slide.addTarget(R.id.browse_title_group);
         return slide;
     }
+
+    private LeanbackTransitionHelper() {
+    }
 }
diff --git a/leanback/src/main/java/androidx/leanback/transition/TransitionHelper.java b/leanback/src/main/java/androidx/leanback/transition/TransitionHelper.java
index 084e1e7..4d8c877 100644
--- a/leanback/src/main/java/androidx/leanback/transition/TransitionHelper.java
+++ b/leanback/src/main/java/androidx/leanback/transition/TransitionHelper.java
@@ -464,4 +464,7 @@
             }
         }
     }
+
+    private TransitionHelper() {
+    }
 }
diff --git a/leanback/src/main/java/androidx/leanback/widget/BackgroundHelper.java b/leanback/src/main/java/androidx/leanback/widget/BackgroundHelper.java
index c50399a..f72ff44 100644
--- a/leanback/src/main/java/androidx/leanback/widget/BackgroundHelper.java
+++ b/leanback/src/main/java/androidx/leanback/widget/BackgroundHelper.java
@@ -40,4 +40,7 @@
             view.setBackground(drawable);
         }
     }
+
+    private BackgroundHelper() {
+    }
 }
diff --git a/leanback/src/main/java/androidx/leanback/widget/FocusHighlightHelper.java b/leanback/src/main/java/androidx/leanback/widget/FocusHighlightHelper.java
index ca5a4fd..7ca9e6f 100644
--- a/leanback/src/main/java/androidx/leanback/widget/FocusHighlightHelper.java
+++ b/leanback/src/main/java/androidx/leanback/widget/FocusHighlightHelper.java
@@ -339,4 +339,10 @@
         }
 
     }
+
+    /** @deprecated This type should not be instantiated as it contains only static methods. */
+    @Deprecated
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    public FocusHighlightHelper() {
+    }
 }
diff --git a/leanback/src/main/java/androidx/leanback/widget/ForegroundHelper.java b/leanback/src/main/java/androidx/leanback/widget/ForegroundHelper.java
index 3742c63..57c2741 100644
--- a/leanback/src/main/java/androidx/leanback/widget/ForegroundHelper.java
+++ b/leanback/src/main/java/androidx/leanback/widget/ForegroundHelper.java
@@ -25,4 +25,7 @@
             view.setForeground(drawable);
         }
     }
+
+    private ForegroundHelper() {
+    }
 }
diff --git a/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java b/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java
index 6ef40e5..62b7bb2 100644
--- a/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java
+++ b/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java
@@ -3427,8 +3427,9 @@
         if (vh == null) {
             return false;
         }
-        return vh.itemView.getLeft() >= 0 && vh.itemView.getRight() < mBaseGridView.getWidth()
-                && vh.itemView.getTop() >= 0 && vh.itemView.getBottom() < mBaseGridView.getHeight();
+        return vh.itemView.getLeft() >= 0 && vh.itemView.getRight() <= mBaseGridView.getWidth()
+                && vh.itemView.getTop() >= 0 && vh.itemView.getBottom()
+                <= mBaseGridView.getHeight();
     }
 
     boolean canScrollTo(View view) {
diff --git a/leanback/src/main/java/androidx/leanback/widget/ItemAlignmentFacetHelper.java b/leanback/src/main/java/androidx/leanback/widget/ItemAlignmentFacetHelper.java
index 6e80100..5ca7458 100644
--- a/leanback/src/main/java/androidx/leanback/widget/ItemAlignmentFacetHelper.java
+++ b/leanback/src/main/java/androidx/leanback/widget/ItemAlignmentFacetHelper.java
@@ -105,4 +105,6 @@
         return alignPos;
     }
 
+    private ItemAlignmentFacetHelper() {
+    }
 }
diff --git a/leanback/src/main/java/androidx/leanback/widget/ItemBridgeAdapter.java b/leanback/src/main/java/androidx/leanback/widget/ItemBridgeAdapter.java
index faa5a69..eaa2787 100644
--- a/leanback/src/main/java/androidx/leanback/widget/ItemBridgeAdapter.java
+++ b/leanback/src/main/java/androidx/leanback/widget/ItemBridgeAdapter.java
@@ -417,6 +417,12 @@
     }
 
     @Override
+    public final boolean onFailedToRecycleView(RecyclerView.ViewHolder holder) {
+        onViewRecycled(holder);
+        return false;
+    }
+
+    @Override
     public final void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
         ViewHolder viewHolder = (ViewHolder) holder;
         onAttachedToWindow(viewHolder);
diff --git a/leanback/src/main/java/androidx/leanback/widget/RoundedRectHelperApi21.java b/leanback/src/main/java/androidx/leanback/widget/RoundedRectHelperApi21.java
index 031b980..470f645 100644
--- a/leanback/src/main/java/androidx/leanback/widget/RoundedRectHelperApi21.java
+++ b/leanback/src/main/java/androidx/leanback/widget/RoundedRectHelperApi21.java
@@ -59,4 +59,7 @@
         }
         view.setClipToOutline(clip);
     }
+
+    private RoundedRectHelperApi21() {
+    }
 }
diff --git a/leanback/src/main/java/androidx/leanback/widget/ShadowHelperApi21.java b/leanback/src/main/java/androidx/leanback/widget/ShadowHelperApi21.java
index 0d97cf9..e32d165 100644
--- a/leanback/src/main/java/androidx/leanback/widget/ShadowHelperApi21.java
+++ b/leanback/src/main/java/androidx/leanback/widget/ShadowHelperApi21.java
@@ -58,4 +58,7 @@
         ShadowImpl impl = (ShadowImpl) object;
         impl.mShadowContainer.setZ(impl.mNormalZ + level * (impl.mFocusedZ - impl.mNormalZ));
     }
+
+    private ShadowHelperApi21() {
+    }
 }
diff --git a/leanback/src/main/java/androidx/leanback/widget/Util.java b/leanback/src/main/java/androidx/leanback/widget/Util.java
index aa81715..bda8ac6 100644
--- a/leanback/src/main/java/androidx/leanback/widget/Util.java
+++ b/leanback/src/main/java/androidx/leanback/widget/Util.java
@@ -43,4 +43,7 @@
         }
         return false;
     }
-}
\ No newline at end of file
+
+    private Util() {
+    }
+}
diff --git a/leanback/src/main/java/androidx/leanback/widget/picker/PickerUtility.java b/leanback/src/main/java/androidx/leanback/widget/picker/PickerUtility.java
index 5347971..c520646 100644
--- a/leanback/src/main/java/androidx/leanback/widget/picker/PickerUtility.java
+++ b/leanback/src/main/java/androidx/leanback/widget/picker/PickerUtility.java
@@ -95,4 +95,7 @@
             return newCalendar;
         }
     }
+
+    private PickerUtility() {
+    }
 }
diff --git a/leanback/src/main/res/values-as/strings.xml b/leanback/src/main/res/values-as/strings.xml
new file mode 100644
index 0000000..eb3b2b0
--- /dev/null
+++ b/leanback/src/main/res/values-as/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="8126335323963415494">"নেভিগেশ্বন মেনু"</string>
+    <string name="orb_search_action" msgid="7534843523462177008">"সন্ধান সম্পৰ্কীয় কাৰ্য"</string>
+    <string name="lb_search_bar_hint" msgid="4819380969103509861">"সন্ধান"</string>
+    <string name="lb_search_bar_hint_speech" msgid="2795474673510974502">"সন্ধান কৰিবলৈ কথা কওক"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="7453744869467668159">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> সন্ধান কৰক"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="5851694095153624617">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> সন্ধান কৰিবলৈ কথা কওক"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="2721825378927619928">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="6173753802428649303">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="1590369760862605402">"প্লে কৰক"</string>
+    <string name="lb_playback_controls_pause" msgid="1769131316742618433">"পজ কৰক"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8966769845721269304">"ফাষ্ট ফৰৱাৰ্ড"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="801276177839339511">"ফাষ্ট ফৰৱার্ড কৰক %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="1412664391757869774">"ৰিৱাইণ্ড কৰক"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="8651612807713092781">"ৰিৱাইণ্ড কৰক %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="4877009494447817003">"পৰৱৰ্তীটোলৈ এৰি যাওক"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="3147124289285911980">"আগৰটোলৈ এৰি যাওক"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2827883329510404797">"অধিক কাৰ্য"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="8332816524260995892">"থাম্ব আপ বাছনি নাইকিয়া কৰক"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1038344559734334272">"থাম্ব আপ বাছনি কৰক"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="5075744418630733006">"থাম্ব ডাউন বাছনি নাইকিয়া কৰক"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2847309435442474470">"থাম্ব ডাউন বাছনি কৰক"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="5812341701962930499">"একো পুনৰাই প্লে নকৰিব"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="5164826436271322261">"সকলো পুনৰাই প্লে কৰক"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="7675097479246139440">"এটা পুনৰাই প্লে কৰক"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="7809089255981448519">"সান-মিহলি সক্ষম কৰক"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8182435535948303910">"সান-মিহলি অক্ষম কৰক"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="1862669142355962638">"উচ্চ মানৰ প্লেবেক সক্ষম কৰক"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="3000046054608531995">"উচ্চ মান অক্ষম কৰক"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="3934392140182327163">"ছাব-টাইটেল সক্ষম কৰক"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="5508271941331836786">"ছাব-টাইটেল অক্ষম কৰক"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="8800305194045609275">"চিত্ৰৰ ভিতৰত চিত্ৰ ম\'ড আৰম্ভ কৰক"</string>
+    <string name="lb_playback_time_separator" msgid="6549544638083578695">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="7794717158616536936">"মিডিয়াৰ নিয়ন্ত্ৰণসমূহ দেখুওৱা হ\'ল"</string>
+    <string name="lb_playback_controls_hidden" msgid="619396299825306757">"মিডিয়াৰ নিয়ন্ত্ৰণসমূহ লুকুৱাই ৰখা হৈছে, দেখুওৱাবলৈ ডি-পেডত টিপক"</string>
+    <string name="lb_guidedaction_finish_title" msgid="7747913934287176843">"সমাপ্ত"</string>
+    <string name="lb_guidedaction_continue_title" msgid="1122271825827282965">"অব্যাহত ৰাখক"</string>
+    <string name="lb_media_player_error" msgid="8748646000835486516">"MediaPlayer ত্ৰুটি ক\'ড %1$d, অতিৰিক্ত %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="7674487829030291492">"আৰম্ভ কৰক"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="4213611627196077555">"পৰৱৰ্তী"</string>
+</resources>
diff --git a/leanback/src/main/res/values-es/strings.xml b/leanback/src/main/res/values-es/strings.xml
index 5d85c92..53e6797 100644
--- a/leanback/src/main/res/values-es/strings.xml
+++ b/leanback/src/main/res/values-es/strings.xml
@@ -47,7 +47,7 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="3000046054608531995">"Inhabilitar alta calidad"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="3934392140182327163">"Habilitar subtítulos"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="5508271941331836786">"Inhabilitar subtítulos"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="8800305194045609275">"Activar modo pantalla en pantalla"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="8800305194045609275">"Activar modo imagen en imagen"</string>
     <string name="lb_playback_time_separator" msgid="6549544638083578695">"/"</string>
     <string name="lb_playback_controls_shown" msgid="7794717158616536936">"Controles multimedia mostrados"</string>
     <string name="lb_playback_controls_hidden" msgid="619396299825306757">"Controles multimedia ocultos (pulsa la cruceta para mostrarlos)"</string>
diff --git a/leanback/src/main/res/values-or/strings.xml b/leanback/src/main/res/values-or/strings.xml
new file mode 100644
index 0000000..ce6d1ea
--- /dev/null
+++ b/leanback/src/main/res/values-or/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="lb_navigation_menu_contentDescription" msgid="8126335323963415494">"ନେଭିଗେଶନ୍ ମେନୁ"</string>
+    <string name="orb_search_action" msgid="7534843523462177008">"ଖୋଜିବା କାମ"</string>
+    <string name="lb_search_bar_hint" msgid="4819380969103509861">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
+    <string name="lb_search_bar_hint_speech" msgid="2795474673510974502">"ଖୋଜିବା ପାଇଁ କୁହନ୍ତୁ"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="7453744869467668159">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ଖୋଜନ୍ତୁ"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="5851694095153624617">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ଖୋଜିବା ପାଇଁ କୁହନ୍ତୁ"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="2721825378927619928">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="6173753802428649303">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="1590369760862605402">"ଚଲାନ୍ତୁ"</string>
+    <string name="lb_playback_controls_pause" msgid="1769131316742618433">"ପଜ୍‍ କରନ୍ତୁ"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8966769845721269304">"ଫାଷ୍ଟ ଫର୍‌ୱାର୍ଡ"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="801276177839339511">"%1$dX ବେଗରେ ଫାଷ୍ଟ ଫରୱାର୍ଡ"</string>
+    <string name="lb_playback_controls_rewind" msgid="1412664391757869774">"ରିୱାଇଣ୍ଡ"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="8651612807713092781">"%1$dX ବେଗରେ ରିୱାଇଣ୍ଡ କରନ୍ତୁ"</string>
+    <string name="lb_playback_controls_skip_next" msgid="4877009494447817003">"ପରବର୍ତ୍ତୀକୁ ଯାଆନ୍ତୁ"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="3147124289285911980">"ପୂର୍ବଟିକୁ ଛାଡ଼ିଦିଅନ୍ତୁ"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2827883329510404797">"ଅଧିକ ଗତିବିଧି"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="8332816524260995892">"ପସନ୍ଦକୁ ଚୟନ କରନ୍ତୁ ନାହିଁ"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1038344559734334272">"ପସନ୍ଦକୁ ଚୟନ କରନ୍ତୁ"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="5075744418630733006">"ପସନ୍ଦହୀନକୁ ଚୟନ କରନ୍ତୁ ନାହିଁ"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2847309435442474470">"ପସନ୍ଦହୀନକୁ ଚୟନ କରନ୍ତୁ"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="5812341701962930499">"କୌଣସିଟି ଦୋହରାନ୍ତୁ ନାହିଁ"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="5164826436271322261">"ସବୁଗୁଡ଼ିକୁ ଦୋହରାନ୍ତୁ"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="7675097479246139440">"ଗୋଟିଏ ଦୋହରାନ୍ତୁ"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="7809089255981448519">"ଅଦଳବଦଳକୁ ସକ୍ଷମ କରନ୍ତୁ"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8182435535948303910">"ଅଦଳବଦଳକୁ ଅକ୍ଷମ କରନ୍ତୁ"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="1862669142355962638">"ଉଚ୍ଚ କ୍ୱାଲିଟୀକୁ ସକ୍ଷମ କରନ୍ତୁ"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="3000046054608531995">"ଉଚ୍ଚ କ୍ୱାଲିଟୀକୁ ଅକ୍ଷମ କରନ୍ତୁ"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="3934392140182327163">"କ୍ଲୋଜଡ୍‍ କ୍ୟାପ୍ସନିଙ୍ଗକୁ ସକ୍ଷମ କରନ୍ତୁ"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="5508271941331836786">"କ୍ଲୋଜଡ୍‍ କ୍ୟାପ୍ସନିଙ୍ଗକୁ ଅକ୍ଷମ କରନ୍ତୁ"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="8800305194045609275">"ଛବି ଭିତରେ ଛବି ମୋଡ୍‌ରେ ପ୍ରବେଶ କରନ୍ତୁ"</string>
+    <string name="lb_playback_time_separator" msgid="6549544638083578695">"/"</string>
+    <string name="lb_playback_controls_shown" msgid="7794717158616536936">"ମିଡିଆ ନିୟନ୍ତ୍ରଣ ଦେଖାଯାଇଛି"</string>
+    <string name="lb_playback_controls_hidden" msgid="619396299825306757">"ମିଡିଆ ନିୟନ୍ତ୍ରଣ ଲୁଚିଯାଇଛି, ଦେଖାଇବାକୁ ଡି-ପ୍ୟାଡ୍‍ ଦବାନ୍ତୁ"</string>
+    <string name="lb_guidedaction_finish_title" msgid="7747913934287176843">"ସମାପ୍ତ କରନ୍ତୁ"</string>
+    <string name="lb_guidedaction_continue_title" msgid="1122271825827282965">"ଜାରି ରଖନ୍ତୁ"</string>
+    <string name="lb_media_player_error" msgid="8748646000835486516">"MediaPlayer ତ୍ରୁଟି କୋଡ୍‍ %1$d ଅତିରିକ୍ତ %2$d"</string>
+    <string name="lb_onboarding_get_started" msgid="7674487829030291492">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="4213611627196077555">"ପରବର୍ତ୍ତୀ"</string>
+</resources>
diff --git a/lifecycle/common/src/main/java/androidx/lifecycle/Lifecycling.java b/lifecycle/common/src/main/java/androidx/lifecycle/Lifecycling.java
index 47c66c8..aeebf90 100644
--- a/lifecycle/common/src/main/java/androidx/lifecycle/Lifecycling.java
+++ b/lifecycle/common/src/main/java/androidx/lifecycle/Lifecycling.java
@@ -178,4 +178,7 @@
     public static String getAdapterName(String className) {
         return className.replace(".", "_") + "_LifecycleAdapter";
     }
+
+    private Lifecycling() {
+    }
 }
diff --git a/lifecycle/extensions/api/current.txt b/lifecycle/extensions/api/current.txt
index dad4ab8..e8c0334 100644
--- a/lifecycle/extensions/api/current.txt
+++ b/lifecycle/extensions/api/current.txt
@@ -33,9 +33,9 @@
     ctor public deprecated ViewModelProviders.DefaultFactory(android.app.Application);
   }
 
-  public class ViewModelStores {
-    method public static androidx.lifecycle.ViewModelStore of(androidx.fragment.app.FragmentActivity);
-    method public static androidx.lifecycle.ViewModelStore of(androidx.fragment.app.Fragment);
+  public deprecated class ViewModelStores {
+    method public static deprecated androidx.lifecycle.ViewModelStore of(androidx.fragment.app.FragmentActivity);
+    method public static deprecated androidx.lifecycle.ViewModelStore of(androidx.fragment.app.Fragment);
   }
 
 }
diff --git a/lifecycle/extensions/src/main/java/androidx/lifecycle/HolderFragment.java b/lifecycle/extensions/src/main/java/androidx/lifecycle/HolderFragment.java
deleted file mode 100644
index 24fa504..0000000
--- a/lifecycle/extensions/src/main/java/androidx/lifecycle/HolderFragment.java
+++ /dev/null
@@ -1,195 +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 androidx.lifecycle;
-
-import android.app.Activity;
-import android.app.Application.ActivityLifecycleCallbacks;
-import android.os.Bundle;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class HolderFragment extends Fragment implements ViewModelStoreOwner {
-    private static final String LOG_TAG = "ViewModelStores";
-
-    private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();
-
-    /**
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public static final String HOLDER_TAG =
-            "androidx.lifecycle.state.StateProviderHolderFragment";
-
-    private ViewModelStore mViewModelStore = new ViewModelStore();
-
-    public HolderFragment() {
-        setRetainInstance(true);
-    }
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        sHolderFragmentManager.holderFragmentCreated(this);
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        mViewModelStore.clear();
-    }
-
-    @NonNull
-    @Override
-    public ViewModelStore getViewModelStore() {
-        return mViewModelStore;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public static HolderFragment holderFragmentFor(FragmentActivity activity) {
-        return sHolderFragmentManager.holderFragmentFor(activity);
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public static HolderFragment holderFragmentFor(Fragment fragment) {
-        return sHolderFragmentManager.holderFragmentFor(fragment);
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    static class HolderFragmentManager {
-        private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
-        private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();
-
-        private ActivityLifecycleCallbacks mActivityCallbacks =
-                new EmptyActivityLifecycleCallbacks() {
-                    @Override
-                    public void onActivityDestroyed(Activity activity) {
-                        HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
-                        if (fragment != null) {
-                            Log.e(LOG_TAG, "Failed to save a ViewModel for " + activity);
-                        }
-                    }
-                };
-
-        private boolean mActivityCallbacksIsAdded = false;
-
-        private FragmentLifecycleCallbacks mParentDestroyedCallback =
-                new FragmentLifecycleCallbacks() {
-                    @Override
-                    public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) {
-                        super.onFragmentDestroyed(fm, parentFragment);
-                        HolderFragment fragment = mNotCommittedFragmentHolders.remove(
-                                parentFragment);
-                        if (fragment != null) {
-                            Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
-                        }
-                    }
-                };
-
-        void holderFragmentCreated(Fragment holderFragment) {
-            Fragment parentFragment = holderFragment.getParentFragment();
-            if (parentFragment != null) {
-                mNotCommittedFragmentHolders.remove(parentFragment);
-                parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(
-                        mParentDestroyedCallback);
-            } else {
-                mNotCommittedActivityHolders.remove(holderFragment.getActivity());
-            }
-        }
-
-        private static HolderFragment findHolderFragment(FragmentManager manager) {
-            if (manager.isDestroyed()) {
-                throw new IllegalStateException("Can't access ViewModels from onDestroy");
-            }
-
-            Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
-            if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
-                throw new IllegalStateException("Unexpected "
-                        + "fragment instance was returned by HOLDER_TAG");
-            }
-            return (HolderFragment) fragmentByTag;
-        }
-
-        private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
-            HolderFragment holder = new HolderFragment();
-            fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
-            return holder;
-        }
-
-        HolderFragment holderFragmentFor(FragmentActivity activity) {
-            FragmentManager fm = activity.getSupportFragmentManager();
-            HolderFragment holder = findHolderFragment(fm);
-            if (holder != null) {
-                return holder;
-            }
-            holder = mNotCommittedActivityHolders.get(activity);
-            if (holder != null) {
-                return holder;
-            }
-
-            if (!mActivityCallbacksIsAdded) {
-                mActivityCallbacksIsAdded = true;
-                activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
-            }
-            holder = createHolderFragment(fm);
-            mNotCommittedActivityHolders.put(activity, holder);
-            return holder;
-        }
-
-        HolderFragment holderFragmentFor(Fragment parentFragment) {
-            FragmentManager fm = parentFragment.getChildFragmentManager();
-            HolderFragment holder = findHolderFragment(fm);
-            if (holder != null) {
-                return holder;
-            }
-            holder = mNotCommittedFragmentHolders.get(parentFragment);
-            if (holder != null) {
-                return holder;
-            }
-
-            parentFragment.getFragmentManager()
-                    .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
-            holder = createHolderFragment(fm);
-            mNotCommittedFragmentHolders.put(parentFragment, holder);
-            return holder;
-        }
-    }
-}
diff --git a/lifecycle/extensions/src/main/java/androidx/lifecycle/LifecycleDispatcher.java b/lifecycle/extensions/src/main/java/androidx/lifecycle/LifecycleDispatcher.java
index f241276..fa1550b 100644
--- a/lifecycle/extensions/src/main/java/androidx/lifecycle/LifecycleDispatcher.java
+++ b/lifecycle/extensions/src/main/java/androidx/lifecycle/LifecycleDispatcher.java
@@ -180,4 +180,7 @@
             dispatchIfLifecycleOwner(f, ON_RESUME);
         }
     }
+
+    private LifecycleDispatcher() {
+    }
 }
diff --git a/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java b/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java
index ad72a54..0b83b92 100644
--- a/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java
+++ b/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java
@@ -102,7 +102,7 @@
         if (factory == null) {
             factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
         }
-        return new ViewModelProvider(ViewModelStores.of(fragment), factory);
+        return new ViewModelProvider(fragment.getViewModelStore(), factory);
     }
 
     /**
@@ -123,7 +123,7 @@
         if (factory == null) {
             factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
         }
-        return new ViewModelProvider(ViewModelStores.of(activity), factory);
+        return new ViewModelProvider(activity.getViewModelStore(), factory);
     }
 
     /**
diff --git a/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelStores.java b/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelStores.java
index 6627239..ba9e15a 100644
--- a/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelStores.java
+++ b/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelStores.java
@@ -16,8 +16,6 @@
 
 package androidx.lifecycle;
 
-import static androidx.lifecycle.HolderFragment.holderFragmentFor;
-
 import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
 import androidx.fragment.app.Fragment;
@@ -25,8 +23,13 @@
 
 /**
  * Factory methods for {@link ViewModelStore} class.
+ *
+ * @deprecated Use {@link FragmentActivity#getViewModelStore()} or
+ * {@link Fragment#getViewModelStore()} to retrieve a {@code ViewModelStore} directly from
+ * activities and fragments.
  */
 @SuppressWarnings("WeakerAccess")
+@Deprecated
 public class ViewModelStores {
 
     private ViewModelStores() {
@@ -37,14 +40,13 @@
      *
      * @param activity an activity whose {@code ViewModelStore} is requested
      * @return a {@code ViewModelStore}
+     * @deprecated Use {@link FragmentActivity#getViewModelStore()}
      */
+    @Deprecated
     @NonNull
     @MainThread
     public static ViewModelStore of(@NonNull FragmentActivity activity) {
-        if (activity instanceof ViewModelStoreOwner) {
-            return ((ViewModelStoreOwner) activity).getViewModelStore();
-        }
-        return holderFragmentFor(activity).getViewModelStore();
+        return activity.getViewModelStore();
     }
 
     /**
@@ -52,13 +54,12 @@
      *
      * @param fragment a fragment whose {@code ViewModelStore} is requested
      * @return a {@code ViewModelStore}
+     * @deprecated Use {@link Fragment#getViewModelStore()}
      */
+    @Deprecated
     @NonNull
     @MainThread
     public static ViewModelStore of(@NonNull Fragment fragment) {
-        if (fragment instanceof ViewModelStoreOwner) {
-            return ((ViewModelStoreOwner) fragment).getViewModelStore();
-        }
-        return holderFragmentFor(fragment).getViewModelStore();
+        return fragment.getViewModelStore();
     }
 }
diff --git a/lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelStore.java b/lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelStore.java
index c831563..29ea55e 100644
--- a/lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelStore.java
+++ b/lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelStore.java
@@ -30,7 +30,7 @@
  * then it should call {@link #clear()} on this {@code ViewModelStore}, so {@code ViewModels} would
  * be notified that they are no longer used.
  * <p>
- * {@link androidx.lifecycle.ViewModelStores} provides a {@code ViewModelStore} for
+ * Use {@link ViewModelStoreOwner#getViewModelStore()} to retrieve a {@code ViewModelStore} for
  * activities and fragments.
  */
 public class ViewModelStore {
diff --git a/media/api21/android/support/v4/media/MediaBrowserCompatApi21.java b/media/api21/android/support/v4/media/MediaBrowserCompatApi21.java
index 4f040df..acd8bf8 100644
--- a/media/api21/android/support/v4/media/MediaBrowserCompatApi21.java
+++ b/media/api21/android/support/v4/media/MediaBrowserCompatApi21.java
@@ -148,5 +148,11 @@
         public static Object getDescription(Object itemObj) {
             return ((MediaBrowser.MediaItem) itemObj).getDescription();
         }
+
+        private MediaItem() {
+        }
+    }
+
+    private MediaBrowserCompatApi21() {
     }
 }
diff --git a/media/api21/android/support/v4/media/MediaDescriptionCompatApi21.java b/media/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
index 4179518..bed7f01 100644
--- a/media/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
+++ b/media/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
@@ -99,5 +99,11 @@
         public static Object build(Object builderObj) {
             return ((MediaDescription.Builder) builderObj).build();
         }
+
+        private Builder() {
+        }
+    }
+
+    private MediaDescriptionCompatApi21() {
     }
 }
diff --git a/media/api21/android/support/v4/media/MediaMetadataCompatApi21.java b/media/api21/android/support/v4/media/MediaMetadataCompatApi21.java
index 4095a39..5ce873a 100644
--- a/media/api21/android/support/v4/media/MediaMetadataCompatApi21.java
+++ b/media/api21/android/support/v4/media/MediaMetadataCompatApi21.java
@@ -83,5 +83,11 @@
         public static Object build(Object builderObj) {
             return ((MediaMetadata.Builder)builderObj).build();
         }
+
+        private Builder() {
+        }
+    }
+
+    private MediaMetadataCompatApi21() {
     }
 }
diff --git a/media/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java b/media/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
index d1d81eb..30d0225 100644
--- a/media/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
+++ b/media/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
@@ -48,4 +48,7 @@
         }
         return result;
     }
+
+    private ParceledListSliceAdapterApi21() {
+    }
 }
diff --git a/media/api21/android/support/v4/media/session/MediaControllerCompatApi21.java b/media/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
index 6c5f860..0cd1cd9 100644
--- a/media/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
+++ b/media/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
@@ -186,6 +186,9 @@
         public static void sendCustomAction(Object controlsObj, String action, Bundle args) {
             ((MediaController.TransportControls) controlsObj).sendCustomAction(action, args);
         }
+
+        private TransportControls() {
+        }
     }
 
     public static class PlaybackInfo {
@@ -258,6 +261,9 @@
                     return AudioManager.STREAM_MUSIC;
             }
         }
+
+        private PlaybackInfo() {
+        }
     }
 
     public static interface Callback {
@@ -320,4 +326,7 @@
                     info.getMaxVolume(), info.getCurrentVolume());
         }
     }
+
+    private MediaControllerCompatApi21() {
+    }
 }
diff --git a/media/api21/android/support/v4/media/session/MediaSessionCompatApi21.java b/media/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
index f0c542b..a359704 100644
--- a/media/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
+++ b/media/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
@@ -269,5 +269,11 @@
         public static long getQueueId(Object queueItem) {
             return ((MediaSession.QueueItem) queueItem).getQueueId();
         }
+
+        private QueueItem() {
+        }
+    }
+
+    private MediaSessionCompatApi21() {
     }
 }
diff --git a/media/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java b/media/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
index 85cd8d0..f4aa9fc 100644
--- a/media/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
+++ b/media/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
@@ -100,5 +100,11 @@
             customActionObj.setExtras(extras);
             return customActionObj.build();
         }
+
+        private CustomAction() {
+        }
+    }
+
+    private PlaybackStateCompatApi21() {
     }
 }
diff --git a/media/api21/androidx/media/AudioAttributesCompatApi21.java b/media/api21/androidx/media/AudioAttributesCompatApi21.java
index 19144c1..ba3d37c 100644
--- a/media/api21/androidx/media/AudioAttributesCompatApi21.java
+++ b/media/api21/androidx/media/AudioAttributesCompatApi21.java
@@ -65,4 +65,7 @@
             return mWrapped;
         }
     }
+
+    private AudioAttributesCompatApi21() {
+    }
 }
diff --git a/media/api21/androidx/media/MediaBrowserServiceCompatApi21.java b/media/api21/androidx/media/MediaBrowserServiceCompatApi21.java
index ca80e08..79c591b 100644
--- a/media/api21/androidx/media/MediaBrowserServiceCompatApi21.java
+++ b/media/api21/androidx/media/MediaBrowserServiceCompatApi21.java
@@ -129,4 +129,7 @@
             mServiceProxy.onLoadChildren(parentId, new ResultWrapper<List<Parcel>>(result));
         }
     }
+
+    private MediaBrowserServiceCompatApi21() {
+    }
 }
diff --git a/media/api21/androidx/media/VolumeProviderCompatApi21.java b/media/api21/androidx/media/VolumeProviderCompatApi21.java
index 6df87d0..6d540b3 100644
--- a/media/api21/androidx/media/VolumeProviderCompatApi21.java
+++ b/media/api21/androidx/media/VolumeProviderCompatApi21.java
@@ -45,4 +45,7 @@
         void onSetVolumeTo(int volume);
         void onAdjustVolume(int delta);
     }
+
+    private VolumeProviderCompatApi21() {
+    }
 }
diff --git a/media/api22/android/support/v4/media/session/MediaSessionCompatApi22.java b/media/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
index 8650400..9a44d8c 100644
--- a/media/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
+++ b/media/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
@@ -25,4 +25,7 @@
     public static void setRatingType(Object sessionObj, int type) {
         ((MediaSession) sessionObj).setRatingType(type);
     }
+
+    private MediaSessionCompatApi22() {
+    }
 }
diff --git a/media/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java b/media/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
index 53b76ce..1be825f 100644
--- a/media/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
+++ b/media/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
@@ -45,4 +45,7 @@
         stateObj.setExtras(extras);
         return stateObj.build();
     }
+
+    private PlaybackStateCompatApi22() {
+    }
 }
diff --git a/media/api23/android/support/v4/media/MediaBrowserCompatApi23.java b/media/api23/android/support/v4/media/MediaBrowserCompatApi23.java
index 6f94d5d..514b1e9 100644
--- a/media/api23/android/support/v4/media/MediaBrowserCompatApi23.java
+++ b/media/api23/android/support/v4/media/MediaBrowserCompatApi23.java
@@ -61,4 +61,7 @@
             mItemCallback.onError(itemId);
         }
     }
+
+    private MediaBrowserCompatApi23() {
+    }
 }
diff --git a/media/api23/android/support/v4/media/MediaDescriptionCompatApi23.java b/media/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
index b702617..ee1b1a1 100644
--- a/media/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
+++ b/media/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
@@ -21,14 +21,20 @@
 import androidx.annotation.RequiresApi;
 
 @RequiresApi(23)
-class MediaDescriptionCompatApi23 extends MediaDescriptionCompatApi21 {
+class MediaDescriptionCompatApi23 {
     public static Uri getMediaUri(Object descriptionObj) {
         return ((MediaDescription) descriptionObj).getMediaUri();
     }
 
-    static class Builder extends MediaDescriptionCompatApi21.Builder {
+    static class Builder {
         public static void setMediaUri(Object builderObj, Uri mediaUri) {
             ((MediaDescription.Builder)builderObj).setMediaUri(mediaUri);
         }
+
+        private Builder() {
+        }
+    }
+
+    private MediaDescriptionCompatApi23() {
     }
 }
diff --git a/media/api23/android/support/v4/media/session/MediaControllerCompatApi23.java b/media/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
index ca424d1..c5137b5 100644
--- a/media/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
+++ b/media/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
@@ -25,9 +25,15 @@
 @RequiresApi(23)
 class MediaControllerCompatApi23 {
 
-    public static class TransportControls extends MediaControllerCompatApi21.TransportControls {
+    public static class TransportControls {
         public static void playFromUri(Object controlsObj, Uri uri, Bundle extras) {
             ((MediaController.TransportControls) controlsObj).playFromUri(uri, extras);
         }
+
+        private TransportControls() {
+        }
+    }
+
+    private MediaControllerCompatApi23() {
     }
 }
diff --git a/media/api23/android/support/v4/media/session/MediaSessionCompatApi23.java b/media/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
index de55764..586ffda 100644
--- a/media/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
+++ b/media/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
@@ -42,4 +42,7 @@
             mCallback.onPlayFromUri(uri, extras);
         }
     }
+
+    private MediaSessionCompatApi23() {
+    }
 }
diff --git a/media/api23/androidx/media/MediaBrowserServiceCompatApi23.java b/media/api23/androidx/media/MediaBrowserServiceCompatApi23.java
index 624b98a..0292fb9 100644
--- a/media/api23/androidx/media/MediaBrowserServiceCompatApi23.java
+++ b/media/api23/androidx/media/MediaBrowserServiceCompatApi23.java
@@ -45,4 +45,7 @@
                     new MediaBrowserServiceCompatApi21.ResultWrapper<Parcel>(result));
         }
     }
+
+    private MediaBrowserServiceCompatApi23() {
+    }
 }
diff --git a/media/api24/android/support/v4/media/session/MediaControllerCompatApi24.java b/media/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
index 9faeff6..f130ed0 100644
--- a/media/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
+++ b/media/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
@@ -25,7 +25,7 @@
 @RequiresApi(24)
 class MediaControllerCompatApi24 {
 
-    public static class TransportControls extends MediaControllerCompatApi23.TransportControls {
+    public static class TransportControls {
         public static void prepare(Object controlsObj) {
             ((MediaController.TransportControls) controlsObj).prepare();
         }
@@ -41,5 +41,11 @@
         public static void prepareFromUri(Object controlsObj, Uri uri, Bundle extras) {
             ((MediaController.TransportControls) controlsObj).prepareFromUri(uri, extras);
         }
+
+        private TransportControls() {
+        }
+    }
+
+    private MediaControllerCompatApi24() {
     }
 }
diff --git a/media/api24/android/support/v4/media/session/MediaSessionCompatApi24.java b/media/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
index 2522e5b..818277f 100644
--- a/media/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
+++ b/media/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
@@ -78,4 +78,7 @@
             mCallback.onPrepareFromUri(uri, extras);
         }
     }
+
+    private MediaSessionCompatApi24() {
+    }
 }
diff --git a/media/api26/android/support/v4/media/MediaBrowserCompatApi26.java b/media/api26/android/support/v4/media/MediaBrowserCompatApi26.java
index 5d556d3..e156c06 100644
--- a/media/api26/android/support/v4/media/MediaBrowserCompatApi26.java
+++ b/media/api26/android/support/v4/media/MediaBrowserCompatApi26.java
@@ -64,4 +64,7 @@
             mSubscriptionCallback.onError(parentId, options);
         }
     }
+
+    private MediaBrowserCompatApi26() {
+    }
 }
diff --git a/media/api26/androidx/media/MediaBrowserServiceCompatApi26.java b/media/api26/androidx/media/MediaBrowserServiceCompatApi26.java
index 0f250dd..99b13a3 100644
--- a/media/api26/androidx/media/MediaBrowserServiceCompatApi26.java
+++ b/media/api26/androidx/media/MediaBrowserServiceCompatApi26.java
@@ -106,4 +106,7 @@
                     parentId, new ResultWrapper(result), options);
         }
     }
+
+    private MediaBrowserServiceCompatApi26() {
+    }
 }
diff --git a/media/src/main/java/androidx/media/MediaBrowserCompatUtils.java b/media/src/main/java/androidx/media/MediaBrowserCompatUtils.java
index d3f8bbb..c553256 100644
--- a/media/src/main/java/androidx/media/MediaBrowserCompatUtils.java
+++ b/media/src/main/java/androidx/media/MediaBrowserCompatUtils.java
@@ -77,4 +77,7 @@
         }
         return false;
     }
+
+    private MediaBrowserCompatUtils() {
+    }
 }
diff --git a/media/src/main/java/androidx/media/MediaBrowserProtocol.java b/media/src/main/java/androidx/media/MediaBrowserProtocol.java
index 251158f..5c85880 100644
--- a/media/src/main/java/androidx/media/MediaBrowserProtocol.java
+++ b/media/src/main/java/androidx/media/MediaBrowserProtocol.java
@@ -200,4 +200,7 @@
      * - replyTo : Callback messenger
      */
     public static final int CLIENT_MSG_SEND_CUSTOM_ACTION = 9;
+
+    private MediaBrowserProtocol() {
+    }
 }
diff --git a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java
index 227afad..ba89f99 100644
--- a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java
+++ b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java
@@ -68,6 +68,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.mediacompat.testlib.util.PollingCheck;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -537,6 +538,7 @@
 
     @Test
     @MediumTest
+    @FlakyTest(bugId = 74093976)
     public void testUnsubscribeWithSubscriptionCallbackForMultipleSubscriptions() throws Exception {
         connectMediaBrowserService();
         final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaBrowserConstants.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaBrowserConstants.java
index f961308..b4bea4e 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaBrowserConstants.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaBrowserConstants.java
@@ -63,4 +63,7 @@
             "test_media_id_children_2", "test_media_id_children_3",
             MEDIA_ID_CHILDREN_DELAYED
     };
+
+    private MediaBrowserConstants() {
+    }
 }
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaControllerConstants.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaControllerConstants.java
index 4978888..e81f7d9 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaControllerConstants.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaControllerConstants.java
@@ -52,4 +52,7 @@
     public static final int SET_CAPTIONING_ENABLED = 320;
     public static final int SET_REPEAT_MODE = 321;
     public static final int SET_SHUFFLE_MODE = 322;
+
+    private MediaControllerConstants() {
+    }
 }
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaSessionConstants.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaSessionConstants.java
index c0a64d4..ffc24a6 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaSessionConstants.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaSessionConstants.java
@@ -57,4 +57,7 @@
 
     public static final int TEST_ERROR_CODE = 0x3;
     public static final String TEST_ERROR_MSG = "test-error-msg";
+
+    private MediaSessionConstants() {
+    }
 }
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/VersionConstants.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/VersionConstants.java
index 4b217b1..5d877f2 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/VersionConstants.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/VersionConstants.java
@@ -25,4 +25,7 @@
 
     public static final String VERSION_TOT = "tot";
     public static final String VERSION_PREVIOUS = "previous";
+
+    private VersionConstants() {
+    }
 }
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/IntentUtil.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/IntentUtil.java
index 8d58a6f..14ba14c 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/IntentUtil.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/IntentUtil.java
@@ -129,4 +129,7 @@
         }
         return intent;
     }
+
+    private IntentUtil() {
+    }
 }
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/TestUtil.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/TestUtil.java
index 21fd223..b06b219 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/TestUtil.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/TestUtil.java
@@ -38,4 +38,7 @@
             assertEquals(expected.get(key), observed.get(key));
         }
     }
+
+    private TestUtil() {
+    }
 }
diff --git a/media/version-compat-tests/previous/client/build.gradle b/media/version-compat-tests/previous/client/build.gradle
index 8b0866e..06e446f 100644
--- a/media/version-compat-tests/previous/client/build.gradle
+++ b/media/version-compat-tests/previous/client/build.gradle
@@ -22,7 +22,7 @@
 
 dependencies {
     androidTestImplementation project(':support-media-compat-test-lib')
-    androidTestImplementation "com.android.support:support-media-compat:27.0.1"
+    androidTestImplementation "com.android.support:support-media-compat:27.1.0"
 
     androidTestImplementation(TEST_RUNNER_TMP, libs.exclude_for_espresso)
 }
diff --git a/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java b/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java
index d65b2bc..891769a 100644
--- a/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java
+++ b/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/MediaBrowserCompatTest.java
@@ -64,6 +64,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.mediacompat.testlib.util.PollingCheck;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -489,6 +490,7 @@
 
     @Test
     @MediumTest
+    @FlakyTest(bugId = 74093976)
     public void testUnsubscribeWithSubscriptionCallbackForMultipleSubscriptions() throws Exception {
         connectMediaBrowserService();
         final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
diff --git a/media/version-compat-tests/previous/service/build.gradle b/media/version-compat-tests/previous/service/build.gradle
index 3e1d9b9..5ac5270 100644
--- a/media/version-compat-tests/previous/service/build.gradle
+++ b/media/version-compat-tests/previous/service/build.gradle
@@ -22,7 +22,7 @@
 
 dependencies {
     androidTestImplementation(project(":support-media-compat-test-lib"))
-    androidTestImplementation "com.android.support:support-media-compat:27.0.1"
+    androidTestImplementation "com.android.support:support-media-compat:27.1.0"
 
     androidTestImplementation(TEST_RUNNER_TMP, libs.exclude_for_espresso)
 }
diff --git a/mediarouter/api24/androidx/mediarouter/media/MediaRouterApi24.java b/mediarouter/api24/androidx/mediarouter/media/MediaRouterApi24.java
index f47b873..800ea22 100644
--- a/mediarouter/api24/androidx/mediarouter/media/MediaRouterApi24.java
+++ b/mediarouter/api24/androidx/mediarouter/media/MediaRouterApi24.java
@@ -24,5 +24,11 @@
         public static int getDeviceType(Object routeObj) {
             return ((android.media.MediaRouter.RouteInfo)routeObj).getDeviceType();
         }
+
+        private RouteInfo() {
+        }
+    }
+
+    private MediaRouterApi24() {
     }
 }
diff --git a/mediarouter/jellybean-mr1/androidx/mediarouter/media/MediaRouterJellybeanMr1.java b/mediarouter/jellybean-mr1/androidx/mediarouter/media/MediaRouterJellybeanMr1.java
index c08f79e..9ca6313 100644
--- a/mediarouter/jellybean-mr1/androidx/mediarouter/media/MediaRouterJellybeanMr1.java
+++ b/mediarouter/jellybean-mr1/androidx/mediarouter/media/MediaRouterJellybeanMr1.java
@@ -52,6 +52,9 @@
             }
             return null;
         }
+
+        private RouteInfo() {
+        }
     }
 
     public static interface Callback extends MediaRouterJellybean.Callback {
@@ -183,4 +186,7 @@
             mCallback.onRoutePresentationDisplayChanged(route);
         }
     }
+
+    private MediaRouterJellybeanMr1() {
+    }
 }
diff --git a/mediarouter/jellybean-mr2/androidx/mediarouter/media/MediaRouterJellybeanMr2.java b/mediarouter/jellybean-mr2/androidx/mediarouter/media/MediaRouterJellybeanMr2.java
index be796ac..bbb5f64 100644
--- a/mediarouter/jellybean-mr2/androidx/mediarouter/media/MediaRouterJellybeanMr2.java
+++ b/mediarouter/jellybean-mr2/androidx/mediarouter/media/MediaRouterJellybeanMr2.java
@@ -37,11 +37,20 @@
         public static boolean isConnecting(Object routeObj) {
             return ((android.media.MediaRouter.RouteInfo)routeObj).isConnecting();
         }
+
+        private RouteInfo() {
+        }
     }
 
     public static final class UserRouteInfo {
         public static void setDescription(Object routeObj, CharSequence description) {
             ((android.media.MediaRouter.UserRouteInfo)routeObj).setDescription(description);
         }
+
+        private UserRouteInfo() {
+        }
+    }
+
+    private MediaRouterJellybeanMr2() {
     }
 }
diff --git a/mediarouter/jellybean/androidx/mediarouter/media/MediaRouterJellybean.java b/mediarouter/jellybean/androidx/mediarouter/media/MediaRouterJellybean.java
index c3f79e8..d73edb7 100644
--- a/mediarouter/jellybean/androidx/mediarouter/media/MediaRouterJellybean.java
+++ b/mediarouter/jellybean/androidx/mediarouter/media/MediaRouterJellybean.java
@@ -197,6 +197,9 @@
         public static boolean isGroup(Object routeObj) {
             return routeObj instanceof android.media.MediaRouter.RouteGroup;
         }
+
+        private RouteInfo() {
+        }
     }
 
     public static final class RouteGroup {
@@ -211,6 +214,9 @@
             }
             return out;
         }
+
+        private RouteGroup() {
+        }
     }
 
     public static final class UserRouteInfo {
@@ -255,6 +261,9 @@
             ((android.media.MediaRouter.UserRouteInfo)routeObj).setRemoteControlClient(
                     (android.media.RemoteControlClient)rccObj);
         }
+
+        private UserRouteInfo() {
+        }
     }
 
     public static final class RouteCategory {
@@ -276,6 +285,9 @@
         public static boolean isGroupable(Object categoryObj) {
             return ((android.media.MediaRouter.RouteCategory)categoryObj).isGroupable();
         }
+
+        private RouteCategory() {
+        }
     }
 
     public static interface Callback {
@@ -461,4 +473,7 @@
             mCallback.onVolumeUpdateRequest(route, direction);
         }
     }
+
+    private MediaRouterJellybean() {
+    }
 }
diff --git a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDialogHelper.java b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDialogHelper.java
index a418d5b..c5e53ee 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDialogHelper.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDialogHelper.java
@@ -149,4 +149,7 @@
         view.draw(canvas);
         return new BitmapDrawable(context.getResources(), bitmap);
     }
+
+    private MediaRouteDialogHelper() {
+    }
 }
diff --git a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderProtocol.java b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderProtocol.java
index 805e9f4..fcf4ba5 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderProtocol.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderProtocol.java
@@ -227,4 +227,7 @@
             return false;
         }
     }
+
+    private MediaRouteProviderProtocol() {
+    }
 }
diff --git a/paging/common/src/main/java/androidx/paging/PagedList.java b/paging/common/src/main/java/androidx/paging/PagedList.java
index 2878290..a11a731 100644
--- a/paging/common/src/main/java/androidx/paging/PagedList.java
+++ b/paging/common/src/main/java/androidx/paging/PagedList.java
@@ -138,7 +138,7 @@
 
     private final AtomicBoolean mDetached = new AtomicBoolean(false);
 
-    protected final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
+    private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
 
     PagedList(@NonNull PagedStorage<T> storage,
             @NonNull Executor mainThreadExecutor,
@@ -158,10 +158,10 @@
      *
      *
      * @param dataSource DataSource providing data to the PagedList
-     * @param mainThreadExecutor Thread that will use and consume data from the PagedList.
-     *                           Generally, this is the UI/main thread.
-     * @param backgroundThreadExecutor Data loading will be done via this executor - should be a
-     *                                 background thread.
+     * @param notifyExecutor Thread that will use and consume data from the PagedList.
+     *                       Generally, this is the UI/main thread.
+     * @param fetchExecutor Data loading will be done via this executor -
+     *                      should be a background thread.
      * @param boundaryCallback Optional boundary callback to attach to the list.
      * @param config PagedList Config, which defines how the PagedList will load data.
      * @param <K> Key type that indicates to the DataSource what data to load.
@@ -171,8 +171,8 @@
      */
     @NonNull
     private static <K, T> PagedList<T> create(@NonNull DataSource<K, T> dataSource,
-            @NonNull Executor mainThreadExecutor,
-            @NonNull Executor backgroundThreadExecutor,
+            @NonNull Executor notifyExecutor,
+            @NonNull Executor fetchExecutor,
             @Nullable BoundaryCallback<T> boundaryCallback,
             @NonNull Config config,
             @Nullable K key) {
@@ -188,16 +188,16 @@
             }
             ContiguousDataSource<K, T> contigDataSource = (ContiguousDataSource<K, T>) dataSource;
             return new ContiguousPagedList<>(contigDataSource,
-                    mainThreadExecutor,
-                    backgroundThreadExecutor,
+                    notifyExecutor,
+                    fetchExecutor,
                     boundaryCallback,
                     config,
                     key,
                     lastLoad);
         } else {
             return new TiledPagedList<>((PositionalDataSource<T>) dataSource,
-                    mainThreadExecutor,
-                    backgroundThreadExecutor,
+                    notifyExecutor,
+                    fetchExecutor,
                     boundaryCallback,
                     config,
                     (key != null) ? (Integer) key : 0);
@@ -224,8 +224,8 @@
     public static final class Builder<Key, Value> {
         private final DataSource<Key, Value> mDataSource;
         private final Config mConfig;
-        private Executor mMainThreadExecutor;
-        private Executor mBackgroundThreadExecutor;
+        private Executor mNotifyExecutor;
+        private Executor mFetchExecutor;
         private BoundaryCallback mBoundaryCallback;
         private Key mInitialKey;
 
@@ -264,30 +264,31 @@
             this(dataSource, new PagedList.Config.Builder().setPageSize(pageSize).build());
         }
         /**
-         * The executor defining where main/UI thread for page loading updates.
+         * The executor defining where page loading updates are dispatched.
          *
-         * @param mainThreadExecutor Executor for main/UI thread to receive {@link Callback} calls.
+         * @param notifyExecutor Executor that receives PagedList updates, and where
+         * {@link Callback} calls are dispatched. Generally, this is the ui/main thread.
          * @return this
          */
         @NonNull
-        public Builder<Key, Value> setMainThreadExecutor(@NonNull Executor mainThreadExecutor) {
-            mMainThreadExecutor = mainThreadExecutor;
+        public Builder<Key, Value> setNotifyExecutor(@NonNull Executor notifyExecutor) {
+            mNotifyExecutor = notifyExecutor;
             return this;
         }
 
         /**
-         * The executor on which background loading will be run.
-         * <p>
-         * Does not affect initial load, which will be done on whichever thread the PagedList is
-         * created on.
+         * The executor used to fetch additional pages from the DataSource.
          *
-         * @param backgroundThreadExecutor Executor for background DataSource loading.
+         * Does not affect initial load, which will be done immediately on whichever thread the
+         * PagedList is created on.
+         *
+         * @param fetchExecutor Executor used to fetch from DataSources, generally a background
+         *                      thread pool for e.g. I/O or network loading.
          * @return this
          */
         @NonNull
-        public Builder<Key, Value> setBackgroundThreadExecutor(
-                @NonNull Executor backgroundThreadExecutor) {
-            mBackgroundThreadExecutor = backgroundThreadExecutor;
+        public Builder<Key, Value> setFetchExecutor(@NonNull Executor fetchExecutor) {
+            mFetchExecutor = fetchExecutor;
             return this;
         }
 
@@ -350,18 +351,18 @@
         @NonNull
         public PagedList<Value> build() {
             // TODO: define defaults, once they can be used in module without android dependency
-            if (mMainThreadExecutor == null) {
+            if (mNotifyExecutor == null) {
                 throw new IllegalArgumentException("MainThreadExecutor required");
             }
-            if (mBackgroundThreadExecutor == null) {
+            if (mFetchExecutor == null) {
                 throw new IllegalArgumentException("BackgroundThreadExecutor required");
             }
 
             //noinspection unchecked
             return PagedList.create(
                     mDataSource,
-                    mMainThreadExecutor,
-                    mBackgroundThreadExecutor,
+                    mNotifyExecutor,
+                    mFetchExecutor,
                     mBoundaryCallback,
                     mConfig,
                     mInitialKey);
@@ -731,7 +732,7 @@
      * Callback signaling when content is loaded into the list.
      * <p>
      * Can be used to listen to items being paged in and out. These calls will be dispatched on
-     * the executor defined by {@link Builder#setMainThreadExecutor(Executor)}, which defaults to
+     * the executor defined by {@link Builder#setNotifyExecutor(Executor)}, which is generally
      * the main/UI thread.
      */
     public abstract static class Callback {
diff --git a/paging/common/src/test/java/androidx/paging/TiledPagedListTest.kt b/paging/common/src/test/java/androidx/paging/TiledPagedListTest.kt
index 2baf0aa..83eb20c 100644
--- a/paging/common/src/test/java/androidx/paging/TiledPagedListTest.kt
+++ b/paging/common/src/test/java/androidx/paging/TiledPagedListTest.kt
@@ -303,8 +303,8 @@
                 .setEnablePlaceholders(false)
                 .build()
         val pagedList = PagedList.Builder<Int, Item>(ListDataSource(ITEMS), config)
-                .setMainThreadExecutor(mMainThread)
-                .setBackgroundThreadExecutor(mBackgroundThread)
+                .setNotifyExecutor(mMainThread)
+                .setFetchExecutor(mBackgroundThread)
                 .setInitialKey(20)
                 .build()
 
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt
index 169779d..8a53b78 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt
@@ -58,8 +58,8 @@
             config: PagedList.Config, data: List<V>, initialKey: Int): PagedList<V> {
         return PagedList.Builder<Int, V>(ListDataSource(data), config)
                 .setInitialKey(initialKey)
-                .setMainThreadExecutor(mMainThread)
-                .setBackgroundThreadExecutor(mPageLoadingThread)
+                .setNotifyExecutor(mMainThread)
+                .setFetchExecutor(mPageLoadingThread)
                 .build()
     }
 
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
index 54955a0..14809c8 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
@@ -96,7 +96,7 @@
         // overriding default arch IO executor
         val livePagedList = LivePagedListBuilder(
                 MockDataSource.Factory(), 2)
-                .setBackgroundThreadExecutor(backgroundExecutor)
+                .setFetchExecutor(backgroundExecutor)
                 .build()
 
         val pagedListHolder: Array<PagedList<String>?> = arrayOfNulls(1)
diff --git a/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java b/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java
index 75c632f..f8af819 100644
--- a/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java
+++ b/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java
@@ -41,7 +41,7 @@
     private PagedList.Config mConfig;
     private DataSource.Factory<Key, Value> mDataSourceFactory;
     private PagedList.BoundaryCallback mBoundaryCallback;
-    private Executor mBackgroundThreadExecutor;
+    private Executor mFetchExecutor = ArchTaskExecutor.getIOThreadExecutor();
 
     /**
      * Creates a LivePagedListBuilder with required parameters.
@@ -51,6 +51,15 @@
      */
     public LivePagedListBuilder(@NonNull DataSource.Factory<Key, Value> dataSourceFactory,
             @NonNull PagedList.Config config) {
+        //noinspection ConstantConditions
+        if (config == null) {
+            throw new IllegalArgumentException("PagedList.Config must be provided");
+        }
+        //noinspection ConstantConditions
+        if (dataSourceFactory == null) {
+            throw new IllegalArgumentException("DataSource.Factory must be provided");
+        }
+
         mDataSourceFactory = dataSourceFactory;
         mConfig = config;
     }
@@ -116,21 +125,18 @@
     }
 
     /**
-     * Sets executor which will be used for background loading of pages.
+     * Sets executor used for background fetching of PagedLists, and the pages within.
      * <p>
      * If not set, defaults to the Arch components I/O thread.
-     * <p>
-     * Does not affect initial load, which will be always be done on done on the Arch components
-     * I/O thread.
      *
-     * @param backgroundThreadExecutor Executor for background DataSource loading.
+     * @param fetchExecutor Executor for fetching data from DataSources.
      * @return this
      */
     @SuppressWarnings("unused")
     @NonNull
-    public LivePagedListBuilder<Key, Value> setBackgroundThreadExecutor(
-            @NonNull Executor backgroundThreadExecutor) {
-        mBackgroundThreadExecutor = backgroundThreadExecutor;
+    public LivePagedListBuilder<Key, Value> setFetchExecutor(
+            @NonNull Executor fetchExecutor) {
+        mFetchExecutor = fetchExecutor;
         return this;
     }
 
@@ -144,18 +150,8 @@
      */
     @NonNull
     public LiveData<PagedList<Value>> build() {
-        if (mConfig == null) {
-            throw new IllegalArgumentException("PagedList.Config must be provided");
-        }
-        if (mDataSourceFactory == null) {
-            throw new IllegalArgumentException("DataSource.Factory must be provided");
-        }
-        if (mBackgroundThreadExecutor == null) {
-            mBackgroundThreadExecutor = ArchTaskExecutor.getIOThreadExecutor();
-        }
-
         return create(mInitialLoadKey, mConfig, mBoundaryCallback, mDataSourceFactory,
-                ArchTaskExecutor.getMainThreadExecutor(), mBackgroundThreadExecutor);
+                ArchTaskExecutor.getMainThreadExecutor(), mFetchExecutor);
     }
 
     @AnyThread
@@ -165,9 +161,9 @@
             @NonNull final PagedList.Config config,
             @Nullable final PagedList.BoundaryCallback boundaryCallback,
             @NonNull final DataSource.Factory<Key, Value> dataSourceFactory,
-            @NonNull final Executor mainThreadExecutor,
-            @NonNull final Executor backgroundThreadExecutor) {
-        return new ComputableLiveData<PagedList<Value>>(backgroundThreadExecutor) {
+            @NonNull final Executor notifyExecutor,
+            @NonNull final Executor fetchExecutor) {
+        return new ComputableLiveData<PagedList<Value>>(fetchExecutor) {
             @Nullable
             private PagedList<Value> mList;
             @Nullable
@@ -198,8 +194,8 @@
                     mDataSource.addInvalidatedCallback(mCallback);
 
                     mList = new PagedList.Builder<>(mDataSource, config)
-                            .setMainThreadExecutor(mainThreadExecutor)
-                            .setBackgroundThreadExecutor(backgroundThreadExecutor)
+                            .setNotifyExecutor(notifyExecutor)
+                            .setFetchExecutor(fetchExecutor)
                             .setBoundaryCallback(boundaryCallback)
                             .setInitialKey(initializeKey)
                             .build();
diff --git a/persistence/db/build.gradle b/persistence/db/build.gradle
index 5c9cb2d..f2e6c8c 100644
--- a/persistence/db/build.gradle
+++ b/persistence/db/build.gradle
@@ -25,6 +25,8 @@
 
 dependencies {
     api(SUPPORT_ANNOTATIONS)
+    testImplementation(JUNIT)
+    testImplementation(MOCKITO_CORE)
 }
 
 // Used by testCompile in room-compiler
diff --git a/persistence/db/src/main/java/androidx/sqlite/db/SimpleSQLiteQuery.java b/persistence/db/src/main/java/androidx/sqlite/db/SimpleSQLiteQuery.java
index bdcbb9f..58c330c 100644
--- a/persistence/db/src/main/java/androidx/sqlite/db/SimpleSQLiteQuery.java
+++ b/persistence/db/src/main/java/androidx/sqlite/db/SimpleSQLiteQuery.java
@@ -16,12 +16,15 @@
 
 package androidx.sqlite.db;
 
+import androidx.annotation.Nullable;
+
 /**
  * A basic implementation of {@link SupportSQLiteQuery} which receives a query and its args and
  * binds args based on the passed in Object type.
  */
 public final class SimpleSQLiteQuery implements SupportSQLiteQuery {
     private final String mQuery;
+    @Nullable
     private final Object[] mBindArgs;
 
     /**
@@ -30,7 +33,7 @@
      * @param query    The query string, can include bind arguments (.e.g ?).
      * @param bindArgs The bind argument value that will replace the placeholders in the query.
      */
-    public SimpleSQLiteQuery(String query, Object[] bindArgs) {
+    public SimpleSQLiteQuery(String query, @Nullable Object[] bindArgs) {
         mQuery = query;
         mBindArgs = bindArgs;
     }
@@ -56,7 +59,7 @@
 
     @Override
     public int getArgCount() {
-        return mBindArgs.length;
+        return mBindArgs == null ? 0 : mBindArgs.length;
     }
 
     /**
diff --git a/persistence/db/src/test/java/androidx/sqlite/db/SimpleSQLiteQueryTestTest.java b/persistence/db/src/test/java/androidx/sqlite/db/SimpleSQLiteQueryTestTest.java
new file mode 100644
index 0000000..7b04a2b
--- /dev/null
+++ b/persistence/db/src/test/java/androidx/sqlite/db/SimpleSQLiteQueryTestTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018 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.sqlite.db;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+@RunWith(JUnit4.class)
+public class SimpleSQLiteQueryTestTest {
+
+    @Test
+    public void getSql() {
+        SimpleSQLiteQuery query = new SimpleSQLiteQuery("foo");
+        assertThat(query.getSql(), is("foo"));
+    }
+
+    @Test
+    public void bindTo_noArgs() {
+        SimpleSQLiteQuery query = new SimpleSQLiteQuery("foo");
+        SupportSQLiteProgram program = Mockito.mock(SupportSQLiteProgram.class);
+        query.bindTo(program);
+        verifyNoMoreInteractions(program);
+    }
+
+    @Test
+    public void bindTo_withArgs() {
+        byte[] bytes = new byte[3];
+        SimpleSQLiteQuery query = new SimpleSQLiteQuery("foo",
+                new Object[]{"bar", 2, true, .5f, null, bytes});
+        SupportSQLiteProgram program = Mockito.mock(SupportSQLiteProgram.class);
+        query.bindTo(program);
+        verify(program).bindString(1, "bar");
+        verify(program).bindLong(2, 2);
+        verify(program).bindLong(3, 1);
+        verify(program).bindDouble(4, .5f);
+        verify(program).bindNull(5);
+        verify(program).bindBlob(6, bytes);
+        verifyNoMoreInteractions(program);
+    }
+
+    @Test
+    public void getArgCount_withArgs() {
+        SimpleSQLiteQuery query = new SimpleSQLiteQuery("foo",
+                new Object[]{"bar", 2, true});
+        assertThat(query.getArgCount(), is(3));
+    }
+
+    @Test
+    public void getArgCount_noArgs() {
+        SimpleSQLiteQuery query = new SimpleSQLiteQuery("foo");
+        assertThat(query.getArgCount(), is(0));
+    }
+}
diff --git a/preference/res/layout-v17/preference_category_material.xml b/preference/res/layout-v17/preference_category_material.xml
index db3abfe..fc39db3 100644
--- a/preference/res/layout-v17/preference_category_material.xml
+++ b/preference/res/layout-v17/preference_category_material.xml
@@ -15,13 +15,49 @@
   ~ limitations under the License
   -->
 
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/title"
+<FrameLayout
+    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_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" />
+    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">
+        <androidx.preference.internal.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="@dimen/preference_category_padding_start">
+        <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>
diff --git a/preference/res/layout-v17/preference_widget_seekbar_material.xml b/preference/res/layout-v17/preference_widget_seekbar_material.xml
new file mode 100644
index 0000000..bc426b9
--- /dev/null
+++ b/preference/res/layout-v17/preference_widget_seekbar_material.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<!-- Layout used by SeekBarPreference for the seekbar widget style. -->
+<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:minHeight="?android:attr/listPreferredItemHeight"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:clipChildren="false"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="-4dp"
+        android:minWidth="60dp"
+        android:gravity="start|center_vertical"
+        android:orientation="horizontal"
+        android:paddingEnd="12dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <androidx.preference.internal.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:maxWidth="48dp"
+            app:maxHeight="48dp" />
+    </LinearLayout>
+
+    <RelativeLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="8dip"
+        android:layout_marginTop="6dip"
+        android:layout_marginBottom="6dip"
+        android:layout_weight="1"
+        android:clipChildren="false"
+        android:clipToPadding="false">
+
+        <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"
+            android:fadingEdge="horizontal"/>
+
+        <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="4"/>
+
+        <!-- Using UnPressableLinearLayout as a workaround to disable the pressed state propagation
+        to the children of this container layout. Otherwise, the animated pressed state will also
+        play for the thumb in the AbsSeekBar in addition to the preference's ripple background.
+        The background of the SeekBar is also set to null to disable the ripple background -->
+        <androidx.preference.UnPressableLinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/summary"
+            android:layout_alignStart="@android:id/title"
+            android:clipChildren="false"
+            android:clipToPadding="false">
+            <SeekBar
+                android:id="@+id/seekbar"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:paddingStart="@dimen/preference_seekbar_padding_start"
+                android:paddingEnd="@dimen/preference_seekbar_padding_end"
+                android:focusable="false"
+                android:clickable="false"
+                android:background="@null" />
+
+            <TextView
+                android:id="@+id/seekbar_value"
+                android:layout_width="@dimen/preference_seekbar_value_width"
+                android:layout_height="match_parent"
+                android:gravity="right|center_vertical"
+                android:fontFamily="sans-serif-condensed"
+                android:singleLine="true"
+                android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
+                android:ellipsize="marquee"
+                android:fadingEdge="horizontal"
+                android:scrollbars="none"/>
+        </androidx.preference.UnPressableLinearLayout>
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/preference/res/layout-v21/preference_category_material.xml b/preference/res/layout-v21/preference_category_material.xml
index dad9a5c..bea9a05 100644
--- a/preference/res/layout-v21/preference_category_material.xml
+++ b/preference/res/layout-v21/preference_category_material.xml
@@ -15,13 +15,52 @@
   ~ limitations under the License
   -->
 
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/title"
+<FrameLayout
+    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_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" />
+    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">
+        <androidx.preference.internal.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="@dimen/preference_category_padding_start">
+        <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>
diff --git a/preference/res/layout-v21/preference_dropdown_material.xml b/preference/res/layout-v21/preference_dropdown_material.xml
index bffd3b3..fcafcae 100644
--- a/preference/res/layout-v21/preference_dropdown_material.xml
+++ b/preference/res/layout-v21/preference_dropdown_material.xml
@@ -15,74 +15,18 @@
   ~ limitations under the License
   -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    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" >
+    android:layout_height="wrap_content">
 
     <Spinner
         android:id="@+id/spinner"
+        android:layout_marginStart="@dimen/preference_no_icon_padding_start"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:visibility="invisible" />
 
-    <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">
-        <androidx.preference.internal.PreferenceImageView
-            android:id="@android:id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            app:maxWidth="48dp"
-            app:maxHeight="48dp" />
-    </LinearLayout>
+    <include layout="@layout/preference_material"/>
 
-    <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>
+</FrameLayout>
diff --git a/preference/res/layout-v21/preference_widget_seekbar_material.xml b/preference/res/layout-v21/preference_widget_seekbar_material.xml
new file mode 100644
index 0000000..b31400d
--- /dev/null
+++ b/preference/res/layout-v21/preference_widget_seekbar_material.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<!-- Layout used by SeekBarPreference for the seekbar widget style. -->
+<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:minHeight="?android:attr/listPreferredItemHeight"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:clipChildren="false"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="-4dp"
+        android:minWidth="60dp"
+        android:gravity="start|center_vertical"
+        android:orientation="horizontal"
+        android:paddingStart="4dp"
+        android:paddingEnd="8dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <androidx.preference.internal.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:maxWidth="48dp"
+            app:maxHeight="48dp" />
+    </LinearLayout>
+
+    <RelativeLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="8dip"
+        android:layout_marginTop="6dip"
+        android:layout_marginBottom="6dip"
+        android:layout_weight="1"
+        android:clipChildren="false"
+        android:clipToPadding="false">
+
+        <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"
+            android:fadingEdge="horizontal"/>
+
+        <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="4"/>
+
+        <!-- Using UnPressableLinearLayout as a workaround to disable the pressed state propagation
+        to the children of this container layout. Otherwise, the animated pressed state will also
+        play for the thumb in the AbsSeekBar in addition to the preference's ripple background.
+        The background of the SeekBar is also set to null to disable the ripple background -->
+        <androidx.preference.UnPressableLinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/summary"
+            android:layout_alignStart="@android:id/title"
+            android:clipChildren="false"
+            android:clipToPadding="false">
+            <SeekBar
+                android:id="@+id/seekbar"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:paddingStart="@dimen/preference_seekbar_padding_start"
+                android:paddingEnd="@dimen/preference_seekbar_padding_end"
+                android:focusable="false"
+                android:clickable="false"
+                android:background="@null" />
+
+            <TextView
+                android:id="@+id/seekbar_value"
+                android:layout_width="@dimen/preference_seekbar_value_width"
+                android:layout_height="match_parent"
+                android:gravity="right|center_vertical"
+                android:fontFamily="sans-serif-condensed"
+                android:singleLine="true"
+                android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
+                android:ellipsize="marquee"
+                android:fadingEdge="horizontal"
+                android:scrollbars="none"/>
+        </androidx.preference.UnPressableLinearLayout>
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/preference/res/layout/expand_button.xml b/preference/res/layout/expand_button.xml
index 16c8143..bb99ebf 100644
--- a/preference/res/layout/expand_button.xml
+++ b/preference/res/layout/expand_button.xml
@@ -57,7 +57,8 @@
             android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorPrimary"
             android:ellipsize="marquee"/>
 
         <TextView
diff --git a/preference/res/layout/preference_category_material.xml b/preference/res/layout/preference_category_material.xml
index e366e7a..3c48792 100644
--- a/preference/res/layout/preference_category_material.xml
+++ b/preference/res/layout/preference_category_material.xml
@@ -15,13 +15,49 @@
   ~ limitations under the License
   -->
 
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/title"
+<FrameLayout
+    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_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" />
+    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">
+        <androidx.preference.internal.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="@dimen/preference_category_padding_start">
+        <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>
diff --git a/preference/res/layout/preference_widget_seekbar_material.xml b/preference/res/layout/preference_widget_seekbar_material.xml
index 8bb80d1..bae0a75 100644
--- a/preference/res/layout/preference_widget_seekbar_material.xml
+++ b/preference/res/layout/preference_widget_seekbar_material.xml
@@ -16,25 +16,38 @@
 
 <!-- Layout used by SeekBarPreference for the seekbar widget style. -->
 <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:minHeight="?android:attr/listPreferredItemHeight"
               android:gravity="center_vertical"
-              android:paddingEnd="?android:attr/scrollbarSize"
+              android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+              android:paddingRight="?android:attr/listPreferredItemPaddingRight"
               android:clipChildren="false"
               android:clipToPadding="false">
 
-    <ImageView
+    <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">
+        <androidx.preference.internal.PreferenceImageView
             android:id="@android:id/icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:minWidth="@dimen/preference_icon_minWidth"/>
+            app:maxWidth="48dp"
+            app:maxHeight="48dp" />
+    </LinearLayout>
 
     <RelativeLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginStart="16dip"
             android:layout_marginEnd="8dip"
             android:layout_marginTop="6dip"
             android:layout_marginBottom="6dip"
@@ -89,7 +102,8 @@
                       android:singleLine="true"
                       android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
                       android:ellipsize="marquee"
-                      android:fadingEdge="horizontal"/>
+                      android:fadingEdge="horizontal"
+                      android:scrollbars="none"/>
         </androidx.preference.UnPressableLinearLayout>
 
     </RelativeLayout>
diff --git a/preference/res/values-sw360dp/config.xml b/preference/res/values-sw360dp/config.xml
new file mode 100644
index 0000000..18345c8
--- /dev/null
+++ b/preference/res/values-sw360dp/config.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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>
+    <bool name="config_materialPreferenceIconSpaceReserved">true</bool>
+</resources>
diff --git a/preference/res/values-sw360dp/dimens.xml b/preference/res/values-sw360dp/dimens.xml
new file mode 100644
index 0000000..e507017
--- /dev/null
+++ b/preference/res/values-sw360dp/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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>
+    <!-- The width of the padding for PreferenceCategory's title/summary -->
+    <dimen name="preference_category_padding_start">56dp</dimen>
+</resources>
diff --git a/preference/res/values-v21/styles.xml b/preference/res/values-v21/styles.xml
new file mode 100644
index 0000000..878b32c
--- /dev/null
+++ b/preference/res/values-v21/styles.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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="preference_no_icon_padding_start">72dp</dimen>
+</resources>
+
diff --git a/preference/res/values/config.xml b/preference/res/values/config.xml
new file mode 100644
index 0000000..b81606e
--- /dev/null
+++ b/preference/res/values/config.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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>
+    <bool name="config_materialPreferenceIconSpaceReserved">false</bool>
+</resources>
diff --git a/preference/res/values/dimens.xml b/preference/res/values/dimens.xml
index 4816e36..41a825b 100644
--- a/preference/res/values/dimens.xml
+++ b/preference/res/values/dimens.xml
@@ -10,4 +10,6 @@
     <dimen name="preference_seekbar_padding_end">22dp</dimen>
     <!-- The width of the TextView indicating the current value of the SeekBarPreference -->
     <dimen name="preference_seekbar_value_width">36dp</dimen>
+    <!-- The width of the padding for PreferenceCategory's title/summary -->
+    <dimen name="preference_category_padding_start">0dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/preference/res/values/styles.xml b/preference/res/values/styles.xml
index be1797b..ce223ee 100644
--- a/preference/res/values/styles.xml
+++ b/preference/res/values/styles.xml
@@ -84,6 +84,10 @@
 
     <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">@bool/config_materialPreferenceIconSpaceReserved</item>
     </style>
 
     <style name="Preference.Information.Material">
@@ -94,40 +98,69 @@
 
     <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">@bool/config_materialPreferenceIconSpaceReserved</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">@bool/config_materialPreferenceIconSpaceReserved</item>
     </style>
 
     <style name="Preference.SwitchPreferenceCompat.Material">
         <item name="android:layout">@layout/preference_material</item>
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="iconSpaceReserved">@bool/config_materialPreferenceIconSpaceReserved</item>
     </style>
 
     <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">@bool/config_materialPreferenceIconSpaceReserved</item>
     </style>
 
     <style name="Preference.SeekBarPreference.Material">
         <item name="android:layout">@layout/preference_widget_seekbar_material</item>
         <item name="adjustable">true</item>
         <item name="showSeekBarValue">true</item>
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="iconSpaceReserved">@bool/config_materialPreferenceIconSpaceReserved</item>
     </style>
 
     <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">@bool/config_materialPreferenceIconSpaceReserved</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">@bool/config_materialPreferenceIconSpaceReserved</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">@bool/config_materialPreferenceIconSpaceReserved</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">@bool/config_materialPreferenceIconSpaceReserved</item>
     </style>
 
     <style name="Preference_TextAppearanceMaterialBody2">
@@ -146,6 +179,7 @@
 
     <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/preference/res/values/themes.xml b/preference/res/values/themes.xml
index 1f8f158..598c24f 100644
--- a/preference/res/values/themes.xml
+++ b/preference/res/values/themes.xml
@@ -51,5 +51,6 @@
         <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/preference/src/main/java/androidx/preference/AndroidResources.java b/preference/src/main/java/androidx/preference/AndroidResources.java
index 00b14f4..ec12596 100644
--- a/preference/src/main/java/androidx/preference/AndroidResources.java
+++ b/preference/src/main/java/androidx/preference/AndroidResources.java
@@ -34,4 +34,6 @@
     public static final int ANDROID_R_EDITTEXT_PREFERENCE_STYLE
             = android.R.attr.editTextPreferenceStyle;
 
+    private AndroidResources() {
+    }
 }
diff --git a/preference/src/main/java/androidx/preference/CollapsiblePreferenceGroupController.java b/preference/src/main/java/androidx/preference/CollapsiblePreferenceGroupController.java
index bcc71ce..3e1d792 100644
--- a/preference/src/main/java/androidx/preference/CollapsiblePreferenceGroupController.java
+++ b/preference/src/main/java/androidx/preference/CollapsiblePreferenceGroupController.java
@@ -133,7 +133,8 @@
 
     private ExpandButton createExpandButton(final PreferenceGroup group,
             List<Preference> collapsedPreferences) {
-        final ExpandButton preference = new ExpandButton(mContext, collapsedPreferences);
+        final ExpandButton preference = new ExpandButton(mContext, collapsedPreferences,
+                group.getId());
         preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
             @Override
             public boolean onPreferenceClick(Preference preference) {
@@ -150,10 +151,16 @@
      * {@link PreferenceGroup}.
      */
     static class ExpandButton extends Preference {
-        ExpandButton(Context context, List<Preference> collapsedPreferences) {
+        private long mId;
+
+        ExpandButton(Context context, List<Preference> collapsedPreferences, long parentId) {
             super(context);
             initLayout();
             setSummary(collapsedPreferences);
+            // Since IDs are unique, using the parentId as a reference ensures that this expand
+            // button will have a unique ID and hence transitions will be correctly animated by
+            // RecyclerView when there are multiple ExpandButtons.
+            mId = parentId + 1000000;
         }
 
         private void initLayout() {
@@ -201,6 +208,11 @@
             super.onBindViewHolder(holder);
             holder.setDividerAllowedAbove(false);
         }
+
+        @Override
+        public long getId() {
+            return mId;
+        }
     }
 
 }
\ No newline at end of file
diff --git a/print/src/main/java/androidx/print/PrintHelper.java b/print/src/main/java/androidx/print/PrintHelper.java
index 2630940..5cee6d3 100644
--- a/print/src/main/java/androidx/print/PrintHelper.java
+++ b/print/src/main/java/androidx/print/PrintHelper.java
@@ -252,7 +252,6 @@
             return;
         }
 
-        final int fittingMode = mScaleMode; // grab the fitting mode at time of call
         PrintManager printManager =
                 (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
         PrintAttributes.MediaSize mediaSize;
@@ -267,42 +266,57 @@
                 .build();
 
         printManager.print(jobName,
-                new PrintDocumentAdapter() {
-                    private PrintAttributes mAttributes;
+                new PrintBitmapAdapter(jobName, mScaleMode, bitmap, callback), attr);
+    }
 
-                    @Override
-                    public void onLayout(PrintAttributes oldPrintAttributes,
-                            PrintAttributes newPrintAttributes,
-                            CancellationSignal cancellationSignal,
-                            LayoutResultCallback layoutResultCallback,
-                            Bundle bundle) {
+    @RequiresApi(19)
+    private class PrintBitmapAdapter extends PrintDocumentAdapter {
+        private final String mJobName;
+        private final int mFittingMode;
+        private final Bitmap mBitmap;
+        private final OnPrintFinishCallback mCallback;
+        private PrintAttributes mAttributes;
 
-                        mAttributes = newPrintAttributes;
+        PrintBitmapAdapter(String jobName, int fittingMode, Bitmap bitmap,
+                OnPrintFinishCallback callback) {
+            mJobName = jobName;
+            mFittingMode = fittingMode;
+            mBitmap = bitmap;
+            mCallback = callback;
+        }
 
-                        PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
-                                .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
-                                .setPageCount(1)
-                                .build();
-                        boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
-                        layoutResultCallback.onLayoutFinished(info, changed);
-                    }
+        @Override
+        public void onLayout(PrintAttributes oldPrintAttributes,
+                PrintAttributes newPrintAttributes,
+                CancellationSignal cancellationSignal,
+                LayoutResultCallback layoutResultCallback,
+                Bundle bundle) {
 
-                    @Override
-                    public void onWrite(PageRange[] pageRanges,
-                            ParcelFileDescriptor fileDescriptor,
-                            CancellationSignal cancellationSignal,
-                            WriteResultCallback writeResultCallback) {
-                        writeBitmap(mAttributes, fittingMode, bitmap, fileDescriptor,
-                                cancellationSignal, writeResultCallback);
-                    }
+            mAttributes = newPrintAttributes;
 
-                    @Override
-                    public void onFinish() {
-                        if (callback != null) {
-                            callback.onFinish();
-                        }
-                    }
-                }, attr);
+            PrintDocumentInfo info = new PrintDocumentInfo.Builder(mJobName)
+                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+                    .setPageCount(1)
+                    .build();
+            boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
+            layoutResultCallback.onLayoutFinished(info, changed);
+        }
+
+        @Override
+        public void onWrite(PageRange[] pageRanges,
+                ParcelFileDescriptor fileDescriptor,
+                CancellationSignal cancellationSignal,
+                WriteResultCallback writeResultCallback) {
+            writeBitmap(mAttributes, mFittingMode, mBitmap, fileDescriptor,
+                    cancellationSignal, writeResultCallback);
+        }
+
+        @Override
+        public void onFinish() {
+            if (mCallback != null) {
+                mCallback.onFinish();
+            }
+        }
     }
 
     /**
@@ -336,148 +350,8 @@
             return;
         }
 
-        final int fittingMode = mScaleMode;
-
-        PrintDocumentAdapter printDocumentAdapter = new PrintDocumentAdapter() {
-            private PrintAttributes mAttributes;
-            AsyncTask<Uri, Boolean, Bitmap> mLoadBitmap;
-            Bitmap mBitmap = null;
-
-            @Override
-            public void onLayout(final PrintAttributes oldPrintAttributes,
-                    final PrintAttributes newPrintAttributes,
-                    final CancellationSignal cancellationSignal,
-                    final LayoutResultCallback layoutResultCallback,
-                    Bundle bundle) {
-
-                synchronized (this) {
-                    mAttributes = newPrintAttributes;
-                }
-
-                if (cancellationSignal.isCanceled()) {
-                    layoutResultCallback.onLayoutCancelled();
-                    return;
-                }
-                // we finished the load
-                if (mBitmap != null) {
-                    PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
-                            .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
-                            .setPageCount(1)
-                            .build();
-                    boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
-                    layoutResultCallback.onLayoutFinished(info, changed);
-                    return;
-                }
-
-                mLoadBitmap = new AsyncTask<Uri, Boolean, Bitmap>() {
-                    @Override
-                    protected void onPreExecute() {
-                        // First register for cancellation requests.
-                        cancellationSignal.setOnCancelListener(
-                                new CancellationSignal.OnCancelListener() {
-                                    @Override
-                                    public void onCancel() { // on different thread
-                                        cancelLoad();
-                                        cancel(false);
-                                    }
-                                });
-                    }
-
-                    @Override
-                    protected Bitmap doInBackground(Uri... uris) {
-                        try {
-                            return loadConstrainedBitmap(imageFile);
-                        } catch (FileNotFoundException e) {
-                          /* ignore */
-                        }
-                        return null;
-                    }
-
-                    @Override
-                    protected void onPostExecute(Bitmap bitmap) {
-                        super.onPostExecute(bitmap);
-
-                        // If orientation was not set by the caller, try to fit the bitmap on
-                        // the current paper by potentially rotating the bitmap by 90 degrees.
-                        if (bitmap != null
-                                && (!PRINT_ACTIVITY_RESPECTS_ORIENTATION || mOrientation == 0)) {
-                            PrintAttributes.MediaSize mediaSize;
-
-                            synchronized (this) {
-                                mediaSize = mAttributes.getMediaSize();
-                            }
-
-                            if (mediaSize != null) {
-                                if (mediaSize.isPortrait() != isPortrait(bitmap)) {
-                                    Matrix rotation = new Matrix();
-
-                                    rotation.postRotate(90);
-                                    bitmap = Bitmap.createBitmap(bitmap, 0, 0,
-                                            bitmap.getWidth(), bitmap.getHeight(), rotation,
-                                            true);
-                                }
-                            }
-                        }
-
-                        mBitmap = bitmap;
-                        if (bitmap != null) {
-                            PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
-                                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
-                                    .setPageCount(1)
-                                    .build();
-
-                            boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
-
-                            layoutResultCallback.onLayoutFinished(info, changed);
-
-                        } else {
-                            layoutResultCallback.onLayoutFailed(null);
-                        }
-                        mLoadBitmap = null;
-                    }
-
-                    @Override
-                    protected void onCancelled(Bitmap result) {
-                        // Task was cancelled, report that.
-                        layoutResultCallback.onLayoutCancelled();
-                        mLoadBitmap = null;
-                    }
-                }.execute();
-            }
-
-            private void cancelLoad() {
-                synchronized (mLock) { // prevent race with set null below
-                    if (mDecodeOptions != null) {
-                        mDecodeOptions.requestCancelDecode();
-                        mDecodeOptions = null;
-                    }
-                }
-            }
-
-            @Override
-            public void onFinish() {
-                super.onFinish();
-                cancelLoad();
-                if (mLoadBitmap != null) {
-                    mLoadBitmap.cancel(true);
-                }
-                if (callback != null) {
-                    callback.onFinish();
-                }
-                if (mBitmap != null) {
-                    mBitmap.recycle();
-                    mBitmap = null;
-                }
-            }
-
-            @Override
-            public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
-                    CancellationSignal cancellationSignal,
-                    WriteResultCallback writeResultCallback) {
-                writeBitmap(mAttributes, fittingMode, mBitmap, fileDescriptor,
-                        cancellationSignal, writeResultCallback);
-            }
-        };
+        PrintDocumentAdapter printDocumentAdapter = new PrintUriAdapter(jobName, imageFile,
+                callback, mScaleMode);
 
         PrintManager printManager =
                 (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
@@ -494,6 +368,161 @@
         printManager.print(jobName, printDocumentAdapter, attr);
     }
 
+    @RequiresApi(19)
+    private class PrintUriAdapter extends PrintDocumentAdapter {
+        private final String mJobName;
+        private final Uri mImageFile;
+        private final OnPrintFinishCallback mCallback;
+        private final int mFittingMode;
+        private PrintAttributes mAttributes;
+        AsyncTask<Uri, Boolean, Bitmap> mLoadBitmap;
+        Bitmap mBitmap;
+
+        PrintUriAdapter(String jobName, Uri imageFile, OnPrintFinishCallback callback,
+                int fittingMode) {
+            mJobName = jobName;
+            mImageFile = imageFile;
+            mCallback = callback;
+            mFittingMode = fittingMode;
+            mBitmap = null;
+        }
+
+        @Override
+        public void onLayout(final PrintAttributes oldPrintAttributes,
+                final PrintAttributes newPrintAttributes,
+                final CancellationSignal cancellationSignal,
+                final LayoutResultCallback layoutResultCallback,
+                Bundle bundle) {
+
+            synchronized (this) {
+                mAttributes = newPrintAttributes;
+            }
+
+            if (cancellationSignal.isCanceled()) {
+                layoutResultCallback.onLayoutCancelled();
+                return;
+            }
+            // we finished the load
+            if (mBitmap != null) {
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(mJobName)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+                        .setPageCount(1)
+                        .build();
+                boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
+                layoutResultCallback.onLayoutFinished(info, changed);
+                return;
+            }
+
+            mLoadBitmap = new AsyncTask<Uri, Boolean, Bitmap>() {
+                @Override
+                protected void onPreExecute() {
+                    // First register for cancellation requests.
+                    cancellationSignal.setOnCancelListener(
+                            new CancellationSignal.OnCancelListener() {
+                                @Override
+                                public void onCancel() { // on different thread
+                                    cancelLoad();
+                                    cancel(false);
+                                }
+                            });
+                }
+
+                @Override
+                protected Bitmap doInBackground(Uri... uris) {
+                    try {
+                        return loadConstrainedBitmap(mImageFile);
+                    } catch (FileNotFoundException e) {
+                        /* ignore */
+                    }
+                    return null;
+                }
+
+                @Override
+                protected void onPostExecute(Bitmap bitmap) {
+                    super.onPostExecute(bitmap);
+
+                    // If orientation was not set by the caller, try to fit the bitmap on
+                    // the current paper by potentially rotating the bitmap by 90 degrees.
+                    if (bitmap != null
+                            && (!PRINT_ACTIVITY_RESPECTS_ORIENTATION || mOrientation == 0)) {
+                        PrintAttributes.MediaSize mediaSize;
+
+                        synchronized (this) {
+                            mediaSize = mAttributes.getMediaSize();
+                        }
+
+                        if (mediaSize != null) {
+                            if (mediaSize.isPortrait() != isPortrait(bitmap)) {
+                                Matrix rotation = new Matrix();
+
+                                rotation.postRotate(90);
+                                bitmap = Bitmap.createBitmap(bitmap, 0, 0,
+                                        bitmap.getWidth(), bitmap.getHeight(), rotation,
+                                        true);
+                            }
+                        }
+                    }
+
+                    mBitmap = bitmap;
+                    if (bitmap != null) {
+                        PrintDocumentInfo info = new PrintDocumentInfo.Builder(mJobName)
+                                .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+                                .setPageCount(1)
+                                .build();
+
+                        boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
+
+                        layoutResultCallback.onLayoutFinished(info, changed);
+
+                    } else {
+                        layoutResultCallback.onLayoutFailed(null);
+                    }
+                    mLoadBitmap = null;
+                }
+
+                @Override
+                protected void onCancelled(Bitmap result) {
+                    // Task was cancelled, report that.
+                    layoutResultCallback.onLayoutCancelled();
+                    mLoadBitmap = null;
+                }
+            }.execute();
+        }
+
+        private void cancelLoad() {
+            synchronized (mLock) { // prevent race with set null below
+                if (mDecodeOptions != null) {
+                    mDecodeOptions.requestCancelDecode();
+                    mDecodeOptions = null;
+                }
+            }
+        }
+
+        @Override
+        public void onFinish() {
+            super.onFinish();
+            cancelLoad();
+            if (mLoadBitmap != null) {
+                mLoadBitmap.cancel(true);
+            }
+            if (mCallback != null) {
+                mCallback.onFinish();
+            }
+            if (mBitmap != null) {
+                mBitmap.recycle();
+                mBitmap = null;
+            }
+        }
+
+        @Override
+        public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
+                CancellationSignal cancellationSignal,
+                WriteResultCallback writeResultCallback) {
+            writeBitmap(mAttributes, mFittingMode, mBitmap, fileDescriptor,
+                    cancellationSignal, writeResultCallback);
+        }
+    }
+
     /**
      * Check if the supplied bitmap should best be printed on a portrait orientation paper.
      *
diff --git a/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventBridge.java b/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventBridge.java
index 6089ec6..e117944 100644
--- a/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventBridge.java
+++ b/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventBridge.java
@@ -139,4 +139,7 @@
             mAdapter.notifyItemChanged(position, SelectionTracker.SELECTION_CHANGED_MARKER);
         }
     }
+
+    private EventBridge() {
+    }
 }
diff --git a/room/common/api/current.txt b/room/common/api/current.txt
index fd32902..250e4ec 100644
--- a/room/common/api/current.txt
+++ b/room/common/api/current.txt
@@ -77,7 +77,7 @@
   }
 
   public class RoomWarnings {
-    ctor public RoomWarnings();
+    ctor public deprecated RoomWarnings();
     field public static final java.lang.String CANNOT_CREATE_VERIFICATION_DATABASE = "ROOM_CANNOT_CREATE_VERIFICATION_DATABASE";
     field public static final java.lang.String CURSOR_MISMATCH = "ROOM_CURSOR_MISMATCH";
     field public static final java.lang.String DEFAULT_CONSTRUCTOR = "ROOM_DEFAULT_CONSTRUCTOR";
diff --git a/room/common/src/main/java/androidx/room/ForeignKey.java b/room/common/src/main/java/androidx/room/ForeignKey.java
index 847941a..54b9252 100644
--- a/room/common/src/main/java/androidx/room/ForeignKey.java
+++ b/room/common/src/main/java/androidx/room/ForeignKey.java
@@ -13,11 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package androidx.room;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import androidx.annotation.IntDef;
 
+import java.lang.annotation.Retention;
+
 /**
  * Declares a foreign key on another {@link Entity}.
  * <p>
@@ -161,6 +164,7 @@
      * {@link #onUpdate()}.
      */
     @IntDef({NO_ACTION, RESTRICT, SET_NULL, SET_DEFAULT, CASCADE})
+    @Retention(SOURCE)
     @interface Action {
     }
 }
diff --git a/room/common/src/main/java/androidx/room/RoomMasterTable.java b/room/common/src/main/java/androidx/room/RoomMasterTable.java
index 6c8c855..c1ab82a 100644
--- a/room/common/src/main/java/androidx/room/RoomMasterTable.java
+++ b/room/common/src/main/java/androidx/room/RoomMasterTable.java
@@ -52,4 +52,7 @@
                 + COLUMN_ID + "," + COLUMN_IDENTITY_HASH + ")"
                 + " VALUES(" + DEFAULT_ID + ", \"" + hash + "\")";
     }
+
+    private RoomMasterTable() {
+    }
 }
diff --git a/room/common/src/main/java/androidx/room/RoomWarnings.java b/room/common/src/main/java/androidx/room/RoomWarnings.java
index a4122bd..5f5d7f1 100644
--- a/room/common/src/main/java/androidx/room/RoomWarnings.java
+++ b/room/common/src/main/java/androidx/room/RoomWarnings.java
@@ -133,4 +133,10 @@
      */
     public static final String RELATION_QUERY_WITHOUT_TRANSACTION =
             "ROOM_RELATION_QUERY_WITHOUT_TRANSACTION";
+
+    /** @deprecated This type should not be instantiated as it contains only static methods. */
+    @Deprecated
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    public RoomWarnings() {
+    }
 }
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/InvalidationTrackerTrojan.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/InvalidationTrackerTrojan.java
index 2e52952..13a5469 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/InvalidationTrackerTrojan.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/InvalidationTrackerTrojan.java
@@ -23,4 +23,7 @@
     public static int countObservers(InvalidationTracker tracker) {
         return tracker.mObserverMap.size();
     }
+
+    private InvalidationTrackerTrojan() {
+    }
 }
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestUtil.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestUtil.java
index b3d5e24..69ac752 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestUtil.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestUtil.java
@@ -106,4 +106,7 @@
         coordinates.lng = Math.random();
         return coordinates;
     }
+
+    private TestUtil() {
+    }
 }
diff --git a/room/migration/src/main/java/androidx/room/migration/bundle/BundleUtil.java b/room/migration/src/main/java/androidx/room/migration/bundle/BundleUtil.java
index 8eb4d10..86888ba 100644
--- a/room/migration/src/main/java/androidx/room/migration/bundle/BundleUtil.java
+++ b/room/migration/src/main/java/androidx/room/migration/bundle/BundleUtil.java
@@ -33,4 +33,7 @@
     static String replaceTableName(String contents, String tableName) {
         return contents.replace(TABLE_NAME_PLACEHOLDER, tableName);
     }
+
+    private BundleUtil() {
+    }
 }
diff --git a/room/migration/src/main/java/androidx/room/migration/bundle/SchemaEqualityUtil.java b/room/migration/src/main/java/androidx/room/migration/bundle/SchemaEqualityUtil.java
index 799c3d8..910958d 100644
--- a/room/migration/src/main/java/androidx/room/migration/bundle/SchemaEqualityUtil.java
+++ b/room/migration/src/main/java/androidx/room/migration/bundle/SchemaEqualityUtil.java
@@ -87,4 +87,7 @@
         }
         return item1.isSchemaEqual(item2);
     }
+
+    private SchemaEqualityUtil() {
+    }
 }
diff --git a/room/runtime/api/current.txt b/room/runtime/api/current.txt
index cfd667d..3f317d5 100644
--- a/room/runtime/api/current.txt
+++ b/room/runtime/api/current.txt
@@ -25,7 +25,7 @@
   }
 
   public class Room {
-    ctor public Room();
+    ctor public deprecated Room();
     method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> databaseBuilder(android.content.Context, java.lang.Class<T>, java.lang.String);
     method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> inMemoryDatabaseBuilder(android.content.Context, java.lang.Class<T>);
     field public static final java.lang.String MASTER_TABLE_NAME = "room_master_table";
diff --git a/room/runtime/src/main/java/androidx/room/Room.java b/room/runtime/src/main/java/androidx/room/Room.java
index 15107fc..2e4dedc 100644
--- a/room/runtime/src/main/java/androidx/room/Room.java
+++ b/room/runtime/src/main/java/androidx/room/Room.java
@@ -100,4 +100,10 @@
                     + klass.getCanonicalName());
         }
     }
+
+    /** @deprecated This type should not be instantiated as it contains only static methods. */
+    @Deprecated
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    public Room() {
+    }
 }
diff --git a/room/runtime/src/main/java/androidx/room/util/StringUtil.java b/room/runtime/src/main/java/androidx/room/util/StringUtil.java
index 8988490..b865138 100644
--- a/room/runtime/src/main/java/androidx/room/util/StringUtil.java
+++ b/room/runtime/src/main/java/androidx/room/util/StringUtil.java
@@ -112,4 +112,7 @@
         }
         return sb.toString();
     }
+
+    private StringUtil() {
+    }
 }
diff --git a/room/rxjava2/api/current.txt b/room/rxjava2/api/current.txt
index be690d9..355c117 100644
--- a/room/rxjava2/api/current.txt
+++ b/room/rxjava2/api/current.txt
@@ -5,7 +5,7 @@
   }
 
   public class RxRoom {
-    ctor public RxRoom();
+    ctor public deprecated RxRoom();
     method public static io.reactivex.Flowable<java.lang.Object> createFlowable(androidx.room.RoomDatabase, java.lang.String...);
     field public static final java.lang.Object NOTHING;
   }
diff --git a/room/rxjava2/src/main/java/androidx/room/RxRoom.java b/room/rxjava2/src/main/java/androidx/room/RxRoom.java
index d2ba8bf..89af1f3 100644
--- a/room/rxjava2/src/main/java/androidx/room/RxRoom.java
+++ b/room/rxjava2/src/main/java/androidx/room/RxRoom.java
@@ -16,7 +16,6 @@
 
 package androidx.room;
 
-import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.arch.core.executor.ArchTaskExecutor;
 
@@ -29,13 +28,14 @@
 import io.reactivex.Flowable;
 import io.reactivex.FlowableEmitter;
 import io.reactivex.FlowableOnSubscribe;
+import io.reactivex.Maybe;
+import io.reactivex.MaybeSource;
 import io.reactivex.Scheduler;
 import io.reactivex.annotations.NonNull;
 import io.reactivex.disposables.Disposable;
 import io.reactivex.disposables.Disposables;
 import io.reactivex.functions.Action;
 import io.reactivex.functions.Function;
-import io.reactivex.functions.Predicate;
 
 /**
  * Helper class to add RxJava2 support to Room.
@@ -103,22 +103,12 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public static <T> Flowable<T> createFlowable(final RoomDatabase database,
             final String[] tableNames, final Callable<T> callable) {
+        final Maybe<T> maybe = Maybe.fromCallable(callable);
         return createFlowable(database, tableNames).observeOn(sAppToolkitIOScheduler)
-                .map(new Function<Object, Optional<T>>() {
+                .flatMapMaybe(new Function<Object, MaybeSource<T>>() {
                     @Override
-                    public Optional<T> apply(@NonNull Object o) throws Exception {
-                        T data = callable.call();
-                        return new Optional<>(data);
-                    }
-                }).filter(new Predicate<Optional<T>>() {
-                    @Override
-                    public boolean test(@NonNull Optional<T> optional) throws Exception {
-                        return optional.mValue != null;
-                    }
-                }).map(new Function<Optional<T>, T>() {
-                    @Override
-                    public T apply(@NonNull Optional<T> optional) throws Exception {
-                        return optional.mValue;
+                    public MaybeSource<T> apply(Object o) throws Exception {
+                        return maybe;
                     }
                 });
     }
@@ -177,12 +167,9 @@
         }
     }
 
-    static class Optional<T> {
-        @Nullable
-        final T mValue;
-
-        Optional(@Nullable T value) {
-            this.mValue = value;
-        }
+    /** @deprecated This type should not be instantiated as it contains only static methods. */
+    @Deprecated
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    public RxRoom() {
     }
 }
diff --git a/samples/SupportCarDemos/src/main/AndroidManifest.xml b/samples/SupportCarDemos/src/main/AndroidManifest.xml
index 97ad4ed..00e4ebb 100644
--- a/samples/SupportCarDemos/src/main/AndroidManifest.xml
+++ b/samples/SupportCarDemos/src/main/AndroidManifest.xml
@@ -70,9 +70,42 @@
                 <category android:name="android.intent.category.SAMPLE_CODE" />
             </intent-filter>
             <meta-data android:name="android.support.PARENT_ACTIVITY"
-                android:value=".SupportCarDemoActivity" />
+                       android:value=".SupportCarDemoActivity" />
         </activity>
 
+        <activity android:name=".AlphaJumpActivity"
+                  android:label="Alpha Jump"
+                  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>
+
+        <activity android:name=".DrawerDemoActivity"
+                  android:label="@string/drawer_demo_activity_title"
+                  android:theme="@style/CarDrawerTheme"
+                  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>
+
+        <activity android:name=".SubheaderListItemActivity"
+                  android:label="SubheaderListItem"
+                  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/AlphaJumpActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/AlphaJumpActivity.java
new file mode 100644
index 0000000..7f491a9
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/AlphaJumpActivity.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2018 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.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import androidx.car.widget.AlphaJumpBucketer;
+import androidx.car.widget.IAlphaJumpAdapter;
+import androidx.car.widget.PagedListView;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * An activity with a long list of cheeses, initially in a random order but you can use alpha jump
+ * to quickly jump to your favourite cheese.
+ */
+public class AlphaJumpActivity extends Activity {
+    private static final String TAG = "AlphaJumpActivity";
+
+    private static final String[] CHEESES = {
+            "Pourly", "Macconais", "Bonchester", "Olivet Cendre", "Fruit Cream Cheese",
+            "Metton (Cancoillotte)", "Lyonnais", "Crema Agria", "Nantais",
+            "Brusselae Kaas (Fromage de Bruxelles)", "Rouleau De Beaulieu", "Flor de Guia",
+            "Poivre d'Ane", "Tomme des Chouans", "Whitestone Farmhouse", "Queso de Murcia",
+            "Saint-Marcellin", "Pave d'Affinois", "Quatre-Vents", "Galette du Paludier", "Pyramide",
+            "Capricorn Goat", "Feta", "Queso del Montsec", "Telemea", "Cooleney",
+            "Buchette d'Anjou", "Banon", "Bosworth", "Bergader", "Mothais a la Feuille",
+            "Mascarpone Torta", "Richelieu", "Guerbigny", "Taupiniere", "Anneau du Vic-Bilh",
+            "Tupi", "Queso Fresco", "Timboon Brie", "Neufchatel", "Blue Castello",
+            "Brebis du Puyfaucon", "Gratte-Paille", "Palet de Babligny", "Caciotta", "Rigotte",
+            "Caciocavallo", "Bleu Des Causses", "Civray", "Bath Cheese", "Farmer", "Cachaille",
+            "Ricotta", "Caravane", "Selles sur Cher", "Chaource", "Cottage Cheese (Australian)",
+            "Pelardon des Corbieres", "Cold Pack", "Queso Fresco (Adobera)", "Bleu de Gex",
+            "Provel", "Torta del Casar", "Golden Cross", "Mascarpone", "Fougerus",
+            "Dessertnyj Belyj", "Fresh Ricotta", "Gris de Lille", "Breakfast Cheese", "Venaco",
+            "Pant ys Gawn", "Mascarpone (Australian)", "Sharpam", "Humboldt Fog",
+            "Evansdale Farmhouse Brie", "Kernhem", "Mozzarella Rolls", "Dolcelatte",
+            "Briquette de Brebis", "Niolo", "Selva", "Dunbarra", "King Island Cape Wickham Brie",
+            "Carre de l'Est", "Broccio", "Castelo Branco", "Finn", "Panela", "Basket Cheese",
+            "Woodside Cabecou", "Truffe", "Flower Marie", "Mozzarella Fresh, in water",
+            "Jubilee Blue", "Coeur de Camembert au Calvados", "Caprice des Dieux", "Caboc",
+            "Crottin du Chavignol", "Cabecou", "Cottage Cheese", "Cashel Blue", "Patefine Fort",
+            "Mahoe Aged Gouda", "Cornish Pepper", "Greuilh", "Ricotta (Australian)", "Grand Vatel",
+            "Prince-Jean", "Coulommiers", "Scamorza", "Romans Part Dieu", "Quark", "Frinault",
+            "Chabichou du Poitou", "Le Lacandou", "Maredsous", "Fin-de-Siecle", "Button (Innes)",
+            "Washed Rind Cheese (Australian)", "Daralagjazsky", "Margotin", "Pithtviers au Foin",
+            "Cathelain", "Yarra Valley Pyramid", "Sirene", "Emlett", "Explorateur", "Bandal",
+            "Lingot Saint Bousquet d'Orb", "Gorgonzola", "Bresse Bleu", "Beer Cheese",
+            "Brinza (Burduf Brinza)", "Bakers", "Little Rydings", "Bryndza"
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_paged_list_view);
+
+        PagedListView pagedListView = findViewById(R.id.paged_list_view);
+        pagedListView.setAdapter(new CheeseAdapter());
+
+        getActionBar().setDisplayHomeAsUpEnabled(true);
+    }
+
+    /**
+     * Adapter that populates a number of items for demo purposes.
+     */
+    public static class CheeseAdapter extends RecyclerView.Adapter<CheeseAdapter.ViewHolder>
+            implements IAlphaJumpAdapter {
+        private String[] mCheeses;
+        private boolean mIsSorted;
+
+        CheeseAdapter() {
+            // Start out not being sorted.
+            mCheeses = CHEESES;
+            mIsSorted = false;
+        }
+
+        @Override
+        public CheeseAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+            View view = inflater.inflate(R.layout.alpha_jump_list_item, parent, false);
+            return new CheeseAdapter.ViewHolder(view);
+        }
+
+        @Override
+        public void onBindViewHolder(CheeseAdapter.ViewHolder holder, int position) {
+            holder.mTextView.setText(mCheeses[position]);
+        }
+
+        @Override
+        public int getItemCount() {
+            return mCheeses.length;
+        }
+
+        @Override
+        public Collection<Bucket> getAlphaJumpBuckets() {
+            if (!mIsSorted) {
+                Log.i(TAG, "Sorting...");
+                // We'll sort the first time we need to populate the buckets.
+                mCheeses = mCheeses.clone();
+                Arrays.sort(mCheeses);
+                mIsSorted = true;
+                notifyDataSetChanged();
+            }
+
+            AlphaJumpBucketer bucketer = new AlphaJumpBucketer();
+            return bucketer.createBuckets(mCheeses);
+        }
+
+        @Override
+        public void onAlphaJumpEnter() {
+            Log.i(TAG, "onAlphaJumpEnter");
+        }
+
+        @Override
+        public void onAlphaJumpLeave(Bucket bucket) {
+            Log.i(TAG, "onAlphaJumpLeave: " + bucket.getLabel());
+        }
+
+        /**
+         * ViewHolder for CheeseAdapter.
+         */
+        public static class ViewHolder extends RecyclerView.ViewHolder {
+            private final TextView mTextView;
+
+            public ViewHolder(View itemView) {
+                super(itemView);
+                mTextView = itemView.findViewById(R.id.text);
+            }
+        }
+    }
+}
diff --git a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/DrawerDemoActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/DrawerDemoActivity.java
new file mode 100644
index 0000000..ff858d4
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/DrawerDemoActivity.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2018 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.content.Context;
+import android.os.Bundle;
+
+import androidx.car.drawer.CarDrawerActivity;
+import androidx.car.drawer.CarDrawerAdapter;
+import androidx.car.drawer.DrawerItemViewHolder;
+
+/**
+ * A demo activity that will inflate a layout with a drawer.
+ */
+public class DrawerDemoActivity extends CarDrawerActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setMainContent(R.layout.drawer_activity);
+        getDrawerController().setRootAdapter(new DrawerRootAdapter(/* context= */ this));
+    }
+
+    private void openSubDrawerLevel() {
+        getDrawerController().pushAdapter(new SubItemAdapter(/* context= */ this));
+    }
+
+    /**
+     * The root drawer view that will delegate clicks to the a sub-drawer.
+     */
+    private class DrawerRootAdapter extends CarDrawerAdapter {
+        private static final int NUM_OF_ITEMS = 10;
+
+        DrawerRootAdapter(Context context) {
+            super(context, /* showDisabledListOnEmpty= */ true);
+            setTitle(getString(R.string.drawer_demo_activity_title));
+        }
+
+        @Override
+        protected void populateViewHolder(DrawerItemViewHolder holder, int position) {
+            holder.getTitle().setText(getString(R.string.drawer_demo_root_item_label, position));
+        }
+
+        @Override
+        protected int getActualItemCount() {
+            return NUM_OF_ITEMS;
+        }
+
+        @Override
+        public void onItemClick(int position) {
+            openSubDrawerLevel();
+        }
+    }
+
+    /**
+     * A sub-level of the drawer that is opened when any of the items in the root drawer view have
+     * been clicked.
+     */
+    private class SubItemAdapter extends CarDrawerAdapter {
+        private static final int NUM_OF_ITEMS = 3;
+
+        SubItemAdapter(Context context) {
+            super(context, /* showDisabledListOnEmpty= */ true);
+            setTitle(getString(R.string.drawer_demo_activity_title));
+        }
+
+        @Override
+        protected void populateViewHolder(DrawerItemViewHolder holder, int position) {
+            holder.getTitle().setText(getString(R.string.drawer_demo_subitem_label, position));
+        }
+
+        @Override
+        protected int getActualItemCount() {
+            return NUM_OF_ITEMS;
+        }
+
+        @Override
+        public void onItemClick(int position) {}
+
+    }
+}
diff --git a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SubheaderListItemActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SubheaderListItemActivity.java
new file mode 100644
index 0000000..3d5054d
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SubheaderListItemActivity.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2018 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.os.Bundle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.car.widget.ListItem;
+import androidx.car.widget.ListItemAdapter;
+import androidx.car.widget.ListItemProvider;
+import androidx.car.widget.PagedListView;
+import androidx.car.widget.SubheaderListItem;
+import androidx.car.widget.TextListItem;
+
+/**
+ * Demo activity for {@link SubheaderListItem}.
+ */
+public class SubheaderListItemActivity extends Activity {
+
+    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), ListItemAdapter.BackgroundStyle.NONE);
+        mPagedListView.setAdapter(adapter);
+        mPagedListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
+        mPagedListView.setDividerVisibilityManager(adapter);
+    }
+
+    private static class SampleProvider extends ListItemProvider {
+
+        private Context mContext;
+        private List<ListItem> mItems;
+
+        private ListItemProvider.ListProvider mListProvider;
+
+        SampleProvider(Context context) {
+            mContext = context;
+            mItems = new ArrayList<>();
+
+            SubheaderListItem subheaderItem;
+            TextListItem item;
+
+            subheaderItem = new SubheaderListItem(mContext,
+                    "subheader matching items without start margin");
+            subheaderItem.setTextStartMarginType(SubheaderListItem.TEXT_START_MARGIN_TYPE_NONE);
+            subheaderItem.setHideDivider(true);
+            mItems.add(subheaderItem);
+
+            item = new TextListItem(mContext);
+            item.setTitle("item");
+            mItems.add(item);
+
+            item = new TextListItem(mContext);
+            item.setTitle("item");
+            mItems.add(item);
+
+            item = new TextListItem(mContext);
+            item.setTitle("item - hides following divider");
+            item.setHideDivider(true);
+            mItems.add(item);
+
+            // ========================
+
+            item = new TextListItem(mContext);
+            item.setPrimaryActionEmptyIcon();
+            item.setTitle("Header");
+            item.setBody("header text with more words");
+            item.setHideDivider(true);
+            mItems.add(item);
+
+            subheaderItem = new SubheaderListItem(mContext,
+                    "subheader matching items with no icon");
+            subheaderItem.setTextStartMarginType(
+                    SubheaderListItem.TEXT_START_MARGIN_TYPE_SMALL);
+            subheaderItem.setHideDivider(true);
+            mItems.add(subheaderItem);
+
+            item = new TextListItem(mContext);
+            item.setPrimaryActionEmptyIcon();
+            item.setTitle("item");
+            mItems.add(item);
+
+            item = new TextListItem(mContext);
+            item.setPrimaryActionEmptyIcon();
+            item.setTitle("item");
+            mItems.add(item);
+
+            item = new TextListItem(mContext);
+            item.setPrimaryActionEmptyIcon();
+            item.setTitle("item - hides following divider");
+            item.setHideDivider(true);
+            mItems.add(item);
+
+            // ========================
+
+            subheaderItem = new SubheaderListItem(mContext,
+                    "subheader matching items with small icons");
+            subheaderItem.setTextStartMarginType(
+                    SubheaderListItem.TEXT_START_MARGIN_TYPE_SMALL);
+            mItems.add(subheaderItem);
+
+            item = new TextListItem(mContext);
+            item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+            item.setTitle("item");
+            mItems.add(item);
+
+            item = new TextListItem(mContext);
+            item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+            item.setTitle("item");
+            mItems.add(item);
+
+            // ========================
+
+            subheaderItem = new SubheaderListItem(mContext,
+                    "subheader matching items with large icons");
+            subheaderItem.setTextStartMarginType(
+                    SubheaderListItem.TEXT_START_MARGIN_TYPE_LARGE);
+            mItems.add(subheaderItem);
+
+            item = new TextListItem(mContext);
+            item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
+            item.setTitle("item");
+            mItems.add(item);
+
+            item = new TextListItem(mContext);
+            item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
+            item.setTitle("item");
+            mItems.add(item);
+
+            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/androidx/car/TextListItemActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/TextListItemActivity.java
index a0972e9..d2106a2 100644
--- a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/TextListItemActivity.java
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/TextListItemActivity.java
@@ -55,7 +55,7 @@
 
         SampleProvider provider = new SampleProvider(this);
         ListItemAdapter adapter = new ListItemAdapter(this, provider,
-                ListItemAdapter.BackgroundStyle.NONE);
+                ListItemAdapter.BackgroundStyle.SOLID);
 
         final boolean[] hideDivider = {true};
         // Demonstrate how to update list item post construction.
diff --git a/samples/SupportCarDemos/src/main/res/layout/alpha_jump_list_item.xml b/samples/SupportCarDemos/src/main/res/layout/alpha_jump_list_item.xml
new file mode 100644
index 0000000..f82247d
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/res/layout/alpha_jump_list_item.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 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.
+  -->
+<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"
+    android:layout_height="52dp"
+    app:cardBackgroundColor="@color/car_card">
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="end"
+        android:textAppearance="@style/TextAppearance.Car.Body1"/>
+</androidx.car.widget.ColumnCardView>
\ No newline at end of file
diff --git a/samples/SupportCarDemos/src/main/res/layout/drawer_activity.xml b/samples/SupportCarDemos/src/main/res/layout/drawer_activity.xml
new file mode 100644
index 0000000..791a615
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/res/layout/drawer_activity.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:background="?android:attr/colorPrimary"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    <androidx.car.widget.PagedListView
+        android:id="@+id/list"
+        android:layout_gravity="center"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:gutter="both"
+        app:listContentTopOffset="@dimen/car_app_bar_height"
+        app:scrollBarEnabled="true"
+        app:scrollBarTopMargin="@dimen/car_app_bar_height"
+        app:showPagedListViewDivider="false" />
+</FrameLayout>
diff --git a/samples/SupportCarDemos/src/main/res/values/strings.xml b/samples/SupportCarDemos/src/main/res/values/strings.xml
index 2e278e2..9a6fc1a 100644
--- a/samples/SupportCarDemos/src/main/res/values/strings.xml
+++ b/samples/SupportCarDemos/src/main/res/values/strings.xml
@@ -49,5 +49,10 @@
         line 28\n
         line 29\n
     </string>
+
+    <!-- String for the drawer demo -->
+    <string name="drawer_demo_activity_title">Drawer Demo</string>
+    <string name="drawer_demo_root_item_label">Item %1$d</string>
+    <string name="drawer_demo_subitem_label">SubItem %1$d</string>
 </resources>
 
diff --git a/samples/SupportCarDemos/src/main/res/values/themes.xml b/samples/SupportCarDemos/src/main/res/values/themes.xml
index ab7f58b..2b50c23 100644
--- a/samples/SupportCarDemos/src/main/res/values/themes.xml
+++ b/samples/SupportCarDemos/src/main/res/values/themes.xml
@@ -18,4 +18,11 @@
     <!-- The main theme for all activities within the Car Demo. -->
     <style name="CarTheme" parent="Theme.Car.Light.NoActionBar">
     </style>
+
+    <!-- A theme for activities within the Car Demo that have a drawer. -->
+    <style name="CarDrawerTheme" parent="Theme.Car.Light.NoActionBar.Drawer">
+        <item name="android:colorPrimary">@color/car_grey_200</item>
+        <item name="android:colorPrimaryDark">@color/car_grey_400</item>
+        <item name="drawerClosedHeaderColor">@color/car_title2_dark</item>
+    </style>
 </resources>
diff --git a/samples/SupportSliceDemos/build.gradle b/samples/SupportSliceDemos/build.gradle
index 53b1bec..4d518ff 100644
--- a/samples/SupportSliceDemos/build.gradle
+++ b/samples/SupportSliceDemos/build.gradle
@@ -43,5 +43,5 @@
 }
 
 supportTestApp {
-    minSdkVersion = 26
+    minSdkVersion = 19
 }
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
index 8f94bb9..08547fb 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
@@ -146,7 +146,7 @@
                 IconCompat.createWithResource(getContext(), R.drawable.weather_1),
                 "Weather is happening!");
         return new ListBuilder(getContext(), sliceUri, INFINITY)
-                .addGrid(gb -> gb
+                .addGridRow(gb -> gb
                         .setPrimaryAction(primaryAction)
                         .addCell(cb -> cb
                                 .addImage(IconCompat.createWithResource(getContext(),
@@ -182,11 +182,17 @@
     }
 
     private Slice createGallery(Uri sliceUri) {
+        SliceAction primaryAction = new SliceAction(
+                getBroadcastIntent(ACTION_TOAST, "open photo album"),
+                IconCompat.createWithResource(getContext(), R.drawable.slices_1),
+                LARGE_IMAGE,
+                "Open photo album");
         return new ListBuilder(getContext(), sliceUri, INFINITY)
                 .setColor(0xff4285F4)
                 .addRow(b -> b
                         .setTitle("Family trip to Hawaii")
-                        .setSubtitle("Sep 30, 2017 - Oct 2, 2017"))
+                        .setSubtitle("Sep 30, 2017 - Oct 2, 2017")
+                        .setPrimaryAction(primaryAction))
                 .addAction(new SliceAction(
                         getBroadcastIntent(ACTION_TOAST, "cast photo album"),
                         IconCompat.createWithResource(getContext(), R.drawable.ic_cast),
@@ -195,7 +201,7 @@
                         getBroadcastIntent(ACTION_TOAST, "share photo album"),
                         IconCompat.createWithResource(getContext(), R.drawable.ic_share),
                         "Share photo album"))
-                .addGrid(b -> b
+                .addGridRow(b -> b
                         .addCell(cb -> cb
                                 .addImage(IconCompat.createWithResource(getContext(),
                                         R.drawable.slices_1),
@@ -224,7 +230,7 @@
                                 .addImage(IconCompat.createWithResource(getContext(),
                                         R.drawable.slices_4),
                                         LARGE_IMAGE))
-                        .addSeeMoreAction(getBroadcastIntent(ACTION_TOAST, "see your gallery"))
+                        .setSeeMoreAction(getBroadcastIntent(ACTION_TOAST, "see your gallery"))
                         .setContentDescription("Images from your trip to Hawaii"))
                 .build();
     }
@@ -239,9 +245,9 @@
                     ICON_IMAGE);
             cb.setContentIntent(pi);
             cb.addTitleText("All cats");
-            gb.addSeeMoreCell(cb);
+            gb.setSeeMoreCell(cb);
         } else {
-            gb.addSeeMoreAction(pi);
+            gb.setSeeMoreAction(pi);
         }
         gb.addCell(new GridRowBuilder.CellBuilder(gb)
                 .addImage(IconCompat.createWithResource(getContext(), R.drawable.cat_1),
@@ -319,7 +325,7 @@
                 .setColor(0xff3949ab)
                 .setHeader(b -> b
                         .setTitle("Mady Pitza")
-                        .setSummarySubtitle("Called " + lastCalledString)
+                        .setSummary("Called " + lastCalledString)
                         .setPrimaryAction(primaryAction))
                 .addRow(b -> b
                         .setTitleItem(
@@ -398,13 +404,13 @@
                 .addAction(new SliceAction(getBroadcastIntent(ACTION_TOAST, "contact host"),
                         IconCompat.createWithResource(getContext(), R.drawable.ic_text),
                         "Contact host"))
-                .addGrid(b -> b
+                .addGridRow(b -> b
                         .addCell(cb -> cb
                                 .addImage(IconCompat.createWithResource(getContext(),
                                         R.drawable.reservation),
                                         LARGE_IMAGE)
                                 .setContentDescription("Image of your reservation in Seattle")))
-                .addGrid(b -> b
+                .addGridRow(b -> b
                         .addCell(cb -> cb
                                 .addTitleText("Check In")
                                 .addText("12:00 PM, Feb 1"))
@@ -430,7 +436,7 @@
                 .setHeader(b -> b
                         .setTitle("Get ride")
                         .setSubtitle(headerSubtitle)
-                        .setSummarySubtitle("Ride to work in 12 min | Ride home in 1 hour 45 min")
+                        .setSummary("Ride to work in 12 min | Ride home in 1 hour 45 min")
                         .setPrimaryAction(primaryAction))
                 .addRow(b -> b
                         .setTitle("Work")
@@ -479,7 +485,8 @@
 
     private Slice createWifiSlice(Uri sliceUri) {
         // Get wifi state
-        WifiManager wifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        WifiManager wifiManager = (WifiManager) getContext()
+                .getApplicationContext().getSystemService(Context.WIFI_SERVICE);
         int wifiState = wifiManager.getWifiState();
         boolean wifiEnabled = false;
         String state;
@@ -545,39 +552,49 @@
 
         // Add see more intent
         if (TEST_CUSTOM_SEE_MORE) {
-            lb.addSeeMoreRow(rb -> rb
+            lb.setSeeMoreRow(rb -> rb
                     .setTitle("See all available networks")
                     .addEndItem(
                             IconCompat.createWithResource(getContext(), R.drawable.ic_right_caret),
                             SMALL_IMAGE)
                     .setPrimaryAction(primaryAction));
         } else {
-            lb.addSeeMoreAction(primaryAction.getAction());
+            lb.setSeeMoreAction(primaryAction.getAction());
         }
         return lb.build();
     }
 
     private Slice createStarRatingInputRange(Uri sliceUri) {
+        IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
+        SliceAction primaryAction =
+                new SliceAction(getBroadcastIntent(ACTION_TOAST, "open star rating"), icon, "Rate");
         return new ListBuilder(getContext(), sliceUri, INFINITY)
                 .setColor(0xffff4081)
                 .addInputRange(c -> c
                         .setTitle("Star rating")
-                        .setThumb(
-                                IconCompat.createWithResource(getContext(), R.drawable.ic_star_on))
-                        .setAction(getBroadcastIntent(ACTION_TOAST_RANGE_VALUE, null))
+                        .setSubtitle("Pick a rating from 0 to 5")
+                        .setThumb(icon)
+                        .setInputAction(getBroadcastIntent(ACTION_TOAST_RANGE_VALUE, null))
                         .setMax(5)
                         .setValue(3)
+                        .setPrimaryAction(primaryAction)
                         .setContentDescription("Slider for star ratings"))
                 .build();
     }
 
     private Slice createDownloadProgressRange(Uri sliceUri) {
+        IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
+        SliceAction primaryAction =
+                new SliceAction(
+                        getBroadcastIntent(ACTION_TOAST, "open download"), icon, "Download");
         return new ListBuilder(getContext(), sliceUri, INFINITY)
                 .setColor(0xffff4081)
                 .addRange(c -> c
                         .setTitle("Download progress")
+                        .setSubtitle("Download is happening")
                         .setMax(100)
-                        .setValue(75))
+                        .setValue(75)
+                        .setPrimaryAction(primaryAction))
                 .build();
     }
 
@@ -657,7 +674,7 @@
                                 updating || TextUtils.isEmpty(title))
                         .setSubtitle(subtitle,
                                 updating || TextUtils.isEmpty(subtitle)))
-                .addGrid(gb -> gb
+                .addGridRow(gb -> gb
                         .addCell(cb -> cb
                                 .addImage(IconCompat.createWithResource(getContext(),
                                         R.drawable.ic_home),
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
index 174705e..673eea1 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
@@ -35,13 +35,14 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.SubMenu;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.CursorAdapter;
 import android.widget.SearchView;
 import android.widget.SimpleCursorAdapter;
+import android.widget.Toast;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.Toolbar;
 import androidx.lifecycle.LiveData;
@@ -52,14 +53,13 @@
 import androidx.slice.widget.SliceView;
 
 import java.util.ArrayList;
-import java.util.Comparator;
+import java.util.Collections;
 import java.util.List;
 
 /**
  * Example use of SliceView. Uses a search bar to select/auto-complete a slice uri which is
  * then displayed in the selected mode with SliceView.
  */
-@RequiresApi(api = 28)
 public class SliceBrowser extends AppCompatActivity implements SliceView.OnSliceActionListener {
 
     private static final String TAG = "SlicePresenter";
@@ -185,9 +185,9 @@
 
     private void authAllSlices() {
         List<ApplicationInfo> packages = getPackageManager().getInstalledApplications(0);
-        packages.forEach(info -> {
+        for (ApplicationInfo info : packages) {
             grantPackage(info.packageName);
-        });
+        }
     }
 
     private void grantPackage(String packageName) {
@@ -256,19 +256,16 @@
         final MatrixCursor c = new MatrixCursor(new String[]{BaseColumns._ID, "uri"});
         ArrayMap<String, Integer> ranking = new ArrayMap<>();
         ArrayList<String> suggestions = new ArrayList();
-        mSliceUris.forEach(uri -> {
+        for (Uri uri : mSliceUris) {
+
             String uriString = uri.toString();
             if (uriString.contains(query)) {
                 ranking.put(uriString, uriString.indexOf(query));
                 suggestions.add(uriString);
             }
-        });
-        suggestions.sort(new Comparator<String>() {
-            @Override
-            public int compare(String o1, String o2) {
-                return Integer.compare(ranking.get(o1), ranking.get(o2));
-            }
-        });
+        }
+        Collections.sort(suggestions, (o1, o2) ->
+                Integer.compare(ranking.get(o1), ranking.get(o2)));
         for (int i = 0; i < suggestions.size(); i++) {
             c.addRow(new Object[]{i, suggestions.get(i)});
         }
@@ -287,10 +284,24 @@
                 R.layout.slice_view, mContainer, false)
                 : new SliceView(getApplicationContext());
         v.setOnSliceActionListener(this);
+        v.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Toast.makeText(getApplicationContext(),
+                        "Custom listener clicked", Toast.LENGTH_SHORT).show();
+            }
+        });
         if (mSliceLiveData != null) {
             mSliceLiveData.removeObservers(this);
         }
         v.setScrollable(SCROLLING_ENABLED);
+        v.setOnLongClickListener(new View.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View v) {
+                Toast.makeText(getApplicationContext(), "LONGPRESS !!", Toast.LENGTH_SHORT).show();
+                return true;
+            }
+        });
         return v;
     }
 }
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_camera.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_camera.xml
index 74b6bf9..bd3436b 100644
--- a/samples/SupportSliceDemos/src/main/res/drawable/ic_camera.xml
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_camera.xml
@@ -24,8 +24,8 @@
         android:pathData="M 12 8.8 C 13.7673111995 8.8 15.2 10.2326888005 15.2 12 C 15.2 13.7673111995 13.7673111995 15.2 12 15.2 C 10.2326888005 15.2 8.8 13.7673111995 8.8 12 C 8.8 10.2326888005 10.2326888005 8.8 12 8.8 Z" />
     <path
         android:fillColor="#000000"
-        android:pathData="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1 .9 2 2 2h16c1.1 0 2-.9
-2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5
+        android:pathData="M9 2L7.17 4H4c-1.1 0-2 0.9-2 2v12c0 1.1 0.9 2 2 2h16c1.1 0 2-0.9
+2-2V6c0-1.1-0.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5
 5-2.24 5-5 5z" />
     <path
         android:pathData="M0 0h24v24H0z" />
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_car.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_car.xml
index 6bab660..0f37425 100644
--- a/samples/SupportSliceDemos/src/main/res/drawable/ic_car.xml
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_car.xml
@@ -21,11 +21,11 @@
 
     <path
         android:fillColor="#000000"
-        android:pathData="M18.92 6.01C18.72 5.42 18.16 5 17.5 5h-11c-.66 0-1.21 .42 -1.42 1.01L3 12v8c0
-.55 .45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55 .45 1 1 1h1c.55 0 1-.45
-1-1v-8l-2.08-5.99zM6.5 16c-.83 0-1.5-.67-1.5-1.5S5.67 13 6.5 13s1.5 .67 1.5
-1.5S7.33 16 6.5 16zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5 .67 1.5
-1.5-.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z" />
+        android:pathData="M18.92 6.01C18.72 5.42 18.16 5 17.5 5h-11c-0.66 0-1.21 0.42 -1.42 1.01L3 12v8c0
+0.55 0.45 1 1 1h1c0.55 0 1-0.45 1-1v-1h12v1c0 0.55 0.45 1 1 1h1c0.55 0 1-0.45
+1-1v-8l-2.08-5.99zM6.5 16c-0.83 0-1.5-0.67-1.5-1.5S5.67 13 6.5 13s1.5 0.67 1.5
+1.5S7.33 16 6.5 16zm11 0c-0.83 0-1.5-0.67-1.5-1.5s0.67-1.5 1.5-1.5 1.5 0.67 1.5
+1.5-0.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z" />
     <path
         android:pathData="M0 0h24v24H0z" />
 </vector>
\ No newline at end of file
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_work.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_work.xml
index 6806402..9e28e15 100644
--- a/samples/SupportSliceDemos/src/main/res/drawable/ic_work.xml
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_work.xml
@@ -23,6 +23,6 @@
         android:pathData="M0 0h24v24H0z" />
     <path
         android:fillColor="#000000"
-        android:pathData="M20 6h-4V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H4c-1.11 0-1.99 .89 -1.99 2L2
-19c0 1.11 .89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-6 0h-4V4h4v2z" />
+        android:pathData="M20 6h-4V4c0-1.11-0.89-2-2-2h-4c-1.11 0-2 0.89-2 2v2H4c-1.11 0-1.99 0.89 -1.99 2L2
+19c0 1.11 0.89 2 2 2h16c1.11 0 2-0.89 2-2V8c0-1.11-0.89-2-2-2zm-6 0h-4V4h4v2z" />
 </vector>
\ No newline at end of file
diff --git a/samples/SupportSliceDemos/src/main/res/layout/activity_layout.xml b/samples/SupportSliceDemos/src/main/res/layout/activity_layout.xml
index feb2119..b1acf00 100644
--- a/samples/SupportSliceDemos/src/main/res/layout/activity_layout.xml
+++ b/samples/SupportSliceDemos/src/main/res/layout/activity_layout.xml
@@ -42,7 +42,7 @@
                 android:background="?android:attr/selectableItemBackground"
                 android:contentInsetStart="0dp"
                 android:contentInsetStartWithNavigation="0dp"
-                android:theme="?android:attr/actionBarTheme">
+                android:theme="?attr/actionBarTheme">
                 <SearchView
                     android:id="@+id/search_view"
                     android:layout_width="match_parent"
diff --git a/samples/SupportSliceDemos/src/main/res/values/styles.xml b/samples/SupportSliceDemos/src/main/res/values/styles.xml
index 38d7298..dc139b6 100644
--- a/samples/SupportSliceDemos/src/main/res/values/styles.xml
+++ b/samples/SupportSliceDemos/src/main/res/values/styles.xml
@@ -18,7 +18,7 @@
 <resources>
 
     <!-- Base application theme. -->
-    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+    <style name="AppTheme" parent="Theme.AppCompat.Light">
         <!-- Customize your theme here. -->
         <item name="colorPrimary">@color/colorPrimary</item>
         <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
diff --git a/settings.gradle b/settings.gradle
index 581897d..65ef3f4 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -44,6 +44,7 @@
 includeProject(":contentpaging", "content")
 includeProject(":coordinatorlayout", "coordinatorlayout")
 includeProject(":core", "compat")
+includeProject(":core-ktx", "core/ktx")
 includeProject(":cursoradapter", "cursoradapter")
 includeProject(":customview", "customview")
 includeProject(":documentfile", "documentfile")
@@ -58,6 +59,7 @@
 includeProject(":heifwriter", "heifwriter")
 includeProject(":interpolator", "interpolator")
 includeProject(":jetifier-core", "jetifier/jetifier/core")
+includeProject(":jetifier-processor", "jetifier/jetifier/processor")
 includeProject(":jetifier-gradle-plugin", "jetifier/jetifier/gradle-plugin")
 includeProject(":jetifier-standalone", "jetifier/jetifier/standalone")
 includeProject(":jetifier-preprocessor", "jetifier/jetifier/preprocessor")
diff --git a/slices/builders/api/current.txt b/slices/builders/api/current.txt
index 3ff195a..4538b46 100644
--- a/slices/builders/api/current.txt
+++ b/slices/builders/api/current.txt
@@ -3,10 +3,10 @@
   public deprecated class GridBuilder extends androidx.slice.builders.TemplateSliceBuilder {
     ctor public GridBuilder(androidx.slice.builders.ListBuilder);
     method public androidx.slice.builders.GridBuilder addCell(androidx.slice.builders.GridBuilder.CellBuilder);
-    method public androidx.slice.builders.GridBuilder addCell(java.util.function.Consumer<androidx.slice.builders.GridBuilder.CellBuilder>);
+    method public androidx.slice.builders.GridBuilder addCell(androidx.core.util.Consumer<androidx.slice.builders.GridBuilder.CellBuilder>);
     method public androidx.slice.builders.GridBuilder addSeeMoreAction(android.app.PendingIntent);
     method public androidx.slice.builders.GridBuilder addSeeMoreCell(androidx.slice.builders.GridBuilder.CellBuilder);
-    method public androidx.slice.builders.GridBuilder addSeeMoreCell(java.util.function.Consumer<androidx.slice.builders.GridBuilder.CellBuilder>);
+    method public androidx.slice.builders.GridBuilder addSeeMoreCell(androidx.core.util.Consumer<androidx.slice.builders.GridBuilder.CellBuilder>);
     method public androidx.slice.builders.GridBuilder setContentDescription(java.lang.CharSequence);
     method public androidx.slice.builders.GridBuilder setPrimaryAction(androidx.slice.builders.SliceAction);
     field public static final deprecated int ICON_IMAGE = 0; // 0x0
@@ -36,12 +36,15 @@
   public class GridRowBuilder extends androidx.slice.builders.TemplateSliceBuilder {
     ctor public GridRowBuilder(androidx.slice.builders.ListBuilder);
     method public androidx.slice.builders.GridRowBuilder addCell(androidx.slice.builders.GridRowBuilder.CellBuilder);
-    method public androidx.slice.builders.GridRowBuilder addCell(java.util.function.Consumer<androidx.slice.builders.GridRowBuilder.CellBuilder>);
-    method public androidx.slice.builders.GridRowBuilder addSeeMoreAction(android.app.PendingIntent);
-    method public androidx.slice.builders.GridRowBuilder addSeeMoreCell(androidx.slice.builders.GridRowBuilder.CellBuilder);
-    method public androidx.slice.builders.GridRowBuilder addSeeMoreCell(java.util.function.Consumer<androidx.slice.builders.GridRowBuilder.CellBuilder>);
+    method public androidx.slice.builders.GridRowBuilder addCell(androidx.core.util.Consumer<androidx.slice.builders.GridRowBuilder.CellBuilder>);
+    method public deprecated androidx.slice.builders.GridRowBuilder addSeeMoreAction(android.app.PendingIntent);
+    method public deprecated androidx.slice.builders.GridRowBuilder addSeeMoreCell(androidx.slice.builders.GridRowBuilder.CellBuilder);
+    method public deprecated androidx.slice.builders.GridRowBuilder addSeeMoreCell(androidx.core.util.Consumer<androidx.slice.builders.GridRowBuilder.CellBuilder>);
     method public androidx.slice.builders.GridRowBuilder setContentDescription(java.lang.CharSequence);
     method public androidx.slice.builders.GridRowBuilder setPrimaryAction(androidx.slice.builders.SliceAction);
+    method public androidx.slice.builders.GridRowBuilder setSeeMoreAction(android.app.PendingIntent);
+    method public androidx.slice.builders.GridRowBuilder setSeeMoreCell(androidx.slice.builders.GridRowBuilder.CellBuilder);
+    method public androidx.slice.builders.GridRowBuilder setSeeMoreCell(androidx.core.util.Consumer<androidx.slice.builders.GridRowBuilder.CellBuilder>);
   }
 
   public static final class GridRowBuilder.CellBuilder extends androidx.slice.builders.TemplateSliceBuilder {
@@ -62,22 +65,25 @@
     ctor public ListBuilder(android.content.Context, android.net.Uri, long);
     method public androidx.slice.builders.ListBuilder addAction(androidx.slice.builders.SliceAction);
     method public deprecated androidx.slice.builders.ListBuilder addGrid(androidx.slice.builders.GridBuilder);
-    method public deprecated androidx.slice.builders.ListBuilder addGrid(java.util.function.Consumer<androidx.slice.builders.GridBuilder>);
+    method public deprecated androidx.slice.builders.ListBuilder addGrid(androidx.core.util.Consumer<androidx.slice.builders.GridBuilder>);
     method public androidx.slice.builders.ListBuilder addGridRow(androidx.slice.builders.GridRowBuilder);
-    method public androidx.slice.builders.ListBuilder addGridRow(java.util.function.Consumer<androidx.slice.builders.GridRowBuilder>);
+    method public androidx.slice.builders.ListBuilder addGridRow(androidx.core.util.Consumer<androidx.slice.builders.GridRowBuilder>);
     method public androidx.slice.builders.ListBuilder addInputRange(androidx.slice.builders.ListBuilder.InputRangeBuilder);
-    method public androidx.slice.builders.ListBuilder addInputRange(java.util.function.Consumer<androidx.slice.builders.ListBuilder.InputRangeBuilder>);
+    method public androidx.slice.builders.ListBuilder addInputRange(androidx.core.util.Consumer<androidx.slice.builders.ListBuilder.InputRangeBuilder>);
     method public androidx.slice.builders.ListBuilder addRange(androidx.slice.builders.ListBuilder.RangeBuilder);
-    method public androidx.slice.builders.ListBuilder addRange(java.util.function.Consumer<androidx.slice.builders.ListBuilder.RangeBuilder>);
+    method public androidx.slice.builders.ListBuilder addRange(androidx.core.util.Consumer<androidx.slice.builders.ListBuilder.RangeBuilder>);
     method public androidx.slice.builders.ListBuilder addRow(androidx.slice.builders.ListBuilder.RowBuilder);
-    method public androidx.slice.builders.ListBuilder addRow(java.util.function.Consumer<androidx.slice.builders.ListBuilder.RowBuilder>);
-    method public androidx.slice.builders.ListBuilder addSeeMoreAction(android.app.PendingIntent);
-    method public androidx.slice.builders.ListBuilder addSeeMoreRow(androidx.slice.builders.ListBuilder.RowBuilder);
-    method public androidx.slice.builders.ListBuilder addSeeMoreRow(java.util.function.Consumer<androidx.slice.builders.ListBuilder.RowBuilder>);
+    method public androidx.slice.builders.ListBuilder addRow(androidx.core.util.Consumer<androidx.slice.builders.ListBuilder.RowBuilder>);
+    method public deprecated androidx.slice.builders.ListBuilder addSeeMoreAction(android.app.PendingIntent);
+    method public deprecated androidx.slice.builders.ListBuilder addSeeMoreRow(androidx.slice.builders.ListBuilder.RowBuilder);
+    method public deprecated androidx.slice.builders.ListBuilder addSeeMoreRow(androidx.core.util.Consumer<androidx.slice.builders.ListBuilder.RowBuilder>);
     method public androidx.slice.builders.ListBuilder setColor(int);
     method public androidx.slice.builders.ListBuilder setHeader(androidx.slice.builders.ListBuilder.HeaderBuilder);
-    method public androidx.slice.builders.ListBuilder setHeader(java.util.function.Consumer<androidx.slice.builders.ListBuilder.HeaderBuilder>);
+    method public androidx.slice.builders.ListBuilder setHeader(androidx.core.util.Consumer<androidx.slice.builders.ListBuilder.HeaderBuilder>);
     method public androidx.slice.builders.ListBuilder setKeywords(java.util.List<java.lang.String>);
+    method public androidx.slice.builders.ListBuilder setSeeMoreAction(android.app.PendingIntent);
+    method public androidx.slice.builders.ListBuilder setSeeMoreRow(androidx.slice.builders.ListBuilder.RowBuilder);
+    method public androidx.slice.builders.ListBuilder setSeeMoreRow(androidx.core.util.Consumer<androidx.slice.builders.ListBuilder.RowBuilder>);
     field public static final int ICON_IMAGE = 0; // 0x0
     field public static final long INFINITY = -1L; // 0xffffffffffffffffL
     field public static final int LARGE_IMAGE = 2; // 0x2
@@ -91,17 +97,22 @@
     method public androidx.slice.builders.ListBuilder.HeaderBuilder setPrimaryAction(androidx.slice.builders.SliceAction);
     method public androidx.slice.builders.ListBuilder.HeaderBuilder setSubtitle(java.lang.CharSequence);
     method public androidx.slice.builders.ListBuilder.HeaderBuilder setSubtitle(java.lang.CharSequence, boolean);
-    method public androidx.slice.builders.ListBuilder.HeaderBuilder setSummarySubtitle(java.lang.CharSequence);
-    method public androidx.slice.builders.ListBuilder.HeaderBuilder setSummarySubtitle(java.lang.CharSequence, boolean);
+    method public androidx.slice.builders.ListBuilder.HeaderBuilder setSummary(java.lang.CharSequence);
+    method public androidx.slice.builders.ListBuilder.HeaderBuilder setSummary(java.lang.CharSequence, boolean);
+    method public deprecated androidx.slice.builders.ListBuilder.HeaderBuilder setSummarySubtitle(java.lang.CharSequence);
+    method public deprecated androidx.slice.builders.ListBuilder.HeaderBuilder setSummarySubtitle(java.lang.CharSequence, boolean);
     method public androidx.slice.builders.ListBuilder.HeaderBuilder setTitle(java.lang.CharSequence);
     method public androidx.slice.builders.ListBuilder.HeaderBuilder setTitle(java.lang.CharSequence, boolean);
   }
 
   public static class ListBuilder.InputRangeBuilder extends androidx.slice.builders.TemplateSliceBuilder {
     ctor public ListBuilder.InputRangeBuilder(androidx.slice.builders.ListBuilder);
-    method public androidx.slice.builders.ListBuilder.InputRangeBuilder setAction(android.app.PendingIntent);
+    method public deprecated androidx.slice.builders.ListBuilder.InputRangeBuilder setAction(android.app.PendingIntent);
     method public androidx.slice.builders.ListBuilder.InputRangeBuilder setContentDescription(java.lang.CharSequence);
+    method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(android.app.PendingIntent);
     method public androidx.slice.builders.ListBuilder.InputRangeBuilder setMax(int);
+    method public androidx.slice.builders.ListBuilder.InputRangeBuilder setPrimaryAction(androidx.slice.builders.SliceAction);
+    method public androidx.slice.builders.ListBuilder.InputRangeBuilder setSubtitle(java.lang.CharSequence);
     method public deprecated androidx.slice.builders.ListBuilder.InputRangeBuilder setThumb(android.graphics.drawable.Icon);
     method public androidx.slice.builders.ListBuilder.InputRangeBuilder setThumb(androidx.core.graphics.drawable.IconCompat);
     method public androidx.slice.builders.ListBuilder.InputRangeBuilder setTitle(java.lang.CharSequence);
@@ -112,6 +123,8 @@
     ctor public ListBuilder.RangeBuilder(androidx.slice.builders.ListBuilder);
     method public androidx.slice.builders.ListBuilder.RangeBuilder setContentDescription(java.lang.CharSequence);
     method public androidx.slice.builders.ListBuilder.RangeBuilder setMax(int);
+    method public androidx.slice.builders.ListBuilder.RangeBuilder setPrimaryAction(androidx.slice.builders.SliceAction);
+    method public androidx.slice.builders.ListBuilder.RangeBuilder setSubtitle(java.lang.CharSequence);
     method public androidx.slice.builders.ListBuilder.RangeBuilder setTitle(java.lang.CharSequence);
     method public androidx.slice.builders.ListBuilder.RangeBuilder setValue(int);
   }
diff --git a/slices/builders/build.gradle b/slices/builders/build.gradle
index 1bf4c29..907a0b4 100644
--- a/slices/builders/build.gradle
+++ b/slices/builders/build.gradle
@@ -35,5 +35,5 @@
     mavenGroup = LibraryGroups.SLICE
     inceptionYear = "2017"
     description = "A set of builders to create templates using SliceProvider APIs"
-    minSdkVersion = 24
+    minSdkVersion = 19
 }
diff --git a/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java
index bd6dbf4..7c672d7 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java
@@ -21,17 +21,15 @@
 import android.app.PendingIntent;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
-import android.os.Build;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Consumer;
 import androidx.slice.builders.impl.TemplateBuilderImpl;
 
-import java.util.function.Consumer;
-
 
 /**
  * Builder to construct a row of slice content in a grid format.
@@ -94,7 +92,6 @@
     /**
      * Add a cell to the grid builder.
      */
-    @RequiresApi(Build.VERSION_CODES.N)
     @NonNull
     public GridBuilder addCell(@NonNull Consumer<CellBuilder> c) {
         CellBuilder b = new CellBuilder(this);
@@ -123,7 +120,7 @@
             throw new IllegalStateException("Trying to add see more cell when one has "
                     + "already been added");
         }
-        mImpl.addSeeMoreCell((TemplateBuilderImpl) builder.mImpl);
+        mImpl.setSeeMoreCell((TemplateBuilderImpl) builder.mImpl);
         mHasSeeMore = true;
         return this;
     }
@@ -143,7 +140,6 @@
      * a row or action has been previously added.
      * </p>
      */
-    @RequiresApi(Build.VERSION_CODES.N)
     @NonNull
     public GridBuilder addSeeMoreCell(@NonNull Consumer<CellBuilder> c) {
         CellBuilder b = new CellBuilder(this);
@@ -166,7 +162,7 @@
             throw new IllegalStateException("Trying to add see more action when one has "
                     + "already been added");
         }
-        mImpl.addSeeMoreAction(intent);
+        mImpl.setSeeMoreAction(intent);
         mHasSeeMore = true;
         return this;
     }
@@ -301,6 +297,7 @@
          */
         @NonNull
         @Deprecated
+        @RequiresApi(23)
         public CellBuilder addLargeImage(@NonNull Icon image) {
             return addImage(image, ListBuilder.LARGE_IMAGE, false /* isLoading */);
         }
@@ -317,6 +314,7 @@
          */
         @NonNull
         @Deprecated
+        @RequiresApi(23)
         public CellBuilder addLargeImage(@Nullable Icon image, boolean isLoading) {
             return addImage(image, ListBuilder.LARGE_IMAGE, isLoading);
         }
@@ -329,6 +327,7 @@
          */
         @NonNull
         @Deprecated
+        @RequiresApi(23)
         public CellBuilder addImage(@NonNull Icon image) {
             return addImage(image, ListBuilder.SMALL_IMAGE, false /* isLoading */);
         }
@@ -338,6 +337,7 @@
          */
         @NonNull
         @Deprecated
+        @RequiresApi(23)
         public CellBuilder addImage(@Nullable Icon image, boolean isLoading) {
             return addImage(image, ListBuilder.SMALL_IMAGE, isLoading);
         }
@@ -347,6 +347,7 @@
          */
         @NonNull
         @Deprecated
+        @RequiresApi(23)
         public CellBuilder addImage(@NonNull Icon image, @ListBuilder.ImageMode int imageMode) {
             return addImage(image, imageMode, false /* isLoading */);
         }
@@ -356,6 +357,7 @@
          */
         @NonNull
         @Deprecated
+        @RequiresApi(23)
         public CellBuilder addImage(@Nullable Icon image, @ListBuilder.ImageMode int imageMode,
                 boolean isLoading) {
             mImpl.addImage(IconCompat.createFromIcon(image), imageMode, isLoading);
diff --git a/slices/builders/src/main/java/androidx/slice/builders/GridRowBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/GridRowBuilder.java
index 6213a4f..9e08bb9 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/GridRowBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/GridRowBuilder.java
@@ -20,24 +20,51 @@
 
 import android.app.PendingIntent;
 import android.net.Uri;
-import android.os.Build;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Consumer;
 import androidx.slice.builders.impl.TemplateBuilderImpl;
 
-import java.util.function.Consumer;
-
 
 /**
- * Builder to construct a row of slice content in a grid format.
+ * Builder to construct a grid row which may be added as a row to {@link ListBuilder}.
  * <p>
- * A grid row is composed of cells, each cell can have a combination of text and images. For more
- * details see {@link CellBuilder}.
- * </p>
+ * A grid row supports cells of vertically laid out content in a single row. Each cell can
+ * contain a combination of text and images and is constructed using a {@link CellBuilder}.
+ * <p>
+ * A grid supports a couple of image types:
+ * <ul>
+ *     <li>{@link ListBuilder#ICON_IMAGE} - icon images are expected to be tintable and are
+ *     shown at a standard icon size.</li>
+ *     <li>{@link ListBuilder#SMALL_IMAGE} - small images are not tinted and are shown at
+ *     a small size.</li>
+ *     <li>{@link ListBuilder#LARGE_IMAGE} - large images are not tinted and are shown as
+ *     large as they can be, in a {@link android.widget.ImageView.ScaleType#CENTER_CROP}</li>
+ * </ul>
+ * <p>
+ * If the first row of your slice was created with {@link GridRowBuilder} then there are a couple
+ * of extra considerations you should take for when the slice is displayed in different modes,
+ * particularly:
+ * <ul>
+ *     <li>{@link androidx.slice.widget.SliceView#MODE_SHORTCUT} - ensure you use
+ *     {@link #setPrimaryAction(SliceAction)} on grid row builder to specify the shortcut action,
+ *     icon, and label. This action will also be activated when the whole row is clicked, unless
+ *     the individual cells in the row have actions set on them.</li>
+ *     <li>{@link androidx.slice.widget.SliceView#MODE_SMALL} - in small format there might not
+ *     be enough space to display a cell containing two lines of text and an image, in this case
+ *     only one line of text will be shown with the image, favoring text added via
+ *     {@link CellBuilder#addTitleText(CharSequence)} or the first text added if no title was
+ *     added.</li>
+ * </ul>
+ * <p>
+ * If more cells are added to the grid row than can be displayed, the cells will be cut off. Using
+ * {@link #addSeeMoreAction(PendingIntent)} you can specify an action to take the user to see the
+ * rest of the content, this will take up space as a cell item in a row if added.
+ *
+ * @see ListBuilder#addGridRow(GridRowBuilder)
  */
 public class GridRowBuilder extends TemplateSliceBuilder {
 
@@ -69,7 +96,6 @@
     /**
      * Add a cell to the grid builder.
      */
-    @RequiresApi(Build.VERSION_CODES.N)
     @NonNull
     public GridRowBuilder addCell(@NonNull Consumer<CellBuilder> c) {
         CellBuilder b = new CellBuilder(this);
@@ -93,12 +119,12 @@
      * </p>
      */
     @NonNull
-    public GridRowBuilder addSeeMoreCell(@NonNull CellBuilder builder) {
+    public GridRowBuilder setSeeMoreCell(@NonNull CellBuilder builder) {
         if (mHasSeeMore) {
             throw new IllegalStateException("Trying to add see more cell when one has "
                     + "already been added");
         }
-        mImpl.addSeeMoreCell((TemplateBuilderImpl) builder.mImpl);
+        mImpl.setSeeMoreCell((TemplateBuilderImpl) builder.mImpl);
         mHasSeeMore = true;
         return this;
     }
@@ -118,8 +144,81 @@
      * a row or action has been previously added.
      * </p>
      */
-    @RequiresApi(Build.VERSION_CODES.N)
     @NonNull
+    public GridRowBuilder setSeeMoreCell(@NonNull Consumer<CellBuilder> c) {
+        CellBuilder b = new CellBuilder(this);
+        c.accept(b);
+        return setSeeMoreCell(b);
+    }
+
+    /**
+     * If all content in a slice cannot be shown, a "see more" affordance may be displayed where
+     * the content is cut off. The action added here should take the user to an activity to see
+     * all of the content, and will be invoked when the "see more" affordance is tapped.
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    @NonNull
+    public GridRowBuilder setSeeMoreAction(@NonNull PendingIntent intent) {
+        if (mHasSeeMore) {
+            throw new IllegalStateException("Trying to add see more action when one has "
+                    + "already been added");
+        }
+        mImpl.setSeeMoreAction(intent);
+        mHasSeeMore = true;
+        return this;
+    }
+
+    /**
+     * If all content in a slice cannot be shown, the cell added here may be displayed where the
+     * content is cut off.
+     * <p>
+     * This method should only be used if you want to display a custom cell to indicate more
+     * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do
+     * choose to specify a custom cell, the cell should have
+     * {@link CellBuilder#setContentIntent(PendingIntent)} specified to take the user to an
+     * activity to see all of the content.
+     * </p>
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     *
+     * @deprecated TO BE REMOVED
+     */
+    @NonNull
+    @Deprecated
+    public GridRowBuilder addSeeMoreCell(@NonNull CellBuilder builder) {
+        if (mHasSeeMore) {
+            throw new IllegalStateException("Trying to add see more cell when one has "
+                    + "already been added");
+        }
+        mImpl.setSeeMoreCell((TemplateBuilderImpl) builder.mImpl);
+        mHasSeeMore = true;
+        return this;
+    }
+
+    /**
+     * If all content in a slice cannot be shown, the cell added here may be displayed where the
+     * content is cut off.
+     * <p>
+     * This method should only be used if you want to display a custom cell to indicate more
+     * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do
+     * choose to specify a custom cell, the cell should have
+     * {@link CellBuilder#setContentIntent(PendingIntent)} specified to take the user to an
+     * activity to see all of the content.
+     * </p>
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     *
+     * @deprecated TO BE REMOVED
+     */
+    @NonNull
+    @Deprecated
     public GridRowBuilder addSeeMoreCell(@NonNull Consumer<CellBuilder> c) {
         CellBuilder b = new CellBuilder(this);
         c.accept(b);
@@ -134,20 +233,30 @@
      * Only one see more affordance can be added, this throws {@link IllegalStateException} if
      * a row or action has been previously added.
      * </p>
+     * @deprecated TO BE REMOVED
      */
+    @Deprecated
     @NonNull
     public GridRowBuilder addSeeMoreAction(@NonNull PendingIntent intent) {
         if (mHasSeeMore) {
             throw new IllegalStateException("Trying to add see more action when one has "
                     + "already been added");
         }
-        mImpl.addSeeMoreAction(intent);
+        mImpl.setSeeMoreAction(intent);
         mHasSeeMore = true;
         return this;
     }
 
     /**
-     * Sets the intent to send when the slice is activated.
+     * Sets the intent to send when the whole grid row is clicked.
+     * <p>
+     * If all the cells in the grid have specified a
+     * {@link CellBuilder#setPrimaryAction(SliceAction)} then the action set here on the
+     * {@link GridRowBuilder} may not ever be invoked.
+     * <p>
+     * If this grid row is the first row in {@link ListBuilder}, the action
+     * set here will be used to represent the slice when presented in
+     * {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}.
      */
     @NonNull
     public GridRowBuilder setPrimaryAction(@NonNull SliceAction action) {
@@ -173,9 +282,11 @@
     }
 
     /**
-     * Sub-builder to construct a cell to be displayed in a grid.
+     * Builder to construct a cell. A cell can be added as an item to GridRowBuilder via
+     * {@link GridRowBuilder#addCell(CellBuilder)}.
      * <p>
-     * Content added to a cell will be displayed in order vertically, for example the below code
+     * A cell supports up to two lines of text and one image. Content added to a cell will be
+     * displayed in the order that the content is added to it. For example, the below code
      * would construct a cell with "First text", and image below it, and then "Second text" below
      * the image.
      *
@@ -185,9 +296,22 @@
      *   .addImage(middleIcon)
      *   .addText("Second text");
      * </pre>
+     * <p>
+     * A cell supports a couple of image types:
+     * <ul>
+     *     <li>{@link ListBuilder#ICON_IMAGE} - icon images are expected to be tintable and are
+     *     shown at a standard icon size.</li>
+     *     <li>{@link ListBuilder#SMALL_IMAGE} - small images are not tinted and are shown at
+     *     a small size.</li>
+     *     <li>{@link ListBuilder#LARGE_IMAGE} - large images are not tinted and are shown as
+     *     large as they can be, in a {@link android.widget.ImageView.ScaleType#CENTER_CROP}</li>
+     * </ul>
      *
-     * A cell can have at most two text items and one image.
-     * </p>
+     * @see GridRowBuilder#addCell(CellBuilder)
+     * @see ListBuilder#addGridRow(GridRowBuilder)
+     * @see ListBuilder#ICON_IMAGE
+     * @see ListBuilder#SMALL_IMAGE
+     * @see ListBuilder#ICON_IMAGE
      */
     public static final class CellBuilder extends TemplateSliceBuilder {
         private androidx.slice.builders.impl.GridRowBuilder.CellBuilder mImpl;
diff --git a/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
index 29d43ac..d59aead 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
-import android.os.Build;
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.IntDef;
@@ -32,33 +31,88 @@
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Consumer;
 import androidx.slice.SliceSpecs;
 import androidx.slice.builders.impl.ListBuilderBasicImpl;
 import androidx.slice.builders.impl.ListBuilderV1Impl;
 import androidx.slice.builders.impl.TemplateBuilderImpl;
 import androidx.slice.core.SliceHints;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
-import java.util.function.Consumer;
+
 
 /**
- * Builder to construct slice content in a list format.
+ * A slice can be constructed with ListBuilder.
  * <p>
- * Use this builder for showing rows of content which is composed of text, images, and actions. For
- * more details see {@link RowBuilder}.
- * </p>
+ * A slice is a piece of templated app content that can be presented outside of the app
+ * in a {@link androidx.slice.widget.SliceView}. To provide a slice you should implement a
+ * {@link androidx.slice.SliceProvider} and use ListBuilder to construct your slice when
+ * {@link androidx.slice.SliceProvider#onBindSlice(Uri)} is called.
  * <p>
- * Slices can be displayed in different formats:
+ * ListBuilder allows you to construct a slice made up of rows of content. A list should
+ * have at least one row of content as well as a primary {@link SliceAction}. The row types that
+ * ListBuilder supports are:
  * <ul>
- *     <li>Shortcut - The slice is displayed as an icon with a text label.</li>
- *     <li>Small - Only a single row of content is displayed in small format, to specify which
- *         row to display in small format see {@link #setHeader(HeaderBuilder)}.</li>
- *     <li>Large - As many rows of content are shown as possible. If the presenter of the slice
- *         allows scrolling then all rows of content will be displayed in a scrollable view.</li>
+ *     <li>{@link HeaderBuilder} - A list can have one header which appears at the top of the list.
+ *     The header can support showing title, subtitle, and a row action. A header can also have
+ *     a summary of the contents of the slice which can be shown when not all of the slice can be
+ *     displayed.
+ *     </li>
+ *     <li>{@link RowBuilder} - A basic row supports title, subtitle, timestamps, images, row
+ *     action, icon actions, and toggle actions.
+ *     </li>
+ *     <li>{@link GridRowBuilder} - A grid row supports cells of vertically laid out content
+ *     displayed in a single row.
+ *     </li>
+ *     <li>{@link RangeBuilder} - A range row supports displaying a horizontal progress indicator.
+ *     </li>
+ *     <li>{@link InputRangeBuilder} - An input range row supports displaying a horizontal slider
+ *     allowing slider input (e.g. brightness or volume slider).
+ *     </li>
  * </ul>
- * </p>
+ * <p>
+ * In addition to rows of content, ListBuilder can also have {@link SliceAction}s added to it. These
+ * actions may appear differently on your slice depending on how the
+ * {@link androidx.slice.widget.SliceView} is configured. Normally the actions would appear in
+ * the header.
+ * <p>
+ * To ensure your slice is presented correctly you should consider the configurations
+ * {@link androidx.slice.widget.SliceView} supports:
+ * <ul>
+ *     <li>{@link androidx.slice.widget.SliceView#MODE_SHORTCUT} - The primary {@link SliceAction}
+ *     of the slice is used your primary action should contain an image and title representative
+ *     of your slice. If providing a tintable icon, use {@link #setColor(int)} to specify the color.
+ *     If a header has been specified for the list, the primary action associated with it will be
+ *     used, otherwise it will be the primary action associated with the first row of the list.
+ *     </li>
+ *     <li>{@link androidx.slice.widget.SliceView#MODE_SMALL} - Only a single row of content is
+ *     displayed in small format. If a header has been specified it will be displayed. If no header
+ *     was set, then the first row will be used and may appear differently depending on the row
+ *     type and the configuration of {@link androidx.slice.widget.SliceView}.
+ *     </li>
+ *     <li>{@link androidx.slice.widget.SliceView#MODE_LARGE} - As many rows of content are shown
+ *     as possible. If the presenter of the slice allows scrolling then all rows of content will
+ *     be displayed in a scrollable view.
+ *     </li>
+ * </ul>
+ * <p>
+ * Note that scrolling on SliceView can be disabled, in which case only the header and one or two
+ * rows of content may be shown for your slice. If your slice contains many rows of content to
+ * scroll through (e.g. list of wifi networks), consider using
+ * {@link #addSeeMoreAction(PendingIntent)} to provide a link to open the activity associated with
+ * the content.
  *
+ * @see HeaderBuilder
  * @see RowBuilder
+ * @see GridRowBuilder
+ * @see RangeBuilder
+ * @see InputRangeBuilder
+ * @see SliceAction
+ * @see androidx.slice.SliceProvider
+ * @see androidx.slice.SliceProvider#onBindSlice(Uri)
+ * @see androidx.slice.widget.SliceView
  */
 public class ListBuilder extends TemplateSliceBuilder {
 
@@ -72,6 +126,7 @@
     @IntDef({
             LARGE_IMAGE, SMALL_IMAGE, ICON_IMAGE, UNKNOWN_IMAGE
     })
+    @Retention(RetentionPolicy.SOURCE)
     public @interface ImageMode{}
 
     /**
@@ -97,7 +152,8 @@
     public static final long INFINITY = SliceHints.INFINITY;
 
     /**
-     * Create a builder which will construct a slice that will display rows of content.
+     * Create a builder which will construct a slice made up of rows of content.
+     *
      * @param uri Uri to tag for this slice.
      *
      * @deprecated TO BE REMOVED; use {@link #ListBuilder(Context, Uri, long)}.
@@ -128,7 +184,7 @@
     }
 
     /**
-     * Add a row to list builder.
+     * Add a row to the list builder.
      */
     @NonNull
     public ListBuilder addRow(@NonNull RowBuilder builder) {
@@ -137,9 +193,8 @@
     }
 
     /**
-     * Add a row the list builder.
+     * Add a row to the list builder.
      */
-    @RequiresApi(api = Build.VERSION_CODES.N)
     @NonNull
     public ListBuilder addRow(@NonNull Consumer<RowBuilder> c) {
         RowBuilder b = new RowBuilder(this);
@@ -164,7 +219,6 @@
      *
      * @deprecated TO BE REMOVED; use {@link #addGridRow(GridRowBuilder)} instead
      */
-    @RequiresApi(api = Build.VERSION_CODES.N)
     @NonNull
     @Deprecated
     public ListBuilder addGrid(@NonNull Consumer<GridBuilder> c) {
@@ -185,7 +239,6 @@
     /**
      * Add a grid row to the list builder.
      */
-    @RequiresApi(api = Build.VERSION_CODES.N)
     @NonNull
     public ListBuilder addGridRow(@NonNull Consumer<GridRowBuilder> c) {
         GridRowBuilder b = new GridRowBuilder(this);
@@ -194,10 +247,20 @@
     }
 
     /**
-     * Adds a header to this template.
+     * Sets a header for this list builder. A list can have only one header. Setting a header allows
+     * some flexibility in what's displayed in your slice when SliceView displays in
+     * {@link androidx.slice.widget.SliceView#MODE_SMALL} and
+     * {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}.
      * <p>
-     * The header should contain a title that is representative of the content in this slice along
-     * with an intent that links to the app activity associated with this content.
+     * In MODE_SMALL, the header row shown if one has been added. The header will also
+     * display the {@link HeaderBuilder#setSummary(CharSequence)} text if it has been
+     * specified, allowing a summary of otherwise hidden content to be shown.
+     * <p>
+     * In MODE_SHORTCUT, the primary action set using
+     * {@link HeaderBuilder#setPrimaryAction(SliceAction)} will be used for the shortcut
+     * representation.
+     *
+     * @see HeaderBuilder#setSummary(CharSequence)
      */
     @NonNull
     public ListBuilder setHeader(@NonNull HeaderBuilder builder) {
@@ -206,12 +269,21 @@
     }
 
     /**
-     * Adds a header to this template.
+     * Sets a header for this list builder. A list can have only one header. Setting a header allows
+     * some flexibility in what's displayed in your slice when SliceView displays in
+     * {@link androidx.slice.widget.SliceView#MODE_SMALL} and
+     * {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}.
      * <p>
-     * The header should contain a title that is representative of the content in this slice along
-     * with an intent that links to the app activity associated with this content.
+     * In MODE_SMALL, the header row shown if one has been added. The header will also
+     * display the {@link HeaderBuilder#setSummary(CharSequence)} text if it has been
+     * specified, allowing a summary of otherwise hidden content to be shown.
+     * <p>
+     * In MODE_SHORTCUT, the primary action set using
+     * {@link HeaderBuilder#setPrimaryAction(SliceAction)} will be used for the shortcut
+     * representation.
+     *
+     * @see HeaderBuilder#setSummary(CharSequence)
      */
-    @RequiresApi(api = Build.VERSION_CODES.N)
     @NonNull
     public ListBuilder setHeader(@NonNull Consumer<HeaderBuilder> c) {
         HeaderBuilder b = new HeaderBuilder(this);
@@ -220,13 +292,17 @@
     }
 
     /**
-     * Adds an action to this template. Actions added with this method are grouped together and
-     * may be shown on the template in large or small formats. Generally these actions will be
-     * displayed in the order they were added, however, if not all actions can be displayed then
-     * actions with a higher priority may be shown first.
+     * Adds an action to this list builder.
+     * <p>
+     * Actions added with this method are grouped together on the list template. These actions may
+     * appear differently on the slice depending on how the {@link androidx.slice.widget.SliceView}
+     * is configured. Generally these actions will be  displayed in the order they were added,
+     * however, if not all actions can be displayed then actions with a higher priority may be shown
+     * first.
      *
      * @see SliceAction
      * @see SliceAction#setPriority(int)
+     * @see androidx.slice.widget.SliceView#setActions(List, int)
      */
     @NonNull
     public ListBuilder addAction(@NonNull SliceAction action) {
@@ -261,6 +337,78 @@
         return this;
     }
 
+
+    /**
+     * If all content in a slice cannot be shown, the row added here may be displayed where the
+     * content is cut off. This row should have an affordance to take the user to an activity to
+     * see all of the content.
+     * <p>
+     * This method should only be used if you want to display a customized row to indicate more
+     * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do
+     * choose to specify a custom row, the row should have a content intent or action end item
+     * specified to take the user to an activity to see all of the content. The row added here
+     * will only appear when not all content can be displayed and it will not be styled any
+     * differently from row built by {@link RowBuilder} normally.
+     * </p>
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    @NonNull
+    public ListBuilder setSeeMoreRow(@NonNull RowBuilder builder) {
+        if (mHasSeeMore) {
+            throw new IllegalArgumentException("Trying to add see more row when one has "
+                    + "already been added");
+        }
+        mImpl.setSeeMoreRow((TemplateBuilderImpl) builder.mImpl);
+        mHasSeeMore = true;
+        return this;
+    }
+
+    /**
+     * If all content in a slice cannot be shown, the row added here may be displayed where the
+     * content is cut off. This row should have an affordance to take the user to an activity to
+     * see all of the content.
+     * <p>
+     * This method should only be used if you want to display a customized row to indicate more
+     * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do
+     * choose to specify a custom row, the row should have a content intent or action end item
+     * specified to take the user to an activity to see all of the content. The row added here
+     * will only appear when not all content can be displayed and it will not be styled any
+     * differently from row built by {@link RowBuilder} normally.
+     * </p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    @NonNull
+    public ListBuilder setSeeMoreRow(@NonNull Consumer<RowBuilder> c) {
+        RowBuilder b = new RowBuilder(this);
+        c.accept(b);
+        return addSeeMoreRow(b);
+    }
+
+    /**
+     * If all content in a slice cannot be shown, a "see more" affordance may be displayed where
+     * the content is cut off. The action added here should take the user to an activity to see
+     * all of the content, and will be invoked when the "see more" affordance is tapped.
+     * <p>
+     * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+     * a row or action has been previously added.
+     * </p>
+     */
+    @NonNull
+    public ListBuilder setSeeMoreAction(@NonNull PendingIntent intent) {
+        if (mHasSeeMore) {
+            throw new IllegalArgumentException("Trying to add see more action when one has "
+                    + "already been added");
+        }
+        mImpl.setSeeMoreAction(intent);
+        mHasSeeMore = true;
+        return this;
+    }
+
     /**
      * If all content in a slice cannot be shown, the row added here may be displayed where the
      * content is cut off. This row should have an affordance to take the user to an activity to
@@ -275,14 +423,17 @@
      * Only one see more affordance can be added, this throws {@link IllegalStateException} if
      * a row or action has been previously added.
      * </p>
+     *
+     * @deprecated TO BE REMOVED
      */
     @NonNull
+    @Deprecated
     public ListBuilder addSeeMoreRow(@NonNull RowBuilder builder) {
         if (mHasSeeMore) {
             throw new IllegalArgumentException("Trying to add see more row when one has "
                     + "already been added");
         }
-        mImpl.addSeeMoreRow((TemplateBuilderImpl) builder.mImpl);
+        mImpl.setSeeMoreRow((TemplateBuilderImpl) builder.mImpl);
         mHasSeeMore = true;
         return this;
     }
@@ -300,13 +451,15 @@
      * Only one see more affordance can be added, this throws {@link IllegalStateException} if
      * a row or action has been previously added.
      * </p>
+     *
+     * @deprecated TO BE REMOVED
      */
-    @RequiresApi(api = Build.VERSION_CODES.N)
+    @Deprecated
     @NonNull
     public ListBuilder addSeeMoreRow(@NonNull Consumer<RowBuilder> c) {
         RowBuilder b = new RowBuilder(this);
         c.accept(b);
-        return addSeeMoreRow(b);
+        return setSeeMoreRow(b);
     }
 
     /**
@@ -317,14 +470,17 @@
      * Only one see more affordance can be added, this throws {@link IllegalStateException} if
      * a row or action has been previously added.
      * </p>
+     *
+     * @deprecated TO BE REMOVED
      */
+    @Deprecated
     @NonNull
     public ListBuilder addSeeMoreAction(@NonNull PendingIntent intent) {
         if (mHasSeeMore) {
             throw new IllegalArgumentException("Trying to add see more action when one has "
                     + "already been added");
         }
-        mImpl.addSeeMoreAction(intent);
+        mImpl.setSeeMoreAction(intent);
         mHasSeeMore = true;
         return this;
     }
@@ -363,7 +519,6 @@
     /**
      * Add an input range row to the list builder.
      */
-    @RequiresApi(api = Build.VERSION_CODES.N)
     @NonNull
     public ListBuilder addInputRange(@NonNull Consumer<InputRangeBuilder> c) {
         InputRangeBuilder inputRangeBuilder = new InputRangeBuilder(this);
@@ -383,7 +538,6 @@
     /**
      * Add a range row to the list builder.
      */
-    @RequiresApi(api = Build.VERSION_CODES.N)
     @NonNull
     public ListBuilder addRange(@NonNull Consumer<RangeBuilder> c) {
         RangeBuilder rangeBuilder = new RangeBuilder(this);
@@ -392,11 +546,24 @@
     }
 
     /**
-     * Builder to construct a range row.
+     * Builder to construct a range row which can be added to a {@link ListBuilder}.
+     * <p>
+     * A range row supports displaying a horizontal progress indicator.
+     *
+     * @see ListBuilder#addRange(Consumer)
+     * @see ListBuilder#addRange(RangeBuilder)
      */
     public static class RangeBuilder extends TemplateSliceBuilder {
         private androidx.slice.builders.impl.ListBuilder.RangeBuilder mImpl;
 
+        /**
+         * Builder to construct a range row which can be added to a {@link ListBuilder}.
+         * <p>
+         * A range row supports displaying a horizontal progress indicator.
+         *
+         * @see ListBuilder#addRange(Consumer)
+         * @see ListBuilder#addRange(RangeBuilder)
+         */
         public RangeBuilder(@NonNull ListBuilder parent) {
             super(parent.mImpl.createRangeBuilder());
         }
@@ -429,6 +596,28 @@
         }
 
         /**
+         * Set the subtitle.
+         */
+        @NonNull
+        public RangeBuilder setSubtitle(@NonNull CharSequence title) {
+            mImpl.setSubtitle(title);
+            return this;
+        }
+
+        /**
+         * Set the primary action for this row.
+         * <p>
+         * The action specified here will be sent when the whole row is clicked. If this
+         * is the first row in a {@link ListBuilder} this action will also be used to define
+         * the {@link androidx.slice.widget.SliceView#MODE_SHORTCUT} representation of the slice.
+         */
+        @NonNull
+        public RangeBuilder setPrimaryAction(@NonNull SliceAction action) {
+            mImpl.setPrimaryAction(action);
+            return this;
+        }
+
+        /**
          * Sets the content description.
          */
         @NonNull
@@ -445,10 +634,23 @@
 
     /**
      * Builder to construct a input range row.
+     * <p>
+     * An input range row supports displaying a horizontal slider allowing slider input.
+     *
+     * @see ListBuilder#addInputRange(Consumer)
+     * @see ListBuilder#addInputRange(InputRangeBuilder)
      */
     public static class InputRangeBuilder extends TemplateSliceBuilder {
         private androidx.slice.builders.impl.ListBuilder.InputRangeBuilder mImpl;
 
+        /**
+         * Builder to construct a input range row.
+         * <p>
+         * An input range row supports displaying a horizontal slider allowing slider input.
+         *
+         * @see ListBuilder#addInputRange(Consumer)
+         * @see ListBuilder#addInputRange(InputRangeBuilder)
+         */
         public InputRangeBuilder(@NonNull ListBuilder parent) {
             super(parent.mImpl.createInputRangeBuilder());
         }
@@ -480,13 +682,33 @@
             return this;
         }
 
+        /**
+         * Set the subtitle.
+         */
+        @NonNull
+        public InputRangeBuilder setSubtitle(@NonNull CharSequence title) {
+            mImpl.setSubtitle(title);
+            return this;
+        }
+
+        /**
+         * Set the {@link PendingIntent} to send when the current value is updated.
+         *
+         * @deprecated TO BE REMOVED; use {@link InputRangeBuilder#setInputAction(PendingIntent)}
+         */
+        @Deprecated
+        @NonNull
+        public InputRangeBuilder setAction(@NonNull PendingIntent action) {
+            mImpl.setInputAction(action);
+            return this;
+        }
 
         /**
          * Set the {@link PendingIntent} to send when the current value is updated.
          */
         @NonNull
-        public InputRangeBuilder setAction(@NonNull PendingIntent action) {
-            mImpl.setAction(action);
+        public InputRangeBuilder setInputAction(@NonNull PendingIntent action) {
+            mImpl.setInputAction(action);
             return this;
         }
 
@@ -494,6 +716,7 @@
          * @deprecated TO BE REMOVED
          */
         @NonNull
+        @RequiresApi(23)
         public InputRangeBuilder setThumb(@NonNull Icon thumb) {
             return setThumb(IconCompat.createFromIcon(thumb));
         }
@@ -508,6 +731,21 @@
         }
 
         /**
+         * Set the primary action for this row.
+         * <p>
+         * The action specified here will be sent when the whole row is clicked, whereas
+         * the action specified via {@link #setAction(PendingIntent)} is used when the slider
+         * is interacted with. Additionally, if this is the first row in a {@link ListBuilder} this
+         * action will also be used to define the
+         * {@link androidx.slice.widget.SliceView#MODE_SHORTCUT} representation of the slice.
+         */
+        @NonNull
+        public InputRangeBuilder setPrimaryAction(@NonNull SliceAction action) {
+            mImpl.setPrimaryAction(action);
+            return this;
+        }
+
+        /**
          * Sets the content description.
          */
         @NonNull
@@ -523,24 +761,39 @@
     }
 
     /**
-     * Sub-builder to construct a row of slice content.
+     * Builder to construct a row. A row can be added as an item to ListBuilder via
+     * {@link ListBuilder#addRow(RowBuilder)}.
      * <p>
-     * Row content can have:
+     * A row supports:
      * <ul>
-     *     <li>Title item - This is only displayed if this is a list item in a large template, it
-     *     will not be shown if this template is being used for small format. The item appears at
-     *     the start of the template. There can only be one title item displayed, and it could be a
-     *     timestamp, image, or a tappable icon.</li>
-     *     <li>Title - Formatted as a title.</li>
-     *     <li>Subtitle - Appears below the title (if one exists) and is formatted as normal text.
+     *     <li>Title item - The title item appears with at the start of the row. There can only
+     *     be one title item added to a row, and it could be a timestamp, image, or a
+     *     {@link SliceAction}.
      *     </li>
-     *     <li>End item -  Appears at the end of the template, there can be multiple end items but
-     *     they are only shown if there's enough space. End items can be a timestamp, image, or a
-     *     tappable icon.</li>
+     *     <li>Title - Single line of text formatted as a title, see
+     *     {@link #setTitle(CharSequence)}.
+     *     </li>
+     *     <li>Subtitle - Single line of text below the title (if one exists) and is formatted as
+     *     normal text, see {@link #setSubtitle(CharSequence)}.
+     *     </li>
+     *     <li>End item -  End items appear at the end of the row. There can be multiple end items
+     *     that show depending on available width. End items can be a timestamp, image, or a
+     *     tappable icon.
+     *     </li>
+     *     <li>Primary action - The primary action for the row, this is the action that will be sent
+     *     when the row is clicked. This is set via {@link #setPrimaryAction(SliceAction)}. If this
+     *     is the only row in a slice, the slice  action set here will be used to represent the
+     *     slice in when slice view is displaying in
+     *     {@link androidx.slice.widget.SliceView#MODE_SMALL}.
+     *     </li>
      * </ul>
-     * </p>
+     * There are a couple of restrictions to how content can be added to a row:
+     * <ul>
+     *     <li>End items cannot contain a mixture of {@link SliceAction}s and Icons.</li>
+     *     <li>There can only be one timestamp added to the row.</li>
+     * </ul>
      *
-     * @see ListBuilder
+     * @see ListBuilder#addRow(RowBuilder)
      */
     public static class RowBuilder extends TemplateSliceBuilder {
 
@@ -552,7 +805,7 @@
         private boolean mHasTimestamp;
 
         /**
-         * Create a builder which will construct a slice displayed in a row format.
+         * Builder to construct a row.
          * @param parent The builder constructing the parent slice.
          */
         public RowBuilder(@NonNull ListBuilder parent) {
@@ -560,7 +813,7 @@
         }
 
         /**
-         * Create a builder which will construct a slice displayed in a row format.
+         * Builder to construct a row.
          * @param uri Uri to tag for this slice.
          */
         public RowBuilder(@NonNull ListBuilder parent, @NonNull Uri uri) {
@@ -568,7 +821,7 @@
         }
 
         /**
-         * Create a builder which will construct a slice displayed in a row format.
+         * Builder to construct a normal row.
          * @param uri Uri to tag for this slice.
          */
         public RowBuilder(@NonNull Context context, @NonNull Uri uri) {
@@ -598,6 +851,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder setTitleItem(@NonNull Icon icon) {
             return setTitleItem(icon, ICON_IMAGE);
         }
@@ -607,6 +861,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder setTitleItem(@Nullable Icon icon, boolean isLoading) {
             return setTitleItem(icon, ICON_IMAGE, isLoading);
         }
@@ -615,6 +870,7 @@
          * @deprecated TO BE REMOVED.
          */
         @Deprecated
+        @RequiresApi(23)
         public RowBuilder setTitleItem(@NonNull Icon icon, @ImageMode int imageMode) {
             mImpl.setTitleItem(IconCompat.createFromIcon(icon), imageMode, false /* isLoading */);
             return this;
@@ -625,6 +881,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder setTitleItem(@Nullable Icon icon, @ImageMode int imageMode,
                 boolean isLoading) {
             mImpl.setTitleItem(IconCompat.createFromIcon(icon), imageMode,
@@ -636,7 +893,7 @@
          * Sets the title item to be the provided icon. There can only be one title item, this
          * will replace any other title items that may have been set.
          *
-         * @deprecated use ListBuilder{@link #setTitleItem(Icon, int)} instead.
+         * @deprecated TO BE REMOVED; use ListBuilder{@link #setTitleItem(Icon, int, boolean)}.
          */
         @Deprecated
         @NonNull
@@ -654,7 +911,7 @@
          * @param isLoading indicates whether the app is doing work to load the added content in the
          *                  background or not.
          *
-         * @deprecated use ListBuilder{@link #setTitleItem(Icon, int, boolean)} instead.
+         * @deprecated TO BE REMOVED; use ListBuilder{@link #setTitleItem(Icon, int, boolean)}.
          */
         @Deprecated
         @NonNull
@@ -727,7 +984,10 @@
         }
 
         /**
-         * Sets the action to be invoked if the user taps on the main content of the template.
+         * The action specified here will be sent when the whole row is clicked.
+         * <p>
+         * If this is the first row in a {@link ListBuilder} this action will also be used to define
+         * the {@link androidx.slice.widget.SliceView#MODE_SHORTCUT} representation of the slice.
          */
         @NonNull
         public RowBuilder setPrimaryAction(@NonNull SliceAction action) {
@@ -736,7 +996,7 @@
         }
 
         /**
-         * Sets the title text.
+         * Sets the title for the row builder.
          */
         @NonNull
         public RowBuilder setTitle(@NonNull CharSequence title) {
@@ -745,7 +1005,7 @@
         }
 
         /**
-         * Sets the title text.
+         * Sets the title for the row builder.
          * <p>
          * Use this method to specify content that will appear in the template once it's been
          * loaded.
@@ -760,7 +1020,7 @@
         }
 
         /**
-         * Sets the subtitle text.
+         * Sets the subtitle for the row builder.
          */
         @NonNull
         public RowBuilder setSubtitle(@NonNull CharSequence subtitle) {
@@ -768,7 +1028,7 @@
         }
 
         /**
-         * Sets the subtitle text.
+         * Sets the subtitle for the row builder.
          * <p>
          * Use this method to specify content that will appear in the template once it's been
          * loaded.
@@ -783,7 +1043,7 @@
         }
 
         /**
-         * Adds a timestamp to be displayed at the end of the row. Only one timestamp can be added,
+         * Adds a timestamp to the end items of the row builder. Only one timestamp can be added,
          * if one is already added this will throw {@link IllegalArgumentException}.
          */
         @NonNull
@@ -802,6 +1062,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder addEndItem(@NonNull Icon icon) {
             return addEndItem(icon, ICON_IMAGE, false /* isLoading */);
         }
@@ -811,6 +1072,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder addEndItem(@NonNull Icon icon, boolean isLoading) {
             return addEndItem(icon, ICON_IMAGE, isLoading);
         }
@@ -820,6 +1082,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder addEndItem(@NonNull Icon icon, @ImageMode int imageMode) {
             return addEndItem(icon, imageMode, false /* isLoading */);
         }
@@ -829,6 +1092,7 @@
          */
         @Deprecated
         @NonNull
+        @RequiresApi(23)
         public RowBuilder addEndItem(@Nullable Icon icon, @ImageMode int imageMode,
                 boolean isLoading) {
             if (mHasEndActionOrToggle) {
@@ -842,7 +1106,7 @@
         }
 
         /**
-         * Adds an icon to be displayed at the end of the row. A mixture of icons and actions
+         * Adds an icon to the end items of the row builder. A mixture of icons and actions
          * is not permitted. If an action has already been added this will throw
          * {@link IllegalArgumentException}.
          *
@@ -874,7 +1138,7 @@
         }
 
         /**
-         * Adds an icon to be displayed at the end of the row.
+         * Adds an icon to the end items of the row builder.
          *
          * @param icon the image to display.
          * @param imageMode the mode that image should be displayed in.
@@ -889,7 +1153,7 @@
         }
 
         /**
-         * Adds an icon to be displayed at the end of the row.
+         * Adds an icon to the end items of the row builder.
          * <p>
          * When set to true, the parameter {@code isLoading} indicates that the app is doing work
          * to load this content in the background, in this case the template displays a placeholder
@@ -917,7 +1181,7 @@
         }
 
         /**
-         * Adds an action to display at the end of the row. A mixture of icons and
+         * Adds an action to the end items of the row builder. A mixture of icons and
          * actions is not permitted. If an icon has already been added, this will throw
          * {@link IllegalArgumentException}.
          */
@@ -927,7 +1191,7 @@
         }
 
         /**
-         * Adds an action to be displayed at the end of the row. A mixture of icons and
+         * Adds an action to the end items of the row builder. A mixture of icons and
          * actions is not permitted. If an icon has already been added, this will throw
          * {@link IllegalArgumentException}.
          * <p>
@@ -980,23 +1244,41 @@
     }
 
     /**
-     * Builder to construct a header. A header is displayed at the top of a list and can have
-     * a title, subtitle, and an action.
+     * Builder to construct a header row.
+     * <p>
+     * A header provides some additional functionality compared to a {@link RowBuilder}. Like
+     * a row, a header has a title, subtitle, and primary action.
+     * <p>
+     * In addition to a row's title, subtitle, and primary action, a header also supports setting
+     * a summary description of the list contents using
+     * {@link HeaderBuilder#setSummary(CharSequence)}. This summary might be used when
+     * the rest of the list content is not shown (e.g. if SliceView presenting slice is
+     * configured to {@link androidx.slice.widget.SliceView#MODE_SMALL}.
+     * <p>
+     * The primary action specified by {@link HeaderBuilder#setPrimaryAction(SliceAction)} will
+     * be used as the PendingIntent sent when header is clicked. This action is also used when
+     * when SliceView displays in {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}.
+     * <p>
+     * Unlike row builder, header builder does not support end items (e.g.
+     * {@link RowBuilder#addEndItem(SliceAction)}). The header may be used to display actions set
+     * on the list via {@link #addAction(SliceAction)}.
      *
      * @see ListBuilder#setHeader(HeaderBuilder)
+     * @see ListBuilder#addAction(SliceAction)
+     * @see SliceAction
      */
     public static class HeaderBuilder extends TemplateSliceBuilder {
         private androidx.slice.builders.impl.ListBuilder.HeaderBuilder mImpl;
 
         /**
-         * Create builder for header templates.
+         * Create builder for a header.
          */
         public HeaderBuilder(@NonNull ListBuilder parent) {
             super(parent.mImpl.createHeaderBuilder());
         }
 
         /**
-         * Create builder for header templates.
+         * Create builder for a header.
          * @hide
          */
         @RestrictTo(LIBRARY_GROUP)
@@ -1005,7 +1287,7 @@
         }
 
         /**
-         * Sets the title to be shown in this header.
+         * Sets the title for the header builder.
          */
         @NonNull
         public HeaderBuilder setTitle(@NonNull CharSequence title) {
@@ -1013,7 +1295,13 @@
         }
 
         /**
-         * Sets the title to be shown in this header.
+         * Sets the title for the header builder.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
          */
         @NonNull
         public HeaderBuilder setTitle(@NonNull CharSequence title, boolean isLoading) {
@@ -1022,7 +1310,7 @@
         }
 
         /**
-         * Sets the subtitle to be shown in this header.
+         * Sets the subtitle for the header builder.
          */
         @NonNull
         public HeaderBuilder setSubtitle(@NonNull CharSequence subtitle) {
@@ -1030,7 +1318,13 @@
         }
 
         /**
-         * Sets the subtitle to be shown in this header.
+         * Sets the subtitle for the header builder.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
          */
         @NonNull
         public HeaderBuilder setSubtitle(@NonNull CharSequence subtitle, boolean isLoading) {
@@ -1042,26 +1336,61 @@
          * Sets the summary subtitle to be shown in this header. If unset, the normal subtitle
          * will be used. The summary is used when the parent template is presented in a
          * small format.
+         * @deprecated TO BE REMOVED; use {@link #setSummary(CharSequence)}
          */
         @NonNull
         public HeaderBuilder setSummarySubtitle(@NonNull CharSequence summarySubtitle) {
-            return setSummarySubtitle(summarySubtitle, false /* isLoading */);
+            return setSummary(summarySubtitle, false /* isLoading */);
         }
 
         /**
          * Sets the summary subtitle to be shown in this header. If unset, the normal subtitle
          * will be used. The summary is used when the parent template is presented in a
          * small format.
+         * @deprecated TO BE REMOVED; use {@link #setSummary(CharSequence, boolean)}
          */
         @NonNull
         public HeaderBuilder setSummarySubtitle(@NonNull CharSequence summarySubtitle,
                 boolean isLoading) {
-            mImpl.setSummarySubtitle(summarySubtitle, isLoading);
+            return setSummary(summarySubtitle, isLoading /* isLoading */);
+        }
+
+        /**
+         * Sets the summary for the header builder. A summary is optional.
+         * <p>
+         * The summary should be a description of the contents of the list. This summary might be
+         * used when the rest of the list content is not shown (e.g. if SliceView presenting slice
+         * is configured to {@link androidx.slice.widget.SliceView#MODE_SMALL}.
+         */
+        @NonNull
+        public HeaderBuilder setSummary(@NonNull CharSequence summary) {
+            return setSummary(summary, false /* isLoading */);
+        }
+
+        /**
+         * Sets the summary for the header builder. A summary is optional.
+         * <p>
+         * The summary should be a description of the contents of the list. This summary might be
+         * used when the rest of the list content is not shown (e.g. if SliceView presenting slice
+         * is configured to {@link androidx.slice.widget.SliceView#MODE_SMALL}.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public HeaderBuilder setSummary(@NonNull CharSequence summary, boolean isLoading) {
+            mImpl.setSummary(summary, isLoading);
             return this;
         }
 
         /**
-         * Sets the action to invoke when the header is activated.
+         * Sets the action to send when the header is clicked.
+         * <p>
+         * Additionally, the action specified here is used when the slice associated with this
+         * header is displayed in {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}.
          */
         @NonNull
         public HeaderBuilder setPrimaryAction(@NonNull SliceAction action) {
diff --git a/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java
index c3f1f92..0459c24 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java
@@ -28,6 +28,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Consumer;
 import androidx.slice.SliceSpecs;
 import androidx.slice.builders.impl.MessagingBasicImpl;
 import androidx.slice.builders.impl.MessagingBuilder;
@@ -35,8 +36,6 @@
 import androidx.slice.builders.impl.MessagingV1Impl;
 import androidx.slice.builders.impl.TemplateBuilderImpl;
 
-import java.util.function.Consumer;
-
 /**
  * Builder to construct slice content in a messaging format.
  * @hide
@@ -70,7 +69,6 @@
     /**
      * Add a subslice to this builder.
      */
-    @RequiresApi(api = Build.VERSION_CODES.N)
     public MessagingSliceBuilder add(Consumer<MessageBuilder> c) {
         MessageBuilder b = new MessageBuilder(this);
         c.accept(b);
@@ -115,6 +113,7 @@
         /**
          * Add the icon used to display contact in the messaging experience
          */
+        @RequiresApi(23)
         public MessageBuilder addSource(Icon source) {
             mImpl.addSource(source);
             return this;
@@ -124,7 +123,9 @@
          * Add the icon used to display contact in the messaging experience
          */
         public MessageBuilder addSource(IconCompat source) {
-            mImpl.addSource(source.toIcon());
+            if (Build.VERSION.SDK_INT >= 23) {
+                mImpl.addSource(source.toIcon());
+            }
             return this;
         }
 
diff --git a/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java b/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java
index e4e28f5..b98c723 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java
@@ -25,6 +25,7 @@
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -41,6 +42,7 @@
      * @deprecated TO BE REMOVED
      */
     @Deprecated
+    @RequiresApi(23)
     public SliceAction(@NonNull PendingIntent action, @NonNull Icon actionIcon,
             @NonNull CharSequence actionTitle) {
         this(action, actionIcon, ICON_IMAGE, actionTitle);
@@ -50,6 +52,7 @@
      * @deprecated TO BE REMOVED
      */
     @Deprecated
+    @RequiresApi(23)
     public SliceAction(@NonNull PendingIntent action, @NonNull Icon actionIcon,
             @ListBuilder.ImageMode int imageMode, @NonNull CharSequence actionTitle) {
         this(action, IconCompat.createFromIcon(actionIcon), imageMode, actionTitle);
@@ -59,6 +62,7 @@
      * @deprecated TO BE REMOVED
      */
     @Deprecated
+    @RequiresApi(23)
     public SliceAction(@NonNull PendingIntent action, @NonNull Icon actionIcon,
             @NonNull CharSequence actionTitle, boolean isChecked) {
         this(action, IconCompat.createFromIcon(actionIcon), actionTitle, isChecked);
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilder.java
index 9b59945..7c12311 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilder.java
@@ -56,7 +56,7 @@
      * a row or action has been previously added.
      * </p>
      */
-    void addSeeMoreCell(TemplateBuilderImpl impl);
+    void setSeeMoreCell(TemplateBuilderImpl impl);
 
     /**
      * If all content in a slice cannot be shown, a "see more" affordance will be displayed where
@@ -67,7 +67,7 @@
      * a row or action has been previously added.
      * </p>
      */
-    void addSeeMoreAction(PendingIntent intent);
+    void setSeeMoreAction(PendingIntent intent);
 
     /**
      * Sets the action to be invoked if the user taps on the main content of the template.
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderBasicImpl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderBasicImpl.java
index e1a0752..8051a10 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderBasicImpl.java
@@ -65,13 +65,13 @@
     /**
      */
     @Override
-    public void addSeeMoreCell(TemplateBuilderImpl impl) {
+    public void setSeeMoreCell(TemplateBuilderImpl impl) {
     }
 
     /**
      */
     @Override
-    public void addSeeMoreAction(PendingIntent intent) {
+    public void setSeeMoreAction(PendingIntent intent) {
     }
 
     /**
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderListV1Impl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderListV1Impl.java
index 29158b4..714b570 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderListV1Impl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderListV1Impl.java
@@ -103,7 +103,7 @@
     /**
      */
     @Override
-    public void addSeeMoreCell(@NonNull TemplateBuilderImpl builder) {
+    public void setSeeMoreCell(@NonNull TemplateBuilderImpl builder) {
         builder.getBuilder().addHints(HINT_SEE_MORE);
         getBuilder().addSubSlice(builder.build());
     }
@@ -111,7 +111,7 @@
     /**
      */
     @Override
-    public void addSeeMoreAction(PendingIntent intent) {
+    public void setSeeMoreAction(PendingIntent intent) {
         getBuilder().addSubSlice(
                 new Slice.Builder(getBuilder())
                         .addHints(HINT_SEE_MORE)
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilder.java
index 0ebd32b..084b85a 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilder.java
@@ -77,7 +77,7 @@
      * a row or action has been previously added.
      * </p>
      */
-    void addSeeMoreRow(TemplateBuilderImpl builder);
+    void setSeeMoreRow(TemplateBuilderImpl builder);
     /**
      * If all content in a slice cannot be shown, a "see more" affordance will be displayed where
      * the content is cut off. The action added here should take the user to an activity to see
@@ -87,7 +87,7 @@
      * a row or action has been previously added.
      * </p>
      */
-    void addSeeMoreAction(PendingIntent intent);
+    void setSeeMoreAction(PendingIntent intent);
 
     /**
      * Sets the color to tint items displayed by this template (e.g. icons).
@@ -156,6 +156,16 @@
         void setTitle(@NonNull CharSequence title);
 
         /**
+         * Set the subtitle.
+         */
+        void setSubtitle(@NonNull CharSequence title);
+
+        /**
+         * Set the primary action.
+         */
+        void setPrimaryAction(@NonNull SliceAction action);
+
+        /**
          * Sets the content description.
          */
         void setContentDescription(CharSequence description);
@@ -168,7 +178,7 @@
         /**
          * Set the {@link PendingIntent} to send when the value changes.
          */
-        void setAction(@NonNull PendingIntent action);
+        void setInputAction(@NonNull PendingIntent action);
 
         /**
          * Set the {@link IconCompat} to be displayed as the thumb on the input range.
@@ -325,7 +335,7 @@
          * will be used. The summary is used when the parent template is presented in a
          * small format.
          */
-        void setSummarySubtitle(CharSequence summarySubtitle, boolean isLoading);
+        void setSummary(CharSequence summarySubtitle, boolean isLoading);
 
         /**
          * Sets the action to invoke when the header is activated.
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java
index 204defa..94143e2 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java
@@ -17,7 +17,7 @@
 package androidx.slice.builders.impl;
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
 
 import android.app.PendingIntent;
 import android.net.Uri;
@@ -85,13 +85,13 @@
     /**
      */
     @Override
-    public void addSeeMoreRow(TemplateBuilderImpl builder) {
+    public void setSeeMoreRow(TemplateBuilderImpl builder) {
     }
 
     /**
      */
     @Override
-    public void addSeeMoreAction(PendingIntent intent) {
+    public void setSeeMoreAction(PendingIntent intent) {
     }
 
     /**
@@ -108,7 +108,7 @@
         for (int i = 0; i < keywords.size(); i++) {
             sb.addText(keywords.get(i), null);
         }
-        getBuilder().addSubSlice(sb.addHints(HINT_KEY_WORDS).build());
+        getBuilder().addSubSlice(sb.addHints(HINT_KEYWORDS).build());
     }
 
     /**
@@ -340,7 +340,7 @@
         /**
          */
         @Override
-        public void setSummarySubtitle(CharSequence summarySubtitle, boolean isLoading) {
+        public void setSummary(CharSequence summarySubtitle, boolean isLoading) {
 
         }
 
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderV1Impl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderV1Impl.java
index e85fbb1..f2ff884 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderV1Impl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderV1Impl.java
@@ -33,7 +33,7 @@
 import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
 import static androidx.slice.builders.ListBuilder.INFINITY;
 import static androidx.slice.builders.ListBuilder.LARGE_IMAGE;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
 import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
 import static androidx.slice.core.SliceHints.HINT_TTL;
 import static androidx.slice.core.SliceHints.SUBTYPE_MAX;
@@ -136,7 +136,7 @@
     /**
      */
     @Override
-    public void addSeeMoreRow(TemplateBuilderImpl builder) {
+    public void setSeeMoreRow(TemplateBuilderImpl builder) {
         builder.getBuilder().addHints(HINT_SEE_MORE);
         getBuilder().addSubSlice(builder.build());
     }
@@ -144,7 +144,7 @@
     /**
      */
     @Override
-    public void addSeeMoreAction(PendingIntent intent) {
+    public void setSeeMoreAction(PendingIntent intent) {
         getBuilder().addSubSlice(
                 new Slice.Builder(getBuilder())
                         .addHints(HINT_SEE_MORE)
@@ -161,7 +161,9 @@
         private int mMax = 100;
         private int mValue = 0;
         private CharSequence mTitle;
+        private CharSequence mSubtitle;
         private CharSequence mContentDescr;
+        private SliceAction mPrimaryAction;
 
         public RangeBuilderImpl(Slice.Builder sb) {
             super(sb, null);
@@ -183,6 +185,16 @@
         }
 
         @Override
+        public void setSubtitle(@NonNull CharSequence title) {
+            mSubtitle = title;
+        }
+
+        @Override
+        public void setPrimaryAction(@NonNull SliceAction action) {
+            mPrimaryAction = action;
+        }
+
+        @Override
         public void setContentDescription(@NonNull CharSequence description) {
             mContentDescr = description;
         }
@@ -192,9 +204,17 @@
             if (mTitle != null) {
                 builder.addText(mTitle, null, HINT_TITLE);
             }
+            if (mSubtitle != null) {
+                builder.addText(mSubtitle, null);
+            }
             if (mContentDescr != null) {
                 builder.addText(mContentDescr, SUBTYPE_CONTENT_DESCRIPTION);
             }
+            if (mPrimaryAction != null) {
+                Slice.Builder sb = new Slice.Builder(
+                        getBuilder()).addHints(HINT_TITLE, HINT_SHORTCUT);
+                builder.addSubSlice(mPrimaryAction.buildSlice(sb), null /* subtype */);
+            }
             builder.addHints(HINT_LIST_ITEM)
                     .addInt(mMax, SUBTYPE_MAX)
                     .addInt(mValue, SUBTYPE_VALUE);
@@ -214,7 +234,7 @@
         }
 
         @Override
-        public void setAction(@NonNull PendingIntent action) {
+        public void setInputAction(@NonNull PendingIntent action) {
             mAction = action;
         }
 
@@ -253,7 +273,7 @@
         for (int i = 0; i < keywords.size(); i++) {
             sb.addText(keywords.get(i), null);
         }
-        getBuilder().addSubSlice(sb.addHints(HINT_KEY_WORDS).build());
+        getBuilder().addSubSlice(sb.addHints(HINT_KEYWORDS).build());
     }
 
     /**
@@ -603,7 +623,7 @@
         /**
          */
         @Override
-        public void setSummarySubtitle(CharSequence summarySubtitle, boolean isLoading) {
+        public void setSummary(CharSequence summarySubtitle, boolean isLoading) {
             mSummaryItem = new SliceItem(summarySubtitle, FORMAT_TEXT, null,
                     new String[] {HINT_SUMMARY});
             if (isLoading) {
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java
index c999594..f53b9f5 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java
@@ -19,7 +19,9 @@
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.graphics.drawable.Icon;
+import android.os.Build;
 
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -44,8 +46,10 @@
     @Override
     public void apply(Slice.Builder builder) {
         if (mLastMessage != null) {
-            if (mLastMessage.mIcon != null) {
-                builder.addIcon(IconCompat.createFromIcon(mLastMessage.mIcon), null);
+            if (Build.VERSION.SDK_INT >= 23) {
+                if (mLastMessage.mIcon != null) {
+                    builder.addIcon(IconCompat.createFromIcon(mLastMessage.mIcon), null);
+                }
             }
             if (mLastMessage.mText != null) {
                 builder.addText(mLastMessage.mText, null);
@@ -75,6 +79,7 @@
     public static final class MessageBuilder extends TemplateBuilderImpl
             implements MessagingBuilder.MessageBuilder {
 
+        @RequiresApi(23)
         private Icon mIcon;
         private CharSequence mText;
         private long mTimestamp;
@@ -94,6 +99,7 @@
         /**
          */
         @Override
+        @RequiresApi(23)
         public void addSource(Icon source) {
             mIcon = source;
         }
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java
index dd66616..a9fb0cc 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java
@@ -20,6 +20,7 @@
 
 import android.graphics.drawable.Icon;
 
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 
 /**
@@ -44,6 +45,7 @@
         /**
          * Add the icon used to display contact in the messaging experience
          */
+        @RequiresApi(23)
         void addSource(Icon source);
 
         /**
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java
index e47871a..99f0f34 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java
@@ -22,6 +22,7 @@
 
 import android.graphics.drawable.Icon;
 
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -85,6 +86,7 @@
         /**
          */
         @Override
+        @RequiresApi(23)
         public void addSource(Icon source) {
             mListBuilder.setTitleItem(IconCompat.createFromIcon(source), SMALL_IMAGE);
         }
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java
index e601491..63214da 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java
@@ -20,6 +20,7 @@
 
 import android.graphics.drawable.Icon;
 
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -71,6 +72,7 @@
         /**
          */
         @Override
+        @RequiresApi(23)
         public void addSource(Icon source) {
             getBuilder().addIcon(IconCompat.createFromIcon(source),
                     android.app.slice.Slice.SUBTYPE_SOURCE);
diff --git a/slices/core/api/current.txt b/slices/core/api/current.txt
index acc5cbd..09a69fd 100644
--- a/slices/core/api/current.txt
+++ b/slices/core/api/current.txt
@@ -7,7 +7,6 @@
   }
 
   public class SliceConvert {
-    ctor public SliceConvert();
     method public static android.app.slice.Slice unwrap(androidx.slice.Slice);
     method public static androidx.slice.Slice wrap(android.app.slice.Slice);
   }
@@ -18,20 +17,33 @@
     method public java.util.List<java.lang.String> getHints();
     method public androidx.core.graphics.drawable.IconCompat getIcon();
     method public int getInt();
+    method public long getLong();
     method public androidx.slice.Slice getSlice();
     method public java.lang.String getSubType();
     method public java.lang.CharSequence getText();
-    method public long getTimestamp();
+    method public deprecated long getTimestamp();
     method public boolean hasHint(java.lang.String);
   }
 
-  public abstract class SliceProvider extends android.content.ContentProvider {
+  public abstract class SliceProvider extends android.content.ContentProvider implements androidx.core.app.CoreComponentFactory.CompatWrapped {
     ctor public SliceProvider();
+    method public final int bulkInsert(android.net.Uri, android.content.ContentValues[]);
+    method public final android.net.Uri canonicalize(android.net.Uri);
+    method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+    method public final java.lang.String getType(android.net.Uri);
+    method public java.lang.Object getWrapper();
+    method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
     method public abstract androidx.slice.Slice onBindSlice(android.net.Uri);
+    method public final boolean onCreate();
     method public abstract boolean onCreateSliceProvider();
+    method public java.util.Collection<android.net.Uri> onGetSliceDescendants(android.net.Uri);
     method public android.net.Uri onMapIntentToUri(android.content.Intent);
     method public void onSlicePinned(android.net.Uri);
     method public void onSliceUnpinned(android.net.Uri);
+    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
+    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
+    method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
   }
 
 }
diff --git a/slices/core/build.gradle b/slices/core/build.gradle
index 7be7400..cb17394 100644
--- a/slices/core/build.gradle
+++ b/slices/core/build.gradle
@@ -39,5 +39,5 @@
     mavenGroup = LibraryGroups.SLICE
     inceptionYear = "2017"
     description = "The slices core library provides utilities for the slices view and provider libraries"
-    minSdkVersion = 24
+    minSdkVersion = 19
 }
diff --git a/slices/core/src/main/java/androidx/slice/ArrayUtils.java b/slices/core/src/main/java/androidx/slice/ArrayUtils.java
index f485c22..f7921c5 100644
--- a/slices/core/src/main/java/androidx/slice/ArrayUtils.java
+++ b/slices/core/src/main/java/androidx/slice/ArrayUtils.java
@@ -72,4 +72,7 @@
         }
         return array;
     }
+
+    private ArrayUtils() {
+    }
 }
diff --git a/slices/core/src/main/java/androidx/slice/Slice.java b/slices/core/src/main/java/androidx/slice/Slice.java
index 84c7df7..d18ef23 100644
--- a/slices/core/src/main/java/androidx/slice/Slice.java
+++ b/slices/core/src/main/java/androidx/slice/Slice.java
@@ -31,14 +31,16 @@
 import static android.app.slice.SliceItem.FORMAT_ACTION;
 import static android.app.slice.SliceItem.FORMAT_IMAGE;
 import static android.app.slice.SliceItem.FORMAT_INT;
+import static android.app.slice.SliceItem.FORMAT_LONG;
 import static android.app.slice.SliceItem.FORMAT_REMOTE_INPUT;
 import static android.app.slice.SliceItem.FORMAT_SLICE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
 
 import static androidx.slice.SliceConvert.unwrap;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
 import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
+import static androidx.slice.core.SliceHints.HINT_PERMISSION_REQUEST;
 import static androidx.slice.core.SliceHints.HINT_TTL;
 
 import android.app.PendingIntent;
@@ -57,6 +59,7 @@
 import androidx.annotation.StringDef;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.core.os.BuildCompat;
+import androidx.core.util.Consumer;
 import androidx.slice.compat.SliceProviderCompat;
 
 import java.util.ArrayList;
@@ -84,9 +87,24 @@
      * @hide
      */
     @RestrictTo(Scope.LIBRARY)
-    @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
-            HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL, HINT_SUMMARY, HINT_SEE_MORE,
-            HINT_SHORTCUT, HINT_KEY_WORDS, HINT_TTL, HINT_LAST_UPDATED})
+    @StringDef({
+            HINT_TITLE,
+            HINT_LIST,
+            HINT_LIST_ITEM,
+            HINT_LARGE,
+            HINT_ACTIONS,
+            HINT_SELECTED,
+            HINT_HORIZONTAL,
+            HINT_NO_TINT,
+            HINT_PARTIAL,
+            HINT_SUMMARY,
+            HINT_SEE_MORE,
+            HINT_SHORTCUT,
+            HINT_KEYWORDS,
+            HINT_TTL,
+            HINT_LAST_UPDATED,
+            HINT_PERMISSION_REQUEST,
+    })
     public @interface SliceHint{ }
 
     private final SliceItem[] mItems;
@@ -269,6 +287,19 @@
         }
 
         /**
+         * Add an action to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
+         */
+        public Slice.Builder addAction(@NonNull Consumer<Uri> action,
+                @NonNull Slice s, @Nullable String subType) {
+            @SliceHint String[] hints = s != null
+                    ? s.getHints().toArray(new String[s.getHints().size()]) : new String[0];
+            mItems.add(new SliceItem(action, s, FORMAT_ACTION, subType, hints));
+            return this;
+        }
+
+        /**
          * Add text to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
@@ -361,6 +392,19 @@
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
+        public Slice.Builder addLong(long time, @Nullable String subType,
+                @SliceHint String... hints) {
+            mItems.add(new SliceItem(time, FORMAT_LONG, subType, hints));
+            return this;
+        }
+
+        /**
+         * Add a timestamp to the slice being constructed
+         * @param subType Optional template-specific type information
+         * @see {@link SliceItem#getSubType()}
+         * @deprecated TO BE REMOVED
+         */
+        @Deprecated
         public Slice.Builder addTimestamp(long time, @Nullable String subType,
                 @SliceHint String... hints) {
             mItems.add(new SliceItem(time, FORMAT_TIMESTAMP, subType, hints));
@@ -396,35 +440,28 @@
     }
 
     /**
-     * @hide
      * @return A string representation of this slice.
      */
-    @RestrictTo(Scope.LIBRARY)
     @Override
     public String toString() {
         return toString("");
     }
 
     /**
+     * @return A string representation of this slice.
      * @hide
      */
     @RestrictTo(Scope.LIBRARY)
     public String toString(String indent) {
         StringBuilder sb = new StringBuilder();
+        sb.append(indent);
+        sb.append("slice: ");
+        sb.append("\n");
+        indent += "   ";
         for (int i = 0; i < mItems.length; i++) {
-            sb.append(indent);
-            if (FORMAT_SLICE.equals(mItems[i].getFormat())) {
-                sb.append("slice:\n");
-                sb.append(mItems[i].getSlice().toString(indent + "   "));
-            } else if (FORMAT_ACTION.equals(mItems[i].getFormat())) {
-                sb.append("action:\n");
-                sb.append(mItems[i].getSlice().toString(indent + "   "));
-            } else if (FORMAT_TEXT.equals(mItems[i].getFormat())) {
-                sb.append("text: ");
-                sb.append(mItems[i].getText());
-                sb.append("\n");
-            } else {
-                sb.append(SliceItem.typeToString(mItems[i].getFormat()));
+            SliceItem item = mItems[i];
+            sb.append(item.toString(indent));
+            if (!FORMAT_SLICE.equals(item.getFormat())) {
                 sb.append("\n");
             }
         }
diff --git a/slices/core/src/main/java/androidx/slice/SliceConvert.java b/slices/core/src/main/java/androidx/slice/SliceConvert.java
index 1d89b8e..73ff368 100644
--- a/slices/core/src/main/java/androidx/slice/SliceConvert.java
+++ b/slices/core/src/main/java/androidx/slice/SliceConvert.java
@@ -144,4 +144,7 @@
         }
         return ret;
     }
+
+    private SliceConvert() {
+    }
 }
diff --git a/slices/core/src/main/java/androidx/slice/SliceItem.java b/slices/core/src/main/java/androidx/slice/SliceItem.java
index 067772e..004e51d 100644
--- a/slices/core/src/main/java/androidx/slice/SliceItem.java
+++ b/slices/core/src/main/java/androidx/slice/SliceItem.java
@@ -19,6 +19,7 @@
 import static android.app.slice.SliceItem.FORMAT_ACTION;
 import static android.app.slice.SliceItem.FORMAT_IMAGE;
 import static android.app.slice.SliceItem.FORMAT_INT;
+import static android.app.slice.SliceItem.FORMAT_LONG;
 import static android.app.slice.SliceItem.FORMAT_REMOTE_INPUT;
 import static android.app.slice.SliceItem.FORMAT_SLICE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
@@ -26,10 +27,12 @@
 
 import android.app.PendingIntent;
 import android.app.RemoteInput;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.text.TextUtils;
-import android.util.Pair;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -37,6 +40,8 @@
 import androidx.annotation.RestrictTo.Scope;
 import androidx.annotation.StringDef;
 import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Consumer;
+import androidx.core.util.Pair;
 
 import java.util.Arrays;
 import java.util.List;
@@ -71,7 +76,7 @@
      */
     @RestrictTo(Scope.LIBRARY)
     @StringDef({FORMAT_SLICE, FORMAT_TEXT, FORMAT_IMAGE, FORMAT_ACTION, FORMAT_INT,
-            FORMAT_TIMESTAMP, FORMAT_REMOTE_INPUT})
+            FORMAT_TIMESTAMP, FORMAT_REMOTE_INPUT, FORMAT_LONG})
     public @interface SliceType {
     }
 
@@ -111,7 +116,16 @@
     @RestrictTo(Scope.LIBRARY)
     public SliceItem(PendingIntent intent, Slice slice, String format, String subType,
             @Slice.SliceHint String[] hints) {
-        this(new Pair<>(intent, slice), format, subType, hints);
+        this(new Pair<Object, Slice>(intent, slice), format, subType, hints);
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY)
+    public SliceItem(Consumer<Uri> action, Slice slice, String format, String subType,
+            @Slice.SliceHint String[] hints) {
+        this(new Pair<Object, Slice>(action, slice), format, subType, hints);
     }
 
     /**
@@ -188,7 +202,20 @@
      * SliceItem
      */
     public PendingIntent getAction() {
-        return ((Pair<PendingIntent, Slice>) mObj).first;
+        return (PendingIntent) ((Pair<Object, Slice>) mObj).first;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public void fireAction(Context context, Intent i) throws PendingIntent.CanceledException {
+        Object action = ((Pair<Object, Slice>) mObj).first;
+        if (action instanceof PendingIntent) {
+            ((PendingIntent) action).send(context, 0, i, null, null);
+        } else {
+            ((Consumer<Uri>) action).accept(getSlice().getUri());
+        }
     }
 
     /**
@@ -215,15 +242,23 @@
      */
     public Slice getSlice() {
         if (FORMAT_ACTION.equals(getFormat())) {
-            return ((Pair<PendingIntent, Slice>) mObj).second;
+            return ((Pair<Object, Slice>) mObj).second;
         }
         return (Slice) mObj;
     }
 
     /**
-     * @return The timestamp held by this {@link android.app.slice.SliceItem#FORMAT_TIMESTAMP}
+     * @return The long held by this {@link android.app.slice.SliceItem#FORMAT_LONG}
      * SliceItem
      */
+    public long getLong() {
+        return (Long) mObj;
+    }
+
+    /**
+     * @deprecated TO BE REMOVED
+     */
+    @Deprecated
     public long getTimestamp() {
         return (Long) mObj;
     }
@@ -301,8 +336,8 @@
                 dest.putParcelable(OBJ, ((Slice) obj).toBundle());
                 break;
             case FORMAT_ACTION:
-                dest.putParcelable(OBJ, ((Pair<PendingIntent, Slice>) obj).first);
-                dest.putBundle(OBJ_2, ((Pair<PendingIntent, Slice>) obj).second.toBundle());
+                dest.putParcelable(OBJ, (PendingIntent) ((Pair<Object, Slice>) obj).first);
+                dest.putBundle(OBJ_2, ((Pair<Object, Slice>) obj).second.toBundle());
                 break;
             case FORMAT_TEXT:
                 dest.putCharSequence(OBJ, (CharSequence) obj);
@@ -328,7 +363,7 @@
                 return in.getCharSequence(OBJ);
             case FORMAT_ACTION:
                 return new Pair<>(
-                        (PendingIntent) in.getParcelable(OBJ),
+                        in.getParcelable(OBJ),
                         new Slice(in.getBundle(OBJ_2)));
             case FORMAT_INT:
                 return in.getInt(OBJ);
@@ -363,31 +398,49 @@
     }
 
     /**
-     * @hide
      * @return A string representation of this slice item.
      */
-    @RestrictTo(Scope.LIBRARY)
     @Override
     public String toString() {
         return toString("");
     }
 
-    private String toString(String indent) {
+    /**
+     * @return A string representation of this slice item.
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY)
+    public String toString(String indent) {
         StringBuilder sb = new StringBuilder();
-        sb.append(indent);
-        if (FORMAT_SLICE.equals(mFormat)) {
-            sb.append("slice:\n");
-            sb.append(getSlice().toString(indent + "   "));
-        } else if (FORMAT_ACTION.equals(mFormat)) {
-            sb.append("action:\n");
-            sb.append(getSlice().toString(indent + "   "));
-        } else if (FORMAT_TEXT.equals(mFormat)) {
-            sb.append("text: ");
-            sb.append(getText());
-            sb.append("\n");
-        } else {
-            sb.append(SliceItem.typeToString(getFormat()));
-            sb.append("\n");
+        if (!FORMAT_SLICE.equals(getFormat())) {
+            sb.append(indent);
+            sb.append(getFormat());
+            sb.append(": ");
+        }
+        switch (getFormat()) {
+            case FORMAT_SLICE:
+                sb.append(getSlice().toString(indent));
+                break;
+            case FORMAT_ACTION:
+                sb.append(getAction());
+                sb.append("\n");
+                sb.append(getSlice().toString(indent));
+                break;
+            case FORMAT_TEXT:
+                sb.append(getText());
+                break;
+            case FORMAT_IMAGE:
+                sb.append(getIcon());
+                break;
+            case FORMAT_INT:
+                sb.append(getInt());
+                break;
+            case FORMAT_TIMESTAMP:
+                sb.append(getTimestamp());
+                break;
+            default:
+                sb.append(SliceItem.typeToString(getFormat()));
+                break;
         }
         return sb.toString();
     }
diff --git a/slices/core/src/main/java/androidx/slice/SliceProvider.java b/slices/core/src/main/java/androidx/slice/SliceProvider.java
index 51ffea6..7ec9232 100644
--- a/slices/core/src/main/java/androidx/slice/SliceProvider.java
+++ b/slices/core/src/main/java/androidx/slice/SliceProvider.java
@@ -15,22 +15,61 @@
  */
 package androidx.slice;
 
+import static android.app.slice.Slice.HINT_SHORTCUT;
+import static android.app.slice.Slice.HINT_TITLE;
+import static android.app.slice.SliceProvider.SLICE_TYPE;
+
+import static androidx.slice.compat.SliceProviderCompat.EXTRA_BIND_URI;
+import static androidx.slice.compat.SliceProviderCompat.EXTRA_INTENT;
+import static androidx.slice.compat.SliceProviderCompat.EXTRA_PKG;
+import static androidx.slice.compat.SliceProviderCompat.EXTRA_PROVIDER_PKG;
+import static androidx.slice.compat.SliceProviderCompat.EXTRA_SLICE;
+import static androidx.slice.compat.SliceProviderCompat.EXTRA_SLICE_DESCENDANTS;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_GET_DESCENDANTS;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_GET_PINNED_SPECS;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_MAP_INTENT;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_MAP_ONLY_INTENT;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_PIN;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_SLICE;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_UNPIN;
+import static androidx.slice.compat.SliceProviderCompat.addSpecs;
+import static androidx.slice.compat.SliceProviderCompat.getSpecs;
+import static androidx.slice.core.SliceHints.HINT_PERMISSION_REQUEST;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ProviderInfo;
+import android.content.pm.PackageManager;
 import android.database.ContentObserver;
+import android.database.Cursor;
 import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.os.StrictMode;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.core.app.CoreComponentFactory;
 import androidx.core.os.BuildCompat;
-import androidx.slice.compat.ContentProviderWrapper;
-import androidx.slice.compat.SliceProviderCompat;
+import androidx.slice.compat.CompatPinnedList;
 import androidx.slice.compat.SliceProviderWrapperContainer;
+import androidx.slice.core.R;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Set;
 
 /**
@@ -72,20 +111,21 @@
  *
  * @see android.app.slice.Slice
  */
-public abstract class SliceProvider extends ContentProviderWrapper {
+public abstract class SliceProvider extends ContentProvider implements
+        CoreComponentFactory.CompatWrapped {
 
     private static Set<SliceSpec> sSpecs;
 
-    @Override
-    public void attachInfo(Context context, ProviderInfo info) {
-        ContentProvider impl;
-        if (BuildCompat.isAtLeastP()) {
-            impl = new SliceProviderWrapperContainer.SliceProviderWrapper(this);
-        } else {
-            impl = new SliceProviderCompat(this);
-        }
-        super.attachInfo(context, info, impl);
-    }
+    private static final String TAG = "SliceProvider";
+
+    private static final String DATA_PREFIX = "slice_data_";
+    private static final long SLICE_BIND_ANR = 2000;
+
+    private static final boolean DEBUG = false;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private CompatPinnedList mPinnedList;
+
+    private String mCallback;
 
     /**
      * Implement this to initialize your slice provider on startup.
@@ -104,6 +144,228 @@
      */
     public abstract boolean onCreateSliceProvider();
 
+    @Override
+    public Object getWrapper() {
+        if (BuildCompat.isAtLeastP()) {
+            return new SliceProviderWrapperContainer.SliceProviderWrapper(this);
+        }
+        return null;
+    }
+
+    @Override
+    public final boolean onCreate() {
+        mPinnedList = new CompatPinnedList(getContext(),
+                DATA_PREFIX + getClass().getName());
+        return onCreateSliceProvider();
+    }
+
+    @Override
+    public final String getType(Uri uri) {
+        if (DEBUG) Log.d(TAG, "getFormat " + uri);
+        return SLICE_TYPE;
+    }
+
+    @Override
+    public Bundle call(String method, String arg, Bundle extras) {
+        if (method.equals(METHOD_SLICE)) {
+            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+            if (Binder.getCallingUid() != Process.myUid()) {
+                getContext().enforceUriPermission(uri, Binder.getCallingPid(),
+                        Binder.getCallingUid(),
+                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                        "Slice binding requires write access to the uri");
+            }
+            Set<SliceSpec> specs = getSpecs(extras);
+
+            Slice s = handleBindSlice(uri, specs, getCallingPackage());
+            Bundle b = new Bundle();
+            b.putParcelable(EXTRA_SLICE, s.toBundle());
+            return b;
+        } else if (method.equals(METHOD_MAP_INTENT)) {
+            Intent intent = extras.getParcelable(EXTRA_INTENT);
+            Uri uri = onMapIntentToUri(intent);
+            Bundle b = new Bundle();
+            if (uri != null) {
+                Set<SliceSpec> specs = getSpecs(extras);
+                Slice s = handleBindSlice(uri, specs, getCallingPackage());
+                b.putParcelable(EXTRA_SLICE, s.toBundle());
+            } else {
+                b.putParcelable(EXTRA_SLICE, null);
+            }
+            return b;
+        } else if (method.equals(METHOD_MAP_ONLY_INTENT)) {
+            Intent intent = extras.getParcelable(EXTRA_INTENT);
+            Uri uri = onMapIntentToUri(intent);
+            Bundle b = new Bundle();
+            b.putParcelable(EXTRA_SLICE, uri);
+            return b;
+        } else if (method.equals(METHOD_PIN)) {
+            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+            Set<SliceSpec> specs = getSpecs(extras);
+            String pkg = extras.getString(EXTRA_PKG);
+            if (mPinnedList.addPin(uri, pkg, specs)) {
+                handleSlicePinned(uri);
+            }
+            return null;
+        } else if (method.equals(METHOD_UNPIN)) {
+            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+            String pkg = extras.getString(EXTRA_PKG);
+            if (mPinnedList.removePin(uri, pkg)) {
+                handleSliceUnpinned(uri);
+            }
+            return null;
+        } else if (method.equals(METHOD_GET_PINNED_SPECS)) {
+            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+            Bundle b = new Bundle();
+            addSpecs(b, mPinnedList.getSpecs(uri));
+            return b;
+        } else if (method.equals(METHOD_GET_DESCENDANTS)) {
+            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+            Bundle b = new Bundle();
+            b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
+                    new ArrayList<>(handleGetDescendants(uri)));
+            return b;
+        }
+        return super.call(method, arg, extras);
+    }
+
+    private Collection<Uri> handleGetDescendants(Uri uri) {
+        mCallback = "onGetSliceDescendants";
+        mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
+            return onGetSliceDescendants(uri);
+        } finally {
+            mHandler.removeCallbacks(mAnr);
+        }
+    }
+
+    private void handleSlicePinned(final Uri sliceUri) {
+        mCallback = "onSlicePinned";
+        mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
+            onSlicePinned(sliceUri);
+        } finally {
+            mHandler.removeCallbacks(mAnr);
+        }
+    }
+
+    private void handleSliceUnpinned(final Uri sliceUri) {
+        mCallback = "onSliceUnpinned";
+        mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
+            onSliceUnpinned(sliceUri);
+        } finally {
+            mHandler.removeCallbacks(mAnr);
+        }
+    }
+
+    private Slice handleBindSlice(final Uri sliceUri, final Set<SliceSpec> specs,
+            final String callingPkg) {
+        // This can be removed once Slice#bindSlice is removed and everyone is using
+        // SliceManager#bindSlice.
+        String pkg = callingPkg != null ? callingPkg
+                : getContext().getPackageManager().getNameForUid(Binder.getCallingUid());
+        if (Binder.getCallingUid() != Process.myUid()) {
+            try {
+                getContext().enforceUriPermission(sliceUri,
+                        Binder.getCallingPid(), Binder.getCallingUid(),
+                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                        "Slice binding requires write access to Uri");
+            } catch (SecurityException e) {
+                return createPermissionSlice(getContext(), sliceUri, pkg);
+            }
+        }
+        return onBindSliceStrict(sliceUri, specs);
+    }
+
+    /**
+     * Generate a slice that contains a permission request.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static Slice createPermissionSlice(Context context, Uri sliceUri,
+            String callingPackage) {
+        Slice.Builder parent = new Slice.Builder(sliceUri);
+
+        Slice.Builder action = new Slice.Builder(parent)
+                .addHints(HINT_TITLE, HINT_SHORTCUT)
+                .addAction(createPermissionIntent(context, sliceUri, callingPackage),
+                        new Slice.Builder(parent).build(), null);
+
+        parent.addSubSlice(new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
+                .addText(getPermissionString(context, callingPackage), null)
+                .addSubSlice(action.build())
+                .build());
+
+        return parent.addHints(HINT_PERMISSION_REQUEST).build();
+    }
+
+    /**
+     * Create a PendingIntent pointing at the permission dialog.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static PendingIntent createPermissionIntent(Context context, Uri sliceUri,
+            String callingPackage) {
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(context.getPackageName(),
+                "androidx.slice.compat.SlicePermissionActivity"));
+        intent.putExtra(EXTRA_BIND_URI, sliceUri);
+        intent.putExtra(EXTRA_PKG, callingPackage);
+        intent.putExtra(EXTRA_PROVIDER_PKG, context.getPackageName());
+        // Unique pending intent.
+        intent.setData(sliceUri.buildUpon().appendQueryParameter("package", callingPackage)
+                .build());
+
+        return PendingIntent.getActivity(context, 0, intent, 0);
+    }
+
+    /**
+     * Get string describing permission request.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static CharSequence getPermissionString(Context context, String callingPackage) {
+        PackageManager pm = context.getPackageManager();
+        try {
+            return context.getString(R.string.abc_slices_permission_request,
+                    pm.getApplicationInfo(callingPackage, 0).loadLabel(pm),
+                    context.getApplicationInfo().loadLabel(pm));
+        } catch (PackageManager.NameNotFoundException e) {
+            // This shouldn't be possible since the caller is verified.
+            throw new RuntimeException("Unknown calling app", e);
+        }
+    }
+
+    private Slice onBindSliceStrict(Uri sliceUri, Set<SliceSpec> specs) {
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+        mCallback = "onBindSlice";
+        mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
+            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+                    .detectAll()
+                    .penaltyDeath()
+                    .build());
+            SliceProvider.setSpecs(specs);
+            try {
+                return onBindSlice(sliceUri);
+            } finally {
+                SliceProvider.setSpecs(null);
+                mHandler.removeCallbacks(mAnr);
+            }
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+    }
+
+    private final Runnable mAnr = new Runnable() {
+        @Override
+        public void run() {
+            Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT);
+            Log.wtf(TAG, "Timed out while handling slice callback " + mCallback);
+        }
+    };
+
     /**
      * Implemented to create a slice.
      * <p>
@@ -166,6 +428,75 @@
     }
 
     /**
+     * Obtains a list of slices that are descendants of the specified Uri.
+     * <p>
+     * Implementing this is optional for a SliceProvider, but does provide a good
+     * discovery mechanism for finding slice Uris.
+     *
+     * @param uri The uri to look for descendants under.
+     * @return All slices within the space.
+     * @see androidx.slice.SliceManager#getSliceDescendants(Uri)
+     */
+    public Collection<Uri> onGetSliceDescendants(Uri uri) {
+        return Collections.emptyList();
+    }
+
+    @Nullable
+    @Override
+    public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable String selection, @Nullable String[] selectionArgs,
+            @Nullable String sortOrder) {
+        return null;
+    }
+
+    @Nullable
+    @Override
+    @RequiresApi(28)
+    public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
+        return null;
+    }
+
+    @Nullable
+    @Override
+    @RequiresApi(16)
+    public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable String selection, @Nullable String[] selectionArgs,
+            @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
+        return null;
+    }
+
+    @Nullable
+    @Override
+    public final Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public final int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
+        return 0;
+    }
+
+    @Override
+    public final int delete(@NonNull Uri uri, @Nullable String selection,
+            @Nullable String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public final int update(@NonNull Uri uri, @Nullable ContentValues values,
+            @Nullable String selection, @Nullable String[] selectionArgs) {
+        return 0;
+    }
+
+    @Nullable
+    @Override
+    @RequiresApi(19)
+    public final Uri canonicalize(@NonNull Uri url) {
+        return null;
+    }
+
+    /**
      * @hide
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
diff --git a/slices/core/src/main/java/androidx/slice/SliceSpec.java b/slices/core/src/main/java/androidx/slice/SliceSpec.java
index 27a4dfb..9bbd2a9 100644
--- a/slices/core/src/main/java/androidx/slice/SliceSpec.java
+++ b/slices/core/src/main/java/androidx/slice/SliceSpec.java
@@ -88,7 +88,7 @@
 
     @Override
     public int hashCode() {
-        return mType.hashCode() + Integer.hashCode(mRevision);
+        return mType.hashCode() + mRevision;
     }
 
     @Override
diff --git a/slices/core/src/main/java/androidx/slice/SliceSpecs.java b/slices/core/src/main/java/androidx/slice/SliceSpecs.java
index f91dec2..760d828 100644
--- a/slices/core/src/main/java/androidx/slice/SliceSpecs.java
+++ b/slices/core/src/main/java/androidx/slice/SliceSpecs.java
@@ -41,4 +41,7 @@
      * a source of where the message came from.
      */
     public static final SliceSpec MESSAGING = new SliceSpec("androidx.slice.MESSAGING", 1);
+
+    private SliceSpecs() {
+    }
 }
diff --git a/slices/core/src/main/java/androidx/slice/compat/ContentProviderWrapper.java b/slices/core/src/main/java/androidx/slice/compat/ContentProviderWrapper.java
deleted file mode 100644
index 45c6ffc..0000000
--- a/slices/core/src/main/java/androidx/slice/compat/ContentProviderWrapper.java
+++ /dev/null
@@ -1,123 +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 androidx.slice.compat;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.pm.ProviderInfo;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
-
-/**
- * @hide
- */
-// TODO: Remove as soon as we have better systems in place for this.
-@RestrictTo(Scope.LIBRARY)
-public class ContentProviderWrapper extends ContentProvider {
-
-    private ContentProvider mImpl;
-
-    /**
-     * Triggers an attach with the object to wrap.
-     */
-    public void attachInfo(Context context, ProviderInfo info, ContentProvider impl) {
-        mImpl = impl;
-        super.attachInfo(context, info);
-        mImpl.attachInfo(context, info);
-    }
-
-    @Override
-    public final boolean onCreate() {
-        return mImpl.onCreate();
-    }
-
-    @Nullable
-    @Override
-    public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
-            @Nullable String selection, @Nullable String[] selectionArgs,
-            @Nullable String sortOrder) {
-        return mImpl.query(uri, projection, selection, selectionArgs, sortOrder);
-    }
-
-    @Nullable
-    @Override
-    @RequiresApi(28)
-    public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
-            @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
-        return mImpl.query(uri, projection, queryArgs, cancellationSignal);
-    }
-
-    @Nullable
-    @Override
-    @RequiresApi(16)
-    public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
-            @Nullable String selection, @Nullable String[] selectionArgs,
-            @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
-        return mImpl.query(uri, projection, selection, selectionArgs, sortOrder,
-                cancellationSignal);
-    }
-
-    @Nullable
-    @Override
-    public final String getType(@NonNull Uri uri) {
-        return mImpl.getType(uri);
-    }
-
-    @Nullable
-    @Override
-    public final Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
-        return mImpl.insert(uri, values);
-    }
-
-    @Override
-    public final int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
-        return mImpl.bulkInsert(uri, values);
-    }
-
-    @Override
-    public final int delete(@NonNull Uri uri, @Nullable String selection,
-            @Nullable String[] selectionArgs) {
-        return mImpl.delete(uri, selection, selectionArgs);
-    }
-
-    @Override
-    public final int update(@NonNull Uri uri, @Nullable ContentValues values,
-            @Nullable String selection, @Nullable String[] selectionArgs) {
-        return mImpl.update(uri, values, selection, selectionArgs);
-    }
-
-    @Nullable
-    @Override
-    public final Bundle call(@NonNull String method, @Nullable String arg,
-            @Nullable Bundle extras) {
-        return mImpl.call(method, arg, extras);
-    }
-
-    @Nullable
-    @Override
-    @RequiresApi(19)
-    public final Uri canonicalize(@NonNull Uri url) {
-        return mImpl.canonicalize(url);
-    }
-}
diff --git a/slices/core/src/main/java/androidx/slice/compat/SlicePermissionActivity.java b/slices/core/src/main/java/androidx/slice/compat/SlicePermissionActivity.java
index d8882fa..f3270f8 100644
--- a/slices/core/src/main/java/androidx/slice/compat/SlicePermissionActivity.java
+++ b/slices/core/src/main/java/androidx/slice/compat/SlicePermissionActivity.java
@@ -17,7 +17,6 @@
 package androidx.slice.compat;
 
 import android.app.Activity;
-import android.app.AlertDialog;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.content.DialogInterface.OnDismissListener;
@@ -26,10 +25,11 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.Uri;
 import android.os.Bundle;
-import androidx.annotation.RestrictTo;
 import android.util.Log;
 import android.widget.TextView;
 
+import androidx.annotation.RestrictTo;
+import androidx.appcompat.app.AlertDialog;
 import androidx.slice.core.R;
 
 /**
diff --git a/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java b/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
index 1e2c8f1..61bc65e 100644
--- a/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
+++ b/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
@@ -15,45 +15,33 @@
  */
 package androidx.slice.compat;
 
-import static android.app.slice.Slice.HINT_LIST_ITEM;
 import static android.app.slice.SliceProvider.SLICE_TYPE;
 
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.ContentProvider;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.database.Cursor;
 import android.net.Uri;
-import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.Parcelable;
-import android.os.Process;
 import android.os.RemoteException;
-import android.os.StrictMode;
-import android.os.StrictMode.ThreadPolicy;
-import android.util.ArraySet;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
 import androidx.slice.Slice;
-import androidx.slice.SliceProvider;
 import androidx.slice.SliceSpec;
-import androidx.slice.core.R;
 import androidx.slice.core.SliceHints;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -61,9 +49,8 @@
  * @hide
  */
 @RestrictTo(Scope.LIBRARY)
-public class SliceProviderCompat extends ContentProvider {
-
-    private static final String TAG = "SliceProvider";
+public class SliceProviderCompat {
+    private static final String TAG = "SliceProviderCompat";
 
     public static final String EXTRA_BIND_URI = "slice_uri";
     public static final String METHOD_SLICE = "bind_slice";
@@ -72,6 +59,7 @@
     public static final String METHOD_UNPIN = "unpin_slice";
     public static final String METHOD_GET_PINNED_SPECS = "get_specs";
     public static final String METHOD_MAP_ONLY_INTENT = "map_only";
+    public static final String METHOD_GET_DESCENDANTS = "get_descendants";
 
     public static final String EXTRA_INTENT = "slice_intent";
     public static final String EXTRA_SLICE = "slice";
@@ -79,238 +67,7 @@
     public static final String EXTRA_SUPPORTED_SPECS_REVS = "revs";
     public static final String EXTRA_PKG = "pkg";
     public static final String EXTRA_PROVIDER_PKG = "provider_pkg";
-    private static final String DATA_PREFIX = "slice_data_";
-
-    private static final long SLICE_BIND_ANR = 2000;
-
-    private static final boolean DEBUG = false;
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-    private SliceProvider mSliceProvider;
-    private CompatPinnedList mPinnedList;
-
-    private String mCallback;
-
-    public SliceProviderCompat(SliceProvider provider) {
-        mSliceProvider = provider;
-    }
-
-    @Override
-    public boolean onCreate() {
-        mPinnedList = new CompatPinnedList(getContext(),
-                DATA_PREFIX + mSliceProvider.getClass().getName());
-        return mSliceProvider.onCreateSliceProvider();
-    }
-
-    @Override
-    public final int update(Uri uri, ContentValues values, String selection,
-            String[] selectionArgs) {
-        if (DEBUG) Log.d(TAG, "update " + uri);
-        return 0;
-    }
-
-    @Override
-    public final int delete(Uri uri, String selection, String[] selectionArgs) {
-        if (DEBUG) Log.d(TAG, "delete " + uri);
-        return 0;
-    }
-
-    @Override
-    public final Cursor query(Uri uri, String[] projection, String selection,
-            String[] selectionArgs, String sortOrder) {
-        if (DEBUG) Log.d(TAG, "query " + uri);
-        return null;
-    }
-
-    @Override
-    public final Cursor query(Uri uri, String[] projection, String selection, String[]
-            selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
-        if (DEBUG) Log.d(TAG, "query " + uri);
-        return null;
-    }
-
-    @Override
-    public final Cursor query(Uri uri, String[] projection, Bundle queryArgs,
-            CancellationSignal cancellationSignal) {
-        if (DEBUG) Log.d(TAG, "query " + uri);
-        return null;
-    }
-
-    @Override
-    public final Uri insert(Uri uri, ContentValues values) {
-        if (DEBUG) Log.d(TAG, "insert " + uri);
-        return null;
-    }
-
-    @Override
-    public final String getType(Uri uri) {
-        if (DEBUG) Log.d(TAG, "getFormat " + uri);
-        return SLICE_TYPE;
-    }
-
-    @Override
-    public Bundle call(String method, String arg, Bundle extras) {
-        if (method.equals(METHOD_SLICE)) {
-            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
-            if (Binder.getCallingUid() != Process.myUid()) {
-                getContext().enforceUriPermission(uri, Binder.getCallingPid(),
-                        Binder.getCallingUid(),
-                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                        "Slice binding requires the permission BIND_SLICE");
-            }
-            Set<SliceSpec> specs = getSpecs(extras);
-
-            Slice s = handleBindSlice(uri, specs, getCallingPackage());
-            Bundle b = new Bundle();
-            b.putParcelable(EXTRA_SLICE, s.toBundle());
-            return b;
-        } else if (method.equals(METHOD_MAP_INTENT)) {
-            Intent intent = extras.getParcelable(EXTRA_INTENT);
-            Uri uri = mSliceProvider.onMapIntentToUri(intent);
-            Bundle b = new Bundle();
-            if (uri != null) {
-                Set<SliceSpec> specs = getSpecs(extras);
-                Slice s = handleBindSlice(uri, specs, getCallingPackage());
-                b.putParcelable(EXTRA_SLICE, s.toBundle());
-            } else {
-                b.putParcelable(EXTRA_SLICE, null);
-            }
-            return b;
-        } else if (method.equals(METHOD_MAP_INTENT)) {
-            Intent intent = extras.getParcelable(EXTRA_INTENT);
-            Uri uri = mSliceProvider.onMapIntentToUri(intent);
-            Bundle b = new Bundle();
-            b.putParcelable(EXTRA_SLICE, uri);
-            return b;
-        } else if (method.equals(METHOD_PIN)) {
-            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
-            Set<SliceSpec> specs = getSpecs(extras);
-            String pkg = extras.getString(EXTRA_PKG);
-            if (mPinnedList.addPin(uri, pkg, specs)) {
-                handleSlicePinned(uri);
-            }
-            return null;
-        } else if (method.equals(METHOD_UNPIN)) {
-            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
-            String pkg = extras.getString(EXTRA_PKG);
-            if (mPinnedList.removePin(uri, pkg)) {
-                handleSliceUnpinned(uri);
-            }
-            return null;
-        } else if (method.equals(METHOD_GET_PINNED_SPECS)) {
-            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
-            Bundle b = new Bundle();
-            addSpecs(b, mPinnedList.getSpecs(uri));
-            return b;
-        }
-        return super.call(method, arg, extras);
-    }
-
-    private void handleSlicePinned(final Uri sliceUri) {
-        mCallback = "onSlicePinned";
-        mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
-        try {
-            mSliceProvider.onSlicePinned(sliceUri);
-        } finally {
-            mHandler.removeCallbacks(mAnr);
-        }
-    }
-
-    private void handleSliceUnpinned(final Uri sliceUri) {
-        mCallback = "onSliceUnpinned";
-        mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
-        try {
-            mSliceProvider.onSliceUnpinned(sliceUri);
-        } finally {
-            mHandler.removeCallbacks(mAnr);
-        }
-    }
-
-    private Slice handleBindSlice(final Uri sliceUri, final Set<SliceSpec> specs,
-            final String callingPkg) {
-        // This can be removed once Slice#bindSlice is removed and everyone is using
-        // SliceManager#bindSlice.
-        String pkg = callingPkg != null ? callingPkg
-                : getContext().getPackageManager().getNameForUid(Binder.getCallingUid());
-        if (Binder.getCallingUid() != Process.myUid()) {
-            try {
-                getContext().enforceUriPermission(sliceUri,
-                        Binder.getCallingPid(), Binder.getCallingUid(),
-                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                        "Slice binding requires write access to Uri");
-            } catch (SecurityException e) {
-                return createPermissionSlice(getContext(), sliceUri, pkg);
-            }
-        }
-        return onBindSliceStrict(sliceUri, specs);
-    }
-
-    /**
-     * Generate a slice that contains a permission request.
-     */
-    public static Slice createPermissionSlice(Context context, Uri sliceUri,
-            String callingPackage) {
-        return new Slice.Builder(sliceUri)
-                .addAction(createPermissionIntent(context, sliceUri, callingPackage),
-                        new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
-                                .addText(getPermissionString(context, callingPackage), null)
-                                .build(), null)
-                .addHints(HINT_LIST_ITEM)
-                .build();
-    }
-
-    /**
-     * Create a PendingIntent pointing at the permission dialog.
-     */
-    public static PendingIntent createPermissionIntent(Context context, Uri sliceUri,
-            String callingPackage) {
-        Intent intent = new Intent();
-        intent.setComponent(new ComponentName(context.getPackageName(),
-                "androidx.slice.compat.SlicePermissionActivity"));
-        intent.putExtra(EXTRA_BIND_URI, sliceUri);
-        intent.putExtra(EXTRA_PKG, callingPackage);
-        intent.putExtra(EXTRA_PROVIDER_PKG, context.getPackageName());
-        // Unique pending intent.
-        intent.setData(sliceUri.buildUpon().appendQueryParameter("package", callingPackage)
-                .build());
-
-        return PendingIntent.getActivity(context, 0, intent, 0);
-    }
-
-    /**
-     * Get string describing permission request.
-     */
-    public static CharSequence getPermissionString(Context context, String callingPackage) {
-        PackageManager pm = context.getPackageManager();
-        try {
-            return context.getString(R.string.abc_slices_permission_request,
-                    pm.getApplicationInfo(callingPackage, 0).loadLabel(pm),
-                    context.getApplicationInfo().loadLabel(pm));
-        } catch (PackageManager.NameNotFoundException e) {
-            // This shouldn't be possible since the caller is verified.
-            throw new RuntimeException("Unknown calling app", e);
-        }
-    }
-
-    private Slice onBindSliceStrict(Uri sliceUri, Set<SliceSpec> specs) {
-        ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
-        mCallback = "onBindSlice";
-        mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
-        try {
-            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
-                    .detectAll()
-                    .penaltyDeath()
-                    .build());
-            SliceProvider.setSpecs(specs);
-            try {
-                return mSliceProvider.onBindSlice(sliceUri);
-            } finally {
-                SliceProvider.setSpecs(null);
-                mHandler.removeCallbacks(mAnr);
-            }
-        } finally {
-            StrictMode.setThreadPolicy(oldPolicy);
-        }
-    }
+    public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants";
 
     /**
      * Compat version of {@link Slice#bindSlice}.
@@ -348,7 +105,10 @@
         }
     }
 
-    private static void addSpecs(Bundle extras, Set<SliceSpec> supportedSpecs) {
+    /**
+     * Compat way to push specs through the call.
+     */
+    public static void addSpecs(Bundle extras, Set<SliceSpec> supportedSpecs) {
         ArrayList<String> types = new ArrayList<>();
         ArrayList<Integer> revs = new ArrayList<>();
         for (SliceSpec spec : supportedSpecs) {
@@ -359,7 +119,10 @@
         extras.putIntegerArrayList(EXTRA_SUPPORTED_SPECS_REVS, revs);
     }
 
-    private static Set<SliceSpec> getSpecs(Bundle extras) {
+    /**
+     * Compat way to push specs through the call.
+     */
+    public static Set<SliceSpec> getSpecs(Bundle extras) {
         ArraySet<SliceSpec> specs = new ArraySet<>();
         ArrayList<String> types = extras.getStringArrayList(EXTRA_SUPPORTED_SPECS);
         ArrayList<Integer> revs = extras.getIntegerArrayList(EXTRA_SUPPORTED_SPECS_REVS);
@@ -540,11 +303,22 @@
         }
     }
 
-    private final Runnable mAnr = new Runnable() {
-        @Override
-        public void run() {
-            Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT);
-            Log.wtf(TAG, "Timed out while handling slice callback " + mCallback);
+    /**
+     * Compat version of {@link android.app.slice.SliceManager#getSliceDescendants(Uri)}
+     */
+    public static @NonNull Collection<Uri> getSliceDescendants(Context context, @NonNull Uri uri) {
+        ContentResolver resolver = context.getContentResolver();
+        try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+            Bundle extras = new Bundle();
+            extras.putParcelable(EXTRA_BIND_URI, uri);
+            final Bundle res = provider.call(METHOD_GET_DESCENDANTS, null, extras);
+            return res.getParcelableArrayList(EXTRA_SLICE_DESCENDANTS);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get slice descendants", e);
         }
-    };
+        return Collections.emptyList();
+    }
+
+    private SliceProviderCompat() {
+    }
 }
diff --git a/slices/core/src/main/java/androidx/slice/compat/SliceProviderWrapperContainer.java b/slices/core/src/main/java/androidx/slice/compat/SliceProviderWrapperContainer.java
index 65505f7..6691488 100644
--- a/slices/core/src/main/java/androidx/slice/compat/SliceProviderWrapperContainer.java
+++ b/slices/core/src/main/java/androidx/slice/compat/SliceProviderWrapperContainer.java
@@ -22,7 +22,9 @@
 import android.app.slice.Slice;
 import android.app.slice.SliceProvider;
 import android.app.slice.SliceSpec;
+import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ProviderInfo;
 import android.net.Uri;
 
 import androidx.annotation.NonNull;
@@ -30,6 +32,7 @@
 import androidx.collection.ArraySet;
 import androidx.slice.SliceConvert;
 
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -50,8 +53,14 @@
         }
 
         @Override
+        public void attachInfo(Context context, ProviderInfo info) {
+            super.attachInfo(context, info);
+            mSliceProvider.attachInfo(context, info);
+        }
+
+        @Override
         public boolean onCreate() {
-            return mSliceProvider.onCreateSliceProvider();
+            return mSliceProvider.onCreate();
         }
 
         @Override
@@ -74,6 +83,11 @@
             mSliceProvider.onSliceUnpinned(sliceUri);
         }
 
+        @Override
+        public Collection<Uri> onGetSliceDescendants(Uri uri) {
+            return mSliceProvider.onGetSliceDescendants(uri);
+        }
+
         /**
          * Maps intents to uris.
          */
@@ -82,4 +96,7 @@
             return mSliceProvider.onMapIntentToUri(intent);
         }
     }
+
+    private SliceProviderWrapperContainer() {
+    }
 }
diff --git a/slices/core/src/main/java/androidx/slice/core/SliceActionImpl.java b/slices/core/src/main/java/androidx/slice/core/SliceActionImpl.java
index b5ca913..4fb81a6 100644
--- a/slices/core/src/main/java/androidx/slice/core/SliceActionImpl.java
+++ b/slices/core/src/main/java/androidx/slice/core/SliceActionImpl.java
@@ -27,7 +27,6 @@
 import static android.app.slice.SliceItem.FORMAT_ACTION;
 import static android.app.slice.SliceItem.FORMAT_IMAGE;
 import static android.app.slice.SliceItem.FORMAT_INT;
-import static android.app.slice.SliceItem.FORMAT_SLICE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
@@ -64,6 +63,7 @@
     private boolean mIsChecked;
     private int mPriority = -1;
     private SliceItem mSliceItem;
+    private SliceItem mActionItem;
 
     /**
      * Construct a SliceAction representing a tappable icon.
@@ -147,40 +147,36 @@
     @RestrictTo(LIBRARY)
     public SliceActionImpl(SliceItem slice) {
         mSliceItem = slice;
-        if (slice.hasHint(HINT_SHORTCUT) && FORMAT_SLICE.equals(slice.getFormat())) {
-            SliceItem actionItem = SliceQuery.find(slice, FORMAT_ACTION);
-            if (actionItem == null) {
-                // Can't have action slice without action
-                return;
-            }
-            mAction = actionItem.getAction();
-            SliceItem iconItem = SliceQuery.find(actionItem.getSlice(), FORMAT_IMAGE);
-            if (iconItem != null) {
-                mIcon = iconItem.getIcon();
-                mImageMode = iconItem.hasHint(HINT_NO_TINT)
-                        ? iconItem.hasHint(HINT_LARGE) ? LARGE_IMAGE : SMALL_IMAGE
-                        : ICON_IMAGE;
-            }
-            SliceItem titleItem = SliceQuery.find(actionItem.getSlice(), FORMAT_TEXT, HINT_TITLE,
-                    null /* nonHints */);
-            if (titleItem != null) {
-                mTitle = titleItem.getText();
-            }
-            SliceItem cdItem = SliceQuery.findSubtype(actionItem.getSlice(), FORMAT_TEXT,
-                    SUBTYPE_CONTENT_DESCRIPTION);
-            if (cdItem != null) {
-                mContentDescription = cdItem.getText();
-            }
-            mIsToggle = SUBTYPE_TOGGLE.equals(actionItem.getSubType());
-            if (mIsToggle) {
-                mIsChecked = actionItem.hasHint(HINT_SELECTED);
-            }
-            SliceItem priority = SliceQuery.findSubtype(actionItem.getSlice(), FORMAT_INT,
-                    SUBTYPE_PRIORITY);
-            mPriority = priority != null ? priority.getInt() : -1;
-        } else if (FORMAT_ACTION.equals(slice.getFormat())) {
-            mAction = slice.getAction();
+        SliceItem actionItem = SliceQuery.find(slice, FORMAT_ACTION);
+        if (actionItem == null) {
+            // Can't have action slice without action
+            return;
         }
+        mActionItem = actionItem;
+        SliceItem iconItem = SliceQuery.find(actionItem.getSlice(), FORMAT_IMAGE);
+        if (iconItem != null) {
+            mIcon = iconItem.getIcon();
+            mImageMode = iconItem.hasHint(HINT_NO_TINT)
+                    ? iconItem.hasHint(HINT_LARGE) ? LARGE_IMAGE : SMALL_IMAGE
+                    : ICON_IMAGE;
+        }
+        SliceItem titleItem = SliceQuery.find(actionItem.getSlice(), FORMAT_TEXT, HINT_TITLE,
+                null /* nonHints */);
+        if (titleItem != null) {
+            mTitle = titleItem.getText();
+        }
+        SliceItem cdItem = SliceQuery.findSubtype(actionItem.getSlice(), FORMAT_TEXT,
+                SUBTYPE_CONTENT_DESCRIPTION);
+        if (cdItem != null) {
+            mContentDescription = cdItem.getText();
+        }
+        mIsToggle = SUBTYPE_TOGGLE.equals(actionItem.getSubType());
+        if (mIsToggle) {
+            mIsChecked = actionItem.hasHint(HINT_SELECTED);
+        }
+        SliceItem priority = SliceQuery.findSubtype(actionItem.getSlice(), FORMAT_INT,
+                SUBTYPE_PRIORITY);
+        mPriority = priority != null ? priority.getInt() : -1;
     }
 
     /**
@@ -218,7 +214,15 @@
     @NonNull
     @Override
     public PendingIntent getAction() {
-        return mAction;
+        return mAction != null ? mAction : mActionItem.getAction();
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public SliceItem getActionItem() {
+        return mActionItem;
     }
 
     /**
diff --git a/slices/core/src/main/java/androidx/slice/core/SliceHints.java b/slices/core/src/main/java/androidx/slice/core/SliceHints.java
index acbdfc6..f546b6c 100644
--- a/slices/core/src/main/java/androidx/slice/core/SliceHints.java
+++ b/slices/core/src/main/java/androidx/slice/core/SliceHints.java
@@ -18,9 +18,13 @@
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import androidx.annotation.IntDef;
 import androidx.annotation.RestrictTo;
 
+import java.lang.annotation.Retention;
+
 /**
  * Temporary class to contain hint constants for slices to be used.
  * @hide
@@ -60,7 +64,7 @@
      * A hint to indicate that the contents of this subslice represent a list of keywords
      * related to the parent slice.
      */
-    public static final String HINT_KEY_WORDS = "key_words";
+    public static final String HINT_KEYWORDS = "keywords";
 
     /**
      * Hint indicating an item representing a time-to-live for the content.
@@ -78,9 +82,12 @@
      */
     public static final String SUBTYPE_MILLIS = "millis";
 
+    public static final String HINT_PERMISSION_REQUEST = "permission_request";
+
     @IntDef({
             LARGE_IMAGE, SMALL_IMAGE, ICON_IMAGE, UNKNOWN_IMAGE
     })
+    @Retention(SOURCE)
     public @interface ImageMode{}
 
     /**
@@ -104,4 +111,7 @@
      * Constant representing infinity.
      */
     public static final long INFINITY = -1;
+
+    private SliceHints() {
+    }
 }
diff --git a/slices/core/src/main/java/androidx/slice/core/SliceQuery.java b/slices/core/src/main/java/androidx/slice/core/SliceQuery.java
index f123df0..626a1dc 100644
--- a/slices/core/src/main/java/androidx/slice/core/SliceQuery.java
+++ b/slices/core/src/main/java/androidx/slice/core/SliceQuery.java
@@ -19,22 +19,16 @@
 import static android.app.slice.SliceItem.FORMAT_ACTION;
 import static android.app.slice.SliceItem.FORMAT_SLICE;
 
-import androidx.annotation.RestrictTo;
 import android.text.TextUtils;
 
-import java.util.ArrayDeque;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Queue;
-import java.util.Spliterators;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
+import androidx.annotation.RestrictTo;
 import androidx.slice.Slice;
 import androidx.slice.SliceItem;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
 /**
  * Utilities for finding content within a Slice.
  * @hide
@@ -98,12 +92,12 @@
      */
     private static boolean contains(SliceItem container, final SliceItem item) {
         if (container == null || item == null) return false;
-        return stream(container).filter(new Predicate<SliceItem>() {
+        return findFirst(filter(stream(container), new Filter<SliceItem>() {
             @Override
-            public boolean test(SliceItem s) {
+            public boolean filter(SliceItem s) {
                 return s == item;
             }
-        }).findAny().isPresent();
+        }), null) != null;
     }
 
     /**
@@ -129,26 +123,26 @@
      */
     public static List<SliceItem> findAll(Slice s, final String format, final String[] hints,
             final String[] nonHints) {
-        return stream(s).filter(new Predicate<SliceItem>() {
+        return collect(filter(stream(s), new Filter<SliceItem>() {
             @Override
-            public boolean test(SliceItem item) {
+            public boolean filter(SliceItem item) {
                 return checkFormat(item, format)
                         && (hasHints(item, hints) && !hasAnyHints(item, nonHints));
             }
-        }).collect(Collectors.<SliceItem>toList());
+        }));
     }
 
     /**
      */
     public static List<SliceItem> findAll(SliceItem s, final String format, final String[] hints,
             final String[] nonHints) {
-        return stream(s).filter(new Predicate<SliceItem>() {
+        return collect(filter(stream(s), new Filter<SliceItem>() {
             @Override
-            public boolean test(SliceItem item) {
+            public boolean filter(SliceItem item) {
                 return checkFormat(item, format)
                         && (hasHints(item, hints) && !hasAnyHints(item, nonHints));
             }
-        }).collect(Collectors.<SliceItem>toList());
+        }));
     }
 
     /**
@@ -179,48 +173,48 @@
      */
     public static SliceItem find(Slice s, final String format, final String[] hints,
             final String[] nonHints) {
-        return stream(s).filter(new Predicate<SliceItem>() {
+        return findFirst(filter(stream(s), new Filter<SliceItem>() {
             @Override
-            public boolean test(SliceItem item) {
+            public boolean filter(SliceItem item) {
                 return checkFormat(item, format)
                         && (hasHints(item, hints) && !hasAnyHints(item, nonHints));
             }
-        }).findFirst().orElse(null);
+        }), null);
     }
 
     /**
      */
     public static SliceItem findSubtype(Slice s, final String format, final String subtype) {
-        return stream(s).filter(new Predicate<SliceItem>() {
+        return findFirst(filter(stream(s), new Filter<SliceItem>() {
             @Override
-            public boolean test(SliceItem item) {
+            public boolean filter(SliceItem item) {
                 return checkFormat(item, format) && checkSubtype(item, subtype);
             }
-        }).findFirst().orElse(null);
+        }), null);
     }
 
     /**
      */
     public static SliceItem findSubtype(SliceItem s, final String format, final String subtype) {
-        return stream(s).filter(new Predicate<SliceItem>() {
+        return findFirst(filter(stream(s), new Filter<SliceItem>() {
             @Override
-            public boolean test(SliceItem item) {
+            public boolean filter(SliceItem item) {
                 return checkFormat(item, format) && checkSubtype(item, subtype);
             }
-        }).findFirst().orElse(null);
+        }), null);
     }
 
     /**
      */
     public static SliceItem find(SliceItem s, final String format, final String[] hints,
             final String[] nonHints) {
-        return stream(s).filter(new Predicate<SliceItem>() {
+        return findFirst(filter(stream(s), new Filter<SliceItem>() {
             @Override
-            public boolean test(SliceItem item) {
+            public boolean filter(SliceItem item) {
                 return checkFormat(item, format)
                         && (hasHints(item, hints) && !hasAnyHints(item, nonHints));
             }
-        }).findFirst().orElse(null);
+        }), null);
     }
 
     private static boolean checkFormat(SliceItem item, String format) {
@@ -233,24 +227,24 @@
 
     /**
      */
-    public static Stream<SliceItem> stream(SliceItem slice) {
-        Queue<SliceItem> items = new ArrayDeque<>();
+    public static Iterator<SliceItem> stream(SliceItem slice) {
+        ArrayList<SliceItem> items = new ArrayList<>();
         items.add(slice);
         return getSliceItemStream(items);
     }
 
     /**
      */
-    public static Stream<SliceItem> stream(Slice slice) {
-        Queue<SliceItem> items = new ArrayDeque<>();
+    public static Iterator<SliceItem> stream(Slice slice) {
+        ArrayList<SliceItem> items = new ArrayList<>();
         items.addAll(slice.getItems());
         return getSliceItemStream(items);
     }
 
     /**
      */
-    private static Stream<SliceItem> getSliceItemStream(final Queue<SliceItem> items) {
-        Iterator<SliceItem> iterator = new Iterator<SliceItem>() {
+    private static Iterator<SliceItem> getSliceItemStream(final ArrayList<SliceItem> items) {
+        return new Iterator<SliceItem>() {
             @Override
             public boolean hasNext() {
                 return items.size() != 0;
@@ -258,7 +252,7 @@
 
             @Override
             public SliceItem next() {
-                SliceItem item = items.poll();
+                SliceItem item = items.remove(0);
                 if (FORMAT_SLICE.equals(item.getFormat())
                         || FORMAT_ACTION.equals(item.getFormat())) {
                     items.addAll(item.getSlice().getItems());
@@ -266,6 +260,54 @@
                 return item;
             }
         };
-        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
+    }
+
+    private static <T> List<T> collect(Iterator<T> iter) {
+        List<T> list = new ArrayList<>();
+        while (iter.hasNext()) list.add(iter.next());
+        return list;
+    }
+
+    private static <T> Iterator<T> filter(final Iterator<T> input, final Filter<T> f) {
+        return new Iterator<T>() {
+            T mNext = findNext();
+
+            private T findNext() {
+                while (input.hasNext()) {
+                    T i = input.next();
+                    if (f.filter(i)) {
+                        return i;
+                    }
+                }
+                return null;
+            }
+
+            @Override
+            public boolean hasNext() {
+                return mNext != null;
+            }
+
+            @Override
+            public T next() {
+                T ret = mNext;
+                mNext = findNext();
+                return ret;
+            }
+        };
+    }
+
+    private static <T> T findFirst(Iterator<T> filter, T def) {
+        while (filter.hasNext()) {
+            T r = filter.next();
+            if (r != null) return r;
+        }
+        return def;
+    }
+
+    private interface Filter<T> {
+        boolean filter(T input);
+    }
+
+    private SliceQuery() {
     }
 }
diff --git a/slices/core/src/main/res/values-as/strings.xml b/slices/core/src/main/res/values-as/strings.xml
new file mode 100644
index 0000000..95722b9
--- /dev/null
+++ b/slices/core/src/main/res/values-as/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+~ Copyright 2018 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_slices_permission_request" msgid="3604847235923472451">"<xliff:g id="APP_0">%1$s</xliff:g>এ <xliff:g id="APP_2">%2$s</xliff:g>ৰ অংশ দেখুওৱাব খুজিছে"</string>
+    <string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g>ক <xliff:g id="APP_2">%2$s</xliff:g>ৰ অংশ দেখুওৱাবলৈ অনুমতি দিবনে?"</string>
+    <string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ই <xliff:g id="APP">%1$s</xliff:g>ৰ তথ্য পঢ়িব পাৰে"</string>
+    <string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ই <xliff:g id="APP">%1$s</xliff:g>ৰ ভিতৰত কাৰ্য কৰিব পাৰে"</string>
+    <string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"<xliff:g id="APP">%1$s</xliff:g>ক যিকোনো এপৰ অংশ দেখুওৱাবলৈ অনুমতি দিয়ক"</string>
+    <string name="abc_slice_permission_allow" msgid="5024599872061409708">"অনুমতি দিয়ক"</string>
+    <string name="abc_slice_permission_deny" msgid="3819478292430407705">"অস্বীকাৰ কৰক"</string>
+</resources>
diff --git a/slices/core/src/main/res/values-be/strings.xml b/slices/core/src/main/res/values-be/strings.xml
index c9fc9a8..5fadc3f 100644
--- a/slices/core/src/main/res/values-be/strings.xml
+++ b/slices/core/src/main/res/values-be/strings.xml
@@ -17,11 +17,11 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="abc_slices_permission_request" msgid="3604847235923472451">"Праграма <xliff:g id="APP_0">%1$s</xliff:g> запытвае дазвол на паказ зрэзаў праграмы <xliff:g id="APP_2">%2$s</xliff:g>"</string>
-    <string name="abc_slice_permission_title" msgid="4175332421259324948">"Дазволіць праграме <xliff:g id="APP_0">%1$s</xliff:g> паказваць зрэзы праграмы <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
+    <string name="abc_slices_permission_request" msgid="3604847235923472451">"Праграма <xliff:g id="APP_0">%1$s</xliff:g> запытвае дазвол на паказ фрагментаў праграмы <xliff:g id="APP_2">%2$s</xliff:g>"</string>
+    <string name="abc_slice_permission_title" msgid="4175332421259324948">"Дазволіць праграме <xliff:g id="APP_0">%1$s</xliff:g> паказваць фрагменты праграмы <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Можа счытваць інфармацыю з праграмы <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Можа выконваць дзеянні ў праграме <xliff:g id="APP">%1$s</xliff:g>"</string>
-    <string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"Дазволіць праграме <xliff:g id="APP">%1$s</xliff:g> паказваць зрэзы іншых праграм"</string>
+    <string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"Дазволіць праграме <xliff:g id="APP">%1$s</xliff:g> паказваць фрагменты іншых праграм"</string>
     <string name="abc_slice_permission_allow" msgid="5024599872061409708">"Дазволіць"</string>
     <string name="abc_slice_permission_deny" msgid="3819478292430407705">"Адмовіць"</string>
 </resources>
diff --git a/slices/core/src/main/res/values-ja/strings.xml b/slices/core/src/main/res/values-ja/strings.xml
index a04df0b..315326f 100644
--- a/slices/core/src/main/res/values-ja/strings.xml
+++ b/slices/core/src/main/res/values-ja/strings.xml
@@ -19,7 +19,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="abc_slices_permission_request" msgid="3604847235923472451">"<xliff:g id="APP_0">%1$s</xliff:g> が <xliff:g id="APP_2">%2$s</xliff:g> のスライスの表示をリクエストしています"</string>
     <string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_2">%2$s</xliff:g> のスライスの表示を <xliff:g id="APP_0">%1$s</xliff:g> に許可しますか?"</string>
-    <string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- <xliff:g id="APP">%1$s</xliff:g> からの情報を読み取ることがあります"</string>
+    <string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- <xliff:g id="APP">%1$s</xliff:g> からの情報を読み取ることができます"</string>
     <string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- <xliff:g id="APP">%1$s</xliff:g> 内部で操作することがあります"</string>
     <string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"すべてのアプリのスライスを表示することを <xliff:g id="APP">%1$s</xliff:g> に許可する"</string>
     <string name="abc_slice_permission_allow" msgid="5024599872061409708">"許可"</string>
diff --git a/slices/core/src/main/res/values-or/strings.xml b/slices/core/src/main/res/values-or/strings.xml
new file mode 100644
index 0000000..f6b9443
--- /dev/null
+++ b/slices/core/src/main/res/values-or/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+~ Copyright 2018 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_slices_permission_request" msgid="3604847235923472451">"<xliff:g id="APP_0">%1$s</xliff:g>, <xliff:g id="APP_2">%2$s</xliff:g> ସ୍ଲାଇସ୍‌କୁ ଦେଖାଇବା ପାଇଁ ଚାହେଁ"</string>
+    <string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_2">%2$s</xliff:g> ସ୍ଲାଇସ୍‌କୁ ଦେଖାଇବା ପାଇଁ <xliff:g id="APP_0">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
+    <string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ଏହା <xliff:g id="APP">%1$s</xliff:g>ରୁ ସୂଚନାକୁ ପଢ଼ିପାରିବ"</string>
+    <string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ଏହା <xliff:g id="APP">%1$s</xliff:g> ଭିତରେ କାମ କରିପାରିବ"</string>
+    <string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"ଯେକୌଣସି ଆପ୍‌ରେ ସ୍ଲାଇସ୍‌କୁ ଦେଖାଇବା ପାଇଁ <xliff:g id="APP">%1$s</xliff:g>କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+    <string name="abc_slice_permission_allow" msgid="5024599872061409708">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+    <string name="abc_slice_permission_deny" msgid="3819478292430407705">"ଅସ୍ଵୀକାର କରନ୍ତୁ"</string>
+</resources>
diff --git a/slices/view/api/current.txt b/slices/view/api/current.txt
index 0936bbf..65d5faa 100644
--- a/slices/view/api/current.txt
+++ b/slices/view/api/current.txt
@@ -3,11 +3,15 @@
   public abstract class SliceManager {
     method public abstract androidx.slice.Slice bindSlice(android.net.Uri);
     method public abstract androidx.slice.Slice bindSlice(android.content.Intent);
+    method public abstract int checkSlicePermission(android.net.Uri, int, int);
     method public static androidx.slice.SliceManager getInstance(android.content.Context);
+    method public abstract java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri);
+    method public abstract void grantSlicePermission(java.lang.String, android.net.Uri);
     method public abstract android.net.Uri mapIntentToUri(android.content.Intent);
     method public abstract void pinSlice(android.net.Uri);
     method public abstract void registerSliceCallback(android.net.Uri, androidx.slice.SliceManager.SliceCallback);
     method public abstract void registerSliceCallback(android.net.Uri, java.util.concurrent.Executor, androidx.slice.SliceManager.SliceCallback);
+    method public abstract void revokeSlicePermission(java.lang.String, android.net.Uri);
     method public abstract void unpinSlice(android.net.Uri);
     method public abstract void unregisterSliceCallback(android.net.Uri, androidx.slice.SliceManager.SliceCallback);
   }
@@ -19,10 +23,16 @@
   public class SliceMetadata {
     method public static androidx.slice.SliceMetadata from(android.content.Context, androidx.slice.Slice);
     method public long getExpiry();
+    method public int getHeaderType();
     method public long getLastUpdatedTime();
     method public int getLoadingState();
+    method public androidx.slice.core.SliceAction getPrimaryAction();
+    method public androidx.core.util.Pair<java.lang.Integer, java.lang.Integer> getRange();
     method public java.util.List<androidx.slice.SliceItem> getSliceActions();
     method public java.util.List<java.lang.String> getSliceKeywords();
+    method public java.util.List<androidx.slice.core.SliceAction> getToggles();
+    method public boolean hasLargeMode();
+    method public boolean isPermissionSlice();
     field public static final int LOADED_ALL = 2; // 0x2
     field public static final int LOADED_NONE = 0; // 0x0
     field public static final int LOADED_PARTIAL = 1; // 0x1
@@ -32,8 +42,8 @@
     method public static deprecated int getLoadingState(androidx.slice.Slice);
     method public static deprecated java.util.List<androidx.slice.SliceItem> getSliceActions(androidx.slice.Slice);
     method public static deprecated java.util.List<java.lang.String> getSliceKeywords(androidx.slice.Slice);
-    method public static androidx.slice.Slice parseSlice(java.io.InputStream, java.lang.String) throws java.io.IOException;
-    method public static void serializeSlice(androidx.slice.Slice, android.content.Context, java.io.OutputStream, java.lang.String, androidx.slice.SliceUtils.SerializeOptions) throws java.io.IOException;
+    method public static androidx.slice.Slice parseSlice(android.content.Context, java.io.InputStream, java.lang.String, androidx.slice.SliceUtils.SliceActionListener) throws java.io.IOException, androidx.slice.SliceUtils.SliceParseException;
+    method public static void serializeSlice(androidx.slice.Slice, android.content.Context, java.io.OutputStream, java.lang.String, androidx.slice.SliceUtils.SerializeOptions) throws java.io.IOException, java.lang.IllegalArgumentException;
     field public static final deprecated int LOADING_ALL = 0; // 0x0
     field public static final deprecated int LOADING_COMPLETE = 2; // 0x2
     field public static final deprecated int LOADING_PARTIAL = 1; // 0x1
@@ -43,11 +53,20 @@
     ctor public SliceUtils.SerializeOptions();
     method public androidx.slice.SliceUtils.SerializeOptions setActionMode(int);
     method public androidx.slice.SliceUtils.SerializeOptions setImageMode(int);
-    field public static final int MODE_DISABLE = 2; // 0x2
+    method public androidx.slice.SliceUtils.SerializeOptions setMaxImageHeight(int);
+    method public androidx.slice.SliceUtils.SerializeOptions setMaxImageWidth(int);
+    field public static final int MODE_CONVERT = 2; // 0x2
     field public static final int MODE_REMOVE = 1; // 0x1
     field public static final int MODE_THROW = 0; // 0x0
   }
 
+  public static abstract interface SliceUtils.SliceActionListener {
+    method public abstract void onSliceAction(android.net.Uri);
+  }
+
+  public static class SliceUtils.SliceParseException extends java.lang.Exception {
+  }
+
 }
 
 package androidx.slice.widget {
@@ -66,7 +85,10 @@
     field public static final int ROW_TYPE_GRID = 1; // 0x1
     field public static final int ROW_TYPE_LIST = 0; // 0x0
     field public static final int ROW_TYPE_MESSAGING = 2; // 0x2
+    field public static final int ROW_TYPE_PROGRESS = 5; // 0x5
     field public static final int ROW_TYPE_SHORTCUT = -1; // 0xffffffff
+    field public static final int ROW_TYPE_SLIDER = 4; // 0x4
+    field public static final int ROW_TYPE_TOGGLE = 3; // 0x3
     field public static final int STATE_OFF = 0; // 0x0
     field public static final int STATE_ON = 1; // 0x1
     field public int actionCount;
@@ -80,12 +102,11 @@
   }
 
   public final class SliceLiveData {
-    ctor public SliceLiveData();
     method public static androidx.lifecycle.LiveData<androidx.slice.Slice> fromIntent(android.content.Context, android.content.Intent);
     method public static androidx.lifecycle.LiveData<androidx.slice.Slice> fromUri(android.content.Context, android.net.Uri);
   }
 
-  public class SliceView extends android.view.ViewGroup implements androidx.lifecycle.Observer {
+  public class SliceView extends android.view.ViewGroup implements androidx.lifecycle.Observer android.view.View.OnClickListener {
     ctor public SliceView(android.content.Context);
     ctor public SliceView(android.content.Context, android.util.AttributeSet);
     ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
@@ -93,6 +114,7 @@
     method public int getMode();
     method public java.util.List<androidx.slice.SliceItem> getSliceActions();
     method public void onChanged(androidx.slice.Slice);
+    method public void onClick(android.view.View);
     method public void setMode(int);
     method public void setOnSliceActionListener(androidx.slice.widget.SliceView.OnSliceActionListener);
     method public void setScrollable(boolean);
diff --git a/slices/view/build.gradle b/slices/view/build.gradle
index 8653955..71de3a7 100644
--- a/slices/view/build.gradle
+++ b/slices/view/build.gradle
@@ -44,5 +44,5 @@
     mavenGroup = LibraryGroups.SLICE
     inceptionYear = "2017"
     description = "A library that handles rendering of slice content into supported templates"
-    minSdkVersion = 24
+    minSdkVersion = 19
 }
diff --git a/slices/view/src/androidTest/AndroidManifest.xml b/slices/view/src/androidTest/AndroidManifest.xml
index 3bb50d5..78f3ad8 100644
--- a/slices/view/src/androidTest/AndroidManifest.xml
+++ b/slices/view/src/androidTest/AndroidManifest.xml
@@ -20,10 +20,15 @@
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
-    <application>
+    <application
+        android:label="Slice Render Test">
         <provider android:name="androidx.slice.SliceManagerTest$TestSliceProvider"
                   android:authorities="androidx.slice.view.test"
-                  android:exported="true"/>
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="androidx.slice.action.TEST" />
+            </intent-filter>
+        </provider>
 
         <activity android:name="androidx.slice.render.SliceRenderActivity"
             android:theme="@style/AppTheme.NoActionBar">
diff --git a/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java b/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java
index 2e5ed54..5564f72 100644
--- a/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java
@@ -30,21 +30,23 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
-import androidx.annotation.NonNull;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+
+import androidx.annotation.NonNull;
 import androidx.core.os.BuildCompat;
+import androidx.slice.render.SliceRenderActivity;
+import androidx.slice.widget.SliceLiveData;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.concurrent.Executor;
 
-import androidx.slice.render.SliceRenderActivity;
-import androidx.slice.widget.SliceLiveData;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class SliceManagerTest {
@@ -126,12 +128,45 @@
     }
 
     @Test
+    public void testMapIntentToUriStatic() {
+        Uri expected = Uri.parse("content://androidx.slice.view.test/render");
+
+        Uri uri = mManager.mapIntentToUri(new Intent(mContext, SliceRenderActivity.class));
+
+        assertEquals(expected, uri);
+    }
+
+    @Test
     public void testMapIntentToUri() {
         Uri expected = Uri.parse("content://androidx.slice.view.test/render");
-        Slice s = new Slice.Builder(expected).build();
-        when(mSliceProvider.onBindSlice(eq(expected))).thenReturn(s);
-        Uri uri = mManager.mapIntentToUri(new Intent(mContext, SliceRenderActivity.class));
+        Intent intent = new Intent("androidx.slice.action.TEST")
+                .setPackage(mContext.getPackageName());
+
+        when(mSliceProvider.onMapIntentToUri(eq(intent))).thenReturn(expected);
+        Uri uri = mManager.mapIntentToUri(intent);
+
         assertEquals(expected, uri);
+        verify(mSliceProvider).onMapIntentToUri(eq(intent));
+    }
+
+    @Test
+    public void testGetDescendants() {
+        Uri uri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(mContext.getPackageName())
+                .build();
+        Collection<Uri> collection = Arrays.asList(
+                uri,
+                uri.buildUpon().appendPath("1").build(),
+                uri.buildUpon().appendPath("2").build()
+        );
+        when(mSliceProvider.onGetSliceDescendants(any(Uri.class)))
+                .thenReturn(collection);
+
+        Collection<Uri> allUris = mManager.getSliceDescendants(uri);
+
+        assertEquals(allUris, collection);
+        verify(mSliceProvider).onGetSliceDescendants(eq(uri));
     }
 
     public static class TestSliceProvider extends SliceProvider {
@@ -176,5 +211,13 @@
                 sSliceProviderReceiver.onSliceUnpinned(sliceUri);
             }
         }
+
+        @Override
+        public Collection<Uri> onGetSliceDescendants(Uri uri) {
+            if (sSliceProviderReceiver != null) {
+                return sSliceProviderReceiver.onGetSliceDescendants(uri);
+            }
+            return super.onGetSliceDescendants(uri);
+        }
     }
 }
diff --git a/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java b/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java
index c416b30..ac3d214 100644
--- a/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java
@@ -22,7 +22,8 @@
 import static androidx.slice.SliceMetadata.LOADED_ALL;
 import static androidx.slice.SliceMetadata.LOADED_NONE;
 import static androidx.slice.SliceMetadata.LOADED_PARTIAL;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
@@ -30,17 +31,33 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertNull;
 
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Pair;
+import androidx.slice.builders.GridRowBuilder;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.SliceAction;
+import androidx.slice.core.SliceActionImpl;
 import androidx.slice.core.SliceHints;
+import androidx.slice.render.SliceRenderActivity;
+import androidx.slice.widget.EventInfo;
+import androidx.slice.widget.SliceLiveData;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -53,11 +70,421 @@
 
     private final Context mContext = InstrumentationRegistry.getContext();
 
+    @Before
+    public void setup() {
+        SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+    }
+
+    @Test
+    public void testGetSliceActionsNull() {
+        Uri uri = Uri.parse("content://pkg/slice");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("Text"));
+
+        SliceMetadata sliceMetadata = SliceMetadata.from(mContext, lb.build());
+        assertNull(sliceMetadata.getSliceActions());
+    }
+
+    @Test
+    public void testGetSliceActions() {
+        Uri uri = Uri.parse("content://pkg/slice");
+        PendingIntent pi = getIntent("");
+        Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+        new Canvas(b).drawColor(0xffff0000);
+        IconCompat icon = IconCompat.createFromIcon(Icon.createWithBitmap(b));
+
+        SliceAction action1 = new SliceAction(pi, icon, "action1");
+        SliceAction action2 = new SliceAction(pi, icon, "action2");
+        SliceAction action3 = new SliceAction(pi, icon, "action3");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("Text"))
+                .addAction(action1)
+                .addAction(action2)
+                .addAction(action3);
+
+        ArrayList<SliceAction> expectedActions = new ArrayList<>();
+        expectedActions.add(action1);
+        expectedActions.add(action2);
+        expectedActions.add(action3);
+
+        SliceMetadata sliceMetadata = SliceMetadata.from(mContext, lb.build());
+        List<SliceItem> actions = sliceMetadata.getSliceActions();
+
+        for (int i = 0; i < expectedActions.size(); i++) {
+            assertEquivalent(expectedActions.get(i), new SliceActionImpl(actions.get(i)));
+        }
+    }
+
+    @Test
+    public void testGetPrimaryActionForGrid() {
+        Uri uri = Uri.parse("content://pkg/slice");
+        PendingIntent pi = getIntent("");
+        Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+        new Canvas(b).drawColor(0xffff0000);
+        IconCompat icon = IconCompat.createFromIcon(Icon.createWithBitmap(b));
+
+        SliceAction primaryAction = new SliceAction(pi, icon, "action");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        GridRowBuilder grb = new GridRowBuilder(lb);
+        grb.setPrimaryAction(primaryAction);
+        grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+        grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+        grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+        lb.addGridRow(grb);
+
+        Slice gridSlice = lb.build();
+        SliceMetadata gridInfo = SliceMetadata.from(mContext, gridSlice);
+        assertEquivalent(primaryAction, gridInfo.getPrimaryAction());
+    }
+
+    @Test
+    public void testGetPrimaryActionForRow() {
+        Uri uri = Uri.parse("content://pkg/slice");
+        PendingIntent pi = getIntent("");
+        Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+        new Canvas(b).drawColor(0xffff0000);
+        IconCompat icon = IconCompat.createFromIcon(Icon.createWithBitmap(b));
+
+        SliceAction primaryAction = new SliceAction(pi, icon, "action");
+        SliceAction endAction = new SliceAction(pi, "toogle action", false);
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        ListBuilder.RowBuilder rb = new ListBuilder.RowBuilder(lb)
+                .setTitle("a title")
+                .addEndItem(endAction)
+                .setPrimaryAction(primaryAction);
+        lb.addRow(rb);
+
+        Slice rowSlice = lb.build();
+        SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+        assertEquivalent(primaryAction, rowInfo.getPrimaryAction());
+    }
+
+    @Test
+    public void testGetPrimaryActionForHeader() {
+        Uri uri = Uri.parse("content://pkg/slice");
+        PendingIntent pi = getIntent("");
+        Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+        new Canvas(b).drawColor(0xffff0000);
+        IconCompat icon = IconCompat.createFromIcon(Icon.createWithBitmap(b));
+
+        SliceAction primaryAction = new SliceAction(pi, icon, "action");
+        SliceAction sliceAction = new SliceAction(pi, "another action", true);
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addAction(sliceAction);
+        ListBuilder.HeaderBuilder hb = new ListBuilder.HeaderBuilder(lb);
+        hb.setTitle("header title");
+        hb.setPrimaryAction(primaryAction);
+        lb.setHeader(hb);
+
+        Slice headerSlice = lb.build();
+        SliceMetadata headerInfo = SliceMetadata.from(mContext, headerSlice);
+        assertEquivalent(primaryAction, headerInfo.getPrimaryAction());
+    }
+
+    @Test
+    public void testGetPrimaryActionNull() {
+        Uri uri = Uri.parse("content://pkg/slice");
+        PendingIntent pi = getIntent("");
+        Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+        new Canvas(b).drawColor(0xffff0000);
+        IconCompat icon = IconCompat.createFromIcon(Icon.createWithBitmap(b));
+
+        SliceAction endAction1 = new SliceAction(pi, icon, "action");
+        SliceAction endAction2 = new SliceAction(pi, "toogle action", false);
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        ListBuilder.RowBuilder rb = new ListBuilder.RowBuilder(lb)
+                .setTitle("a title")
+                .addEndItem(endAction1)
+                .addEndItem(endAction2);
+        lb.addRow(rb);
+
+        Slice rowSlice = lb.build();
+        SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+        assertNull(rowInfo.getPrimaryAction());
+    }
+
+    @Test
+    public void testGetHeaderTypeGrid() {
+        Uri uri = Uri.parse("content://pkg/slice");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        GridRowBuilder grb = new GridRowBuilder(lb);
+        grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+        grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+        grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+        lb.addGridRow(grb);
+
+        Slice gridSlice = lb.build();
+        SliceMetadata gridInfo = SliceMetadata.from(mContext, gridSlice);
+        assertEquals(EventInfo.ROW_TYPE_GRID, gridInfo.getHeaderType());
+    }
+
+    @Test
+    public void testGetHeaderTypeRow() {
+        Uri uri = Uri.parse("content://pkg/slice");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("a title"));
+
+        Slice rowSlice = lb.build();
+        SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+        assertEquals(EventInfo.ROW_TYPE_LIST, rowInfo.getHeaderType());
+    }
+
+    @Test
+    public void testGetHeaderTypeToggle() {
+        Uri uri = Uri.parse("content://pkg/slice");
+        PendingIntent pi = getIntent("");
+
+        SliceAction toggleAction = new SliceAction(pi, "toggle", false /* isChecked */);
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addRow(new ListBuilder.RowBuilder(lb)
+                .setTitle("another title")
+                .addEndItem(toggleAction));
+
+        Slice toggleSlice = lb.build();
+        SliceMetadata toggleInfo = SliceMetadata.from(mContext, toggleSlice);
+        assertEquals(EventInfo.ROW_TYPE_TOGGLE, toggleInfo.getHeaderType());
+    }
+
+    @Test
+    public void testGetHeaderTypeSlider() {
+        Uri uri = Uri.parse("content://pkg/slice");
+        PendingIntent pi = getIntent("");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addInputRange(new ListBuilder.InputRangeBuilder(lb)
+                .setTitle("another title")
+                .setValue(5)
+                .setMax(10)
+                .setAction(pi));
+
+        Slice sliderSlice = lb.build();
+        SliceMetadata sliderInfo = SliceMetadata.from(mContext, sliderSlice);
+        assertEquals(EventInfo.ROW_TYPE_SLIDER, sliderInfo.getHeaderType());
+    }
+
+    @Test
+    public void testGetHeaderTypeProgress() {
+        Uri uri = Uri.parse("content://pkg/slice");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addRange(new ListBuilder.RangeBuilder(lb)
+                .setTitle("another title")
+                .setValue(5)
+                .setMax(10));
+
+        Slice sliderSlice = lb.build();
+        SliceMetadata progressInfo = SliceMetadata.from(mContext, sliderSlice);
+        assertEquals(EventInfo.ROW_TYPE_PROGRESS, progressInfo.getHeaderType());
+    }
+
+    @Test
+    public void testGetHeaderTypeHeader() {
+        Uri uri = Uri.parse("content://pkg/slice");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        ListBuilder.HeaderBuilder hb = new ListBuilder.HeaderBuilder(lb);
+        hb.setTitle("header title");
+        lb.setHeader(hb);
+
+        Slice headerSlice = lb.build();
+        SliceMetadata headerInfo = SliceMetadata.from(mContext, headerSlice);
+        assertEquals(EventInfo.ROW_TYPE_LIST, headerInfo.getHeaderType());
+    }
+
+    @Test
+    public void testHasLargeModeFullGrid() {
+        // If a grid is "full" with two text and an image there is a different small / large state.
+        Uri uri = Uri.parse("content://pkg/slice");
+        Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+        new Canvas(b).drawColor(0xffff0000);
+        Icon icon = Icon.createWithBitmap(b);
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        GridRowBuilder grb = new GridRowBuilder(lb);
+        grb.addCell(new GridRowBuilder.CellBuilder(grb)
+                .addText("some text")
+                .addText("more text")
+                .addImage(IconCompat.createFromIcon(icon), ICON_IMAGE));
+        grb.addCell(new GridRowBuilder.CellBuilder(grb)
+                .addText("some text")
+                .addText("more text")
+                .addImage(IconCompat.createFromIcon(icon), ICON_IMAGE));
+        grb.addCell(new GridRowBuilder.CellBuilder(grb)
+                .addText("some text")
+                .addText("more text")
+                .addImage(IconCompat.createFromIcon(icon), ICON_IMAGE));
+        lb.addGridRow(grb);
+
+        Slice gridSlice = lb.build();
+        SliceMetadata gridInfo = SliceMetadata.from(mContext, gridSlice);
+        assertTrue(gridInfo.hasLargeMode());
+    }
+
+    @Test
+    public void testHasLargeModeGrid() {
+        // If a grid is not "full" either no image or an image and one text item, small / large
+        // is the same.
+        Uri uri = Uri.parse("content://pkg/slice");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        GridRowBuilder grb = new GridRowBuilder(lb);
+        grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+        grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+        grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+        lb.addGridRow(grb);
+
+        Slice gridSlice = lb.build();
+        SliceMetadata gridInfo = SliceMetadata.from(mContext, gridSlice);
+        assertTrue(!gridInfo.hasLargeMode());
+    }
+
+    @Test
+    public void testGetLargeModeMultipleRows() {
+        Uri uri = Uri.parse("content://pkg/slice");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("a title"));
+        lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("another row another title"));
+
+        Slice rowSlice = lb.build();
+        SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+        assertTrue(rowInfo.hasLargeMode());
+    }
+
+    @Test
+    public void testGetLargeModeSingleRow() {
+        Uri uri = Uri.parse("content://pkg/slice");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("a title"));
+
+        Slice rowSlice = lb.build();
+        SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+        assertTrue(!rowInfo.hasLargeMode());
+    }
+
+    @Test
+    public void testGetTogglesNone() {
+        Uri uri = Uri.parse("content://pkg/slice");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("a title"));
+
+        Slice rowSlice = lb.build();
+        SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+        assertTrue(rowInfo.getToggles().isEmpty());
+    }
+
+    @Test
+    public void testGetTogglesSingle() {
+        Uri uri = Uri.parse("content://pkg/slice");
+        PendingIntent pi = getIntent("");
+
+        SliceAction toggleAction = new SliceAction(pi, "toggle", false /* isChecked */);
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addRow(new ListBuilder.RowBuilder(lb)
+                .setTitle("another title")
+                .addEndItem(toggleAction));
+
+        Slice toggleSlice = lb.build();
+        SliceMetadata toggleInfo = SliceMetadata.from(mContext, toggleSlice);
+        List<androidx.slice.core.SliceAction> actualToggles = toggleInfo.getToggles();
+        assertEquals(1, actualToggles.size());
+        assertEquivalent(toggleAction, actualToggles.get(0));
+    }
+
+    @Test
+    public void testGetTogglesMultiple() {
+        Uri uri = Uri.parse("content://pkg/slice");
+        PendingIntent pi = getIntent("");
+        Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+        new Canvas(b).drawColor(0xffff0000);
+        IconCompat icon = IconCompat.createFromIcon(Icon.createWithBitmap(b));
+
+        SliceAction toggleAction = new SliceAction(pi, icon, "toggle", false /* isChecked */);
+        SliceAction toggleAction2 = new SliceAction(pi, icon, "toggle2", true /* isChecked */);
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addRow(new ListBuilder.RowBuilder(lb)
+                .setTitle("another title")
+                .addEndItem(toggleAction)
+                .addEndItem(toggleAction2));
+
+        Slice toggleSlice = lb.build();
+        SliceMetadata toggleInfo = SliceMetadata.from(mContext, toggleSlice);
+        List<androidx.slice.core.SliceAction> actualToggles = toggleInfo.getToggles();
+
+        List<SliceAction> expectedToggles = new ArrayList<>();
+        expectedToggles.add(toggleAction);
+        expectedToggles.add(toggleAction2);
+
+        for (int i = 0; i < expectedToggles.size(); i++) {
+            assertEquivalent(expectedToggles.get(i), actualToggles.get(i));
+        }
+    }
+
+    @Test
+    public void testGetRangeNull() {
+        Uri uri = Uri.parse("content://pkg/slice");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("a title"));
+
+        Slice rowSlice = lb.build();
+        SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+        assertNull(rowInfo.getRange());
+    }
+
+    @Test
+    public void testGetRangeForSlider() {
+        Uri uri = Uri.parse("content://pkg/slice");
+        PendingIntent pi = getIntent("");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addInputRange(new ListBuilder.InputRangeBuilder(lb)
+                .setTitle("another title")
+                .setValue(5)
+                .setMax(10)
+                .setAction(pi));
+
+        Slice sliderSlice = lb.build();
+        SliceMetadata sliderInfo = SliceMetadata.from(mContext, sliderSlice);
+        Pair<Integer, Integer> values = sliderInfo.getRange();
+        assertEquals(5, (int) values.first);
+        assertEquals(10, (int) values.second);
+    }
+
+    @Test
+    public void testGetRangeForProgress() {
+        Uri uri = Uri.parse("content://pkg/slice");
+
+        ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+        lb.addRange(new ListBuilder.RangeBuilder(lb)
+                .setTitle("another title")
+                .setValue(5)
+                .setMax(10));
+
+        Slice sliderSlice = lb.build();
+        SliceMetadata progressInfo = SliceMetadata.from(mContext, sliderSlice);
+        Pair<Integer, Integer> values = progressInfo.getRange();
+        assertEquals(5, (int) values.first);
+        assertEquals(10, (int) values.second);
+
+    }
+
     @Test
     public void testKeywords() {
         Uri uri = Uri.parse("content://pkg/slice");
         Slice keywordSlice = new Slice.Builder(uri)
-                .addHints(HINT_KEY_WORDS)
+                .addHints(HINT_KEYWORDS)
                 .addText("keyword1", null)
                 .addText("keyword2", null)
                 .addText("keyword3", null).build();
@@ -67,8 +494,8 @@
                 .addSubSlice(keywordSlice)
                 .build();
 
-        SliceMetadata sliceInfo1 = SliceMetadata.from(mContext, slice);
-        List<String> sliceKeywords = sliceInfo1.getSliceKeywords();
+        SliceMetadata SliceMetadata1 = SliceMetadata.from(mContext, slice);
+        List<String> sliceKeywords = SliceMetadata1.getSliceKeywords();
         String[] expectedList = new String[] {"keyword1", "keyword2", "keyword3"};
         assertArrayEquals(expectedList, sliceKeywords.toArray());
 
@@ -77,12 +504,12 @@
                 .addText("Some text", null, HINT_TITLE)
                 .addText("Some other text", null).build();
 
-        SliceMetadata sliceInfo2 = SliceMetadata.from(mContext, slice2);
-        List<String> slice2Keywords = sliceInfo2.getSliceKeywords();
+        SliceMetadata SliceMetadata2 = SliceMetadata.from(mContext, slice2);
+        List<String> slice2Keywords = SliceMetadata2.getSliceKeywords();
         assertNull(slice2Keywords);
 
         // Make sure empty list if specified to have no keywords
-        Slice noKeywordSlice = new Slice.Builder(uri).addHints(HINT_KEY_WORDS).build();
+        Slice noKeywordSlice = new Slice.Builder(uri).addHints(HINT_KEYWORDS).build();
         Slice slice3 = new Slice.Builder(uri)
                 .addText("Some text", null, HINT_TITLE)
                 .addSubSlice(noKeywordSlice)
@@ -97,18 +524,18 @@
     public void testGetLoadingState() {
         Uri uri = Uri.parse("content://pkg/slice");
         Slice s1 = new Slice.Builder(uri).build();
-        SliceMetadata sliceInfo1 = SliceMetadata.from(mContext, s1);
-        int actualState1 = sliceInfo1.getLoadingState();
+        SliceMetadata SliceMetadata1 = SliceMetadata.from(mContext, s1);
+        int actualState1 = SliceMetadata1.getLoadingState();
         assertEquals(LOADED_NONE, actualState1);
 
         Slice s2 = new Slice.Builder(uri).addText(null, null, HINT_PARTIAL).build();
-        SliceMetadata sliceInfo2 = SliceMetadata.from(mContext, s2);
-        int actualState2 = sliceInfo2.getLoadingState();
+        SliceMetadata SliceMetadata2 = SliceMetadata.from(mContext, s2);
+        int actualState2 = SliceMetadata2.getLoadingState();
         assertEquals(LOADED_PARTIAL, actualState2);
 
         Slice s3 = new Slice.Builder(uri).addText("Text", null).build();
-        SliceMetadata sliceInfo3 = SliceMetadata.from(mContext, s3);
-        int actualState3 = sliceInfo3.getLoadingState();
+        SliceMetadata SliceMetadata3 = SliceMetadata.from(mContext, s3);
+        int actualState3 = SliceMetadata3.getLoadingState();
         assertEquals(LOADED_ALL, actualState3);
     }
 
@@ -160,4 +587,32 @@
         long retrievedLastUpdated2 = si2.getLastUpdatedTime();
         assertEquals(0, retrievedLastUpdated2);
     }
+
+    @Test
+    public void testIsPermissionSlice() {
+        Uri uri = Uri.parse("content://pkg/slice");
+        Slice permissionSlice =
+                SliceProvider.createPermissionSlice(mContext, uri, mContext.getPackageName());
+
+        SliceMetadata metadata = SliceMetadata.from(mContext, permissionSlice);
+        assertEquals(true, metadata.isPermissionSlice());
+    }
+
+    private PendingIntent getIntent(String action) {
+        Intent intent = new Intent(action);
+        intent.setClassName(mContext.getPackageName(), SliceRenderActivity.class.getName());
+        return PendingIntent.getActivity(mContext, 0, intent, 0);
+    }
+
+    private void assertEquivalent(androidx.slice.core.SliceAction desired,
+            androidx.slice.core.SliceAction actual) {
+        assertEquals(desired.getTitle(), actual.getTitle());
+        assertEquals(desired.getContentDescription(), actual.getContentDescription());
+        assertEquals(desired.isToggle(), actual.isToggle());
+        assertEquals(desired.isDefaultToggle(), actual.isDefaultToggle());
+        assertEquals(desired.isChecked(), actual.isChecked());
+        assertEquals(desired.getPriority(), actual.getPriority());
+        assertEquals(desired.getIcon() == null, actual.getIcon() == null);
+        assertEquals(desired.getImageMode(), actual.getImageMode());
+    }
 }
diff --git a/slices/view/src/androidTest/java/androidx/slice/SliceXmlTest.java b/slices/view/src/androidTest/java/androidx/slice/SliceXmlTest.java
index 298d498..d1a647c 100644
--- a/slices/view/src/androidTest/java/androidx/slice/SliceXmlTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/SliceXmlTest.java
@@ -24,6 +24,11 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -33,6 +38,8 @@
 import android.support.test.runner.AndroidJUnit4;
 
 import androidx.core.graphics.drawable.IconCompat;
+import androidx.slice.core.SliceQuery;
+import androidx.slice.view.R;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -51,7 +58,7 @@
     public void testThrowForAction() throws IOException {
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         Slice s = new Slice.Builder(Uri.parse("content://pkg/slice"))
-                .addAction(null, null, null)
+                .addAction((PendingIntent) null, null, null)
                 .build();
         SliceUtils.serializeSlice(s, mContext, outputStream, "UTF-8", new SliceUtils
                 .SerializeOptions());
@@ -81,7 +88,7 @@
     public void testNoThrowForAction() throws IOException {
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         Slice s = new Slice.Builder(Uri.parse("content://pkg/slice"))
-                .addAction(null, null, null)
+                .addAction((PendingIntent) null, null, null)
                 .build();
         SliceUtils.serializeSlice(s, mContext, outputStream, "UTF-8", new SliceUtils
                 .SerializeOptions().setActionMode(SliceUtils.SerializeOptions.MODE_REMOVE));
@@ -108,7 +115,7 @@
     }
 
     @Test
-    public void testSerialization() throws IOException {
+    public void testSerialization() throws Exception {
         Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
         new Canvas(b).drawColor(0xffff0000);
         // Create a slice containing all the types in a hierarchy.
@@ -118,24 +125,32 @@
                         .build())
                 .addIcon(IconCompat.createWithBitmap(b), null)
                 .addText("Some text", null)
-                .addAction(null, new Slice.Builder(Uri.parse("content://pkg/slice/sub"))
+                .addAction((PendingIntent) null,
+                        new Slice.Builder(Uri.parse("content://pkg/slice/action"))
                         .addText("Action text", null)
                         .build(), null)
                 .addInt(0xff00ff00, "subtype")
+                .addIcon(IconCompat.createWithResource(mContext, R.drawable.abc_slice_see_more_bg),
+                        null)
                 .addHints("Hint 1", "Hint 2")
                 .build();
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 
         SliceUtils.serializeSlice(before, mContext, outputStream, "UTF-8",
                 new SliceUtils.SerializeOptions()
-                        .setImageMode(SliceUtils.SerializeOptions.MODE_DISABLE)
-                        .setActionMode(SliceUtils.SerializeOptions.MODE_DISABLE));
+                        .setImageMode(SliceUtils.SerializeOptions.MODE_CONVERT)
+                        .setActionMode(SliceUtils.SerializeOptions.MODE_CONVERT));
 
         byte[] bytes = outputStream.toByteArray();
         ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
-        Slice after = SliceUtils.parseSlice(inputStream, "UTF-8");
+        SliceUtils.SliceActionListener listener = mock(SliceUtils.SliceActionListener.class);
+        Slice after = SliceUtils.parseSlice(mContext, inputStream, "UTF-8", listener);
 
         assertEquivalent(before, after);
+
+        SliceItem action = SliceQuery.find(after, FORMAT_ACTION);
+        action.fireAction(null, null);
+        verify(listener).onSliceAction(eq(Uri.parse("content://pkg/slice/action")));
     }
 
     private void assertEquivalent(Slice desired, Slice actual) {
diff --git a/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java b/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java
index cf524fa..0100d84 100644
--- a/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java
+++ b/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java
@@ -36,6 +36,7 @@
 
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
+import androidx.slice.SliceProvider;
 import androidx.slice.builders.GridRowBuilder;
 import androidx.slice.builders.ListBuilder;
 import androidx.slice.builders.MessagingSliceBuilder;
@@ -58,7 +59,7 @@
 
     public static final String[] URI_PATHS = {"message", "wifi", "wifi2", "note", "ride",
             "ride-ttl", "toggle", "toggle2", "contact", "gallery", "subscription", "subscription2",
-            "weather", "reservation"};
+            "weather", "reservation", "inputrange", "range", "permission"};
 
     private final Context mContext;
 
@@ -112,6 +113,12 @@
                 return createWeather(sliceUri);
             case "/reservation":
                 return createReservationSlice(sliceUri);
+            case "/inputrange":
+                return createStarRatingInputRange(sliceUri);
+            case "/range":
+                return createDownloadProgressRange(sliceUri);
+            case "/permission":
+                return createPermissionSlice(sliceUri);
         }
         throw new IllegalArgumentException("Unknown uri " + sliceUri);
     }
@@ -156,7 +163,7 @@
         ListBuilder b = new ListBuilder(getContext(), sliceUri, INFINITY);
         GridRowBuilder gb = new GridRowBuilder(b);
         PendingIntent pi = getBroadcastIntent(ACTION_TOAST, "see more of your gallery");
-        gb.addSeeMoreAction(pi);
+        gb.setSeeMoreAction(pi);
         gb.addCell(new GridRowBuilder.CellBuilder(gb)
                 .addImage(IconCompat.createWithResource(getContext(), R.drawable.slices_1),
                         LARGE_IMAGE))
@@ -191,9 +198,9 @@
                     ICON_IMAGE);
             cb.setContentIntent(pi);
             cb.addText("All cats");
-            gb.addSeeMoreCell(cb);
+            gb.setSeeMoreCell(cb);
         } else {
-            gb.addSeeMoreAction(pi);
+            gb.setSeeMoreAction(pi);
         }
         gb.addCell(new GridRowBuilder.CellBuilder(gb)
                 .addImage(IconCompat.createWithResource(getContext(), R.drawable.cat_1),
@@ -417,7 +424,8 @@
                     .setTitle("Network" + networkName);
             boolean locked = i % 3 == 0;
             if (locked) {
-                rb.addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_lock));
+                rb.addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_lock),
+                        ICON_IMAGE);
             }
             String message = locked ? "Open wifi password dialog" : "Connect to " + networkName;
             rb.setPrimaryAction(new SliceAction(getBroadcastIntent(ACTION_TOAST, message), icon,
@@ -425,13 +433,14 @@
             lb.addRow(rb);
         }
         if (customSeeMore) {
-            lb.addSeeMoreRow(new ListBuilder.RowBuilder(lb)
+            lb.setSeeMoreRow(new ListBuilder.RowBuilder(lb)
                     .setTitle("See all available networks")
                     .addEndItem(
-                            IconCompat.createWithResource(getContext(), R.drawable.ic_right_caret))
+                            IconCompat.createWithResource(getContext(), R.drawable.ic_right_caret),
+                            ICON_IMAGE)
                     .setPrimaryAction(primaryAction));
         } else {
-            lb.addSeeMoreAction(primaryAction.getAction());
+            lb.setSeeMoreAction(primaryAction.getAction());
         }
         return lb.build();
     }
@@ -466,6 +475,47 @@
                 .build();
     }
 
+    private Slice createStarRatingInputRange(Uri sliceUri) {
+        IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
+        SliceAction primaryAction =
+                new SliceAction(getBroadcastIntent(ACTION_TOAST, "open star rating"), icon,
+                        "Rate");
+        ListBuilder lb = new ListBuilder(getContext(), sliceUri, INFINITY);
+        return lb.setColor(0xffff4081)
+                .addInputRange(new ListBuilder.InputRangeBuilder(lb)
+                        .setTitle("Star rating")
+                        .setSubtitle("Pick a rating from 0 to 5")
+                        .setThumb(icon)
+                        .setInputAction(getBroadcastIntent(ACTION_TOAST, "range changed"))
+                        .setMax(5)
+                        .setValue(3)
+                        .setPrimaryAction(primaryAction)
+                        .setContentDescription("Slider for star ratings"))
+                .build();
+    }
+
+    private Slice createDownloadProgressRange(Uri sliceUri) {
+        IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
+        SliceAction primaryAction =
+                new SliceAction(
+                        getBroadcastIntent(ACTION_TOAST, "open download"), icon,
+                        "Download");
+        ListBuilder lb = new ListBuilder(getContext(), sliceUri, INFINITY);
+        return lb.setColor(0xffff4081)
+                .addRange(new ListBuilder.RangeBuilder(lb)
+                        .setTitle("Download progress")
+                        .setSubtitle("Download is happening")
+                        .setMax(100)
+                        .setValue(75)
+                        .setPrimaryAction(primaryAction))
+                .build();
+    }
+
+    private Slice createPermissionSlice(Uri uri) {
+        return SliceProvider.createPermissionSlice(getContext(), uri,
+                getContext().getPackageName());
+    }
+
     private PendingIntent getIntent(String action) {
         Intent intent = new Intent(action);
         intent.setClassName(getContext().getPackageName(), SliceRenderActivity.class.getName());
diff --git a/slices/view/src/androidTest/java/androidx/slice/render/SliceRenderer.java b/slices/view/src/androidTest/java/androidx/slice/render/SliceRenderer.java
index 5625c34..0734edf 100644
--- a/slices/view/src/androidTest/java/androidx/slice/render/SliceRenderer.java
+++ b/slices/view/src/androidTest/java/androidx/slice/render/SliceRenderer.java
@@ -22,7 +22,7 @@
 import android.app.ProgressDialog;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.os.AsyncTask;
+import android.net.Uri;
 import android.os.Handler;
 import android.util.Log;
 import android.util.TypedValue;
@@ -30,24 +30,33 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.util.concurrent.CountDownLatch;
-
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.slice.Slice;
 import androidx.slice.SliceProvider;
+import androidx.slice.SliceUtils;
 import androidx.slice.view.test.R;
 import androidx.slice.widget.SliceLiveData;
 import androidx.slice.widget.SliceView;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
 public class SliceRenderer {
 
     private static final String TAG = "SliceRenderer";
     public static final String SCREENSHOT_DIR = "slice-screenshots";
+
+    private static final int MAX_CONCURRENT = 5;
+
     private static File sScreenshotDirectory;
 
+    private final Object mRenderLock = new Object();
+
     private final Activity mContext;
     private final View mLayout;
     private final SliceView mSV1;
@@ -121,20 +130,46 @@
 
 
     private void doRender() {
-        File output = getScreenshotDirectory();
+        final File output = getScreenshotDirectory();
         if (!output.exists()) {
             output.mkdir();
         }
-        mDoneLatch = new CountDownLatch(SliceCreator.URI_PATHS.length);
-        for (String slice : SliceCreator.URI_PATHS) {
-            doRender(slice, new File(output, String.format("%s.png", slice)),
-                    true /* scrollable */);
+        mDoneLatch = new CountDownLatch(SliceCreator.URI_PATHS.length * 2 + 2);
+
+        ExecutorService executor = Executors.newFixedThreadPool(5);
+        for (final String slice : SliceCreator.URI_PATHS) {
+            final Slice s = mSliceCreator.onBindSlice(SliceCreator.getUri(slice, mContext));
+
+            executor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    doRender(slice, s, new File(output, String.format("%s.png", slice)),
+                            true /* scrollable */);
+                }
+            });
+            final Slice serialized = serAndUnSer(s);
+            executor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    doRender(slice + "-ser", serialized, new File(output, String.format(
+                            "%s-serialized.png", slice)), true /* scrollable */);
+                }
+            });
             if (slice.equals("wifi") || slice.equals("wifi2")) {
                 // Test scrolling
-                doRender(slice, new File(output, String.format("%s-no-scroll.png", slice)),
-                        false /* scrollable */);
+                executor.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        doRender(slice + "-ns", s, new File(output, String.format(
+                                "%s-no-scroll.png", slice)), false /* scrollable */);
+                    }
+                });
             }
         }
+        try {
+            mDoneLatch.await();
+        } catch (InterruptedException e) {
+        }
         Log.d(TAG, "Wrote render to " + output.getAbsolutePath());
         mContext.runOnUiThread(new Runnable() {
             @Override
@@ -142,71 +177,83 @@
                 ((ViewGroup) mParent.getParent()).removeView(mParent);
             }
         });
+    }
+
+    private Slice serAndUnSer(Slice s) {
         try {
-            mDoneLatch.await();
-        } catch (InterruptedException e) {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            SliceUtils.serializeSlice(s, mContext, outputStream, "UTF-8",
+                    new SliceUtils.SerializeOptions()
+                            .setImageMode(SliceUtils.SerializeOptions.MODE_CONVERT)
+                            .setActionMode(SliceUtils.SerializeOptions.MODE_CONVERT));
+
+            byte[] bytes = outputStream.toByteArray();
+            ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
+            return SliceUtils.parseSlice(mContext, inputStream, "UTF-8",
+                    new SliceUtils.SliceActionListener() {
+                        @Override
+                        public void onSliceAction(Uri actionUri) {
+                        }
+                    });
+        } catch (Exception e) {
+            throw new RuntimeException(e);
         }
     }
 
-    private void doRender(final String slice, final File file, final boolean scrollable) {
+    private void doRender(final String slice, final Slice s, final File file,
+            final boolean scrollable) {
         Log.d(TAG, "Rendering " + slice + " to " + file.getAbsolutePath());
 
-        final Slice s = mSliceCreator.onBindSlice(SliceCreator.getUri(slice, mContext));
-
-        final CountDownLatch l = new CountDownLatch(1);
-        mContext.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mSV1.setSlice(s);
-                mSV2.setSlice(s);
-                mSV3.setSlice(s);
-                mSV3.setScrollable(scrollable);
-                mSV1.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+        try {
+            final CountDownLatch l = new CountDownLatch(1);
+            final Bitmap[] b = new Bitmap[1];
+            synchronized (mRenderLock) {
+                mContext.runOnUiThread(new Runnable() {
                     @Override
-                    public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                        mSV1.removeOnLayoutChangeListener(this);
-                        mSV1.postDelayed(new Runnable() {
+                    public void run() {
+                        mSV1.setSlice(s);
+                        mSV2.setSlice(s);
+                        mSV3.setSlice(s);
+                        mSV3.setScrollable(scrollable);
+                        mSV1.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
                             @Override
-                            public void run() {
-                                Log.d(TAG, "Drawing " + slice);
-                                Bitmap b = Bitmap.createBitmap(mLayout.getMeasuredWidth(),
-                                        mLayout.getMeasuredHeight(),
-                                        Bitmap.Config.ARGB_8888);
+                            public void onLayoutChange(View v, int left, int top, int right,
+                                    int bottom,
+                                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                                mSV1.removeOnLayoutChangeListener(this);
+                                mSV1.postDelayed(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        Log.d(TAG, "Drawing " + slice);
+                                        b[0] = Bitmap.createBitmap(mLayout.getMeasuredWidth(),
+                                                mLayout.getMeasuredHeight(),
+                                                Bitmap.Config.ARGB_8888);
 
-                                mLayout.draw(new Canvas(b));
-                                try {
-                                    doCompress(slice, b, new FileOutputStream(file));
-                                } catch (FileNotFoundException e) {
-                                    throw new RuntimeException(e);
-                                }
-                                l.countDown();
+                                        mLayout.draw(new Canvas(b[0]));
+                                        l.countDown();
+                                    }
+                                }, 10);
                             }
-                        }, 10);
+                        });
                     }
                 });
+                l.await();
             }
-        });
-        try {
-            l.await();
-        } catch (InterruptedException e) {
+            doCompress(slice, b[0], new FileOutputStream(file));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
         }
     }
 
     private void doCompress(final String slice, final Bitmap b, final FileOutputStream s) {
-        AsyncTask.execute(new Runnable() {
-            @Override
-            public void run() {
-                Log.d(TAG, "Compressing " + slice);
-                if (!b.compress(Bitmap.CompressFormat.PNG, 100, s)) {
-                    throw new RuntimeException("Unable to compress");
-                }
+        Log.d(TAG, "Compressing " + slice);
+        if (!b.compress(Bitmap.CompressFormat.PNG, 100, s)) {
+            throw new RuntimeException("Unable to compress");
+        }
 
-                b.recycle();
-                Log.d(TAG, "Done " + slice);
-                mDoneLatch.countDown();
-            }
-        });
+        b.recycle();
+        mDoneLatch.countDown();
+        Log.d(TAG, "Done " + slice);
     }
 
     public void renderAll(final Runnable runnable) {
diff --git a/slices/view/src/main/java/androidx/slice/SliceManager.java b/slices/view/src/main/java/androidx/slice/SliceManager.java
index a97acd0..63c56e8 100644
--- a/slices/view/src/main/java/androidx/slice/SliceManager.java
+++ b/slices/view/src/main/java/androidx/slice/SliceManager.java
@@ -18,13 +18,16 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.core.content.PermissionChecker;
 import androidx.core.os.BuildCompat;
 
+import java.util.Collection;
 import java.util.Set;
 import java.util.concurrent.Executor;
 
@@ -164,6 +167,62 @@
     public abstract @Nullable Uri mapIntentToUri(@NonNull Intent intent);
 
     /**
+     * Determine whether a particular process and user ID has been granted
+     * permission to access a specific slice URI.
+     *
+     * @param uri The uri that is being checked.
+     * @param pid The process ID being checked against.  Must be &gt; 0.
+     * @param uid The user ID being checked against.  A uid of 0 is the root
+     * user, which will pass every permission check.
+     *
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the given
+     * pid/uid is allowed to access that uri, or
+     * {@link PackageManager#PERMISSION_DENIED} if it is not.
+     *
+     * @see #grantSlicePermission(String, Uri)
+     */
+    @PermissionChecker.PermissionResult
+    public abstract int checkSlicePermission(@NonNull Uri uri, int pid, int uid);
+
+    /**
+     * Grant permission to access a specific slice Uri to another package.
+     *
+     * @param toPackage The package you would like to allow to access the Uri.
+     * @param uri The Uri you would like to grant access to.
+     *
+     * @see #revokeSlicePermission
+     */
+    public abstract void grantSlicePermission(@NonNull String toPackage, @NonNull Uri uri);
+
+    /**
+     * Remove permissions to access a particular content provider Uri
+     * that were previously added with {@link #grantSlicePermission} for a specific target
+     * package.  The given Uri will match all previously granted Uris that are the same or a
+     * sub-path of the given Uri.  That is, revoking "content://foo/target" will
+     * revoke both "content://foo/target" and "content://foo/target/sub", but not
+     * "content://foo".  It will not remove any prefix grants that exist at a
+     * higher level.
+     *
+     * @param toPackage The package you would like to allow to access the Uri.
+     * @param uri The Uri you would like to revoke access to.
+     *
+     * @see #grantSlicePermission
+     */
+    public abstract void revokeSlicePermission(@NonNull String toPackage, @NonNull Uri uri);
+
+    /**
+     * Obtains a list of slices that are descendants of the specified Uri.
+     * <p>
+     * Not all slice providers will implement this functionality, in which case,
+     * an empty collection will be returned.
+     *
+     * @param uri The uri to look for descendants under.
+     * @return All slices within the space.
+     * @see SliceProvider#onGetSliceDescendants(Uri)
+     */
+    public abstract @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri);
+
+    /**
      * Class that listens to changes in {@link Slice}s.
      */
     public interface SliceCallback {
diff --git a/slices/view/src/main/java/androidx/slice/SliceManagerBase.java b/slices/view/src/main/java/androidx/slice/SliceManagerBase.java
index e68223b..fb9fccd 100644
--- a/slices/view/src/main/java/androidx/slice/SliceManagerBase.java
+++ b/slices/view/src/main/java/androidx/slice/SliceManagerBase.java
@@ -19,9 +19,11 @@
 import static androidx.slice.widget.SliceLiveData.SUPPORTED_SPECS;
 
 import android.content.Context;
+import android.content.Intent;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.ArrayMap;
@@ -29,6 +31,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
+import androidx.core.content.PermissionChecker;
 
 import java.util.concurrent.Executor;
 
@@ -68,6 +71,32 @@
         if (impl != null) impl.stopListening();
     }
 
+    @Override
+    @PermissionChecker.PermissionResult
+    public int checkSlicePermission(@NonNull Uri uri, int pid, int uid) {
+        // TODO: Switch off Uri permissions.
+        return mContext.checkUriPermission(uri, pid, uid,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    @Override
+    public void grantSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
+        // TODO: Switch off Uri permissions.
+        mContext.grantUriPermission(toPackage, uri,
+                Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                        | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+    }
+
+    @Override
+    public void revokeSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
+        // TODO: Switch off Uri permissions.
+        if (Build.VERSION.SDK_INT >= 26) {
+            mContext.revokeUriPermission(toPackage, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        }
+    }
+
+
     private SliceListenerImpl getListener(Uri uri, SliceCallback callback,
             SliceListenerImpl listener) {
         Pair<Uri, SliceCallback> key = new Pair<>(uri, callback);
diff --git a/slices/view/src/main/java/androidx/slice/SliceManagerCompat.java b/slices/view/src/main/java/androidx/slice/SliceManagerCompat.java
index 8abacd1..1badbb4 100644
--- a/slices/view/src/main/java/androidx/slice/SliceManagerCompat.java
+++ b/slices/view/src/main/java/androidx/slice/SliceManagerCompat.java
@@ -28,6 +28,7 @@
 import androidx.slice.compat.SliceProviderCompat;
 import androidx.slice.widget.SliceLiveData;
 
+import java.util.Collection;
 import java.util.Set;
 
 
@@ -73,4 +74,9 @@
     public Uri mapIntentToUri(@NonNull Intent intent) {
         return SliceProviderCompat.mapIntentToUri(mContext, intent);
     }
+
+    @Override
+    public Collection<Uri> getSliceDescendants(Uri uri) {
+        return SliceProviderCompat.getSliceDescendants(mContext, uri);
+    }
 }
diff --git a/slices/view/src/main/java/androidx/slice/SliceManagerWrapper.java b/slices/view/src/main/java/androidx/slice/SliceManagerWrapper.java
index af072e6..e0c0342 100644
--- a/slices/view/src/main/java/androidx/slice/SliceManagerWrapper.java
+++ b/slices/view/src/main/java/androidx/slice/SliceManagerWrapper.java
@@ -30,6 +30,7 @@
 import androidx.annotation.RestrictTo;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
@@ -71,22 +72,23 @@
     @Nullable
     @Override
     public androidx.slice.Slice bindSlice(@NonNull Uri uri) {
-        return SliceConvert.wrap(android.app.slice.Slice.bindSlice(
-                mContext.getContentResolver(), uri, mSpecs));
+        return SliceConvert.wrap(mManager.bindSlice(uri, mSpecs));
     }
 
     @Nullable
     @Override
     public androidx.slice.Slice bindSlice(@NonNull Intent intent) {
-        return SliceConvert.wrap(android.app.slice.Slice.bindSlice(
-                mContext, intent, mSpecs));
+        return SliceConvert.wrap(mManager.bindSlice(intent, mSpecs));
+    }
+
+    @Override
+    public Collection<Uri> getSliceDescendants(Uri uri) {
+        return mManager.getSliceDescendants(uri);
     }
 
     @Nullable
     @Override
     public Uri mapIntentToUri(@NonNull Intent intent) {
-        // TODO: Switch over to mapIntentToUri once it lands in prebuilt.
-        Slice slice = bindSlice(intent);
-        return slice != null ? slice.getUri() : null;
+        return mManager.mapIntentToUri(intent);
     }
 }
diff --git a/slices/view/src/main/java/androidx/slice/SliceMetadata.java b/slices/view/src/main/java/androidx/slice/SliceMetadata.java
index bf96854..d8258bf 100644
--- a/slices/view/src/main/java/androidx/slice/SliceMetadata.java
+++ b/slices/view/src/main/java/androidx/slice/SliceMetadata.java
@@ -17,15 +17,22 @@
 package androidx.slice;
 
 import static android.app.slice.Slice.HINT_ACTIONS;
+import static android.app.slice.Slice.HINT_HORIZONTAL;
 import static android.app.slice.Slice.HINT_PARTIAL;
 import static android.app.slice.Slice.HINT_SHORTCUT;
+import static android.app.slice.SliceItem.FORMAT_INT;
 import static android.app.slice.SliceItem.FORMAT_SLICE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
 
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
 import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
+import static androidx.slice.core.SliceHints.HINT_PERMISSION_REQUEST;
 import static androidx.slice.core.SliceHints.HINT_TTL;
+import static androidx.slice.core.SliceHints.SUBTYPE_MAX;
+import static androidx.slice.core.SliceHints.SUBTYPE_VALUE;
+import static androidx.slice.widget.EventInfo.ROW_TYPE_PROGRESS;
+import static androidx.slice.widget.EventInfo.ROW_TYPE_SLIDER;
 
 import android.content.Context;
 import android.text.TextUtils;
@@ -34,8 +41,18 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.core.util.Pair;
+import androidx.slice.core.SliceAction;
+import androidx.slice.core.SliceActionImpl;
 import androidx.slice.core.SliceQuery;
+import androidx.slice.widget.EventInfo;
+import androidx.slice.widget.GridContent;
+import androidx.slice.widget.ListContent;
+import androidx.slice.widget.RowContent;
+import androidx.slice.widget.SliceView;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -51,6 +68,7 @@
     @IntDef({
             LOADED_NONE, LOADED_PARTIAL, LOADED_ALL
     })
+    @Retention(RetentionPolicy.SOURCE)
     public @interface SliceLoadingState{}
 
     /**
@@ -70,7 +88,11 @@
     private Context mContext;
     private long mExpiry;
     private long mLastUpdated;
+    private ListContent mListContent;
+    private SliceItem mHeaderItem;
+    private SliceActionImpl mPrimaryAction;
     private List<SliceItem> mSliceActions;
+    private @EventInfo.SliceRowType int mTemplateType;
 
     /**
      * Create a SliceMetadata object to provide access to some information around the slice and
@@ -104,6 +126,15 @@
             mLastUpdated = updatedItem.getTimestamp();
         }
         mSliceActions = getSliceActions(mSlice);
+
+        mListContent = new ListContent(context, slice);
+        mHeaderItem = mListContent.getHeaderItem();
+        mTemplateType = mListContent.getHeaderTemplateType();
+
+        SliceItem action = mListContent.getPrimaryAction();
+        if (action != null) {
+            mPrimaryAction = new SliceActionImpl(action);
+        }
     }
 
     /**
@@ -115,12 +146,84 @@
     }
 
     /**
+     * @return the primary action for this slice, null if none specified.
+     */
+    @Nullable
+    public SliceAction getPrimaryAction() {
+        return mPrimaryAction;
+    }
+
+    /**
+     * @return the type of row that is used for the header of this slice, -1 if unknown.
+     */
+    public @EventInfo.SliceRowType int getHeaderType() {
+        return mTemplateType;
+    }
+
+    /**
+     * @return whether this slice has content to show when presented
+     * in {@link SliceView#MODE_LARGE}.
+     */
+    public boolean hasLargeMode() {
+        boolean isHeaderFullGrid = false;
+        if (mHeaderItem != null && mHeaderItem.hasHint(HINT_HORIZONTAL)) {
+            GridContent gc = new GridContent(mContext, mHeaderItem);
+            isHeaderFullGrid = gc.hasImage() && gc.getMaxCellLineCount() > 1;
+        }
+        return mListContent.getRowItems().size() > 1 || isHeaderFullGrid;
+    }
+
+    /**
+     * @return the toggles associated with the header of this slice.
+     */
+    public List<SliceAction> getToggles() {
+        List<SliceAction> toggles = new ArrayList<>();
+        // Is it the primary action?
+        if (mPrimaryAction != null && mPrimaryAction.isToggle()) {
+            toggles.add(mPrimaryAction);
+        } else if (mSliceActions != null && mSliceActions.size() > 0) {
+            for (int i = 0; i < mSliceActions.size(); i++) {
+                SliceAction action = new SliceActionImpl(mSliceActions.get(i));
+                if (action.isToggle()) {
+                    toggles.add(action);
+                }
+            }
+        } else {
+            RowContent rc = new RowContent(mContext, mHeaderItem, true /* isHeader */);
+            toggles = rc.getToggleItems();
+        }
+        return toggles;
+    }
+
+    /**
+     * Gets the range information associated with a progress bar or input range associated with this
+     * slice, if it exists.
+     *
+     * @return a pair where the first item is the current value of the range and the second item is
+     * the maximum value of the range.
+     */
+    @Nullable
+    public Pair<Integer, Integer> getRange() {
+        if (mTemplateType == ROW_TYPE_SLIDER
+                || mTemplateType == ROW_TYPE_PROGRESS) {
+            RowContent rc = new RowContent(mContext, mHeaderItem, true /* isHeader */);
+            SliceItem range = rc.getRange();
+            SliceItem maxItem = SliceQuery.findSubtype(range, FORMAT_INT, SUBTYPE_MAX);
+            SliceItem currentItem = SliceQuery.findSubtype(range, FORMAT_INT, SUBTYPE_VALUE);
+            int max = maxItem != null ? maxItem.getInt() : -1;
+            int current = currentItem != null ? currentItem.getInt() : -1;
+            return new Pair<>(current, max);
+        }
+        return null;
+    }
+
+    /**
      * @return the list of keywords associated with the provided slice, null if no keywords were
      * specified or an empty list if the slice was specified to have no keywords.
      */
     @Nullable
     public List<String> getSliceKeywords() {
-        SliceItem keywordGroup = SliceQuery.find(mSlice, FORMAT_SLICE, HINT_KEY_WORDS, null);
+        SliceItem keywordGroup = SliceQuery.find(mSlice, FORMAT_SLICE, HINT_KEYWORDS, null);
         if (keywordGroup != null) {
             List<SliceItem> itemList = SliceQuery.findAll(keywordGroup, FORMAT_TEXT);
             if (itemList != null) {
@@ -180,6 +283,18 @@
     }
 
     /**
+     * To present a slice from another app, the app must grant uri permissions for the slice. If
+     * these permissions have not been granted and the app slice is requested then
+     * a permission request slice will be returned instead, allowing the user to grant permission.
+     * This method can be used to identify if a slice is a permission request.
+     *
+     * @return whether this slice represents a permission request.
+     */
+    public boolean isPermissionSlice() {
+        return mSlice.hasHint(HINT_PERMISSION_REQUEST);
+    }
+
+    /**
      * @return the group of actions associated with the provided slice, if they exist.
      * @hide
      */
diff --git a/slices/view/src/main/java/androidx/slice/SliceUtils.java b/slices/view/src/main/java/androidx/slice/SliceUtils.java
index 40d82bc..442b324 100644
--- a/slices/view/src/main/java/androidx/slice/SliceUtils.java
+++ b/slices/view/src/main/java/androidx/slice/SliceUtils.java
@@ -28,9 +28,12 @@
 import static androidx.slice.SliceMetadata.LOADED_ALL;
 import static androidx.slice.SliceMetadata.LOADED_NONE;
 import static androidx.slice.SliceMetadata.LOADED_PARTIAL;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.content.Context;
+import android.net.Uri;
 import android.text.TextUtils;
 
 import androidx.annotation.IntDef;
@@ -42,6 +45,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -65,10 +69,11 @@
      * @param output The output of the serialization.
      * @param encoding The encoding to use for serialization.
      * @param options Options defining how to handle non-serializable items.
+     * @throws IllegalArgumentException if the slice cannot be serialized using the given options.
      */
     public static void serializeSlice(@NonNull Slice s, @NonNull Context context,
             @NonNull OutputStream output, @NonNull String encoding,
-            @NonNull SerializeOptions options) throws IOException {
+            @NonNull SerializeOptions options) throws IOException, IllegalArgumentException {
         SliceXml.serializeSlice(s, context, output, encoding, options);
     }
 
@@ -76,13 +81,18 @@
      * Parse a slice that has been previously serialized.
      * <p>
      * Parses a slice that was serialized with {@link #serializeSlice}.
+     * <p>
+     * Note: Slices returned by this cannot be passed to {@link SliceConvert#unwrap(Slice)}.
      *
      * @param input The input stream to read from.
      * @param encoding The encoding to read as.
+     * @param listener Listener used to handle actions when reconstructing the slice.
+     * @throws SliceParseException if the InputStream cannot be parsed.
      */
-    public static @NonNull Slice parseSlice(@NonNull InputStream input, @NonNull String encoding)
-            throws IOException {
-        return SliceXml.parseSlice(input, encoding);
+    public static @NonNull Slice parseSlice(@NonNull Context context, @NonNull InputStream input,
+            @NonNull String encoding, @NonNull SliceActionListener listener)
+            throws IOException, SliceParseException {
+        return SliceXml.parseSlice(context, input, encoding, listener);
     }
 
     /**
@@ -101,17 +111,22 @@
         /**
          * Constant indicating that the SliceItem should be serialized as much as possible.
          * <p>
-         * For images this means it will be replaced with an empty image. For actions, the
-         * action will be removed but the content of the action will be serialized.
+         * For images this means they will be attempted to be serialized. For actions, the
+         * action will be removed but the content of the action will be serialized. The action
+         * may be triggered later on a de-serialized slice by binding the slice again and activating
+         * a pending-intent at the same location as the serialized action.
          */
-        public static final int MODE_DISABLE = 2;
+        public static final int MODE_CONVERT = 2;
 
-        @IntDef({MODE_THROW, MODE_REMOVE, MODE_DISABLE})
+        @IntDef({MODE_THROW, MODE_REMOVE, MODE_CONVERT})
+        @Retention(SOURCE)
         @interface FormatMode {
         }
 
         private int mActionMode = MODE_THROW;
         private int mImageMode = MODE_THROW;
+        private int mMaxWidth = 1000;
+        private int mMaxHeight = 1000;
 
         /**
          * @hide
@@ -149,6 +164,22 @@
         }
 
         /**
+         * @hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        public int getMaxWidth() {
+            return mMaxWidth;
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        public int getMaxHeight() {
+            return mMaxHeight;
+        }
+
+        /**
          * Sets how {@link android.app.slice.SliceItem#FORMAT_ACTION} items should be handled.
          *
          * The default mode is {@link #MODE_THROW}.
@@ -169,6 +200,30 @@
             mImageMode = mode;
             return this;
         }
+
+        /**
+         * Set the maximum width of an image to use when serializing.
+         * <p>
+         * Will only be used if the {@link #setImageMode(int)} is set to {@link #MODE_CONVERT}.
+         * Any images larger than the maximum size will be scaled down to fit within that size.
+         * The default value is 1000.
+         */
+        public SerializeOptions setMaxImageWidth(int width) {
+            mMaxWidth = width;
+            return this;
+        }
+
+        /**
+         * Set the maximum height of an image to use when serializing.
+         * <p>
+         * Will only be used if the {@link #setImageMode(int)} is set to {@link #MODE_CONVERT}.
+         * Any images larger than the maximum size will be scaled down to fit within that size.
+         * The default value is 1000.
+         */
+        public SerializeOptions setMaxImageHeight(int height) {
+            mMaxHeight = height;
+            return this;
+        }
     }
 
     /**
@@ -239,7 +294,7 @@
     @Deprecated
     @Nullable
     public static List<String> getSliceKeywords(@NonNull Slice slice) {
-        SliceItem keywordGroup = SliceQuery.find(slice, FORMAT_SLICE, HINT_KEY_WORDS, null);
+        SliceItem keywordGroup = SliceQuery.find(slice, FORMAT_SLICE, HINT_KEYWORDS, null);
         if (keywordGroup != null) {
             List<SliceItem> itemList = SliceQuery.findAll(keywordGroup, FORMAT_TEXT);
             if (itemList != null) {
@@ -255,4 +310,39 @@
         }
         return null;
     }
+
+    /**
+     * A listener used to receive events on slices parsed with
+     * {@link #parseSlice(Context, InputStream, String, SliceActionListener)}.
+     */
+    public interface SliceActionListener {
+        /**
+         * Called when an action is triggered on a slice parsed with
+         * {@link #parseSlice(Context, InputStream, String, SliceActionListener)}.
+         * @param actionUri The uri of the action selected.
+         */
+        void onSliceAction(Uri actionUri);
+    }
+
+    /**
+     * Exception thrown during
+     * {@link #parseSlice(Context, InputStream, String, SliceActionListener)}.
+     */
+    public static class SliceParseException extends Exception {
+        /**
+         * @hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        public SliceParseException(String s, Throwable e) {
+            super(s, e);
+        }
+
+        /**
+         * @hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        public SliceParseException(String s) {
+            super(s);
+        }
+    }
 }
diff --git a/slices/view/src/main/java/androidx/slice/SliceXml.java b/slices/view/src/main/java/androidx/slice/SliceXml.java
index e8747be..ba423ad 100644
--- a/slices/view/src/main/java/androidx/slice/SliceXml.java
+++ b/slices/view/src/main/java/androidx/slice/SliceXml.java
@@ -20,22 +20,31 @@
 import static org.xmlpull.v1.XmlPullParser.TEXT;
 
 import android.annotation.SuppressLint;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.text.Html;
 import android.text.Spanned;
 import android.text.TextUtils;
+import android.util.Base64;
 
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Consumer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlPullParserFactory;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -50,14 +59,24 @@
     private static final String NAMESPACE = null;
 
     private static final String TAG_SLICE = "slice";
+    private static final String TAG_ACTION = "action";
     private static final String TAG_ITEM = "item";
 
     private static final String ATTR_URI = "uri";
     private static final String ATTR_FORMAT = "format";
     private static final String ATTR_SUBTYPE = "subtype";
     private static final String ATTR_HINTS = "hints";
+    private static final String ATTR_ICON_TYPE = "iconType";
+    private static final String ATTR_ICON_PACKAGE = "pkg";
+    private static final String ATTR_ICON_RES_TYPE = "resType";
 
-    public static Slice parseSlice(InputStream input, String encoding) throws IOException {
+    private static final String ICON_TYPE_RES = "res";
+    private static final String ICON_TYPE_URI = "uri";
+    private static final String ICON_TYPE_DEFAULT = "def";
+
+    public static Slice parseSlice(Context context, InputStream input,
+            String encoding, SliceUtils.SliceActionListener listener)
+            throws IOException, SliceUtils.SliceParseException {
         try {
             XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
             parser.setInput(input, encoding);
@@ -70,7 +89,7 @@
                 if (type != START_TAG) {
                     continue;
                 }
-                s = parseSlice(parser);
+                s = parseSlice(context, parser, listener);
             }
             return s;
         } catch (XmlPullParserException e) {
@@ -79,9 +98,10 @@
     }
 
     @SuppressLint("WrongConstant")
-    private static Slice parseSlice(XmlPullParser parser)
-            throws IOException, XmlPullParserException {
-        if (!TAG_SLICE.equals(parser.getName())) {
+    private static Slice parseSlice(Context context, XmlPullParser parser,
+            SliceUtils.SliceActionListener listener)
+            throws IOException, XmlPullParserException, SliceUtils.SliceParseException {
+        if (!TAG_SLICE.equals(parser.getName()) && !TAG_ACTION.equals(parser.getName())) {
             throw new IOException("Unexpected tag " + parser.getName());
         }
         int outerDepth = parser.getDepth();
@@ -94,20 +114,24 @@
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
             if (type == START_TAG && TAG_ITEM.equals(parser.getName())) {
-                parseItem(b, parser);
+                parseItem(context, b, parser, listener);
             }
         }
         return b.build();
     }
 
     @SuppressLint("WrongConstant")
-    private static void parseItem(Slice.Builder b, XmlPullParser parser)
-            throws IOException, XmlPullParserException {
+    private static void parseItem(Context context, Slice.Builder b,
+            XmlPullParser parser, final SliceUtils.SliceActionListener listener)
+            throws IOException, XmlPullParserException, SliceUtils.SliceParseException {
         int type;
         int outerDepth = parser.getDepth();
         String format = parser.getAttributeValue(NAMESPACE, ATTR_FORMAT);
         String subtype = parser.getAttributeValue(NAMESPACE, ATTR_SUBTYPE);
         String hintStr = parser.getAttributeValue(NAMESPACE, ATTR_HINTS);
+        String iconType = parser.getAttributeValue(NAMESPACE, ATTR_ICON_TYPE);
+        String pkg = parser.getAttributeValue(NAMESPACE, ATTR_ICON_PACKAGE);
+        String resType = parser.getAttributeValue(NAMESPACE, ATTR_ICON_RES_TYPE);
         String[] hints = hints(hintStr);
         String v;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -118,16 +142,37 @@
                         // Nothing for now.
                         break;
                     case android.app.slice.SliceItem.FORMAT_IMAGE:
-                        v = parser.getText();
-                        if (!TextUtils.isEmpty(v)) {
-                            if (android.os.Build.VERSION.SDK_INT
-                                    >= android.os.Build.VERSION_CODES.M) {
-                                String[] split = v.split(",");
-                                int w = Integer.parseInt(split[0]);
-                                int h = Integer.parseInt(split[1]);
-                                Bitmap image = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8);
+                        switch (iconType) {
+                            case ICON_TYPE_RES:
+                                String resName = parser.getText();
+                                try {
+                                    Resources r = context.getPackageManager()
+                                                .getResourcesForApplication(pkg);
+                                    int id = r.getIdentifier(resName, resType, pkg);
+                                    if (id != 0) {
+                                        b.addIcon(IconCompat.createWithResource(
+                                                context.createPackageContext(pkg, 0), id), subtype,
+                                                hints);
+                                    } else {
+                                        throw new SliceUtils.SliceParseException(
+                                                "Cannot find resource " + pkg + ":" + resType
+                                                        + "/" + resName);
+                                    }
+                                } catch (PackageManager.NameNotFoundException e) {
+                                    throw new SliceUtils.SliceParseException(
+                                            "Invalid icon package " + pkg, e);
+                                }
+                                break;
+                            case ICON_TYPE_URI:
+                                v = parser.getText();
+                                b.addIcon(IconCompat.createWithContentUri(v), subtype, hints);
+                                break;
+                            default:
+                                v = parser.getText();
+                                byte[] data = Base64.decode(v, Base64.NO_WRAP);
+                                Bitmap image = BitmapFactory.decodeByteArray(data, 0, data.length);
                                 b.addIcon(IconCompat.createWithBitmap(image), subtype, hints);
-                            }
+                                break;
                         }
                         break;
                     case android.app.slice.SliceItem.FORMAT_INT:
@@ -138,15 +183,22 @@
                         v = parser.getText();
                         b.addText(Html.fromHtml(v), subtype, hints);
                         break;
-                    case android.app.slice.SliceItem.FORMAT_TIMESTAMP:
+                    case android.app.slice.SliceItem.FORMAT_LONG:
                         v = parser.getText();
-                        b.addTimestamp(Long.parseLong(v), subtype, hints);
+                        b.addLong(Long.parseLong(v), subtype, hints);
                         break;
                     default:
                         throw new IllegalArgumentException("Unrecognized format " + format);
                 }
             } else if (type == START_TAG && TAG_SLICE.equals(parser.getName())) {
-                b.addSubSlice(parseSlice(parser), subtype);
+                b.addSubSlice(parseSlice(context, parser, listener), subtype);
+            } else if (type == START_TAG && TAG_ACTION.equals(parser.getName())) {
+                b.addAction(new Consumer<Uri>() {
+                    @Override
+                    public void accept(Uri uri) {
+                        listener.onSliceAction(uri);
+                    }
+                }, parseSlice(context, parser, listener), subtype);
             }
         }
     }
@@ -162,7 +214,7 @@
             serializer.setOutput(output, encoding);
             serializer.startDocument(encoding, null);
 
-            serialize(s, context, options, serializer);
+            serialize(s, context, options, serializer, false, null);
 
             serializer.endDocument();
             serializer.flush();
@@ -172,9 +224,12 @@
     }
 
     private static void serialize(Slice s, Context context, SliceUtils.SerializeOptions options,
-            XmlSerializer serializer) throws IOException {
-        serializer.startTag(NAMESPACE, TAG_SLICE);
+            XmlSerializer serializer, boolean isAction, String subType) throws IOException {
+        serializer.startTag(NAMESPACE, isAction ? TAG_ACTION : TAG_SLICE);
         serializer.attribute(NAMESPACE, ATTR_URI, s.getUri().toString());
+        if (subType != null) {
+            serializer.attribute(NAMESPACE, ATTR_SUBTYPE, subType);
+        }
         if (!s.getHints().isEmpty()) {
             serializer.attribute(NAMESPACE, ATTR_HINTS, hintStr(s.getHints()));
         }
@@ -182,7 +237,7 @@
             serialize(item, context, options, serializer);
         }
 
-        serializer.endTag(NAMESPACE, TAG_SLICE);
+        serializer.endTag(NAMESPACE, isAction ? TAG_ACTION : TAG_SLICE);
     }
 
     private static void serialize(SliceItem item, Context context,
@@ -201,27 +256,45 @@
 
         switch (format) {
             case android.app.slice.SliceItem.FORMAT_ACTION:
-                if (options.getActionMode() == SliceUtils.SerializeOptions.MODE_DISABLE) {
-                    serialize(item.getSlice(), context, options, serializer);
+                if (options.getActionMode() == SliceUtils.SerializeOptions.MODE_CONVERT) {
+                    serialize(item.getSlice(), context, options, serializer, true,
+                            item.getSubType());
+                } else if (options.getActionMode() == SliceUtils.SerializeOptions.MODE_THROW) {
+                    throw new IllegalArgumentException("Slice contains an action " + item);
                 }
                 break;
             case android.app.slice.SliceItem.FORMAT_REMOTE_INPUT:
                 // Nothing for now.
                 break;
             case android.app.slice.SliceItem.FORMAT_IMAGE:
-                if (options.getImageMode() == SliceUtils.SerializeOptions.MODE_DISABLE) {
-                    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
-                        Drawable d = item.getIcon().loadDrawable(context);
-                        serializer.text(String.format("%d,%d",
-                                d.getIntrinsicWidth(), d.getIntrinsicHeight()));
+                if (options.getImageMode() == SliceUtils.SerializeOptions.MODE_CONVERT) {
+                    IconCompat icon = item.getIcon();
+
+                    switch (icon.getType()) {
+                        case Icon.TYPE_RESOURCE:
+                            serializeResIcon(serializer, icon, context);
+                            break;
+                        case Icon.TYPE_URI:
+                            Uri uri = icon.getUri();
+                            if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
+                                serializeFileIcon(serializer, icon, context);
+                            } else {
+                                serializeIcon(serializer, icon, context, options);
+                            }
+                            break;
+                        default:
+                            serializeIcon(serializer, icon, context, options);
+                            break;
                     }
+                } else if (options.getImageMode() == SliceUtils.SerializeOptions.MODE_THROW) {
+                    throw new IllegalArgumentException("Slice contains an image " + item);
                 }
                 break;
             case android.app.slice.SliceItem.FORMAT_INT:
                 serializer.text(String.valueOf(item.getInt()));
                 break;
             case android.app.slice.SliceItem.FORMAT_SLICE:
-                serialize(item.getSlice(), context, options, serializer);
+                serialize(item.getSlice(), context, options, serializer, false, item.getSubType());
                 break;
             case android.app.slice.SliceItem.FORMAT_TEXT:
                 if (item.getText() instanceof Spanned) {
@@ -230,8 +303,8 @@
                     serializer.text(String.valueOf(item.getText()));
                 }
                 break;
-            case android.app.slice.SliceItem.FORMAT_TIMESTAMP:
-                serializer.text(String.valueOf(item.getTimestamp()));
+            case android.app.slice.SliceItem.FORMAT_LONG:
+                serializer.text(String.valueOf(item.getLong()));
                 break;
             default:
                 throw new IllegalArgumentException("Unrecognized format " + format);
@@ -239,7 +312,57 @@
         serializer.endTag(NAMESPACE, TAG_ITEM);
     }
 
+    private static void serializeResIcon(XmlSerializer serializer, IconCompat icon, Context context)
+            throws IOException {
+        try {
+            Resources res = context.getPackageManager().getResourcesForApplication(
+                    icon.getResPackage());
+            int id = icon.getResId();
+            serializer.attribute(NAMESPACE, ATTR_ICON_TYPE, ICON_TYPE_RES);
+            serializer.attribute(NAMESPACE, ATTR_ICON_PACKAGE, res.getResourcePackageName(id));
+            serializer.attribute(NAMESPACE, ATTR_ICON_RES_TYPE, res.getResourceTypeName(id));
+            serializer.text(res.getResourceEntryName(id));
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalArgumentException("Slice contains invalid icon", e);
+        }
+    }
+
+    private static void serializeFileIcon(XmlSerializer serializer, IconCompat icon,
+            Context context) throws IOException {
+        serializer.attribute(NAMESPACE, ATTR_ICON_TYPE, ICON_TYPE_URI);
+        serializer.text(icon.getUri().toString());
+    }
+
+    private static void serializeIcon(XmlSerializer serializer, IconCompat icon,
+            Context context, SliceUtils.SerializeOptions options) throws IOException {
+        Drawable d = icon.loadDrawable(context);
+        int width = d.getIntrinsicWidth();
+        int height = d.getIntrinsicHeight();
+        if (width > options.getMaxWidth()) {
+            height = (int) (options.getMaxWidth() * height / (double) width);
+            width = options.getMaxWidth();
+        }
+        if (height > options.getMaxHeight()) {
+            width = (int) (options.getMaxHeight() * width / (double) height);
+            height = options.getMaxHeight();
+        }
+        Bitmap b = Bitmap.createBitmap(width, height,
+                Bitmap.Config.ARGB_8888);
+        Canvas c = new Canvas(b);
+        d.setBounds(0, 0, c.getWidth(), c.getHeight());
+        d.draw(c);
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        b.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
+        b.recycle();
+
+        serializer.attribute(NAMESPACE, ATTR_ICON_TYPE, ICON_TYPE_DEFAULT);
+        serializer.text(new String(Base64.encode(outputStream.toByteArray(), Base64.NO_WRAP)));
+    }
+
     private static String hintStr(List<String> hints) {
         return TextUtils.join(",", hints);
     }
+
+    private SliceXml() {
+    }
 }
diff --git a/slices/view/src/main/java/androidx/slice/widget/ActionRow.java b/slices/view/src/main/java/androidx/slice/widget/ActionRow.java
index 98ebfd2..55bbca1 100644
--- a/slices/view/src/main/java/androidx/slice/widget/ActionRow.java
+++ b/slices/view/src/main/java/androidx/slice/widget/ActionRow.java
@@ -22,13 +22,14 @@
 
 import static androidx.slice.core.SliceHints.ICON_IMAGE;
 
-import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
 import android.app.RemoteInput;
 import android.app.slice.Slice;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
+import android.os.Build;
+import android.util.Log;
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewParent;
@@ -39,6 +40,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.core.widget.ImageViewCompat;
@@ -55,6 +57,8 @@
 public class ActionRow extends FrameLayout {
 
     private static final int MAX_ACTIONS = 5;
+    private static final String TAG = "ActionRow";
+
     private final int mSize;
     private final int mIconPadding;
     private final LinearLayout mActionsGroup;
@@ -118,22 +122,16 @@
             }
             final SliceItem input = SliceQuery.find(action, FORMAT_REMOTE_INPUT);
             final SliceItem image = SliceQuery.find(action, FORMAT_IMAGE);
-            if (input != null && image != null
-                    && input.getRemoteInput().getAllowFreeFormInput()) {
-                boolean tint = !image.hasHint(HINT_NO_TINT);
-                addAction(image.getIcon(), tint).setOnClickListener(
-                        new OnClickListener() {
-                            @Override
-                            public void onClick(View v) {
-                                handleRemoteInputClick(v, action.getAction(),
-                                        input.getRemoteInput());
-                            }
-                        });
-                createRemoteInputView(mColor, getContext());
+            if (input != null && image != null) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                    handleSetRemoteInputActions(input, image, action);
+                } else {
+                    Log.w(TAG, "Received RemoteInput on API <20 " + input);
+                }
             } else if (action.hasHint(Slice.HINT_SHORTCUT)) {
                 final SliceActionImpl ac = new SliceActionImpl(action);
                 IconCompat iconItem = ac.getIcon();
-                if (iconItem != null && ac.getAction() != null) {
+                if (iconItem != null && ac.getActionItem() != null) {
                     boolean tint = ac.getImageMode() == ICON_IMAGE;
                     addAction(iconItem, tint).setOnClickListener(
                             new OnClickListener() {
@@ -141,7 +139,7 @@
                                 public void onClick(View v) {
                                     try {
                                         // TODO - should log events here
-                                        ac.getAction().send();
+                                        ac.getActionItem().fireAction(null, null);
                                     } catch (CanceledException e) {
                                         e.printStackTrace();
                                     }
@@ -157,6 +155,24 @@
         mActionsGroup.addView(child, new LinearLayout.LayoutParams(mSize, mSize, 1));
     }
 
+    @RequiresApi(21)
+    private void handleSetRemoteInputActions(final SliceItem input, SliceItem image,
+            final SliceItem action) {
+        if (input.getRemoteInput().getAllowFreeFormInput()) {
+            boolean tint = !image.hasHint(HINT_NO_TINT);
+            addAction(image.getIcon(), tint).setOnClickListener(
+                    new OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            handleRemoteInputClick(v, action,
+                                    input.getRemoteInput());
+                        }
+                    });
+            createRemoteInputView(mColor, getContext());
+        }
+    }
+
+    @RequiresApi(21)
     private void createRemoteInputView(int color, Context context) {
         View riv = RemoteInputView.inflate(context, this);
         riv.setVisibility(View.INVISIBLE);
@@ -164,7 +180,8 @@
         riv.setBackgroundColor(color);
     }
 
-    private boolean handleRemoteInputClick(View view, PendingIntent pendingIntent,
+    @RequiresApi(21)
+    private boolean handleRemoteInputClick(View view, SliceItem action,
             RemoteInput input) {
         if (input == null) {
             return false;
@@ -205,7 +222,7 @@
                 Math.max((w - cx) + cy, (w - cx) + (h - cy)));
 
         riv.setRevealParameters(cx, cy, r);
-        riv.setPendingIntent(pendingIntent);
+        riv.setAction(action);
         riv.setRemoteInput(new RemoteInput[] {
                 input
         }, input);
@@ -213,6 +230,7 @@
         return true;
     }
 
+    @RequiresApi(21)
     private RemoteInputView findRemoteInputView(View v) {
         if (v == null) {
             return null;
diff --git a/slices/view/src/main/java/androidx/slice/widget/EventInfo.java b/slices/view/src/main/java/androidx/slice/widget/EventInfo.java
index 0b93381..9b3af24 100644
--- a/slices/view/src/main/java/androidx/slice/widget/EventInfo.java
+++ b/slices/view/src/main/java/androidx/slice/widget/EventInfo.java
@@ -19,6 +19,9 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.RestrictTo;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Represents information associated with a logged event on {@link SliceView}.
  */
@@ -29,8 +32,15 @@
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     @IntDef({
-            ROW_TYPE_SHORTCUT, ROW_TYPE_LIST, ROW_TYPE_GRID, ROW_TYPE_MESSAGING
+            ROW_TYPE_SHORTCUT,
+            ROW_TYPE_LIST,
+            ROW_TYPE_GRID,
+            ROW_TYPE_MESSAGING,
+            ROW_TYPE_TOGGLE,
+            ROW_TYPE_SLIDER,
+            ROW_TYPE_PROGRESS,
     })
+    @Retention(RetentionPolicy.SOURCE)
     public @interface SliceRowType {}
 
     /**
@@ -49,6 +59,18 @@
      * Indicates the row is represented as a messaging template.
      */
     public static final int ROW_TYPE_MESSAGING = 2;
+    /**
+     * Indicates the row represents a toggleable item.
+     */
+    public static final int ROW_TYPE_TOGGLE = 3;
+    /**
+     * Indicates the row represents an range input slider.
+     */
+    public static final int ROW_TYPE_SLIDER = 4;
+    /**
+     * Indicates the row represents a progress indicator.
+     */
+    public static final int ROW_TYPE_PROGRESS = 5;
 
     /**
      * @hide
@@ -58,6 +80,7 @@
             ACTION_TYPE_TOGGLE, ACTION_TYPE_BUTTON, ACTION_TYPE_SLIDER, ACTION_TYPE_CONTENT,
             ACTION_TYPE_SEE_MORE
     })
+    @Retention(RetentionPolicy.SOURCE)
     public @interface SliceActionType{}
 
     /**
@@ -91,6 +114,7 @@
     @IntDef({
             POSITION_START, POSITION_END, POSITION_CELL
     })
+    @Retention(RetentionPolicy.SOURCE)
     public @interface SliceButtonPosition{}
 
     /**
@@ -268,6 +292,12 @@
                 return "MESSAGING";
             case ROW_TYPE_SHORTCUT:
                 return "SHORTCUT";
+            case ROW_TYPE_TOGGLE:
+                return "TOGGLE";
+            case ROW_TYPE_SLIDER:
+                return "SLIDER";
+            case ROW_TYPE_PROGRESS:
+                return "PROGRESS";
             default:
                 return "unknown row type: " + type;
         }
diff --git a/slices/view/src/main/java/androidx/slice/widget/GridContent.java b/slices/view/src/main/java/androidx/slice/widget/GridContent.java
index 14a05aa..69d5c44 100644
--- a/slices/view/src/main/java/androidx/slice/widget/GridContent.java
+++ b/slices/view/src/main/java/androidx/slice/widget/GridContent.java
@@ -30,7 +30,7 @@
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
 
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
 import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
 import static androidx.slice.core.SliceHints.HINT_TTL;
 import static androidx.slice.core.SliceHints.ICON_IMAGE;
@@ -198,7 +198,7 @@
         for (int i = 0; i < items.size(); i++) {
             SliceItem item = items.get(i);
             boolean isNonCellContent = item.hasAnyHints(HINT_SHORTCUT, HINT_SEE_MORE,
-                    HINT_KEY_WORDS, HINT_TTL, HINT_LAST_UPDATED);
+                    HINT_KEYWORDS, HINT_TTL, HINT_LAST_UPDATED);
             if (SUBTYPE_CONTENT_DESCRIPTION.equals(item.getSubType())) {
                 mContentDescr = item;
             } else if (item.hasHint(HINT_LIST_ITEM) && !isNonCellContent) {
@@ -337,7 +337,7 @@
         private boolean isValidCellContent(SliceItem cellItem) {
             final String format = cellItem.getFormat();
             boolean isNonCellContent = SUBTYPE_CONTENT_DESCRIPTION.equals(cellItem.getSubType())
-                    || cellItem.hasAnyHints(HINT_KEY_WORDS, HINT_TTL, HINT_LAST_UPDATED);
+                    || cellItem.hasAnyHints(HINT_KEYWORDS, HINT_TTL, HINT_LAST_UPDATED);
             return !isNonCellContent
                     && (FORMAT_TEXT.equals(format)
                     || FORMAT_TIMESTAMP.equals(format)
diff --git a/slices/view/src/main/java/androidx/slice/widget/GridRowView.java b/slices/view/src/main/java/androidx/slice/widget/GridRowView.java
index dce37e0..a8afb7c 100644
--- a/slices/view/src/main/java/androidx/slice/widget/GridRowView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/GridRowView.java
@@ -17,7 +17,6 @@
 package androidx.slice.widget;
 
 import static android.app.slice.Slice.HINT_LARGE;
-import static android.app.slice.Slice.HINT_LIST_ITEM;
 import static android.app.slice.Slice.HINT_NO_TINT;
 import static android.app.slice.Slice.HINT_TITLE;
 import static android.app.slice.SliceItem.FORMAT_ACTION;
@@ -133,16 +132,9 @@
         }
     }
 
-    /**
-     * This is called when GridView is presented in small format.
-     */
     @Override
     public void setSlice(Slice slice) {
-        resetView();
-        mRowIndex = 0;
-        SliceItem item = SliceQuery.find(slice, FORMAT_SLICE, HINT_LIST_ITEM, null);
-        mGridContent = new GridContent(getContext(), item);
-        populateViews(mGridContent);
+        // Nothing to do
     }
 
     /**
@@ -164,7 +156,7 @@
                     EventInfo.ROW_TYPE_GRID, mRowIndex);
             Pair<SliceItem, EventInfo> tagItem = new Pair<>(gc.getContentIntent(), info);
             mViewContainer.setTag(tagItem);
-            makeClickable(mViewContainer);
+            makeClickable(mViewContainer, true);
         }
         CharSequence contentDescr = gc.getContentDescription();
         if (contentDescr != null) {
@@ -222,7 +214,7 @@
         info.setPosition(EventInfo.POSITION_CELL, index, total);
         Pair<SliceItem, EventInfo> tagItem = new Pair<>(seeMoreItem, info);
         seeMoreView.setTag(tagItem);
-        makeClickable(seeMoreView);
+        makeClickable(seeMoreView, true);
     }
 
     /**
@@ -301,7 +293,7 @@
                 info.setPosition(EventInfo.POSITION_CELL, index, total);
                 Pair<SliceItem, EventInfo> tagItem = new Pair<>(contentIntentItem, info);
                 cellContainer.setTag(tagItem);
-                makeClickable(cellContainer);
+                makeClickable(cellContainer, true);
             }
         }
     }
@@ -347,10 +339,12 @@
         return addedView != null;
     }
 
-    private void makeClickable(View layout) {
-        layout.setOnClickListener(this);
-        layout.setForeground(SliceViewUtil.getDrawable(getContext(),
-                android.R.attr.selectableItemBackground));
+    private void makeClickable(View layout, boolean isClickable) {
+        layout.setOnClickListener(isClickable ? this : null);
+        layout.setBackground(isClickable
+                ? SliceViewUtil.getDrawable(getContext(), android.R.attr.selectableItemBackground)
+                : null);
+        layout.setClickable(isClickable);
     }
 
     @Override
@@ -360,7 +354,7 @@
         final EventInfo info = tagItem.second;
         if (actionItem != null && FORMAT_ACTION.equals(actionItem.getFormat())) {
             try {
-                actionItem.getAction().send();
+                actionItem.fireAction(null, null);
                 if (mObserver != null) {
                     mObserver.onSliceAction(info, actionItem);
                 }
@@ -373,5 +367,6 @@
     @Override
     public void resetView() {
         mViewContainer.removeAllViews();
+        makeClickable(mViewContainer, false);
     }
 }
diff --git a/slices/view/src/main/java/androidx/slice/widget/LargeSliceAdapter.java b/slices/view/src/main/java/androidx/slice/widget/LargeSliceAdapter.java
index ffa64c4..9247b78 100644
--- a/slices/view/src/main/java/androidx/slice/widget/LargeSliceAdapter.java
+++ b/slices/view/src/main/java/androidx/slice/widget/LargeSliceAdapter.java
@@ -27,22 +27,23 @@
 
 import android.app.slice.Slice;
 import android.content.Context;
-import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 
 import androidx.annotation.RestrictTo;
+import androidx.collection.ArrayMap;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.slice.SliceItem;
 import androidx.slice.core.SliceQuery;
 import androidx.slice.view.R;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
-import java.util.function.Consumer;
 
 /**
  * @hide
@@ -67,12 +68,25 @@
     private List<SliceItem> mSliceActions;
     private boolean mShowLastUpdated;
     private long mLastUpdated;
+    private SliceView mParent;
+    private LargeTemplateView mTemplateView;
 
     public LargeSliceAdapter(Context context) {
         mContext = context;
         setHasStableIds(true);
     }
 
+    /**
+     * Sets the SliceView parent and the template parent.
+     */
+    public void setParents(SliceView parent, LargeTemplateView templateView) {
+        mParent = parent;
+        mTemplateView = templateView;
+    }
+
+    /**
+     * Sets the observer to pass down to child views.
+     */
     public void setSliceObserver(SliceView.OnSliceActionListener observer) {
         mSliceObserver = observer;
     }
@@ -151,17 +165,7 @@
     @Override
     public void onBindViewHolder(SliceViewHolder holder, int position) {
         SliceWrapper slice = mSlices.get(position);
-        if (holder.mSliceView != null) {
-            final boolean isHeader = position == HEADER_INDEX;
-            holder.mSliceView.setTint(mColor);
-            holder.mSliceView.setStyle(mAttrs);
-            holder.mSliceView.setSliceItem(slice.mItem, isHeader, position, mSliceObserver);
-            if (isHeader && holder.mSliceView instanceof RowView) {
-                holder.mSliceView.setSliceActions(mSliceActions);
-                holder.mSliceView.setLastUpdated(mLastUpdated);
-                holder.mSliceView.setShowLastUpdated(mShowLastUpdated);
-            }
-        }
+        holder.bind(slice.mItem, position);
     }
 
     private void notifyHeaderChanged() {
@@ -184,7 +188,8 @@
                         null);
                 break;
         }
-        ((SliceChildView) v).setMode(MODE_LARGE);
+        int mode = mParent != null ? mParent.getMode() : MODE_LARGE;
+        ((SliceChildView) v).setMode(mode);
         return v;
     }
 
@@ -221,12 +226,53 @@
     /**
      * A {@link RecyclerView.ViewHolder} for presenting slices in {@link LargeSliceAdapter}.
      */
-    public static class SliceViewHolder extends RecyclerView.ViewHolder {
-        public final SliceChildView mSliceView;
+    public class SliceViewHolder extends RecyclerView.ViewHolder implements View.OnTouchListener,
+            View.OnClickListener {
+        public final SliceChildView mSliceChildView;
 
         public SliceViewHolder(View itemView) {
             super(itemView);
-            mSliceView = itemView instanceof SliceChildView ? (SliceChildView) itemView : null;
+            mSliceChildView = itemView instanceof SliceChildView ? (SliceChildView) itemView : null;
+        }
+
+        void bind(SliceItem item, int position) {
+            if (mSliceChildView == null || item == null) {
+                return;
+            }
+            // Click listener used to pipe click events to parent
+            mSliceChildView.setOnClickListener(this);
+            // Touch listener used to pipe events to touch feedback drawable
+            mSliceChildView.setOnTouchListener(this);
+
+            final boolean isHeader = position == HEADER_INDEX;
+            mSliceChildView.setTint(mColor);
+            mSliceChildView.setStyle(mAttrs);
+            mSliceChildView.setSliceItem(item, isHeader, position, mSliceObserver);
+            if (isHeader && mSliceChildView instanceof RowView) {
+                mSliceChildView.setSliceActions(mSliceActions);
+                mSliceChildView.setLastUpdated(mLastUpdated);
+                mSliceChildView.setShowLastUpdated(mShowLastUpdated);
+            }
+            int[] info = new int[2];
+            info[0] = ListContent.getRowType(mContext, item, isHeader, mSliceActions);
+            info[1] = position;
+            mSliceChildView.setTag(info);
+        }
+
+        @Override
+        public void onClick(View v) {
+            if (mParent != null) {
+                mParent.setClickInfo((int[]) v.getTag());
+                mParent.performClick();
+            }
+        }
+
+        @Override
+        public boolean onTouch(View v, MotionEvent event) {
+            if (mTemplateView != null) {
+                mTemplateView.onForegroundActivated(event);
+            }
+            return false;
         }
     }
 
@@ -249,25 +295,24 @@
 
         private String genString(SliceItem item) {
             final StringBuilder builder = new StringBuilder();
-            SliceQuery.stream(item).forEach(new Consumer<SliceItem>() {
-                @Override
-                public void accept(SliceItem i) {
-                    builder.append(i.getFormat());
-                    //i.removeHint(Slice.HINT_SELECTED);
-                    builder.append(i.getHints());
-                    switch (i.getFormat()) {
-                        case FORMAT_IMAGE:
-                            builder.append(i.getIcon());
-                            break;
-                        case FORMAT_TEXT:
-                            builder.append(i.getText());
-                            break;
-                        case FORMAT_INT:
-                            builder.append(i.getInt());
-                            break;
-                    }
+            Iterator<SliceItem> items = SliceQuery.stream(item);
+            while (items.hasNext()) {
+                SliceItem i = items.next();
+                builder.append(i.getFormat());
+                //i.removeHint(Slice.HINT_SELECTED);
+                builder.append(i.getHints());
+                switch (i.getFormat()) {
+                    case FORMAT_IMAGE:
+                        builder.append(i.getIcon());
+                        break;
+                    case FORMAT_TEXT:
+                        builder.append(i.getText());
+                        break;
+                    case FORMAT_INT:
+                        builder.append(i.getInt());
+                        break;
                 }
-            });
+            }
             return builder.toString();
         }
 
diff --git a/slices/view/src/main/java/androidx/slice/widget/LargeTemplateView.java b/slices/view/src/main/java/androidx/slice/widget/LargeTemplateView.java
index 36409e8..0c0aba8 100644
--- a/slices/view/src/main/java/androidx/slice/widget/LargeTemplateView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/LargeTemplateView.java
@@ -16,8 +16,14 @@
 
 package androidx.slice.widget;
 
+import static android.app.slice.Slice.HINT_HORIZONTAL;
+
 import android.content.Context;
+import android.os.Build;
 import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
 
 import androidx.annotation.RestrictTo;
 import androidx.recyclerview.widget.LinearLayoutManager;
@@ -26,6 +32,7 @@
 import androidx.slice.SliceItem;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -34,6 +41,8 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 public class LargeTemplateView extends SliceChildView {
 
+    private SliceView mParent;
+    private final View mForeground;
     private final LargeSliceAdapter mAdapter;
     private final RecyclerView mRecyclerView;
     private Slice mSlice;
@@ -41,6 +50,7 @@
     private ListContent mListContent;
     private List<SliceItem> mDisplayedItems = new ArrayList<>();
     private int mDisplayedItemsHeight = 0;
+    private int[] mLoc = new int[2];
 
     public LargeTemplateView(Context context) {
         super(context);
@@ -49,6 +59,23 @@
         mAdapter = new LargeSliceAdapter(context);
         mRecyclerView.setAdapter(mAdapter);
         addView(mRecyclerView);
+
+        mForeground = new View(getContext());
+        mForeground.setBackground(SliceViewUtil.getDrawable(getContext(),
+                android.R.attr.selectableItemBackground));
+        addView(mForeground);
+
+        FrameLayout.LayoutParams lp = (LayoutParams) mForeground.getLayoutParams();
+        lp.width = LayoutParams.MATCH_PARENT;
+        lp.height = LayoutParams.MATCH_PARENT;
+        mForeground.setLayoutParams(lp);
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mParent = (SliceView) getParent();
+        mAdapter.setParents(mParent, this);
     }
 
     @Override
@@ -61,20 +88,62 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
+    /**
+     * Called when the foreground view handling touch feedback should be activated.
+     * @param event the event to handle.
+     */
+    public void onForegroundActivated(MotionEvent event) {
+        if (mParent != null && !mParent.isSliceViewClickable()) {
+            // Only show highlight if clickable
+            mForeground.setPressed(false);
+            return;
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            mForeground.getLocationOnScreen(mLoc);
+            final int x = (int) (event.getRawX() - mLoc[0]);
+            final int y = (int) (event.getRawY() - mLoc[1]);
+            mForeground.getBackground().setHotspot(x, y);
+        }
+        int action = event.getActionMasked();
+        if (action == MotionEvent.ACTION_DOWN) {
+            mForeground.setPressed(true);
+        } else if (action == MotionEvent.ACTION_CANCEL
+                || action == MotionEvent.ACTION_UP
+                || action == MotionEvent.ACTION_MOVE) {
+            mForeground.setPressed(false);
+        }
+    }
+
+    @Override
+    public void setMode(int newMode) {
+        super.setMode(newMode);
+        updateDisplayedItems(getMeasuredHeight());
+    }
+
     @Override
     public int getActualHeight() {
         return mDisplayedItemsHeight;
     }
 
     @Override
-    public void setTint(int tint) {
-        super.setTint(tint);
-        populate();
+    public int getSmallHeight() {
+        if (mListContent == null || mListContent.getHeaderItem() == null) {
+            return 0;
+        }
+        SliceItem headerItem = mListContent.getHeaderItem();
+        if (headerItem.hasHint(HINT_HORIZONTAL)) {
+            GridContent gc = new GridContent(getContext(), headerItem);
+            return gc.getSmallHeight();
+        } else {
+            RowContent rc = new RowContent(getContext(), headerItem, mListContent.hasHeader());
+            return rc.getSmallHeight();
+        }
     }
 
     @Override
-    public @SliceView.SliceMode int getMode() {
-        return SliceView.MODE_LARGE;
+    public void setTint(int tint) {
+        super.setTint(tint);
+        populate();
     }
 
     @Override
@@ -148,7 +217,12 @@
             mDisplayedItems = mListContent.getRowItems();
         }
         mDisplayedItemsHeight = ListContent.getListHeight(getContext(), mDisplayedItems);
-        mAdapter.setSliceItems(mDisplayedItems, mTintColor);
+        if (getMode() == SliceView.MODE_LARGE) {
+            mAdapter.setSliceItems(mDisplayedItems, mTintColor);
+        } else if (getMode() == SliceView.MODE_SMALL) {
+            mAdapter.setSliceItems(
+                    Collections.singletonList(mDisplayedItems.get(0)), mTintColor);
+        }
     }
 
     @Override
diff --git a/slices/view/src/main/java/androidx/slice/widget/ListContent.java b/slices/view/src/main/java/androidx/slice/widget/ListContent.java
index 955144d..b728a69 100644
--- a/slices/view/src/main/java/androidx/slice/widget/ListContent.java
+++ b/slices/view/src/main/java/androidx/slice/widget/ListContent.java
@@ -27,7 +27,7 @@
 import static android.app.slice.SliceItem.FORMAT_SLICE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
 import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
 import static androidx.slice.core.SliceHints.HINT_TTL;
 
@@ -39,6 +39,8 @@
 import androidx.slice.Slice;
 import androidx.slice.SliceItem;
 import androidx.slice.SliceMetadata;
+import androidx.slice.core.SliceAction;
+import androidx.slice.core.SliceActionImpl;
 import androidx.slice.core.SliceQuery;
 
 import java.util.ArrayList;
@@ -81,7 +83,7 @@
         for (int i = 0; i < children.size(); i++) {
             final SliceItem child = children.get(i);
             final String format = child.getFormat();
-            boolean isNonRowContent = child.hasAnyHints(HINT_ACTIONS, HINT_SEE_MORE, HINT_KEY_WORDS,
+            boolean isNonRowContent = child.hasAnyHints(HINT_ACTIONS, HINT_SEE_MORE, HINT_KEYWORDS,
                     HINT_TTL, HINT_LAST_UPDATED);
             if (!isNonRowContent && (FORMAT_ACTION.equals(format) || FORMAT_SLICE.equals(format))) {
                 if (mHeaderItem == null && !child.hasHint(HINT_LIST_ITEM)) {
@@ -202,19 +204,78 @@
     }
 
     /**
+     * @return the type of template that the header represents.
+     */
+    public int getHeaderTemplateType() {
+        return getRowType(mContext, mHeaderItem, true, mSliceActions);
+    }
+
+    /**
+     * The type of template that the provided row item represents.
+     *
+     * @param context context used for this slice.
+     * @param rowItem the row item to determine the template type of.
+     * @param isHeader whether this row item is used as a header.
+     * @param actions the actions associated with this slice, only matter if this row is the header.
+     * @return the type of template the provided row item represents.
+     */
+    public static int getRowType(Context context, SliceItem rowItem, boolean isHeader,
+                                 List<SliceItem> actions) {
+        if (rowItem != null) {
+            if (rowItem.hasHint(HINT_HORIZONTAL)) {
+                return EventInfo.ROW_TYPE_GRID;
+            } else {
+                RowContent rc = new RowContent(context, rowItem, isHeader);
+                SliceItem actionItem = rc.getPrimaryAction();
+                SliceAction primaryAction = null;
+                if (actionItem != null) {
+                    primaryAction = new SliceActionImpl(actionItem);
+                }
+                if (rc.getRange() != null) {
+                    return FORMAT_ACTION.equals(rc.getRange().getFormat())
+                            ? EventInfo.ROW_TYPE_SLIDER
+                            : EventInfo.ROW_TYPE_PROGRESS;
+                } else if (primaryAction != null && primaryAction.isToggle()) {
+                    return EventInfo.ROW_TYPE_TOGGLE;
+                } else if (isHeader && actions != null) {
+                    for (int i = 0; i < actions.size(); i++) {
+                        if (new SliceActionImpl(actions.get(i)).isToggle()) {
+                            return EventInfo.ROW_TYPE_TOGGLE;
+                        }
+                    }
+                    return EventInfo.ROW_TYPE_LIST;
+                } else {
+                    return rc.getToggleItems().size() > 0
+                            ? EventInfo.ROW_TYPE_TOGGLE
+                            : EventInfo.ROW_TYPE_LIST;
+                }
+            }
+        }
+        return EventInfo.ROW_TYPE_LIST;
+    }
+
+    /**
      * @return the primary action for this list; i.e. action on the header or first row.
      */
     @Nullable
     public SliceItem getPrimaryAction() {
-        RowContent rc = new RowContent(mContext, mHeaderItem, false);
-        return rc.getPrimaryAction();
+        if (mHeaderItem != null) {
+            if (mHeaderItem.hasHint(HINT_HORIZONTAL)) {
+                GridContent gc = new GridContent(mContext, mHeaderItem);
+                return gc.getContentIntent();
+            } else {
+                RowContent rc = new RowContent(mContext, mHeaderItem, false);
+                return rc.getPrimaryAction();
+            }
+        }
+        return null;
     }
 
     @Nullable
     private static SliceItem findHeaderItem(@NonNull Slice slice) {
         // See if header is specified
         String[] nonHints = new String[] {HINT_LIST_ITEM, HINT_SHORTCUT, HINT_ACTIONS,
-                HINT_KEY_WORDS, HINT_TTL, HINT_LAST_UPDATED};
+                HINT_KEYWORDS, HINT_TTL, HINT_LAST_UPDATED};
         SliceItem header = SliceQuery.find(slice, FORMAT_SLICE, null, nonHints);
         if (header != null && isValidHeader(header)) {
             return header;
@@ -239,7 +300,7 @@
 
     private static boolean isValidHeader(SliceItem sliceItem) {
         if (FORMAT_SLICE.equals(sliceItem.getFormat()) && !sliceItem.hasAnyHints(HINT_LIST_ITEM,
-                HINT_ACTIONS, HINT_KEY_WORDS)) {
+                HINT_ACTIONS, HINT_KEYWORDS)) {
              // Minimum valid header is a slice with text
             SliceItem item = SliceQuery.find(sliceItem, FORMAT_TEXT, (String) null, null);
             return item != null;
diff --git a/slices/view/src/main/java/androidx/slice/widget/MessageView.java b/slices/view/src/main/java/androidx/slice/widget/MessageView.java
index 98d1a92..6035d0a 100644
--- a/slices/view/src/main/java/androidx/slice/widget/MessageView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/MessageView.java
@@ -24,18 +24,18 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
-import androidx.annotation.RestrictTo;
 import android.text.SpannableStringBuilder;
 import android.util.TypedValue;
 import android.widget.ImageView;
 import android.widget.TextView;
 
-import java.util.function.Consumer;
-
+import androidx.annotation.RestrictTo;
 import androidx.slice.Slice;
 import androidx.slice.SliceItem;
 import androidx.slice.core.SliceQuery;
 
+import java.util.List;
+
 /**
  * @hide
  */
@@ -91,15 +91,13 @@
             mIcon.setImageBitmap(SliceViewUtil.getCircularBitmap(iconBm));
         }
         final SpannableStringBuilder builder = new SpannableStringBuilder();
-        SliceQuery.findAll(slice, FORMAT_TEXT).forEach(new Consumer<SliceItem>() {
-            @Override
-            public void accept(SliceItem text) {
-                if (builder.length() != 0) {
-                    builder.append('\n');
-                }
-                builder.append(text.getText());
+        List<SliceItem> items = SliceQuery.findAll(slice, FORMAT_TEXT);
+        for (SliceItem text : items) {
+            if (builder.length() != 0) {
+                builder.append('\n');
             }
-        });
+            builder.append(text.getText());
+        }
         mDetails.setText(builder.toString());
     }
 }
diff --git a/slices/view/src/main/java/androidx/slice/widget/RemoteInputView.java b/slices/view/src/main/java/androidx/slice/widget/RemoteInputView.java
index be0a7b7..d4ff299 100644
--- a/slices/view/src/main/java/androidx/slice/widget/RemoteInputView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/RemoteInputView.java
@@ -23,9 +23,8 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
-import androidx.annotation.RestrictTo;
-import androidx.core.content.ContextCompat;
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.util.AttributeSet;
@@ -48,6 +47,10 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.core.content.ContextCompat;
+import androidx.slice.SliceItem;
 import androidx.slice.view.R;
 
 /**
@@ -57,6 +60,7 @@
  */
 // TODO this should be unified with SystemUI RemoteInputView (b/67527720)
 @RestrictTo(RestrictTo.Scope.LIBRARY)
+@RequiresApi(21)
 public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
 
     private static final String TAG = "RemoteInput";
@@ -69,7 +73,7 @@
     private RemoteEditText mEditText;
     private ImageButton mSendButton;
     private ProgressBar mProgressBar;
-    private PendingIntent mPendingIntent;
+    private SliceItem mAction;
     private RemoteInput[] mRemoteInputs;
     private RemoteInput mRemoteInput;
 
@@ -136,11 +140,11 @@
         // but that's an edge case, and also because we can't always know which package will receive
         // an intent, so we just reset for the creator.
         //getContext().getSystemService(ShortcutManager.class).onApplicationActive(
-        //        mPendingIntent.getCreatorPackage(),
+        //        mAction.getCreatorPackage(),
         //        getContext().getUserId());
 
         try {
-            mPendingIntent.send(getContext(), 0, fillInIntent);
+            mAction.fireAction(getContext(), fillInIntent);
             reset();
         } catch (PendingIntent.CanceledException e) {
             Log.i(TAG, "Unable to send remote input result", e);
@@ -182,8 +186,8 @@
     /**
      * Set the pending intent for remote input.
      */
-    public void setPendingIntent(PendingIntent pendingIntent) {
-        mPendingIntent = pendingIntent;
+    public void setAction(SliceItem action) {
+        mAction = action;
     }
 
     /**
@@ -302,8 +306,8 @@
         }
 
         private void defocusIfNeeded(boolean animate) {
-            if (mRemoteInputView != null || isTemporarilyDetached()) {
-                if (isTemporarilyDetached()) {
+            if (mRemoteInputView != null || isTemporarilyDetachedCompat()) {
+                if (isTemporarilyDetachedCompat()) {
                     // We might get reattached but then the other one of HUN / expanded might steal
                     // our focus, so we'll need to save our text here.
                 }
@@ -318,6 +322,13 @@
             }
         }
 
+        private boolean isTemporarilyDetachedCompat() {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                return isTemporarilyDetached();
+            }
+            return false;
+        }
+
         @Override
         protected void onVisibilityChanged(View changedView, int visibility) {
             super.onVisibilityChanged(changedView, visibility);
diff --git a/slices/view/src/main/java/androidx/slice/widget/RowContent.java b/slices/view/src/main/java/androidx/slice/widget/RowContent.java
index 7565895..728715e 100644
--- a/slices/view/src/main/java/androidx/slice/widget/RowContent.java
+++ b/slices/view/src/main/java/androidx/slice/widget/RowContent.java
@@ -31,7 +31,7 @@
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
 
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
 import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
 import static androidx.slice.core.SliceHints.HINT_TTL;
 import static androidx.slice.core.SliceHints.SUBTYPE_RANGE;
@@ -44,6 +44,8 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.slice.SliceItem;
+import androidx.slice.core.SliceAction;
+import androidx.slice.core.SliceActionImpl;
 import androidx.slice.core.SliceQuery;
 import androidx.slice.view.R;
 
@@ -65,6 +67,7 @@
     private SliceItem mSubtitleItem;
     private SliceItem mSummaryItem;
     private ArrayList<SliceItem> mEndItems = new ArrayList<>();
+    private ArrayList<SliceAction> mToggleItems = new ArrayList<>();
     private SliceItem mRange;
     private SliceItem mContentDescr;
     private boolean mEndItemsContainAction;
@@ -72,11 +75,14 @@
     private int mLineCount = 0;
     private int mMaxHeight;
     private int mMinHeight;
+    private int mMaxRangeHeight;
 
     public RowContent(Context context, SliceItem rowSlice, boolean isHeader) {
         populate(rowSlice, isHeader);
         mMaxHeight = context.getResources().getDimensionPixelSize(R.dimen.abc_slice_row_max_height);
         mMinHeight = context.getResources().getDimensionPixelSize(R.dimen.abc_slice_row_min_height);
+        mMaxRangeHeight = context.getResources().getDimensionPixelSize(
+                R.dimen.abc_slice_row_range_max_height);
     }
 
     /**
@@ -92,7 +98,7 @@
         // Find primary action first (otherwise filtered out of valid row items)
         String[] hints = new String[] {HINT_SHORTCUT, HINT_TITLE};
         mPrimaryAction = SliceQuery.find(rowSlice, FORMAT_SLICE, hints,
-                new String[] { HINT_ACTIONS, HINT_KEY_WORDS } /* nonHints */);
+                new String[] { HINT_ACTIONS, HINT_KEYWORDS} /* nonHints */);
 
         if (mPrimaryAction == null && FORMAT_ACTION.equals(rowSlice.getFormat())
                 && rowSlice.getSlice().getItems().size() == 1) {
@@ -149,32 +155,36 @@
             if (hasText(mSubtitleItem)) {
                 mLineCount++;
             }
-            // Special rules for end items: only one timestamp, can't be mixture of icons / actions
+            // Special rules for end items: only one timestamp
             boolean hasTimestamp = mStartItem != null
                     && FORMAT_TIMESTAMP.equals(mStartItem.getFormat());
-            String desiredFormat = null;
             for (int i = 0; i < endItems.size(); i++) {
                 final SliceItem item = endItems.get(i);
-                boolean isAction = FORMAT_SLICE.equals(item.getFormat())
-                        && item.hasHint(HINT_SHORTCUT);
+                boolean isAction = SliceQuery.find(item, FORMAT_ACTION) != null;
                 if (FORMAT_TIMESTAMP.equals(item.getFormat())) {
                     if (!hasTimestamp) {
                         hasTimestamp = true;
                         mEndItems.add(item);
                     }
-                } else if (desiredFormat == null) {
-                    desiredFormat = item.getFormat();
-                    mEndItems.add(item);
-                    mEndItemsContainAction |= isAction;
-                } else if (desiredFormat.equals(item.getFormat())) {
-                    mEndItems.add(item);
-                    mEndItemsContainAction |= isAction;
+                } else {
+                    processContent(item, isAction);
                 }
             }
         }
         return isValid();
     }
 
+    private void processContent(@NonNull SliceItem item, boolean isAction) {
+        if (isAction) {
+            SliceAction ac = new SliceActionImpl(item);
+            if (ac.isToggle()) {
+                mToggleItems.add(ac);
+            }
+        }
+        mEndItems.add(item);
+        mEndItemsContainAction |= isAction;
+    }
+
     /**
      * @return the {@link SliceItem} used to populate this row.
      */
@@ -236,6 +246,13 @@
     }
 
     /**
+     * @return a list of toggles associated with this row.
+     */
+    public ArrayList<SliceAction> getToggleItems() {
+        return mToggleItems;
+    }
+
+    /**
      * @return the content description to use for this row.
      */
     @Nullable
@@ -261,16 +278,22 @@
      * @return the height to display a row at when it is used as a small template.
      */
     public int getSmallHeight() {
-        return mMaxHeight;
+        return (getRange() != null && mLineCount > 1)
+                ? mMaxRangeHeight
+                : mMaxHeight;
     }
 
     /**
      * @return the height the content in this template requires to be displayed.
      */
     public int getActualHeight() {
-        return isValid()
-                ? (getLineCount() > 1 || mIsHeader) ? mMaxHeight : mMinHeight
-                : 0;
+        if (!isValid()) {
+            return 0;
+        }
+        if (getRange() != null && mLineCount > 1) {
+            return mMaxRangeHeight;
+        }
+        return (getLineCount() > 1 || mIsHeader) ? mMaxHeight : mMinHeight;
     }
 
     private static boolean hasText(SliceItem textSlice) {
@@ -342,7 +365,7 @@
      * @return whether this item is valid content to display in a row.
      */
     private static boolean isValidRowContent(SliceItem slice, SliceItem item) {
-        if (item.hasAnyHints(HINT_KEY_WORDS, HINT_TTL, HINT_LAST_UPDATED)) {
+        if (item.hasAnyHints(HINT_KEYWORDS, HINT_TTL, HINT_LAST_UPDATED)) {
             return false;
         }
         if (FORMAT_SLICE.equals(item.getFormat()) && !item.hasHint(HINT_SHORTCUT)) {
diff --git a/slices/view/src/main/java/androidx/slice/widget/RowView.java b/slices/view/src/main/java/androidx/slice/widget/RowView.java
index 21264f5..782b5f4 100644
--- a/slices/view/src/main/java/androidx/slice/widget/RowView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/RowView.java
@@ -17,6 +17,7 @@
 package androidx.slice.widget;
 
 import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
+import static android.app.slice.Slice.HINT_LIST_ITEM;
 import static android.app.slice.Slice.HINT_NO_TINT;
 import static android.app.slice.Slice.HINT_PARTIAL;
 import static android.app.slice.Slice.HINT_SHORTCUT;
@@ -34,11 +35,11 @@
 import static androidx.slice.core.SliceHints.SUBTYPE_VALUE;
 import static androidx.slice.widget.SliceView.MODE_SMALL;
 
-import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
 import android.text.SpannableString;
 import android.text.TextUtils;
 import android.text.style.StyleSpan;
@@ -59,6 +60,7 @@
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.RestrictTo;
+import androidx.core.graphics.drawable.DrawableCompat;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
 import androidx.slice.SliceItem;
@@ -83,6 +85,7 @@
     // The number of items that fit on the right hand side of a small slice
     private static final int MAX_END_ITEMS = 3;
 
+    private LinearLayout mRootView;
     private LinearLayout mStartContainer;
     private LinearLayout mContent;
     private TextView mPrimaryText;
@@ -112,6 +115,7 @@
                 R.dimen.abc_slice_small_image_size);
         mPadding = getContext().getResources().getDimensionPixelSize(R.dimen.abc_slice_padding);
         inflate(context, R.layout.abc_slice_small_template, this);
+        mRootView = findViewById(R.id.row_view);
 
         mStartContainer = (LinearLayout) findViewById(R.id.icon_frame);
         mContent = (LinearLayout) findViewById(android.R.id.content);
@@ -168,6 +172,11 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
+    @Override
+    public void setSlice(Slice slice) {
+        // Nothing to do
+    }
+
     /**
      * This is called when RowView is being used as a component in a large template.
      */
@@ -176,25 +185,12 @@
             SliceView.OnSliceActionListener observer) {
         setSliceActionListener(observer);
         mRowIndex = index;
-        mIsHeader = isHeader;
+        mIsHeader = !slice.hasHint(HINT_LIST_ITEM);
         mHeaderActions = null;
         mRowContent = new RowContent(getContext(), slice, mIsHeader);
         populateViews();
     }
 
-    /**
-     * This is called when RowView is being used as a small template.
-     */
-    @Override
-    public void setSlice(Slice slice) {
-        mRowIndex = 0;
-        mIsHeader = true;
-        mHeaderActions = null;
-        ListContent lc = new ListContent(getContext(), slice);
-        mRowContent = new RowContent(getContext(), lc.getHeaderItem(), true /* isHeader */);
-        populateViews();
-    }
-
     private void populateViews() {
         resetView();
         if (mRowContent.isDefaultSeeMore()) {
@@ -231,59 +227,54 @@
                 : mRowContent.getSubtitleItem();
         addSubtitle(subtitleItem);
 
-        final SliceItem range = mRowContent.getRange();
-        if (range != null) {
-            addRange(range);
-            return;
-        }
-
         SliceItem primaryAction = mRowContent.getPrimaryAction();
         if (primaryAction != null) {
             mRowAction = new SliceActionImpl(primaryAction);
             if (mRowAction.isToggle()) {
                 // If primary action is a toggle, add it and we're done
                 addToggle(mRowAction, mTintColor, mEndContainer);
-                setViewClickable(this, true);
+                setViewClickable(mRootView, true);
                 return;
             }
         }
 
+        final SliceItem range = mRowContent.getRange();
+        if (range != null) {
+            if (mRowAction != null) {
+                setViewClickable(mRootView, true);
+            }
+            addRange(range);
+            return;
+        }
+
         // If we're here we can can show end items; check for top level actions first
         List<SliceItem> endItems = mRowContent.getEndItems();
-        String desiredFormat = FORMAT_ACTION;
         if (mIsHeader && mHeaderActions != null && mHeaderActions.size() > 0) {
             // Use these if we have them instead
             endItems = mHeaderActions;
-        } else if (!endItems.isEmpty()) {
-            // Prefer to show actions as end items if possible; fall back to the first format type.
-            SliceItem firstEndItem = endItems.get(0);
-            desiredFormat = mRowContent.endItemsContainAction()
-                    ? FORMAT_ACTION : firstEndItem.getSlice().getItems().get(0).getFormat();
         }
         boolean hasRowAction = mRowAction != null;
         if (endItems.isEmpty()) {
-            if (hasRowAction) setViewClickable(this, true);
+            if (hasRowAction) setViewClickable(mRootView, true);
             return;
         }
 
         // If we're here we might be able to show end items
         int itemCount = 0;
         boolean firstItemIsADefaultToggle = false;
+        boolean hasEndItemAction = false;
         for (int i = 0; i < endItems.size(); i++) {
             final SliceItem endItem = endItems.get(i);
-            final String endFormat = endItem.hasHint(HINT_SHORTCUT)
-                    ? FORMAT_ACTION
-                    : endItem.getSlice().getItems().get(0).getFormat();
-            // Only show one type of format at the end of the slice, use whatever is first
-            if (itemCount < MAX_END_ITEMS
-                    && (desiredFormat.equals(endFormat)
-                    || FORMAT_TIMESTAMP.equals(endFormat))) {
+            if (itemCount < MAX_END_ITEMS) {
                 final EventInfo info = new EventInfo(getMode(),
                         EventInfo.ACTION_TYPE_BUTTON,
                         EventInfo.ROW_TYPE_LIST, mRowIndex);
                 info.setPosition(EventInfo.POSITION_END, i,
                         Math.min(endItems.size(), MAX_END_ITEMS));
                 if (addItem(endItem, mTintColor, false /* isStart */, mPadding, info)) {
+                    if (FORMAT_ACTION.equals(endItem.getFormat())) {
+                        hasEndItemAction = true;
+                    }
                     itemCount++;
                     if (itemCount == 1) {
                         firstItemIsADefaultToggle = !mToggles.isEmpty()
@@ -293,7 +284,6 @@
             }
         }
 
-        boolean hasEndItemAction = FORMAT_ACTION.contentEquals(desiredFormat);
         // If there is a row action and the first end item is a default toggle, show the divider.
         mDivider.setVisibility(hasRowAction && firstItemIsADefaultToggle
                 ? View.VISIBLE : View.GONE);
@@ -301,17 +291,15 @@
             if (itemCount > 0 && hasEndItemAction) {
                 setViewClickable(mContent, true);
             } else {
-                setViewClickable(this, true);
+                setViewClickable(mRootView, true);
             }
-        } else {
+        } else if (mRowContent.endItemsContainAction() && itemCount == 1) {
             // If the only end item is an action, make the whole row clickable.
-            if (mRowContent.endItemsContainAction() && itemCount == 1) {
-                SliceItem unwrappedActionItem = endItems.get(0).getSlice().getItems().get(0);
-                if (!SUBTYPE_TOGGLE.equals(unwrappedActionItem.getSubType())) {
-                    mRowAction = new SliceActionImpl(endItems.get(0));
-                }
-                setViewClickable(this, true);
+            SliceItem unwrappedActionItem = endItems.get(0).getSlice().getItems().get(0);
+            if (!SUBTYPE_TOGGLE.equals(unwrappedActionItem.getSubType())) {
+                mRowAction = new SliceActionImpl(endItems.get(0));
             }
+            setViewClickable(mRootView, true);
         }
     }
 
@@ -358,19 +346,28 @@
             progressBar.setProgress(progress.getInt());
         }
         progressBar.setVisibility(View.VISIBLE);
+        if (mTintColor != -1) {
+            Drawable drawable = DrawableCompat.wrap(progressBar.getProgressDrawable());
+            DrawableCompat.setTint(drawable, mTintColor);
+            mProgressBar.setProgressDrawable(drawable);
+        }
         if (isSeekBar) {
             SliceItem thumb = SliceQuery.find(range, FORMAT_IMAGE);
             if (thumb != null) {
                 mSeekBar.setThumb(thumb.getIcon().loadDrawable(getContext()));
             }
+            if (mTintColor != -1) {
+                Drawable drawable = DrawableCompat.wrap(mSeekBar.getThumb());
+                DrawableCompat.setTint(drawable, mTintColor);
+                mSeekBar.setThumb(drawable);
+            }
             mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                 @Override
                 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                     try {
-                        PendingIntent pi = range.getAction();
-                        Intent i = new Intent().putExtra(EXTRA_RANGE_VALUE, progress);
                         // TODO: sending this PendingIntent should be rate limited.
-                        pi.send(getContext(), 0, i, null, null);
+                        range.fireAction(getContext(),
+                                new Intent().putExtra(EXTRA_RANGE_VALUE, progress));
                     } catch (CanceledException e) { }
                 }
 
@@ -417,13 +414,12 @@
             @Override
             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                 try {
-                    PendingIntent pi = actionContent.getAction();
                     Intent i = new Intent().putExtra(EXTRA_TOGGLE_STATE, isChecked);
-                    pi.send(getContext(), 0, i, null, null);
+                    actionContent.getActionItem().fireAction(getContext(), i);
                     if (mObserver != null) {
                         final EventInfo info = new EventInfo(getMode(),
                                 EventInfo.ACTION_TYPE_TOGGLE,
-                                EventInfo.ROW_TYPE_LIST, mRowIndex);
+                                EventInfo.ROW_TYPE_TOGGLE, mRowIndex);
                         info.state = isChecked ? EventInfo.STATE_ON : EventInfo.STATE_OFF;
                         mObserver.onSliceAction(info, actionContent.getSliceItem());
                     }
@@ -504,7 +500,7 @@
                 @Override
                 public void onClick(View v) {
                     try {
-                        finalAction.getAction().send();
+                        finalAction.getActionItem().fireAction(null, null);
                         if (mObserver != null) {
                             mObserver.onSliceAction(info, finalAction.getSliceItem());
                         }
@@ -531,7 +527,7 @@
                                 EventInfo.ROW_TYPE_LIST, mRowIndex);
                         mObserver.onSliceAction(info, mRowContent.getSlice());
                     }
-                    mRowContent.getSlice().getAction().send();
+                    mRowContent.getSlice().fireAction(null, null);
                 } catch (CanceledException e) {
                     Log.w(TAG, "PendingIntent for slice cannot be sent", e);
                 }
@@ -546,10 +542,10 @@
 
     @Override
     public void onClick(View view) {
-        if (mRowAction != null && mRowAction.getAction() != null && !mRowAction.isToggle()) {
+        if (mRowAction != null && mRowAction.getActionItem() != null && !mRowAction.isToggle()) {
             // Check for a row action
             try {
-                mRowAction.getAction().send();
+                mRowAction.getActionItem().fireAction(null, null);
                 if (mObserver != null) {
                     EventInfo info = new EventInfo(getMode(), EventInfo.ACTION_TYPE_CONTENT,
                             EventInfo.ROW_TYPE_LIST, mRowIndex);
@@ -566,14 +562,15 @@
 
     private void setViewClickable(View layout, boolean isClickable) {
         layout.setOnClickListener(isClickable ? this : null);
-        layout.setBackground(isClickable ? SliceViewUtil.getDrawable(getContext(),
-                android.R.attr.selectableItemBackground) : null);
+        layout.setBackground(isClickable
+                ? SliceViewUtil.getDrawable(getContext(), android.R.attr.selectableItemBackground)
+                : null);
         layout.setClickable(isClickable);
     }
 
     @Override
     public void resetView() {
-        setViewClickable(this, false);
+        setViewClickable(mRootView, false);
         setViewClickable(mContent, false);
         mStartContainer.removeAllViews();
         mEndContainer.removeAllViews();
diff --git a/slices/view/src/main/java/androidx/slice/widget/ShortcutView.java b/slices/view/src/main/java/androidx/slice/widget/ShortcutView.java
index b068c26..d200512 100644
--- a/slices/view/src/main/java/androidx/slice/widget/ShortcutView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/ShortcutView.java
@@ -114,7 +114,7 @@
         if (!callOnClick()) {
             try {
                 if (mActionItem != null) {
-                    mActionItem.getAction().send();
+                    mActionItem.fireAction(null, null);
                 } else {
                     Intent intent = new Intent(Intent.ACTION_VIEW).setData(mUri);
                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceLiveData.java b/slices/view/src/main/java/androidx/slice/widget/SliceLiveData.java
index aa73ad5..8639893 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceLiveData.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceLiveData.java
@@ -63,8 +63,7 @@
 
     /**
      * Produces an {@link LiveData} that tracks a Slice for a given Uri. To use
-     * this method your app must have the permission to the slice Uri or hold
-     * {@link android.Manifest.permission#BIND_SLICE}).
+     * this method your app must have the permission to the slice Uri.
      */
     public static LiveData<Slice> fromUri(Context context, Uri uri) {
         return new SliceLiveDataImpl(context.getApplicationContext(), uri);
@@ -72,8 +71,7 @@
 
     /**
      * Produces an {@link LiveData} that tracks a Slice for a given Intent. To use
-     * this method your app must have the permission to the slice Uri or hold
-     * {@link android.Manifest.permission#BIND_SLICE}).
+     * this method your app must have the permission to the slice Uri.
      */
     public static LiveData<Slice> fromIntent(@NonNull Context context, @NonNull Intent intent) {
         return new SliceLiveDataImpl(context.getApplicationContext(), intent);
@@ -134,4 +132,7 @@
             }
         };
     }
+
+    private SliceLiveData() {
+    }
 }
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceView.java b/slices/view/src/main/java/androidx/slice/widget/SliceView.java
index 272bbdc..c9acef4 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceView.java
@@ -16,30 +16,38 @@
 
 package androidx.slice.widget;
 
-import static android.app.slice.Slice.HINT_HORIZONTAL;
 import static android.app.slice.Slice.SUBTYPE_COLOR;
 import static android.app.slice.SliceItem.FORMAT_INT;
 
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.ColorDrawable;
+import android.os.Handler;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.lifecycle.Observer;
 import androidx.slice.Slice;
 import androidx.slice.SliceItem;
 import androidx.slice.SliceMetadata;
+import androidx.slice.core.SliceActionImpl;
 import androidx.slice.core.SliceHints;
 import androidx.slice.core.SliceQuery;
 import androidx.slice.view.R;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
@@ -73,7 +81,7 @@
  * </pre>
  * @see SliceLiveData
  */
-public class SliceView extends ViewGroup implements Observer<Slice> {
+public class SliceView extends ViewGroup implements Observer<Slice>, View.OnClickListener {
 
     private static final String TAG = "SliceView";
 
@@ -98,6 +106,7 @@
     @IntDef({
             MODE_SMALL, MODE_LARGE, MODE_SHORTCUT
     })
+    @Retention(RetentionPolicy.SOURCE)
     public @interface SliceMode {}
 
     /**
@@ -117,23 +126,33 @@
 
     private int mMode = MODE_LARGE;
     private Slice mCurrentSlice;
+    private ListContent mListContent;
     private SliceChildView mCurrentView;
     private List<SliceItem> mActions;
-    private final ActionRow mActionRow;
+    private ActionRow mActionRow;
 
     private boolean mShowActions = false;
     private boolean mIsScrollable = true;
     private boolean mShowLastUpdated = true;
 
-    private final int mShortcutSize;
-    private final int mMinLargeHeight;
-    private final int mMaxLargeHeight;
-    private final int mActionRowHeight;
+    private int mShortcutSize;
+    private int mMinLargeHeight;
+    private int mMaxLargeHeight;
+    private int mActionRowHeight;
 
     private AttributeSet mAttrs;
     private int mThemeTintColor = -1;
 
     private OnSliceActionListener mSliceObserver;
+    private int mTouchSlopSquared;
+    private View.OnLongClickListener mLongClickListener;
+    private View.OnClickListener mOnClickListener;
+    private int mDownX;
+    private int mDownY;
+    private boolean mPressing;
+    private boolean mInLongpress;
+    private Handler mHandler;
+    int[] mClickInfo;
 
     public SliceView(Context context) {
         this(context, null);
@@ -144,11 +163,17 @@
     }
 
     public SliceView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, R.style.Widget_SliceView);
+        super(context, attrs, defStyleAttr);
+        init(context, attrs, defStyleAttr, R.style.Widget_SliceView);
     }
 
+    @RequiresApi(21)
     public SliceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        init(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         mAttrs = attrs;
         TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SliceView,
                 defStyleAttr, defStyleRes);
@@ -157,19 +182,126 @@
         } finally {
             a.recycle();
         }
-        // TODO: action row background should support light / dark / maybe presenter customization
-        mActionRow = new ActionRow(getContext(), true);
-        mActionRow.setBackground(new ColorDrawable(0xffeeeeee));
-        mCurrentView = new LargeTemplateView(getContext());
-        mCurrentView.setMode(getMode());
-        addView(mCurrentView.getView(), getChildLp(mCurrentView.getView()));
-        addView(mActionRow, getChildLp(mActionRow));
         mShortcutSize = getContext().getResources()
                 .getDimensionPixelSize(R.dimen.abc_slice_shortcut_size);
         mMinLargeHeight = getResources().getDimensionPixelSize(R.dimen.abc_slice_large_height);
         mMaxLargeHeight = getResources().getDimensionPixelSize(R.dimen.abc_slice_max_large_height);
         mActionRowHeight = getResources().getDimensionPixelSize(
                 R.dimen.abc_slice_action_row_height);
+
+        mCurrentView = new LargeTemplateView(getContext());
+        mCurrentView.setMode(getMode());
+        addView(mCurrentView, getChildLp(mCurrentView));
+
+        // TODO: action row background should support light / dark / maybe presenter customization
+        mActionRow = new ActionRow(getContext(), true);
+        mActionRow.setBackground(new ColorDrawable(0xffeeeeee));
+        addView(mActionRow, getChildLp(mActionRow));
+
+        final int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+        mTouchSlopSquared = slop * slop;
+        mHandler = new Handler();
+
+        super.setOnClickListener(this);
+    }
+
+    /**
+     * Indicates whether this view reacts to click events or not.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    public boolean isSliceViewClickable() {
+        return mOnClickListener != null
+                || (mListContent != null && mListContent.getPrimaryAction() != null);
+    }
+
+    /**
+     * Sets the event info for logging a click.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    public void setClickInfo(int[] info) {
+        mClickInfo = info;
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (mListContent != null && mListContent.getPrimaryAction() != null) {
+            try {
+                SliceActionImpl sa = new SliceActionImpl(mListContent.getPrimaryAction());
+                sa.getAction().send();
+                if (mSliceObserver != null && mClickInfo != null && mClickInfo.length > 1) {
+                    EventInfo eventInfo = new EventInfo(getMode(),
+                            EventInfo.ACTION_TYPE_CONTENT, mClickInfo[0], mClickInfo[1]);
+                    mSliceObserver.onSliceAction(eventInfo, mListContent.getPrimaryAction());
+                }
+            } catch (PendingIntent.CanceledException e) {
+                e.printStackTrace();
+            }
+        } else if (mOnClickListener != null) {
+            mOnClickListener.onClick(this);
+        }
+    }
+
+    @Override
+    public void setOnClickListener(View.OnClickListener listener) {
+        mOnClickListener = listener;
+    }
+
+    @Override
+    public void setOnLongClickListener(View.OnLongClickListener listener) {
+        super.setOnLongClickListener(listener);
+        mLongClickListener = listener;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        boolean ret = super.onInterceptTouchEvent(ev);
+        if (mLongClickListener != null) {
+            return handleTouchForLongpress(ev);
+        }
+        return ret;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        boolean ret = super.onTouchEvent(ev);
+        if (mLongClickListener != null) {
+            return handleTouchForLongpress(ev);
+        }
+        return ret;
+    }
+
+    private boolean handleTouchForLongpress(MotionEvent ev) {
+        int action = ev.getActionMasked();
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mHandler.removeCallbacks(mLongpressCheck);
+                mDownX = (int) ev.getRawX();
+                mDownY = (int) ev.getRawY();
+                mPressing = true;
+                mInLongpress = false;
+                mHandler.postDelayed(mLongpressCheck, ViewConfiguration.getLongPressTimeout());
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final int deltaX = (int) ev.getRawX() - mDownX;
+                final int deltaY = (int) ev.getRawY() - mDownY;
+                int distance = (deltaX * deltaX) + (deltaY * deltaY);
+                if (distance > mTouchSlopSquared) {
+                    mPressing = false;
+                    mHandler.removeCallbacks(mLongpressCheck);
+                }
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                mPressing = false;
+                mInLongpress = false;
+                mHandler.removeCallbacks(mLongpressCheck);
+                break;
+        }
+        return mInLongpress;
     }
 
     private int getHeightForMode() {
@@ -348,18 +480,6 @@
     }
 
     /**
-     * Sets whether this view should display when the slice was last updated.
-     *
-     * @param showLastUpdated whether the view should display when the slice was last updated.
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public void setShowLastUpdated(boolean showLastUpdated) {
-        mShowLastUpdated = showLastUpdated;
-        mCurrentView.setShowLastUpdated(showLastUpdated);
-    }
-
-    /**
      * @hide
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -401,53 +521,37 @@
         return mShowActions;
     }
 
-    private SliceChildView createView(int mode, boolean isGrid) {
-        switch (mode) {
-            case MODE_SHORTCUT:
-                return new ShortcutView(getContext());
-            case MODE_SMALL:
-                return isGrid ? new GridRowView(getContext()) : new RowView(getContext());
-        }
-        return new LargeTemplateView(getContext());
-    }
-
     private void reinflate() {
         if (mCurrentSlice == null) {
             mCurrentView.resetView();
             return;
         }
-        ListContent lc = new ListContent(getContext(), mCurrentSlice);
-        if (!lc.isValid()) {
+        mListContent = new ListContent(getContext(), mCurrentSlice);
+        if (!mListContent.isValid()) {
             mCurrentView.resetView();
-            mCurrentView.setVisibility(View.GONE);
             return;
         }
+
         // TODO: Smarter mapping here from one state to the next.
         int mode = getMode();
-        boolean reuseView = mode == mCurrentView.getMode();
-        SliceItem header = lc.getHeaderItem();
-        boolean isSmallGrid = header != null && SliceQuery.hasHints(header, HINT_HORIZONTAL);
-        if (reuseView && mode == MODE_SMALL) {
-            reuseView = (mCurrentView instanceof GridRowView) == isSmallGrid;
-        }
-        if (!reuseView) {
+        boolean isCurrentViewShortcut = mCurrentView instanceof ShortcutView;
+        if (mode == MODE_SHORTCUT && !isCurrentViewShortcut) {
             removeAllViews();
-            mCurrentView = createView(mode, isSmallGrid);
-            if (mSliceObserver != null) {
-                mCurrentView.setSliceActionListener(mSliceObserver);
-            }
-            addView(mCurrentView.getView(), getChildLp(mCurrentView.getView()));
-            addView(mActionRow, getChildLp(mActionRow));
-            mCurrentView.setMode(mode);
+            mCurrentView = new ShortcutView(getContext());
+            addView(mCurrentView, getChildLp(mCurrentView));
+        } else if (mode != MODE_SHORTCUT && isCurrentViewShortcut) {
+            removeAllViews();
+            mCurrentView = new LargeTemplateView(getContext());
+            addView(mCurrentView, getChildLp(mCurrentView));
         }
-        // Scrolling
-        if (mode == MODE_LARGE && (mCurrentView instanceof LargeTemplateView)) {
+        mCurrentView.setMode(mode);
+
+        mCurrentView.setSliceActionListener(mSliceObserver);
+        if (mCurrentView instanceof LargeTemplateView) {
             ((LargeTemplateView) mCurrentView).setScrollable(mIsScrollable);
         }
-        // Styles
         mCurrentView.setStyle(mAttrs);
         mCurrentView.setTint(getTintColor());
-        mCurrentView.setVisibility(lc.isValid() ? View.VISIBLE : View.GONE);
 
         // Check if the slice content is expired and show when it was last updated
         SliceMetadata sliceMetadata = SliceMetadata.from(getContext(), mCurrentSlice);
@@ -455,7 +559,7 @@
         long expiry = sliceMetadata.getExpiry();
         long now = System.currentTimeMillis();
         mCurrentView.setLastUpdated(lastUpdated);
-        boolean expired = expiry != SliceHints.INFINITY && now > expiry;
+        boolean expired = expiry != 0 && expiry != SliceHints.INFINITY && now > expiry;
         mCurrentView.setShowLastUpdated(mShowLastUpdated && expired);
 
         // Set the slice
@@ -524,4 +628,15 @@
                 return "unknown mode: " + mode;
         }
     }
+
+    Runnable mLongpressCheck = new Runnable() {
+        @Override
+        public void run() {
+            if (mPressing && mLongClickListener != null) {
+                mInLongpress = true;
+                mLongClickListener.onLongClick(SliceView.this);
+                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+            }
+        }
+    };
 }
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceViewUtil.java b/slices/view/src/main/java/androidx/slice/widget/SliceViewUtil.java
index c8e3d74..78ac154 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceViewUtil.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceViewUtil.java
@@ -38,7 +38,6 @@
 import androidx.annotation.AttrRes;
 import androidx.annotation.ColorInt;
 import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.content.ContextCompat;
 import androidx.core.graphics.drawable.IconCompat;
@@ -139,7 +138,6 @@
 
     /**
      */
-    @RequiresApi(23)
     public static IconCompat createIconFromDrawable(Drawable d) {
         if (d instanceof BitmapDrawable) {
             return IconCompat.createWithBitmap(((BitmapDrawable) d).getBitmap());
@@ -200,4 +198,7 @@
         return DateUtils.getRelativeTimeSpanString(time, Calendar.getInstance().getTimeInMillis(),
                 DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
     }
+
+    private SliceViewUtil() {
+    }
 }
diff --git a/slices/view/src/main/res/layout/abc_slice_small_template.xml b/slices/view/src/main/res/layout/abc_slice_small_template.xml
index 1327066..867de1f 100644
--- a/slices/view/src/main/res/layout/abc_slice_small_template.xml
+++ b/slices/view/src/main/res/layout/abc_slice_small_template.xml
@@ -16,11 +16,11 @@
   -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/row_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:gravity="center_vertical"
     android:layout_gravity="center_vertical"
-    android:background="?android:attr/activatedBackgroundIndicator"
     android:orientation="horizontal"
     android:clipToPadding="false">
 
@@ -91,7 +91,8 @@
         android:background="?android:attr/listDivider"
         android:visibility="gone"/>
 
-    <LinearLayout android:id="@android:id/widget_frame"
+    <LinearLayout
+        android:id="@android:id/widget_frame"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:paddingStart="8dp"
diff --git a/slices/view/src/main/res/values-af/strings.xml b/slices/view/src/main/res/values-af/strings.xml
index 812f811..cfe8138 100644
--- a/slices/view/src/main/res/values-af/strings.xml
+++ b/slices/view/src/main/res/values-af/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Meer"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Wys meer"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Opgedateer om <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min. gelede</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min. gelede</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> jaar gelede</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> jaar gelede</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> dae gelede</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> dag gelede</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Kon nie koppel nie"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-am/strings.xml b/slices/view/src/main/res/values-am/strings.xml
index 86c2c4e..da542e1 100644
--- a/slices/view/src/main/res/values-am/strings.xml
+++ b/slices/view/src/main/res/values-am/strings.xml
@@ -20,4 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"ተጨማሪ"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"ተጨማሪ አሳይ"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"የተዘመነው <xliff:g id="TIME">%1$s</xliff:g> ላይ"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ደቂቃ በፊት</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ደቂቃ በፊት</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ዓመት በፊት</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ዓመት በፊት</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ቀናት በፊት</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ቀናት በፊት</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"መገናኘት አልተቻለም"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ar/strings.xml b/slices/view/src/main/res/values-ar/strings.xml
index 3acde5d..1e9ca94 100644
--- a/slices/view/src/main/res/values-ar/strings.xml
+++ b/slices/view/src/main/res/values-ar/strings.xml
@@ -20,4 +20,30 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"بالإضافة إلى <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"المزيد"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"عرض المزيد"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"وقت التحديث الأخير: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="zero">قبل <xliff:g id="ID_2">%d</xliff:g> دقيقة</item>
+      <item quantity="two">قبل دقيقتين (<xliff:g id="ID_2">%d</xliff:g>)</item>
+      <item quantity="few">قبل <xliff:g id="ID_2">%d</xliff:g> دقائق</item>
+      <item quantity="many">قبل <xliff:g id="ID_2">%d</xliff:g> دقيقة</item>
+      <item quantity="other">قبل <xliff:g id="ID_2">%d</xliff:g> دقيقة</item>
+      <item quantity="one">قبل <xliff:g id="ID_1">%d</xliff:g> دقيقة</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="zero">قبل <xliff:g id="ID_2">%d</xliff:g> سنة</item>
+      <item quantity="two">قبل سنتين (<xliff:g id="ID_2">%d</xliff:g>)</item>
+      <item quantity="few">قبل <xliff:g id="ID_2">%d</xliff:g> سنوات</item>
+      <item quantity="many">قبل <xliff:g id="ID_2">%d</xliff:g> سنة</item>
+      <item quantity="other">قبل <xliff:g id="ID_2">%d</xliff:g> سنة</item>
+      <item quantity="one">قبل <xliff:g id="ID_1">%d</xliff:g> سنة</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="zero">قبل <xliff:g id="ID_2">%d</xliff:g> يوم</item>
+      <item quantity="two">قبل يومين (<xliff:g id="ID_2">%d</xliff:g>)</item>
+      <item quantity="few">قبل <xliff:g id="ID_2">%d</xliff:g> أيام</item>
+      <item quantity="many">قبل <xliff:g id="ID_2">%d</xliff:g> يومًا</item>
+      <item quantity="other">قبل <xliff:g id="ID_2">%d</xliff:g> يوم</item>
+      <item quantity="one">قبل <xliff:g id="ID_1">%d</xliff:g> يوم</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"تعذّر الاتصال."</string>
 </resources>
diff --git a/slices/view/src/main/res/values-as/strings.xml b/slices/view/src/main/res/values-as/strings.xml
new file mode 100644
index 0000000..77f9af6
--- /dev/null
+++ b/slices/view/src/main/res/values-as/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+    <string name="abc_slice_more" msgid="1983560225998630901">"অধিক"</string>
+    <string name="abc_slice_show_more" msgid="1567717014004692768">"অধিক দেখুৱাওক"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> আপডেট কৰা হৈছিল"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"সংযোগ কৰিব পৰা নগ\'ল"</string>
+</resources>
diff --git a/slices/view/src/main/res/values-az/strings.xml b/slices/view/src/main/res/values-az/strings.xml
index 8ff3908..837f08c 100644
--- a/slices/view/src/main/res/values-az/strings.xml
+++ b/slices/view/src/main/res/values-az/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Digər"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Digərinə baxın"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> tarixində yenilənib"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> dəq əvvəl</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> dəq əvvəl</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> il əvvəl</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> il əvvəl</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> gün əvvəl</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> gün əvvəl</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Qoşulmaq mümkün olmadı"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-b+sr+Latn/strings.xml b/slices/view/src/main/res/values-b+sr+Latn/strings.xml
index 22e42e7..8deb17c 100644
--- a/slices/view/src/main/res/values-b+sr+Latn/strings.xml
+++ b/slices/view/src/main/res/values-b+sr+Latn/strings.xml
@@ -20,4 +20,21 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"i još <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Još"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Prikaži više"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Ažurirano <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one">pre <xliff:g id="ID_2">%d</xliff:g> min</item>
+      <item quantity="few">pre <xliff:g id="ID_2">%d</xliff:g> min</item>
+      <item quantity="other">pre <xliff:g id="ID_2">%d</xliff:g> min</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one">pre <xliff:g id="ID_2">%d</xliff:g> god</item>
+      <item quantity="few">pre <xliff:g id="ID_2">%d</xliff:g> god</item>
+      <item quantity="other">pre <xliff:g id="ID_2">%d</xliff:g> god</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one">pre <xliff:g id="ID_2">%d</xliff:g> dan</item>
+      <item quantity="few">pre <xliff:g id="ID_2">%d</xliff:g> dana</item>
+      <item quantity="other">pre <xliff:g id="ID_2">%d</xliff:g> dana</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Povezivanje nije uspelo"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-be/strings.xml b/slices/view/src/main/res/values-be/strings.xml
index 6ea856b..f1ae045 100644
--- a/slices/view/src/main/res/values-be/strings.xml
+++ b/slices/view/src/main/res/values-be/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"Яшчэ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Яшчэ"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Яшчэ"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Абноўлена <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Не атрымалася падключыцца"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-bg/strings.xml b/slices/view/src/main/res/values-bg/strings.xml
index 882def9..b019a46 100644
--- a/slices/view/src/main/res/values-bg/strings.xml
+++ b/slices/view/src/main/res/values-bg/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Още"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Показване на още"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Актуализирано <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other">Преди <xliff:g id="ID_2">%d</xliff:g> мин</item>
+      <item quantity="one">Преди <xliff:g id="ID_1">%d</xliff:g> мин</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other">Преди <xliff:g id="ID_2">%d</xliff:g> год</item>
+      <item quantity="one">Преди <xliff:g id="ID_1">%d</xliff:g> год</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other">Преди <xliff:g id="ID_2">%d</xliff:g> дни</item>
+      <item quantity="one">Преди <xliff:g id="ID_1">%d</xliff:g> ден</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Не можа да се установи връзка"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-bn/strings.xml b/slices/view/src/main/res/values-bn/strings.xml
index eb83395..3b8acdf 100644
--- a/slices/view/src/main/res/values-bn/strings.xml
+++ b/slices/view/src/main/res/values-bn/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>টি"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"আরও"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"আরও দেখুন"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> আপডেট করা হয়েছে"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"কানেক্ট করা যায়নি"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-bs/strings.xml b/slices/view/src/main/res/values-bs/strings.xml
index 693f885..cb84c18 100644
--- a/slices/view/src/main/res/values-bs/strings.xml
+++ b/slices/view/src/main/res/values-bs/strings.xml
@@ -20,8 +20,21 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Više"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Prikaži više"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Ažurirano <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one">Prije <xliff:g id="ID_2">%d</xliff:g> min.</item>
+      <item quantity="few">Prije <xliff:g id="ID_2">%d</xliff:g> min.</item>
+      <item quantity="other">Prije <xliff:g id="ID_2">%d</xliff:g> min.</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one">Prije <xliff:g id="ID_2">%d</xliff:g> god.</item>
+      <item quantity="few">Prije <xliff:g id="ID_2">%d</xliff:g> god.</item>
+      <item quantity="other">Prije <xliff:g id="ID_2">%d</xliff:g> god.</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one">Prije <xliff:g id="ID_2">%d</xliff:g> dan</item>
+      <item quantity="few">Prije <xliff:g id="ID_2">%d</xliff:g> dana</item>
+      <item quantity="other">Prije <xliff:g id="ID_2">%d</xliff:g> dana</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Povezivanje nije uspjelo"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ca/strings.xml b/slices/view/src/main/res/values-ca/strings.xml
index 4ac9c6d..52e1988 100644
--- a/slices/view/src/main/res/values-ca/strings.xml
+++ b/slices/view/src/main/res/values-ca/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> més"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Més"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Mostra\'n més"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"S\'ha actualitzat <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"No s\'ha pogut connectar"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-cs/strings.xml b/slices/view/src/main/res/values-cs/strings.xml
index 278810c..c4c1fce 100644
--- a/slices/view/src/main/res/values-cs/strings.xml
+++ b/slices/view/src/main/res/values-cs/strings.xml
@@ -20,4 +20,24 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"a ještě <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Více"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Zobrazit více"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Aktualizováno <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="few">před <xliff:g id="ID_2">%d</xliff:g> min</item>
+      <item quantity="many">před <xliff:g id="ID_2">%d</xliff:g> min</item>
+      <item quantity="other">před <xliff:g id="ID_2">%d</xliff:g> min</item>
+      <item quantity="one">před <xliff:g id="ID_1">%d</xliff:g> min</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="few">před <xliff:g id="ID_2">%d</xliff:g> lety</item>
+      <item quantity="many">před <xliff:g id="ID_2">%d</xliff:g> roku</item>
+      <item quantity="other">před <xliff:g id="ID_2">%d</xliff:g> lety</item>
+      <item quantity="one">před <xliff:g id="ID_1">%d</xliff:g> rokem</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="few">před <xliff:g id="ID_2">%d</xliff:g> dny</item>
+      <item quantity="many">před <xliff:g id="ID_2">%d</xliff:g> dne</item>
+      <item quantity="other">před <xliff:g id="ID_2">%d</xliff:g> dny</item>
+      <item quantity="one">před <xliff:g id="ID_1">%d</xliff:g> dnem</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Nelze se připojit"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-da/strings.xml b/slices/view/src/main/res/values-da/strings.xml
index 806a5fe..33d9c9d 100644
--- a/slices/view/src/main/res/values-da/strings.xml
+++ b/slices/view/src/main/res/values-da/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> mere"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Mere"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Se mere"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Opdateret <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Der kunne ikke oprettes forbindelse"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-de/strings.xml b/slices/view/src/main/res/values-de/strings.xml
index f6a1e32..96da7eb 100644
--- a/slices/view/src/main/res/values-de/strings.xml
+++ b/slices/view/src/main/res/values-de/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Mehr"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Mehr anzeigen"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Aktualisiert: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Verbindung nicht möglich"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-el/strings.xml b/slices/view/src/main/res/values-el/strings.xml
index 19963f2..31092af 100644
--- a/slices/view/src/main/res/values-el/strings.xml
+++ b/slices/view/src/main/res/values-el/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Περισσότ."</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Εμφάνιση περισσότερων"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Ενημερώθηκε <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> λεπ. πριν</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> λεπ. πριν</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> χρ. πριν</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> χρ. πριν</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ημ. πριν</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> ημ. πριν</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Αδυναμία σύνδεσης"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-en-rAU/strings.xml b/slices/view/src/main/res/values-en-rAU/strings.xml
index f222ba5..8fa5b45 100644
--- a/slices/view/src/main/res/values-en-rAU/strings.xml
+++ b/slices/view/src/main/res/values-en-rAU/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"More"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Show more"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Updated <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min ago</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min ago</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> yr ago</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> yr ago</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> days ago</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> day ago</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Couldn\'t connect"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-en-rCA/strings.xml b/slices/view/src/main/res/values-en-rCA/strings.xml
index f222ba5..8fa5b45 100644
--- a/slices/view/src/main/res/values-en-rCA/strings.xml
+++ b/slices/view/src/main/res/values-en-rCA/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"More"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Show more"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Updated <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min ago</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min ago</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> yr ago</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> yr ago</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> days ago</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> day ago</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Couldn\'t connect"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-en-rGB/strings.xml b/slices/view/src/main/res/values-en-rGB/strings.xml
index f222ba5..8fa5b45 100644
--- a/slices/view/src/main/res/values-en-rGB/strings.xml
+++ b/slices/view/src/main/res/values-en-rGB/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"More"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Show more"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Updated <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min ago</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min ago</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> yr ago</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> yr ago</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> days ago</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> day ago</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Couldn\'t connect"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-en-rIN/strings.xml b/slices/view/src/main/res/values-en-rIN/strings.xml
index f222ba5..8fa5b45 100644
--- a/slices/view/src/main/res/values-en-rIN/strings.xml
+++ b/slices/view/src/main/res/values-en-rIN/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"More"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Show more"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Updated <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min ago</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min ago</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> yr ago</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> yr ago</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> days ago</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> day ago</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Couldn\'t connect"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-en-rXC/strings.xml b/slices/view/src/main/res/values-en-rXC/strings.xml
index 243dccb..b24c1e6 100644
--- a/slices/view/src/main/res/values-en-rXC/strings.xml
+++ b/slices/view/src/main/res/values-en-rXC/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‎‏‏‏‏‎‎‏‎‎‏‎‏‏‎‏‏‏‏‎‏‎‏‏‎‏‏‎‎+ ‎‏‎‎‏‏‎<xliff:g id="NUMBER">%1$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎More‎‏‎‎‏‎"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎Show more‎‏‎‎‏‎"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‎‎‎Updated ‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="ID_2">%d</xliff:g>‎‏‎‎‏‏‏‎ min ago‎‏‎‎‏‎</item>
+      <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="ID_1">%d</xliff:g>‎‏‎‎‏‏‏‎ min ago‎‏‎‎‏‎</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="ID_2">%d</xliff:g>‎‏‎‎‏‏‏‎ yr ago‎‏‎‎‏‎</item>
+      <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="ID_1">%d</xliff:g>‎‏‎‎‏‏‏‎ yr ago‎‏‎‎‏‎</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="ID_2">%d</xliff:g>‎‏‎‎‏‏‏‎ days ago‎‏‎‎‏‎</item>
+      <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="ID_1">%d</xliff:g>‎‏‎‎‏‏‏‎ day ago‎‏‎‎‏‎</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‎‏‏‎‎‏‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎Couldn\'t connect‎‏‎‎‏‎"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-es-rUS/strings.xml b/slices/view/src/main/res/values-es-rUS/strings.xml
index 7fc5b56..124c40c 100644
--- a/slices/view/src/main/res/values-es-rUS/strings.xml
+++ b/slices/view/src/main/res/values-es-rUS/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> más"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Más"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Mostrar más"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Última actualización: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"No se pudo establecer conexión"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-es/strings.xml b/slices/view/src/main/res/values-es/strings.xml
index b81e23c..57685ba 100644
--- a/slices/view/src/main/res/values-es/strings.xml
+++ b/slices/view/src/main/res/values-es/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> más"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Más"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Ver más"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Última actualización: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"No se ha podido establecer la conexión"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-et/strings.xml b/slices/view/src/main/res/values-et/strings.xml
index 80bec54..4bd8a92 100644
--- a/slices/view/src/main/res/values-et/strings.xml
+++ b/slices/view/src/main/res/values-et/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"ja veel <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Rohkem"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Kuva rohkem"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Värskendatud kell <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Ühendamine ebaõnnestus"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-eu/strings.xml b/slices/view/src/main/res/values-eu/strings.xml
index 295f184..e689ada 100644
--- a/slices/view/src/main/res/values-eu/strings.xml
+++ b/slices/view/src/main/res/values-eu/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"Beste <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Gehiago"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Erakutsi gehiago"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Azken eguneratzea: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Ezin izan da konektatu"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-fa/strings.xml b/slices/view/src/main/res/values-fa/strings.xml
index 3ef8f85..cb94e5d 100644
--- a/slices/view/src/main/res/values-fa/strings.xml
+++ b/slices/view/src/main/res/values-fa/strings.xml
@@ -20,4 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"‎+ <xliff:g id="NUMBER">%1$d</xliff:g>‎"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"بیشتر"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"نمایش موارد بیشتر"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"زمان به‌روزرسانی <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> دقیقه قبل</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> دقیقه قبل</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> سال قبل</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> سال قبل</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> روز قبل</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> روز قبل</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"متصل نشد"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-fi/strings.xml b/slices/view/src/main/res/values-fi/strings.xml
index 0e37b9d..bc37611 100644
--- a/slices/view/src/main/res/values-fi/strings.xml
+++ b/slices/view/src/main/res/values-fi/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Lisää"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Näytä lisää"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Päivitetty <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Ei yhteyttä"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-fr-rCA/strings.xml b/slices/view/src/main/res/values-fr-rCA/strings.xml
index cc10117..1563136 100644
--- a/slices/view/src/main/res/values-fr-rCA/strings.xml
+++ b/slices/view/src/main/res/values-fr-rCA/strings.xml
@@ -20,4 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Plus"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Plus"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Mise à jour : <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Impossible de se connecter"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-fr/strings.xml b/slices/view/src/main/res/values-fr/strings.xml
index b4f147b..6762544 100644
--- a/slices/view/src/main/res/values-fr/strings.xml
+++ b/slices/view/src/main/res/values-fr/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> autres"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Plus"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Afficher plus"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Dernière mise à jour : <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Impossible de se connecter"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-gl/strings.xml b/slices/view/src/main/res/values-gl/strings.xml
index f8686de..70b31bb 100644
--- a/slices/view/src/main/res/values-gl/strings.xml
+++ b/slices/view/src/main/res/values-gl/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> máis"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Máis"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Amosar máis"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Contido actualizado (<xliff:g id="TIME">%1$s</xliff:g>)"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other">Hai <xliff:g id="ID_2">%d</xliff:g> min</item>
+      <item quantity="one">Hai <xliff:g id="ID_1">%d</xliff:g> min</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other">Hai <xliff:g id="ID_2">%d</xliff:g> anos</item>
+      <item quantity="one">Hai <xliff:g id="ID_1">%d</xliff:g> ano</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other">Hai <xliff:g id="ID_2">%d</xliff:g> días</item>
+      <item quantity="one">Hai <xliff:g id="ID_1">%d</xliff:g> día</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Non se puido establecer conexión"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-gu/strings.xml b/slices/view/src/main/res/values-gu/strings.xml
index 4cf8b47..d6a7ee8 100644
--- a/slices/view/src/main/res/values-gu/strings.xml
+++ b/slices/view/src/main/res/values-gu/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"વધુ"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"વધુ બતાવો"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> અપડેટ થયું"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"કનેક્ટ કરી શકાયું નથી"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-hi/strings.xml b/slices/view/src/main/res/values-hi/strings.xml
index 3688497..380025e 100644
--- a/slices/view/src/main/res/values-hi/strings.xml
+++ b/slices/view/src/main/res/values-hi/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"ज़्यादा देखें"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"ज़्यादा देखें"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> बजे अपडेट किया गया"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"कनेक्‍ट नहीं हो पाया"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-hr/strings.xml b/slices/view/src/main/res/values-hr/strings.xml
index 5ec52b8..f44b06c 100644
--- a/slices/view/src/main/res/values-hr/strings.xml
+++ b/slices/view/src/main/res/values-hr/strings.xml
@@ -20,8 +20,21 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"još <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Više"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Prikaži više"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Ažurirano <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one">Prije <xliff:g id="ID_2">%d</xliff:g> min</item>
+      <item quantity="few">Prije <xliff:g id="ID_2">%d</xliff:g> min</item>
+      <item quantity="other">Prije <xliff:g id="ID_2">%d</xliff:g> min</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one">Prije <xliff:g id="ID_2">%d</xliff:g> godinu</item>
+      <item quantity="few">Prije <xliff:g id="ID_2">%d</xliff:g> godine</item>
+      <item quantity="other">Prije <xliff:g id="ID_2">%d</xliff:g> godina</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one">Prije <xliff:g id="ID_2">%d</xliff:g> dan</item>
+      <item quantity="few">Prije <xliff:g id="ID_2">%d</xliff:g> dana</item>
+      <item quantity="other">Prije <xliff:g id="ID_2">%d</xliff:g> dana</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Povezivanje nije moguće"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-hu/strings.xml b/slices/view/src/main/res/values-hu/strings.xml
index 957dd68..c56143b 100644
--- a/slices/view/src/main/res/values-hu/strings.xml
+++ b/slices/view/src/main/res/values-hu/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Több"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Több megjelenítése"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Frissítve: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Nem sikerült kapcsolódni"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-hy/strings.xml b/slices/view/src/main/res/values-hy/strings.xml
index 49e731b..bb6b812 100644
--- a/slices/view/src/main/res/values-hy/strings.xml
+++ b/slices/view/src/main/res/values-hy/strings.xml
@@ -20,4 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Ավելին"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Ցուցադրել ավելի շատ"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Թարմացվել է <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Չհաջողվեց միանալ"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-in/strings.xml b/slices/view/src/main/res/values-in/strings.xml
index 7d46931..5641ebb 100644
--- a/slices/view/src/main/res/values-in/strings.xml
+++ b/slices/view/src/main/res/values-in/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Lainnya"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Tampilkan lainnya"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Diupdate <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> menit lalu</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> menit lalu</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> tahun lalu</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> tahun lalu</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> hari lalu</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> hari lalu</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Tidak dapat terhubung"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-is/strings.xml b/slices/view/src/main/res/values-is/strings.xml
index 231dd20..c059c03 100644
--- a/slices/view/src/main/res/values-is/strings.xml
+++ b/slices/view/src/main/res/values-is/strings.xml
@@ -20,4 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Meira"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Sýna meira"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Uppfært <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Tenging mistókst"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-it/strings.xml b/slices/view/src/main/res/values-it/strings.xml
index 8f0a761..05410e8 100644
--- a/slices/view/src/main/res/values-it/strings.xml
+++ b/slices/view/src/main/res/values-it/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Altro"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Mostra altro"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Aggiornamento: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Impossibile collegarsi"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-iw/strings.xml b/slices/view/src/main/res/values-iw/strings.xml
index c160611..5c55fe2 100644
--- a/slices/view/src/main/res/values-iw/strings.xml
+++ b/slices/view/src/main/res/values-iw/strings.xml
@@ -20,4 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"עוד"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"הצג יותר"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"עודכן ב-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"לא ניתן היה להתחבר"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ja/strings.xml b/slices/view/src/main/res/values-ja/strings.xml
index a18751f..286949f 100644
--- a/slices/view/src/main/res/values-ja/strings.xml
+++ b/slices/view/src/main/res/values-ja/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"他 <xliff:g id="NUMBER">%1$d</xliff:g> 件"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"もっと見る"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"もっと見る"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"更新時刻: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"接続できませんでした"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ka/strings.xml b/slices/view/src/main/res/values-ka/strings.xml
index 8701f5d..542c6cc 100644
--- a/slices/view/src/main/res/values-ka/strings.xml
+++ b/slices/view/src/main/res/values-ka/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"მეტი"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"მეტის ჩვენება"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"განახლების დრო: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"დაკავშირება ვერ მოხერხდა"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-kk/strings.xml b/slices/view/src/main/res/values-kk/strings.xml
index 1867ada..231a499 100644
--- a/slices/view/src/main/res/values-kk/strings.xml
+++ b/slices/view/src/main/res/values-kk/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Тағы"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Толығырақ көрсету"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Жаңартылған уақыты: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Байланыс орнатылмады"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-km/strings.xml b/slices/view/src/main/res/values-km/strings.xml
index 3f50a99..1e78acb 100644
--- a/slices/view/src/main/res/values-km/strings.xml
+++ b/slices/view/src/main/res/values-km/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"ច្រើន​ទៀត"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"បង្ហាញ​ច្រើនទៀត"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"បាន​ធ្វើ​បច្ចុប្បន្នភាព​កាលពី <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> នាទី​មុន</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> នាទី​មុន</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ឆ្នាំ​មុន</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> ឆ្នាំ​មុន</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ថ្ងៃ​មុន</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> ថ្ងៃ​មុន</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"មិន​អាចភ្ជាប់​បានទេ"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-kn/strings.xml b/slices/view/src/main/res/values-kn/strings.xml
index e8224f3..ed7dcd6 100644
--- a/slices/view/src/main/res/values-kn/strings.xml
+++ b/slices/view/src/main/res/values-kn/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"ಇನ್ನಷ್ಟು"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"ಹೆಚ್ಚು ತೋರಿಸಿ"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> ಅನ್ನು ಅಪ್‌ಡೇಟ್‌ ಮಾಡಲಾಗಿದೆ"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ನಿಮಿಷದ ಹಿಂದೆ</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ನಿಮಿಷದ ಹಿಂದೆ</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ವರ್ಷದ ಹಿಂದೆ</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ವರ್ಷದ ಹಿಂದೆ</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ದಿನಗಳ ಹಿಂದೆ</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ದಿನಗಳ ಹಿಂದೆ</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ko/strings.xml b/slices/view/src/main/res/values-ko/strings.xml
index 8e7cb24..1ec58f0 100644
--- a/slices/view/src/main/res/values-ko/strings.xml
+++ b/slices/view/src/main/res/values-ko/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g>개 더보기"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"더보기"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"더보기"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g>에 업데이트됨"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"연결할 수 없음"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ky/strings.xml b/slices/view/src/main/res/values-ky/strings.xml
index 910ae6d..06244ab 100644
--- a/slices/view/src/main/res/values-ky/strings.xml
+++ b/slices/view/src/main/res/values-ky/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Дагы"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Дагы көрсөтүү"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> жаңыртылды"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Туташпай койду"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-lo/strings.xml b/slices/view/src/main/res/values-lo/strings.xml
index d266a43..44111ea 100644
--- a/slices/view/src/main/res/values-lo/strings.xml
+++ b/slices/view/src/main/res/values-lo/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"ເພີ່ມເຕີມ"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"ສະແດງເພີ່ມເຕີມ"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"ອັບເດດເມື່ອ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"ບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-lt/strings.xml b/slices/view/src/main/res/values-lt/strings.xml
index cc4b804..e4094b4 100644
--- a/slices/view/src/main/res/values-lt/strings.xml
+++ b/slices/view/src/main/res/values-lt/strings.xml
@@ -20,4 +20,24 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"Dar <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Daugiau"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Rodyti daugiau"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Atnaujinta <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one">Prieš <xliff:g id="ID_2">%d</xliff:g> min.</item>
+      <item quantity="few">Prieš <xliff:g id="ID_2">%d</xliff:g> min.</item>
+      <item quantity="many">Prieš <xliff:g id="ID_2">%d</xliff:g> min.</item>
+      <item quantity="other">Prieš <xliff:g id="ID_2">%d</xliff:g> min.</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one">Prieš <xliff:g id="ID_2">%d</xliff:g> m.</item>
+      <item quantity="few">Prieš <xliff:g id="ID_2">%d</xliff:g> m.</item>
+      <item quantity="many">Prieš <xliff:g id="ID_2">%d</xliff:g> m.</item>
+      <item quantity="other">Prieš <xliff:g id="ID_2">%d</xliff:g> m.</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one">Prieš <xliff:g id="ID_2">%d</xliff:g> d.</item>
+      <item quantity="few">Prieš <xliff:g id="ID_2">%d</xliff:g> d.</item>
+      <item quantity="many">Prieš <xliff:g id="ID_2">%d</xliff:g> d.</item>
+      <item quantity="other">Prieš <xliff:g id="ID_2">%d</xliff:g> d.</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Prisijungti nepavyko"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-lv/strings.xml b/slices/view/src/main/res/values-lv/strings.xml
index 042436d..45be57c 100644
--- a/slices/view/src/main/res/values-lv/strings.xml
+++ b/slices/view/src/main/res/values-lv/strings.xml
@@ -20,4 +20,21 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"Vēl <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Vēl"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Rādīt vairāk"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Atjaunināts <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="zero">Pirms <xliff:g id="ID_2">%d</xliff:g> minūtēm</item>
+      <item quantity="one">Pirms <xliff:g id="ID_2">%d</xliff:g> minūtes</item>
+      <item quantity="other">Pirms <xliff:g id="ID_2">%d</xliff:g> minūtēm</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="zero">Pirms <xliff:g id="ID_2">%d</xliff:g> gadiem</item>
+      <item quantity="one">Pirms <xliff:g id="ID_2">%d</xliff:g> gada</item>
+      <item quantity="other">Pirms <xliff:g id="ID_2">%d</xliff:g> gadiem</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="zero">Pirms <xliff:g id="ID_2">%d</xliff:g> dienām</item>
+      <item quantity="one">Pirms <xliff:g id="ID_2">%d</xliff:g> dienas</item>
+      <item quantity="other">Pirms <xliff:g id="ID_2">%d</xliff:g> dienām</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Nevarēja izveidot savienojumu"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-mk/strings.xml b/slices/view/src/main/res/values-mk/strings.xml
index b3c5da4..ff88cd7 100644
--- a/slices/view/src/main/res/values-mk/strings.xml
+++ b/slices/view/src/main/res/values-mk/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Повеќе"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Прикажи повеќе"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Ажурирано <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one">Пред <xliff:g id="ID_2">%d</xliff:g> мин.</item>
+      <item quantity="other">Пред <xliff:g id="ID_2">%d</xliff:g> мин.</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one">Пред <xliff:g id="ID_2">%d</xliff:g> год.</item>
+      <item quantity="other">Пред <xliff:g id="ID_2">%d</xliff:g> год.</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one">Пред <xliff:g id="ID_2">%d</xliff:g> ден</item>
+      <item quantity="other">Пред <xliff:g id="ID_2">%d</xliff:g> дена</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Не може да се поврзе"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ml/strings.xml b/slices/view/src/main/res/values-ml/strings.xml
index e2dc0b1..b67ae80 100644
--- a/slices/view/src/main/res/values-ml/strings.xml
+++ b/slices/view/src/main/res/values-ml/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"കൂടുതൽ"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"കൂടുതൽ കാണിക്കുക"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> അപ്‌ഡേറ്റ് ചെയ്‌തു"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> മിനിറ്റ് മുൻപ്</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> മിനിറ്റ് മുൻപ്</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> വർഷം മുൻപ്</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> വർഷം മുൻപ്</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ദിവസം മുൻപ്</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> ദിവസം മുൻപ്</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"കണക്റ്റ് ചെയ്യാനായില്ല"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-mn/strings.xml b/slices/view/src/main/res/values-mn/strings.xml
index 740f455..beaa1a3 100644
--- a/slices/view/src/main/res/values-mn/strings.xml
+++ b/slices/view/src/main/res/values-mn/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Бусад"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Дэлгэрэнгүй үзэх"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> шинэчилсэн"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Холбогдож чадсангүй"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-mr/strings.xml b/slices/view/src/main/res/values-mr/strings.xml
index ab06bea..693c821 100644
--- a/slices/view/src/main/res/values-mr/strings.xml
+++ b/slices/view/src/main/res/values-mr/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"आणखी"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"आणखी दाखवा"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> ला अपडेट केले"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"कनेक्ट करता आले नाही"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ms/strings.xml b/slices/view/src/main/res/values-ms/strings.xml
index 03bda10..eced3de 100644
--- a/slices/view/src/main/res/values-ms/strings.xml
+++ b/slices/view/src/main/res/values-ms/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Lagi"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Tunjukkan lagi"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Dikemas kini pada <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Tidak dapat menyambung"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-my/strings.xml b/slices/view/src/main/res/values-my/strings.xml
index 754d40e..4a3e3d4 100644
--- a/slices/view/src/main/res/values-my/strings.xml
+++ b/slices/view/src/main/res/values-my/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"နောက်ထပ်"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"နောက်ထပ် ပြပါ"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> က အပ်ဒိတ်လုပ်ထားသည်"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other">ပြီးခဲ့သော<xliff:g id="ID_2">%d</xliff:g>မိနစ်</item>
+      <item quantity="one">ပြီးခဲ့သော<xliff:g id="ID_1">%d</xliff:g>မိနစ်</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other">ပြီးခဲ့သော <xliff:g id="ID_2">%d</xliff:g>နှစ်</item>
+      <item quantity="one">ပြီးခဲ့သော <xliff:g id="ID_1">%d</xliff:g>နှစ်</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other">ပြီးခဲ့သော <xliff:g id="ID_2">%d</xliff:g> ရက်</item>
+      <item quantity="one">ပြီးခဲ့သော <xliff:g id="ID_1">%d</xliff:g> ရက်</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"ချိတ်ဆက်၍ မရပါ"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-nb/strings.xml b/slices/view/src/main/res/values-nb/strings.xml
index e977965..6576626 100644
--- a/slices/view/src/main/res/values-nb/strings.xml
+++ b/slices/view/src/main/res/values-nb/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Mer"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Vis mer"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Oppdatert <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Kunne ikke koble til"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ne/strings.xml b/slices/view/src/main/res/values-ne/strings.xml
index 6a95860..beb8b31 100644
--- a/slices/view/src/main/res/values-ne/strings.xml
+++ b/slices/view/src/main/res/values-ne/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"थप"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"थप देखाउनुहोस्"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"अद्यावधिक गरिएको समय: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> मिनेटअघि</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> मिनेटअघि</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> वर्षअघि</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> वर्षअघि</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> दिनअघि</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> दिनअघि</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"जडान गर्न सकिएन"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-nl/strings.xml b/slices/view/src/main/res/values-nl/strings.xml
index ed28ef2..1ba58e9 100644
--- a/slices/view/src/main/res/values-nl/strings.xml
+++ b/slices/view/src/main/res/values-nl/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Meer"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Meer weergeven"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Geüpdatet: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Kan geen verbinding maken"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-or/strings.xml b/slices/view/src/main/res/values-or/strings.xml
new file mode 100644
index 0000000..1099e80
--- /dev/null
+++ b/slices/view/src/main/res/values-or/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+    <string name="abc_slice_more" msgid="1983560225998630901">"ଅଧିକ"</string>
+    <string name="abc_slice_show_more" msgid="1567717014004692768">"ଅଧିକ ଦେଖାନ୍ତୁ"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g>ରେ ଅପଡେଟ୍ ହୋଇଥିଲା"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"କନେକ୍ଟ ହେଲାନାହିଁ"</string>
+</resources>
diff --git a/slices/view/src/main/res/values-pa/strings.xml b/slices/view/src/main/res/values-pa/strings.xml
index cd6db9b..8926d0f 100644
--- a/slices/view/src/main/res/values-pa/strings.xml
+++ b/slices/view/src/main/res/values-pa/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"ਹੋਰ"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"ਹੋਰ ਦਿਖਾਓ"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> ਅੱਪਡੇਟ ਕੀਤੀ ਗਈ"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-pl/strings.xml b/slices/view/src/main/res/values-pl/strings.xml
index 5fe2d2b..fa565f3 100644
--- a/slices/view/src/main/res/values-pl/strings.xml
+++ b/slices/view/src/main/res/values-pl/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Więcej"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Pokaż więcej"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Aktualizacja: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Nie udało się połączyć"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-pt-rBR/strings.xml b/slices/view/src/main/res/values-pt-rBR/strings.xml
index a163c4a..79cc7b7 100644
--- a/slices/view/src/main/res/values-pt-rBR/strings.xml
+++ b/slices/view/src/main/res/values-pt-rBR/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"Mais <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Mais"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Mostrar mais"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Atualizado às <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> min atrás</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min atrás</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ano atrás</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> anos atrás</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> dia atrás</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> dias atrás</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Não foi possível conectar"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-pt-rPT/strings.xml b/slices/view/src/main/res/values-pt-rPT/strings.xml
index 7d4c127..b81ec60 100644
--- a/slices/view/src/main/res/values-pt-rPT/strings.xml
+++ b/slices/view/src/main/res/values-pt-rPT/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Mais"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Mostrar mais"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Atualizado: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other">Há <xliff:g id="ID_2">%d</xliff:g> minutos.</item>
+      <item quantity="one">Há <xliff:g id="ID_1">%d</xliff:g> minuto.</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other">Há <xliff:g id="ID_2">%d</xliff:g> anos.</item>
+      <item quantity="one">Há <xliff:g id="ID_1">%d</xliff:g> ano.</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other">Há <xliff:g id="ID_2">%d</xliff:g> dias.</item>
+      <item quantity="one">Há <xliff:g id="ID_1">%d</xliff:g> dia.</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Não foi possível ligar."</string>
 </resources>
diff --git a/slices/view/src/main/res/values-pt/strings.xml b/slices/view/src/main/res/values-pt/strings.xml
index a163c4a..79cc7b7 100644
--- a/slices/view/src/main/res/values-pt/strings.xml
+++ b/slices/view/src/main/res/values-pt/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"Mais <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Mais"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Mostrar mais"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Atualizado às <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> min atrás</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min atrás</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ano atrás</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> anos atrás</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> dia atrás</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> dias atrás</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Não foi possível conectar"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ro/strings.xml b/slices/view/src/main/res/values-ro/strings.xml
index ed61fde..a3f9ff2 100644
--- a/slices/view/src/main/res/values-ro/strings.xml
+++ b/slices/view/src/main/res/values-ro/strings.xml
@@ -20,8 +20,21 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Mai mult"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Vedeți mai multe"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Actualizat la <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="few">Acum <xliff:g id="ID_2">%d</xliff:g> min.</item>
+      <item quantity="other">Acum <xliff:g id="ID_2">%d</xliff:g> de min.</item>
+      <item quantity="one">Acum <xliff:g id="ID_1">%d</xliff:g> min.</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="few">Acum <xliff:g id="ID_2">%d</xliff:g> ani</item>
+      <item quantity="other">Acum <xliff:g id="ID_2">%d</xliff:g> de ani</item>
+      <item quantity="one">Acum <xliff:g id="ID_1">%d</xliff:g> an</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="few">Acum <xliff:g id="ID_2">%d</xliff:g> zile</item>
+      <item quantity="other">Acum <xliff:g id="ID_2">%d</xliff:g> de zile</item>
+      <item quantity="one">Acum <xliff:g id="ID_1">%d</xliff:g> zi</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Nu s-a putut conecta"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ru/strings.xml b/slices/view/src/main/res/values-ru/strings.xml
index e3e95f7..85953aa 100644
--- a/slices/view/src/main/res/values-ru/strings.xml
+++ b/slices/view/src/main/res/values-ru/strings.xml
@@ -20,4 +20,24 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Ещё"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Ещё"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Обновлено <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> мин. назад</item>
+      <item quantity="few"><xliff:g id="ID_2">%d</xliff:g> мин. назад</item>
+      <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> мин. назад</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> мин. назад</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> г. назад</item>
+      <item quantity="few"><xliff:g id="ID_2">%d</xliff:g> г. назад</item>
+      <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> лет назад</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> г. назад</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> дн. назад</item>
+      <item quantity="few"><xliff:g id="ID_2">%d</xliff:g> дн. назад</item>
+      <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> дн. назад</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> дн. назад</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Ошибка подключения"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-si/strings.xml b/slices/view/src/main/res/values-si/strings.xml
index 58feb78..be85112 100644
--- a/slices/view/src/main/res/values-si/strings.xml
+++ b/slices/view/src/main/res/values-si/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"තව"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"තව පෙන්වන්න"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> යාවත්කාලීන කරන ලදී"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"සම්බන්ධ වීමට නොහැකි විය"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-sk/strings.xml b/slices/view/src/main/res/values-sk/strings.xml
index fe77506..a153760 100644
--- a/slices/view/src/main/res/values-sk/strings.xml
+++ b/slices/view/src/main/res/values-sk/strings.xml
@@ -20,8 +20,24 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Viac"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Zobraziť viac"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Aktualizované <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="few">Pred <xliff:g id="ID_2">%d</xliff:g> min</item>
+      <item quantity="many">Pred <xliff:g id="ID_2">%d</xliff:g> min</item>
+      <item quantity="other">Pred <xliff:g id="ID_2">%d</xliff:g> min</item>
+      <item quantity="one">Pred <xliff:g id="ID_1">%d</xliff:g> min</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="few">Pred <xliff:g id="ID_2">%d</xliff:g> rokmi</item>
+      <item quantity="many">Pred <xliff:g id="ID_2">%d</xliff:g> rokom</item>
+      <item quantity="other">Pred <xliff:g id="ID_2">%d</xliff:g> rokmi</item>
+      <item quantity="one">Pred <xliff:g id="ID_1">%d</xliff:g> rokom</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="few">Pred <xliff:g id="ID_2">%d</xliff:g> dňami</item>
+      <item quantity="many">Pred <xliff:g id="ID_2">%d</xliff:g> dňami</item>
+      <item quantity="other">Pred <xliff:g id="ID_2">%d</xliff:g> dňami</item>
+      <item quantity="one">Pred <xliff:g id="ID_1">%d</xliff:g> dňom</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Nepodarilo sa pripojiť"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-sl/strings.xml b/slices/view/src/main/res/values-sl/strings.xml
index 802caf1..0389500 100644
--- a/slices/view/src/main/res/values-sl/strings.xml
+++ b/slices/view/src/main/res/values-sl/strings.xml
@@ -20,8 +20,24 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"in še <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Več"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Pokaži več"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Posodobljeno: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one">pred <xliff:g id="ID_2">%d</xliff:g> minuto</item>
+      <item quantity="two">pred <xliff:g id="ID_2">%d</xliff:g> minutama</item>
+      <item quantity="few">pred <xliff:g id="ID_2">%d</xliff:g> minutami</item>
+      <item quantity="other">pred <xliff:g id="ID_2">%d</xliff:g> minutami</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one">pred <xliff:g id="ID_2">%d</xliff:g> letom</item>
+      <item quantity="two">pred <xliff:g id="ID_2">%d</xliff:g> letoma</item>
+      <item quantity="few">pred <xliff:g id="ID_2">%d</xliff:g> leti</item>
+      <item quantity="other">pred <xliff:g id="ID_2">%d</xliff:g> leti</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one">pred <xliff:g id="ID_2">%d</xliff:g> dnem</item>
+      <item quantity="two">pred <xliff:g id="ID_2">%d</xliff:g> dnevoma</item>
+      <item quantity="few">pred <xliff:g id="ID_2">%d</xliff:g> dnevi</item>
+      <item quantity="other">pred <xliff:g id="ID_2">%d</xliff:g> dnevi</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Povezava ni mogoča"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-sq/strings.xml b/slices/view/src/main/res/values-sq/strings.xml
index e1cdae8..0359c86 100644
--- a/slices/view/src/main/res/values-sq/strings.xml
+++ b/slices/view/src/main/res/values-sq/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Më shumë"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Shfaq më shumë"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Përditësuar <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> minuta më parë</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> minutë më parë</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> vite më parë</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> vit më parë</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ditë më parë</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> ditë më parë</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Nuk mund të lidhej"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-sr/strings.xml b/slices/view/src/main/res/values-sr/strings.xml
index 627ede6..718f3d7 100644
--- a/slices/view/src/main/res/values-sr/strings.xml
+++ b/slices/view/src/main/res/values-sr/strings.xml
@@ -20,4 +20,21 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"и још <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Још"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Прикажи више"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Ажурирано <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one">пре <xliff:g id="ID_2">%d</xliff:g> мин</item>
+      <item quantity="few">пре <xliff:g id="ID_2">%d</xliff:g> мин</item>
+      <item quantity="other">пре <xliff:g id="ID_2">%d</xliff:g> мин</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one">пре <xliff:g id="ID_2">%d</xliff:g> год</item>
+      <item quantity="few">пре <xliff:g id="ID_2">%d</xliff:g> год</item>
+      <item quantity="other">пре <xliff:g id="ID_2">%d</xliff:g> год</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one">пре <xliff:g id="ID_2">%d</xliff:g> дан</item>
+      <item quantity="few">пре <xliff:g id="ID_2">%d</xliff:g> дана</item>
+      <item quantity="other">пре <xliff:g id="ID_2">%d</xliff:g> дана</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Повезивање није успело"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-sv/strings.xml b/slices/view/src/main/res/values-sv/strings.xml
index f8ed0c7..bc6947d 100644
--- a/slices/view/src/main/res/values-sv/strings.xml
+++ b/slices/view/src/main/res/values-sv/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"<xliff:g id="NUMBER">%1$d</xliff:g> till"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Mer"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Visa mer"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Uppdaterades <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Det gick inte att ansluta"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-sw/strings.xml b/slices/view/src/main/res/values-sw/strings.xml
index b23ed5e..609c51f 100644
--- a/slices/view/src/main/res/values-sw/strings.xml
+++ b/slices/view/src/main/res/values-sw/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Mengine"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Onyesha mengine"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Ilisasishwa <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other">Dakika <xliff:g id="ID_2">%d</xliff:g> zilizopita</item>
+      <item quantity="one">Dakika <xliff:g id="ID_1">%d</xliff:g> iliyopita</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other">Miaka <xliff:g id="ID_2">%d</xliff:g> iliyopita</item>
+      <item quantity="one">Mwaka <xliff:g id="ID_1">%d</xliff:g> uliopita</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other">Siku <xliff:g id="ID_2">%d</xliff:g> zilizopita</item>
+      <item quantity="one">Siku <xliff:g id="ID_1">%d</xliff:g> iliyopita</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Imeshindwa kuunganisha"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ta/strings.xml b/slices/view/src/main/res/values-ta/strings.xml
index 9f2be0e..5b36b79 100644
--- a/slices/view/src/main/res/values-ta/strings.xml
+++ b/slices/view/src/main/res/values-ta/strings.xml
@@ -22,6 +22,9 @@
     <string name="abc_slice_show_more" msgid="1567717014004692768">"மேலும் காட்டு"</string>
     <!-- no translation found for abc_slice_updated (8155085405396453848) -->
     <skip />
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
     <!-- no translation found for abc_slice_error (4188371422904147368) -->
     <skip />
 </resources>
diff --git a/slices/view/src/main/res/values-te/strings.xml b/slices/view/src/main/res/values-te/strings.xml
index 0eb1da7..82137b8 100644
--- a/slices/view/src/main/res/values-te/strings.xml
+++ b/slices/view/src/main/res/values-te/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"మరింత"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"మరింత చూపు"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"అప్‌డేట్ చేసిన సమయం <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> నిమి క్రితం</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> నిమి క్రితం</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> సం క్రితం</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> సం క్రితం</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> రోజుల క్రితం</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> రోజు క్రితం</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"కనెక్ట్ చేయడం సాధ్యపడలేదు"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-th/strings.xml b/slices/view/src/main/res/values-th/strings.xml
index 4cdd991..3ae19da 100644
--- a/slices/view/src/main/res/values-th/strings.xml
+++ b/slices/view/src/main/res/values-th/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"เพิ่มเติม"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"แสดงเพิ่ม"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"อัปเดตเมื่อ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> นาทีที่แล้ว</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> นาทีที่แล้ว</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ปีที่แล้ว</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> ปีที่แล้ว</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> วันที่แล้ว</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> วันที่แล้ว</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"เชื่อมต่อไม่ได้"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-tl/strings.xml b/slices/view/src/main/res/values-tl/strings.xml
index 05917b0..d2d5526 100644
--- a/slices/view/src/main/res/values-tl/strings.xml
+++ b/slices/view/src/main/res/values-tl/strings.xml
@@ -20,4 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Higit pa"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Magpakita pa"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Na-update noong <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> minuto ang nakalipas</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> na minuto ang nakalipas</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> taon ang nakalipas</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> na taon ang nakalipas</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> araw ang nakalipas</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> na araw ang nakalipas</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Hindi makakonekta"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-tr/strings.xml b/slices/view/src/main/res/values-tr/strings.xml
index 09a589e..84da5fb 100644
--- a/slices/view/src/main/res/values-tr/strings.xml
+++ b/slices/view/src/main/res/values-tr/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Diğer"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Daha fazla göster"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Güncellenme zamanı: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> dk. önce</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> dk. önce</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> yıl önce</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> yıl önce</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> gün önce</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> gün önce</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Bağlanılamadı"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-uk/strings.xml b/slices/view/src/main/res/values-uk/strings.xml
index 06c02bc..0706d80 100644
--- a/slices/view/src/main/res/values-uk/strings.xml
+++ b/slices/view/src/main/res/values-uk/strings.xml
@@ -20,8 +20,24 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Більше"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Показати більше"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Оновлено: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> хвилину тому</item>
+      <item quantity="few"><xliff:g id="ID_2">%d</xliff:g> хвилини тому</item>
+      <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> хвилин тому</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> хвилини тому</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> рік тому</item>
+      <item quantity="few"><xliff:g id="ID_2">%d</xliff:g> роки тому</item>
+      <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> років тому</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> року тому</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> день тому</item>
+      <item quantity="few"><xliff:g id="ID_2">%d</xliff:g> дні тому</item>
+      <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> днів тому</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> дня тому</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Не вдалося під’єднатися"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-ur/strings.xml b/slices/view/src/main/res/values-ur/strings.xml
index f12d6b3..4f51700 100644
--- a/slices/view/src/main/res/values-ur/strings.xml
+++ b/slices/view/src/main/res/values-ur/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"‎‎‎‎‎+ <xliff:g id="NUMBER">%1$d</xliff:g>‎‎"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"مزید"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"مزید دکھائیں"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> اپ ڈیٹ کیا گیا"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"منسلک نہیں ہو سکا"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-uz/strings.xml b/slices/view/src/main/res/values-uz/strings.xml
index cbc9979..2250c91 100644
--- a/slices/view/src/main/res/values-uz/strings.xml
+++ b/slices/view/src/main/res/values-uz/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Yana"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Yana"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Yangilandi: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Ulanib bo‘lmadi"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-vi/strings.xml b/slices/view/src/main/res/values-vi/strings.xml
index 3890636..c9129be 100644
--- a/slices/view/src/main/res/values-vi/strings.xml
+++ b/slices/view/src/main/res/values-vi/strings.xml
@@ -20,8 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Thêm"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Hiển thị thêm"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Đã cập nhật lúc <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"Không thể kết nối"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-zh-rCN/strings.xml b/slices/view/src/main/res/values-zh-rCN/strings.xml
index ffb63a4..2ecb835 100644
--- a/slices/view/src/main/res/values-zh-rCN/strings.xml
+++ b/slices/view/src/main/res/values-zh-rCN/strings.xml
@@ -20,4 +20,9 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"更多"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"显示更多"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"更新时间:<xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+    <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+    <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+    <string name="abc_slice_error" msgid="4188371422904147368">"无法连接"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-zh-rHK/strings.xml b/slices/view/src/main/res/values-zh-rHK/strings.xml
index 565b565..d1ac276 100644
--- a/slices/view/src/main/res/values-zh-rHK/strings.xml
+++ b/slices/view/src/main/res/values-zh-rHK/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"更多"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"顯示更多"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"更新時間:<xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> 分鐘前</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> 分鐘前</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> 年前</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> 年前</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> 天前</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> 天前</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"無法連線"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-zh-rTW/strings.xml b/slices/view/src/main/res/values-zh-rTW/strings.xml
index 565b565..d1ac276 100644
--- a/slices/view/src/main/res/values-zh-rTW/strings.xml
+++ b/slices/view/src/main/res/values-zh-rTW/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"更多"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"顯示更多"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"更新時間:<xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> 分鐘前</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> 分鐘前</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> 年前</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> 年前</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> 天前</item>
+      <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> 天前</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"無法連線"</string>
 </resources>
diff --git a/slices/view/src/main/res/values-zu/strings.xml b/slices/view/src/main/res/values-zu/strings.xml
index c5ebb10..966bb1b 100644
--- a/slices/view/src/main/res/values-zu/strings.xml
+++ b/slices/view/src/main/res/values-zu/strings.xml
@@ -20,8 +20,18 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"Okuningi"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"Bonisa okuningi"</string>
-    <!-- no translation found for abc_slice_updated (8155085405396453848) -->
-    <skip />
-    <!-- no translation found for abc_slice_error (4188371422904147368) -->
-    <skip />
+    <string name="abc_slice_updated" msgid="8155085405396453848">"Kubuyekezwe ngo-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> iminithi eledlule</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> iminithi eledlule</item>
+    </plurals>
+    <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> unyaka owedlule</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> unyaka owedlule</item>
+    </plurals>
+    <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+      <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> izinsuku ezedlule</item>
+      <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> izinsuku ezedlule</item>
+    </plurals>
+    <string name="abc_slice_error" msgid="4188371422904147368">"Ayikwazanga ukuxhuma"</string>
 </resources>
diff --git a/slices/view/src/main/res/values/attrs.xml b/slices/view/src/main/res/values/attrs.xml
index 5779d31..f3e621b 100644
--- a/slices/view/src/main/res/values/attrs.xml
+++ b/slices/view/src/main/res/values/attrs.xml
@@ -17,19 +17,30 @@
 
 <resources>
     <declare-styleable name="SliceView">
-        <!-- Colors -->
+        <!-- Color to use for any title text within the slice. -->
         <attr name="titleColor" format="color" />
+        <!-- Color to use for any other text within the slice. -->
         <attr name="subtitleColor" format="color" />
+        <!-- Color to use for tinting elements within the slice. Normally an app
+         providing a slice is able to specify a color to use for tinting, specifying
+         a tint color here will override the app supplied color. -->
         <attr name="tintColor" format="color" />
 
-        <!-- Text sizes -->
+        <!-- Text size to use for title text in the header of the slice. -->
         <attr name="headerTitleSize" format="dimension" />
+        <!-- Text size to use for subtitle text in the header of the slice. -->
         <attr name="headerSubtitleSize" format="dimension"/>
+        <!-- Text size to use for title text in a non-header row of the slice. -->
         <attr name="titleSize" format="dimension" />
+        <!-- Text size to use for subtitle text in a non-header row of the slice. -->
         <attr name="subtitleSize" format="dimension" />
+        <!-- Text size to use for title text in a grid row of the slice. -->
         <attr name="gridTitleSize" format="dimension" />
+        <!-- Text size to use for the subtitle text in a grid row of the slice. -->
         <attr name="gridSubtitleSize" format="dimension" />
     </declare-styleable>
 
+    <!-- To apply a style for all slices shown within an activity or app you
+         may set this in your app theme pointing to your custom SliceView style.-->
     <attr name="sliceViewStyle" format="reference"/>
 </resources>
\ No newline at end of file
diff --git a/slices/view/src/main/res/values/dimens.xml b/slices/view/src/main/res/values/dimens.xml
index bb90106..19e3327 100644
--- a/slices/view/src/main/res/values/dimens.xml
+++ b/slices/view/src/main/res/values/dimens.xml
@@ -42,6 +42,8 @@
     <dimen name="abc_slice_row_max_height">60dp</dimen>
     <!-- Min height of a row showing an input field that is active -->
     <dimen name="abc_slice_row_active_input_height">120dp</dimen>
+    <!-- Max height of of a row view with a range element -->
+    <dimen name="abc_slice_row_range_max_height">86dp</dimen>
 
     <!-- Grid view sizes-->
     <!-- Height of a grid row displaying only text or only small images (but not both) -->
diff --git a/testutils/src/main/java/androidx/testutils/AppCompatActivityUtils.java b/testutils/src/main/java/androidx/testutils/AppCompatActivityUtils.java
index e36a2ec..104fb44 100644
--- a/testutils/src/main/java/androidx/testutils/AppCompatActivityUtils.java
+++ b/testutils/src/main/java/androidx/testutils/AppCompatActivityUtils.java
@@ -92,4 +92,7 @@
         RecreatedAppCompatActivity.clearState();
         return newActivity;
     }
+
+    private AppCompatActivityUtils() {
+    }
 }
diff --git a/testutils/src/main/java/androidx/testutils/FragmentActivityUtils.java b/testutils/src/main/java/androidx/testutils/FragmentActivityUtils.java
index 136cc31..43404f0 100644
--- a/testutils/src/main/java/androidx/testutils/FragmentActivityUtils.java
+++ b/testutils/src/main/java/androidx/testutils/FragmentActivityUtils.java
@@ -89,4 +89,7 @@
         RecreatedActivity.clearState();
         return newActivity;
     }
+
+    private FragmentActivityUtils() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/AnimatorUtils.java b/transition/src/main/java/androidx/transition/AnimatorUtils.java
index 452bc66..6772a60 100644
--- a/transition/src/main/java/androidx/transition/AnimatorUtils.java
+++ b/transition/src/main/java/androidx/transition/AnimatorUtils.java
@@ -77,4 +77,7 @@
         void onAnimationResume(Animator animation);
 
     }
+
+    private AnimatorUtils() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/GhostViewUtils.java b/transition/src/main/java/androidx/transition/GhostViewUtils.java
index 447f728..9e460ca 100644
--- a/transition/src/main/java/androidx/transition/GhostViewUtils.java
+++ b/transition/src/main/java/androidx/transition/GhostViewUtils.java
@@ -38,4 +38,6 @@
         }
     }
 
+    private GhostViewUtils() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/ImageViewUtils.java b/transition/src/main/java/androidx/transition/ImageViewUtils.java
index a7c947c..9d73e9e 100644
--- a/transition/src/main/java/androidx/transition/ImageViewUtils.java
+++ b/transition/src/main/java/androidx/transition/ImageViewUtils.java
@@ -105,4 +105,6 @@
         }
     }
 
+    private ImageViewUtils() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/MatrixUtils.java b/transition/src/main/java/androidx/transition/MatrixUtils.java
index a04ebe2..9a13796 100644
--- a/transition/src/main/java/androidx/transition/MatrixUtils.java
+++ b/transition/src/main/java/androidx/transition/MatrixUtils.java
@@ -204,4 +204,6 @@
 
     };
 
+    private MatrixUtils() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/ObjectAnimatorUtils.java b/transition/src/main/java/androidx/transition/ObjectAnimatorUtils.java
index a7fb97d..bd73b58 100644
--- a/transition/src/main/java/androidx/transition/ObjectAnimatorUtils.java
+++ b/transition/src/main/java/androidx/transition/ObjectAnimatorUtils.java
@@ -31,4 +31,6 @@
         return ObjectAnimator.ofFloat(target, new PathProperty<>(property, path), 0f, 1f);
     }
 
+    private ObjectAnimatorUtils() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/PropertyValuesHolderUtils.java b/transition/src/main/java/androidx/transition/PropertyValuesHolderUtils.java
index 2ea657c..42527c4 100644
--- a/transition/src/main/java/androidx/transition/PropertyValuesHolderUtils.java
+++ b/transition/src/main/java/androidx/transition/PropertyValuesHolderUtils.java
@@ -41,4 +41,6 @@
         return PropertyValuesHolder.ofFloat(new PathProperty<>(property, path), 0f, 1f);
     }
 
+    private PropertyValuesHolderUtils() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/Styleable.java b/transition/src/main/java/androidx/transition/Styleable.java
index cdfb9ec..40f3cca 100644
--- a/transition/src/main/java/androidx/transition/Styleable.java
+++ b/transition/src/main/java/androidx/transition/Styleable.java
@@ -175,4 +175,6 @@
         int PATTERN_PATH_DATA = 0;
     }
 
+    private Styleable() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/TransitionUtils.java b/transition/src/main/java/androidx/transition/TransitionUtils.java
index 7a1b7ec..39de677 100644
--- a/transition/src/main/java/androidx/transition/TransitionUtils.java
+++ b/transition/src/main/java/androidx/transition/TransitionUtils.java
@@ -130,4 +130,6 @@
 
     }
 
+    private TransitionUtils() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java b/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java
index 004c1a4..c3e30e8 100644
--- a/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java
+++ b/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java
@@ -133,4 +133,6 @@
         }
     }
 
+    private TranslationAnimationCreator() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/ViewGroupUtils.java b/transition/src/main/java/androidx/transition/ViewGroupUtils.java
index 55ab3ff..dee0fa9 100644
--- a/transition/src/main/java/androidx/transition/ViewGroupUtils.java
+++ b/transition/src/main/java/androidx/transition/ViewGroupUtils.java
@@ -47,4 +47,6 @@
         }
     }
 
+    private ViewGroupUtils() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/ViewGroupUtilsApi14.java b/transition/src/main/java/androidx/transition/ViewGroupUtilsApi14.java
index 70af497..32d546a 100644
--- a/transition/src/main/java/androidx/transition/ViewGroupUtilsApi14.java
+++ b/transition/src/main/java/androidx/transition/ViewGroupUtilsApi14.java
@@ -126,4 +126,6 @@
         }
     }
 
+    private ViewGroupUtilsApi14() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/ViewGroupUtilsApi18.java b/transition/src/main/java/androidx/transition/ViewGroupUtilsApi18.java
index bd0938a..e4d4ffa 100644
--- a/transition/src/main/java/androidx/transition/ViewGroupUtilsApi18.java
+++ b/transition/src/main/java/androidx/transition/ViewGroupUtilsApi18.java
@@ -59,4 +59,6 @@
         }
     }
 
+    private ViewGroupUtilsApi18() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/ViewOverlayApi14.java b/transition/src/main/java/androidx/transition/ViewOverlayApi14.java
index 12ebf77..049e685 100644
--- a/transition/src/main/java/androidx/transition/ViewOverlayApi14.java
+++ b/transition/src/main/java/androidx/transition/ViewOverlayApi14.java
@@ -351,5 +351,6 @@
         }
     }
 
-
+    private ViewOverlayApi14() {
+    }
 }
diff --git a/transition/src/main/java/androidx/transition/ViewUtils.java b/transition/src/main/java/androidx/transition/ViewUtils.java
index 89eaadd..d770ab6 100644
--- a/transition/src/main/java/androidx/transition/ViewUtils.java
+++ b/transition/src/main/java/androidx/transition/ViewUtils.java
@@ -222,4 +222,6 @@
         }
     }
 
+    private ViewUtils() {
+    }
 }
diff --git a/tv-provider/api/current.txt b/tv-provider/api/current.txt
index ec21ef7..0c0a202 100644
--- a/tv-provider/api/current.txt
+++ b/tv-provider/api/current.txt
@@ -71,7 +71,7 @@
   }
 
   public class ChannelLogoUtils {
-    ctor public ChannelLogoUtils();
+    ctor public deprecated ChannelLogoUtils();
     method public static android.graphics.Bitmap loadChannelLogo(android.content.Context, long);
     method public static boolean storeChannelLogo(android.content.Context, long, android.net.Uri);
     method public static boolean storeChannelLogo(android.content.Context, long, android.graphics.Bitmap);
diff --git a/tv-provider/src/main/java/androidx/tvprovider/media/tv/ChannelLogoUtils.java b/tv-provider/src/main/java/androidx/tvprovider/media/tv/ChannelLogoUtils.java
index 85b2bce..5e1aa19 100644
--- a/tv-provider/src/main/java/androidx/tvprovider/media/tv/ChannelLogoUtils.java
+++ b/tv-provider/src/main/java/androidx/tvprovider/media/tv/ChannelLogoUtils.java
@@ -165,4 +165,10 @@
         urlConnection.setReadTimeout(READ_TIMEOUT_MS_FOR_URLCONNECTION);
         return urlConnection;
     }
+
+    /** @deprecated This type should not be instantiated as it contains only static methods. */
+    @Deprecated
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    public ChannelLogoUtils() {
+    }
 }
diff --git a/tv-provider/src/main/java/androidx/tvprovider/media/tv/CollectionUtils.java b/tv-provider/src/main/java/androidx/tvprovider/media/tv/CollectionUtils.java
index 14fbc77..595e451 100644
--- a/tv-provider/src/main/java/androidx/tvprovider/media/tv/CollectionUtils.java
+++ b/tv-provider/src/main/java/androidx/tvprovider/media/tv/CollectionUtils.java
@@ -46,4 +46,7 @@
         }
         return result;
     }
+
+    private CollectionUtils() {
+    }
 }
diff --git a/tv-provider/src/main/java/androidx/tvprovider/media/tv/PreviewChannel.java b/tv-provider/src/main/java/androidx/tvprovider/media/tv/PreviewChannel.java
index 9bdc108..d976798 100644
--- a/tv-provider/src/main/java/androidx/tvprovider/media/tv/PreviewChannel.java
+++ b/tv-provider/src/main/java/androidx/tvprovider/media/tv/PreviewChannel.java
@@ -331,6 +331,9 @@
         public static final int COL_INTERNAL_PROVIDER_FLAG2 = 9;
         public static final int COL_INTERNAL_PROVIDER_FLAG3 = 10;
         public static final int COL_INTERNAL_PROVIDER_FLAG4 = 11;
+
+        private Columns() {
+        }
     }
 
     /**
diff --git a/v7/appcompat/res/values-as/strings.xml b/v7/appcompat/res/values-as/strings.xml
index 1f90618..a3574d7 100644
--- a/v7/appcompat/res/values-as/strings.xml
+++ b/v7/appcompat/res/values-as/strings.xml
@@ -29,13 +29,19 @@
     <string name="abc_searchview_description_voice" msgid="893419373245838918">"কণ্ঠধ্বনিৰ যোগেৰে সন্ধান কৰক"</string>
     <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"এটা এপ্ বাছনি কৰক"</string>
     <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"সকলো চাওক"</string>
-    <!-- no translation found for abc_shareactionprovider_share_with_application (3300176832234831527) -->
-    <skip />
+    <string name="abc_shareactionprovider_share_with_application" msgid="3300176832234831527">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>ৰ জৰিয়তে শ্বেয়াৰ কৰক"</string>
     <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"ইয়াৰ জৰিয়তে শ্বেয়াৰ কৰক"</string>
-    <!-- no translation found for abc_capital_on (3405795526292276155) -->
-    <skip />
-    <!-- no translation found for abc_capital_off (121134116657445385) -->
-    <skip />
-    <!-- no translation found for search_menu_title (146198913615257606) -->
-    <skip />
+    <string name="abc_capital_on" msgid="3405795526292276155">"অন কৰক"</string>
+    <string name="abc_capital_off" msgid="121134116657445385">"অফ কৰক"</string>
+    <string name="search_menu_title" msgid="146198913615257606">"অনুসন্ধান কৰক"</string>
+    <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"মেনু+"</string>
+    <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"মেটা+"</string>
+    <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"CTRL+"</string>
+    <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+    <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"শ্বিফ্ট+"</string>
+    <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+    <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"ফাংশ্বন+"</string>
+    <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"স্পেচ"</string>
+    <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"এণ্টাৰ"</string>
+    <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"মচক"</string>
 </resources>
diff --git a/v7/appcompat/res/values-gu/strings.xml b/v7/appcompat/res/values-gu/strings.xml
index a8b0c48..390d59b 100644
--- a/v7/appcompat/res/values-gu/strings.xml
+++ b/v7/appcompat/res/values-gu/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="abc_action_mode_done" msgid="4076576682505996667">"થઈ ગયું"</string>
     <string name="abc_action_bar_home_description" msgid="4600421777120114993">"હોમ પર નેવિગેટ કરો"</string>
-    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"ઉપર નેવિગેટ કરો"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"ઉપર નૅવિગેટ કરો"</string>
     <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"વધુ વિકલ્પો"</string>
     <string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"સંકુચિત કરો"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"શોધો"</string>
diff --git a/v7/appcompat/res/values-or/strings.xml b/v7/appcompat/res/values-or/strings.xml
index 4486724..86e4fb7 100644
--- a/v7/appcompat/res/values-or/strings.xml
+++ b/v7/appcompat/res/values-or/strings.xml
@@ -29,30 +29,19 @@
     <string name="abc_searchview_description_voice" msgid="893419373245838918">"ଭଏସ୍‌ ସର୍ଚ୍ଚ"</string>
     <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"ଗୋଟିଏ ଆପ୍‍ ବାଛନ୍ତୁ"</string>
     <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"ସବୁ ଦେଖନ୍ତୁ"</string>
-    <!-- no translation found for abc_shareactionprovider_share_with_application (3300176832234831527) -->
-    <skip />
+    <string name="abc_shareactionprovider_share_with_application" msgid="3300176832234831527">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> ସହ ଶେୟାର୍‍ କରନ୍ତୁ"</string>
     <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"ଏହାଙ୍କ ସହ ଶେୟାର୍‌ କରନ୍ତୁ"</string>
     <string name="abc_capital_on" msgid="3405795526292276155">"ଅନ୍"</string>
     <string name="abc_capital_off" msgid="121134116657445385">"ଅଫ୍"</string>
     <string name="search_menu_title" msgid="146198913615257606">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
-    <!-- no translation found for abc_prepend_shortcut_label (1351762916121158029) -->
-    <skip />
-    <!-- no translation found for abc_menu_meta_shortcut_label (7643535737296831317) -->
-    <skip />
-    <!-- no translation found for abc_menu_ctrl_shortcut_label (1324831542140195728) -->
-    <skip />
-    <!-- no translation found for abc_menu_alt_shortcut_label (1302280443949172191) -->
-    <skip />
-    <!-- no translation found for abc_menu_shift_shortcut_label (8126296154200614004) -->
-    <skip />
-    <!-- no translation found for abc_menu_sym_shortcut_label (9002602288060866689) -->
-    <skip />
-    <!-- no translation found for abc_menu_function_shortcut_label (4792426091847145555) -->
-    <skip />
-    <!-- no translation found for abc_menu_space_shortcut_label (2378550843553983978) -->
-    <skip />
-    <!-- no translation found for abc_menu_enter_shortcut_label (8341180395196749340) -->
-    <skip />
-    <!-- no translation found for abc_menu_delete_shortcut_label (8362206064229013510) -->
-    <skip />
+    <string name="abc_prepend_shortcut_label" msgid="1351762916121158029">"ମେନୁ"</string>
+    <string name="abc_menu_meta_shortcut_label" msgid="7643535737296831317">"Meta+"</string>
+    <string name="abc_menu_ctrl_shortcut_label" msgid="1324831542140195728">"Ctrl+"</string>
+    <string name="abc_menu_alt_shortcut_label" msgid="1302280443949172191">"Alt+"</string>
+    <string name="abc_menu_shift_shortcut_label" msgid="8126296154200614004">"Shift+"</string>
+    <string name="abc_menu_sym_shortcut_label" msgid="9002602288060866689">"Sym+"</string>
+    <string name="abc_menu_function_shortcut_label" msgid="4792426091847145555">"Function+"</string>
+    <string name="abc_menu_space_shortcut_label" msgid="2378550843553983978">"ସ୍ପେସ୍‍"</string>
+    <string name="abc_menu_enter_shortcut_label" msgid="8341180395196749340">"ଏଣ୍ଟର୍"</string>
+    <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"ଡିଲିଟ୍‍"</string>
 </resources>
diff --git a/v7/appcompat/res/values/bools.xml b/v7/appcompat/res/values/bools.xml
index c38c0ee..793719b 100644
--- a/v7/appcompat/res/values/bools.xml
+++ b/v7/appcompat/res/values/bools.xml
@@ -18,8 +18,6 @@
 
     <bool name="abc_action_bar_embed_tabs">true</bool>
 
-    <bool name="abc_config_showMenuShortcutsWhenKeyboardPresent">false</bool>
-
     <!-- Whether to allow vertically stacked button bars. This is disabled for
          configurations with a small (e.g. less than 320dp) screen height. -->
     <bool name="abc_allow_stacked_button_bar">false</bool>
diff --git a/v7/appcompat/src/androidTest/java/androidx/appcompat/app/DrawerLayoutDoubleTest.java b/v7/appcompat/src/androidTest/java/androidx/appcompat/app/DrawerLayoutDoubleTest.java
index 9b2decc..ad78cc0 100755
--- a/v7/appcompat/src/androidTest/java/androidx/appcompat/app/DrawerLayoutDoubleTest.java
+++ b/v7/appcompat/src/androidTest/java/androidx/appcompat/app/DrawerLayoutDoubleTest.java
@@ -97,7 +97,7 @@
     }
 
     @Test(expected=IllegalArgumentException.class)
-    @SmallTest
+    @LargeTest
     public void testCloseNonExistentDrawer() {
         // Note that we're expecting the closeDrawer action to result in an exception being
         // thrown since mContentView is not a drawer.
diff --git a/v7/appcompat/src/main/java/androidx/appcompat/app/ActionBarDrawerToggleHoneycomb.java b/v7/appcompat/src/main/java/androidx/appcompat/app/ActionBarDrawerToggleHoneycomb.java
index ac02d1b..f4102c3 100644
--- a/v7/appcompat/src/main/java/androidx/appcompat/app/ActionBarDrawerToggleHoneycomb.java
+++ b/v7/appcompat/src/main/java/androidx/appcompat/app/ActionBarDrawerToggleHoneycomb.java
@@ -136,4 +136,7 @@
             }
         }
     }
+
+    private ActionBarDrawerToggleHoneycomb() {
+    }
 }
diff --git a/v7/appcompat/src/main/java/androidx/appcompat/app/ResourcesFlusher.java b/v7/appcompat/src/main/java/androidx/appcompat/app/ResourcesFlusher.java
index 8e1d113..2cd12d2 100644
--- a/v7/appcompat/src/main/java/androidx/appcompat/app/ResourcesFlusher.java
+++ b/v7/appcompat/src/main/java/androidx/appcompat/app/ResourcesFlusher.java
@@ -205,4 +205,7 @@
         }
         return false;
     }
+
+    private ResourcesFlusher() {
+    }
 }
diff --git a/v7/appcompat/src/main/java/androidx/appcompat/view/menu/MenuBuilder.java b/v7/appcompat/src/main/java/androidx/appcompat/view/menu/MenuBuilder.java
index a1a20e5..2b5e653 100644
--- a/v7/appcompat/src/main/java/androidx/appcompat/view/menu/MenuBuilder.java
+++ b/v7/appcompat/src/main/java/androidx/appcompat/view/menu/MenuBuilder.java
@@ -35,14 +35,15 @@
 import android.view.MenuItem;
 import android.view.SubMenu;
 import android.view.View;
+import android.view.ViewConfiguration;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
-import androidx.appcompat.R;
 import androidx.core.content.ContextCompat;
 import androidx.core.internal.view.SupportMenu;
 import androidx.core.internal.view.SupportMenuItem;
 import androidx.core.view.ActionProvider;
+import androidx.core.view.ViewConfigurationCompat;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -816,7 +817,8 @@
     private void setShortcutsVisibleInner(boolean shortcutsVisible) {
         mShortcutsVisible = shortcutsVisible
                 && mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS
-                && mResources.getBoolean(R.bool.abc_config_showMenuShortcutsWhenKeyboardPresent);
+                && ViewConfigurationCompat.shouldShowMenuShortcutsWhenKeyboardPresent(
+                        ViewConfiguration.get(mContext), mContext);
     }
 
     /**
diff --git a/v7/appcompat/src/main/java/androidx/appcompat/widget/ActionBarBackgroundDrawable.java b/v7/appcompat/src/main/java/androidx/appcompat/widget/ActionBarBackgroundDrawable.java
index 175cdb3..010a30b 100644
--- a/v7/appcompat/src/main/java/androidx/appcompat/widget/ActionBarBackgroundDrawable.java
+++ b/v7/appcompat/src/main/java/androidx/appcompat/widget/ActionBarBackgroundDrawable.java
@@ -23,6 +23,7 @@
 import android.graphics.drawable.Drawable;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 
 class ActionBarBackgroundDrawable extends Drawable {
 
@@ -62,6 +63,7 @@
     }
 
     @Override
+    @RequiresApi(21)
     public void getOutline(@NonNull Outline outline) {
         if (mContainer.mIsSplit) {
             if (mContainer.mSplitBackground != null) {
diff --git a/v7/appcompat/src/main/java/androidx/appcompat/widget/AppCompatHintHelper.java b/v7/appcompat/src/main/java/androidx/appcompat/widget/AppCompatHintHelper.java
index ed28d99..bacff63 100644
--- a/v7/appcompat/src/main/java/androidx/appcompat/widget/AppCompatHintHelper.java
+++ b/v7/appcompat/src/main/java/androidx/appcompat/widget/AppCompatHintHelper.java
@@ -40,4 +40,6 @@
         return ic;
     }
 
+    private AppCompatHintHelper() {
+    }
 }
diff --git a/v7/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java b/v7/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java
index a972ca6..ee00ded 100644
--- a/v7/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java
+++ b/v7/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java
@@ -111,4 +111,6 @@
         return ColorUtils.setAlphaComponent(color, Math.round(originalAlpha * alpha));
     }
 
+    private ThemeUtils() {
+    }
 }
diff --git a/v7/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAutofillTest.java b/v7/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAutofillTest.java
new file mode 100644
index 0000000..3ccb11e
--- /dev/null
+++ b/v7/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAutofillTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018 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.recyclerview.widget;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.core.view.ViewCompat;
+import androidx.recyclerview.test.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RecyclerViewAutofillTest  {
+    static Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void initializeWithAutofillDisabled()  {
+        RecyclerView recyclerView = new RecyclerView(getContext());
+        int importance = ViewCompat.getImportantForAutofill(recyclerView);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            assertEquals(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, importance);
+        } else {
+            assertEquals(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO, importance);
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    public void testXmlValues() {
+        ViewGroup parent = (ViewGroup) LayoutInflater.from(getContext())
+                .inflate(R.layout.autofill_rv, null);
+        assertEquals(View.IMPORTANT_FOR_AUTOFILL_YES,
+                parent.findViewById(R.id.autofill_yes).getImportantForAutofill());
+        assertEquals(View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS,
+                parent.findViewById(R.id.autofill_yesExcludeDescendants).getImportantForAutofill());
+        assertEquals(View.IMPORTANT_FOR_AUTOFILL_NO,
+                parent.findViewById(R.id.autofill_no).getImportantForAutofill());
+
+        // NOTE: RV overrides auto specifically
+        assertEquals(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS,
+                parent.findViewById(R.id.autofill_auto).getImportantForAutofill());
+    }
+}
diff --git a/v7/recyclerview/src/androidTest/res/layout/autofill_rv.xml b/v7/recyclerview/src/androidTest/res/layout/autofill_rv.xml
new file mode 100644
index 0000000..fb838f3
--- /dev/null
+++ b/v7/recyclerview/src/androidTest/res/layout/autofill_rv.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/autofill_yes"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:importantForAutofill="yes"/>
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/autofill_yesExcludeDescendants"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:importantForAutofill="yesExcludeDescendants"/>
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/autofill_no"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:importantForAutofill="no"/>>
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/autofill_auto"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:importantForAutofill="auto"/>>
+</LinearLayout>
diff --git a/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java b/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
index 2d09cda..08196eb 100644
--- a/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
+++ b/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
@@ -21,6 +21,7 @@
 import static androidx.core.view.ViewCompat.TYPE_NON_TOUCH;
 import static androidx.core.view.ViewCompat.TYPE_TOUCH;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -625,6 +626,7 @@
         mItemAnimator.setListener(mItemAnimatorListener);
         initAdapterManager();
         initChildrenHelper();
+        initAutofill();
         // If not explicitly specified this view is important for accessibility.
         if (ViewCompat.getImportantForAccessibility(this)
                 == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
@@ -690,6 +692,20 @@
     }
 
     /**
+     * If not explicitly specified, this view and its children don't support autofill.
+     * <p>
+     * This is done because autofill's means of uniquely identifying views doesn't work out of the
+     * box with View recycling.
+     */
+    @SuppressLint("InlinedApi")
+    private void initAutofill() {
+        if (ViewCompat.getImportantForAutofill(this) == View.IMPORTANT_FOR_AUTOFILL_AUTO) {
+            ViewCompat.setImportantForAutofill(this,
+                    View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
+        }
+    }
+
+    /**
      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
      */
diff --git a/v7/recyclerview/src/main/java/androidx/recyclerview/widget/ScrollbarHelper.java b/v7/recyclerview/src/main/java/androidx/recyclerview/widget/ScrollbarHelper.java
index 683f877..8cc24ae 100644
--- a/v7/recyclerview/src/main/java/androidx/recyclerview/widget/ScrollbarHelper.java
+++ b/v7/recyclerview/src/main/java/androidx/recyclerview/widget/ScrollbarHelper.java
@@ -95,4 +95,7 @@
         // estimate a size for full list.
         return (int) ((float) laidOutArea / laidOutRange * state.getItemCount());
     }
+
+    private ScrollbarHelper() {
+    }
 }
diff --git a/wear/res/layout/ws_single_page_nav_drawer_4_item.xml b/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
index 7057ec8..3e8b5d4 100644
--- a/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
@@ -46,7 +46,7 @@
     <androidx.wear.widget.CircledImageView
         android:id="@+id/ws_nav_drawer_icon_1"
         app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="@id/ws_nav_drawer_guide_start"
         tools:ignore="ContentDescription"
         style="@style/WsSinglePageNavDrawerIconStyle" />
@@ -54,7 +54,7 @@
     <androidx.wear.widget.CircledImageView
         android:id="@+id/ws_nav_drawer_icon_2"
         app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         tools:ignore="ContentDescription"
@@ -63,7 +63,7 @@
     <androidx.wear.widget.CircledImageView
         android:id="@+id/ws_nav_drawer_icon_3"
         app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="@id/ws_nav_drawer_guide_end"
         tools:ignore="ContentDescription"
         style="@style/WsSinglePageNavDrawerIconStyle" />
diff --git a/wear/res/values-as/strings.xml b/wear/res/values-as/strings.xml
new file mode 100644
index 0000000..2a51efa
--- /dev/null
+++ b/wear/res/values-as/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="ws_navigation_drawer_content_description" msgid="7216697245762194759">"নেভিগেশ্বন ড্ৰৱাৰ"</string>
+    <string name="ws_action_drawer_content_description" msgid="1837365417701148489">"কাৰ্য ড্ৰৱাৰ"</string>
+</resources>
diff --git a/wear/res/values-or/strings.xml b/wear/res/values-or/strings.xml
new file mode 100644
index 0000000..53e75b3
--- /dev/null
+++ b/wear/res/values-or/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="ws_navigation_drawer_content_description" msgid="7216697245762194759">"ନେଭିଗେଶନ୍ ପ୍ୟାନେଲ୍"</string>
+    <string name="ws_action_drawer_content_description" msgid="1837365417701148489">"କାର୍ଯ୍ୟକାରୀ ପ୍ୟାନେଲ୍"</string>
+</resources>
diff --git a/wear/src/main/java/androidx/wear/ambient/SharedLibraryVersion.java b/wear/src/main/java/androidx/wear/ambient/SharedLibraryVersion.java
index 228956a..2c7f14d 100644
--- a/wear/src/main/java/androidx/wear/ambient/SharedLibraryVersion.java
+++ b/wear/src/main/java/androidx/wear/ambient/SharedLibraryVersion.java
@@ -73,6 +73,9 @@
             }
             return WearableSharedLib.version();
         }
+
+        private VersionHolder() {
+        }
     }
 
     // Lazy initialization holder class (see Effective Java item 71)
@@ -90,5 +93,8 @@
             }
             return true;
         }
+
+        private PresenceHolder() {
+        }
     }
 }
diff --git a/webkit/src/main/java/androidx/webkit/WebViewCompat.java b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
index 45def2e..bdd53c7 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewCompat.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
@@ -285,8 +285,13 @@
                 webviewPackageName = (String) webviewUpdateServiceClass.getMethod(
                         "getCurrentWebViewPackageName").invoke(null);
             }
-        } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException
-                | NoSuchMethodException  e) {
+        } catch (ClassNotFoundException e) {
+            return null;
+        } catch (IllegalAccessException e) {
+            return null;
+        } catch (InvocationTargetException e) {
+            return null;
+        } catch (NoSuchMethodException e) {
             return null;
         }
         if (webviewPackageName == null) return null;
diff --git a/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java b/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java
index 5bb0666..33ac145 100644
--- a/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java
+++ b/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java
@@ -65,12 +65,17 @@
             return (InvocationHandler) createProviderFactoryMethod.invoke(null,
                     BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
                             new SupportLibraryInfo()));
-        } catch (IllegalAccessException | InvocationTargetException | ClassNotFoundException
-                | NoSuchMethodException e) {
-            // TODO(gsennton) if this happens we should avoid throwing an exception! And probably
-            // declare that the list of features supported by the WebView APK is empty.
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e);
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        } catch (NoSuchMethodException e) {
             throw new RuntimeException(e);
         }
+        // TODO(gsennton) if the above happens we should avoid throwing an exception! And probably
+        // declare that the list of features supported by the WebView APK is empty.
     }
 
     private static WebViewProviderFactoryBoundaryInterface createGlueProviderFactory() {
@@ -104,4 +109,7 @@
             throw new RuntimeException(e);
         }
     }
+
+    private WebViewGlueCommunicator() {
+    }
 }