Import translations. DO NOT MERGE am: 2cce48fde4  -s ours
am: 168cbe5d83  -s ours

Change-Id: I7e483fc719cf5e9613e7d6296fa583a206a33299
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 421dd64..b45eaff 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,7 @@
 [Hook Scripts]
-checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py
+                  --sha ${PREUPLOAD_COMMIT}
+                  --config_xml tools/checkstyle/checkstyle.xml
 
 [Builtin Hooks]
 commit_msg_test_field = true
diff --git a/library/Android.mk b/library/Android.mk
index 094d795..26f5e1f 100644
--- a/library/Android.mk
+++ b/library/Android.mk
@@ -36,10 +36,10 @@
 LOCAL_MODULE := setup-wizard-lib-gingerbread-compat
 LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/main/res \
-    $(LOCAL_PATH)/eclair-mr1/res \
-    $(LOCAL_PATH)/full-support/res
+    $(LOCAL_PATH)/gingerbread/res \
+    $(LOCAL_PATH)/recyclerview/res
 LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, main/src eclair-mr1/src full-support/src)
+LOCAL_SRC_FILES := $(call all-java-files-under, main/src gingerbread/src recyclerview/src)
 
 ifdef LOCAL_USE_AAPT2
 
diff --git a/library/common-gingerbread.mk b/library/common-gingerbread.mk
index 3cc58ec..6ced8b0 100644
--- a/library/common-gingerbread.mk
+++ b/library/common-gingerbread.mk
@@ -31,8 +31,8 @@
 ifeq (,$(findstring setup-wizard-lib-gingerbread-compat,$(LOCAL_STATIC_JAVA_LIBRARIES)))
 LOCAL_RESOURCE_DIR += \
     $(suwlib_dir)/main/res \
-    $(suwlib_dir)/eclair-mr1/res \
-    $(suwlib_dir)/full-support/res
+    $(suwlib_dir)/gingerbread/res \
+    $(suwlib_dir)/recyclerview/res
 LOCAL_AAPT_FLAGS += --extra-packages com.android.setupwizardlib
 LOCAL_STATIC_JAVA_LIBRARIES += setup-wizard-lib-gingerbread-compat
 endif
diff --git a/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_left_arrow.png b/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_left_arrow.png
deleted file mode 100644
index 1364d5c..0000000
--- a/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_left_arrow.png
+++ /dev/null
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_right_arrow.png b/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_right_arrow.png
deleted file mode 100644
index ff7a66e..0000000
--- a/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_right_arrow.png
+++ /dev/null
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_left_arrow.png b/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_left_arrow.png
deleted file mode 100644
index 0bd2a76..0000000
--- a/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_left_arrow.png
+++ /dev/null
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_right_arrow.png b/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_right_arrow.png
deleted file mode 100644
index c367b7f..0000000
--- a/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_right_arrow.png
+++ /dev/null
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_left_arrow.png b/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_left_arrow.png
deleted file mode 100644
index 2fac071..0000000
--- a/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_left_arrow.png
+++ /dev/null
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_right_arrow.png b/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_right_arrow.png
deleted file mode 100644
index c1a0765..0000000
--- a/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_right_arrow.png
+++ /dev/null
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png b/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png
deleted file mode 100644
index ea343d0..0000000
--- a/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png
+++ /dev/null
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png b/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png
deleted file mode 100644
index ae30d61..0000000
--- a/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png
+++ /dev/null
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_left_arrow.png b/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_left_arrow.png
deleted file mode 100644
index fcd2c64..0000000
--- a/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_left_arrow.png
+++ /dev/null
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_right_arrow.png b/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_right_arrow.png
deleted file mode 100644
index eacab40..0000000
--- a/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_right_arrow.png
+++ /dev/null
Binary files differ
diff --git a/library/eclair-mr1/res/drawable/suw_ic_expand_less.xml b/library/eclair-mr1/res/drawable/suw_ic_expand_less.xml
deleted file mode 100644
index e639a61..0000000
--- a/library/eclair-mr1/res/drawable/suw_ic_expand_less.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="18dp"
-    android:height="18dp"
-    android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
-    <path
-        android:fillColor="#ff000000"
-        android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14z" />
-</vector>
diff --git a/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_down_arrow.png
similarity index 100%
rename from library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_down_arrow.png
rename to library/gingerbread/res/drawable-hdpi/suw_navbar_ic_down_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_left_arrow.png b/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_left_arrow.png
new file mode 100644
index 0000000..decccac
--- /dev/null
+++ b/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_left_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_right_arrow.png b/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_right_arrow.png
new file mode 100644
index 0000000..5ba56b7
--- /dev/null
+++ b/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_right_arrow.png
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-ldrtl/suw_navbar_ic_back.xml b/library/gingerbread/res/drawable-ldrtl/suw_navbar_ic_back.xml
similarity index 100%
rename from library/eclair-mr1/res/drawable-ldrtl/suw_navbar_ic_back.xml
rename to library/gingerbread/res/drawable-ldrtl/suw_navbar_ic_back.xml
diff --git a/library/eclair-mr1/res/drawable-ldrtl/suw_navbar_ic_next.xml b/library/gingerbread/res/drawable-ldrtl/suw_navbar_ic_next.xml
similarity index 100%
rename from library/eclair-mr1/res/drawable-ldrtl/suw_navbar_ic_next.xml
rename to library/gingerbread/res/drawable-ldrtl/suw_navbar_ic_next.xml
diff --git a/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_down_arrow.png
similarity index 100%
rename from library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_down_arrow.png
rename to library/gingerbread/res/drawable-mdpi/suw_navbar_ic_down_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_left_arrow.png b/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_left_arrow.png
new file mode 100644
index 0000000..1e2984a
--- /dev/null
+++ b/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_left_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_right_arrow.png b/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_right_arrow.png
new file mode 100644
index 0000000..b8365e6
--- /dev/null
+++ b/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_right_arrow.png
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_down_arrow.png
similarity index 100%
rename from library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_down_arrow.png
rename to library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_down_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_left_arrow.png b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_left_arrow.png
new file mode 100644
index 0000000..2d2046c
--- /dev/null
+++ b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_left_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_right_arrow.png b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_right_arrow.png
new file mode 100644
index 0000000..46abe31
--- /dev/null
+++ b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_right_arrow.png
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_down_arrow.png
similarity index 100%
rename from library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_down_arrow.png
rename to library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_down_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png
new file mode 100644
index 0000000..97fed92
--- /dev/null
+++ b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png
new file mode 100644
index 0000000..f874955
--- /dev/null
+++ b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png
Binary files differ
diff --git a/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_down_arrow.png
similarity index 100%
rename from library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_down_arrow.png
rename to library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_down_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_left_arrow.png b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_left_arrow.png
new file mode 100644
index 0000000..a0ebbb9
--- /dev/null
+++ b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_left_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_right_arrow.png b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_right_arrow.png
new file mode 100644
index 0000000..c98b882
--- /dev/null
+++ b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_right_arrow.png
Binary files differ
diff --git a/library/eclair-mr1/res/drawable/suw_card_bg_dark.xml b/library/gingerbread/res/drawable/suw_card_bg_dark.xml
similarity index 100%
rename from library/eclair-mr1/res/drawable/suw_card_bg_dark.xml
rename to library/gingerbread/res/drawable/suw_card_bg_dark.xml
diff --git a/library/eclair-mr1/res/drawable/suw_card_bg_light.xml b/library/gingerbread/res/drawable/suw_card_bg_light.xml
similarity index 100%
rename from library/eclair-mr1/res/drawable/suw_card_bg_light.xml
rename to library/gingerbread/res/drawable/suw_card_bg_light.xml
diff --git a/library/eclair-mr1/res/drawable/suw_ic_expand.xml b/library/gingerbread/res/drawable/suw_ic_expand.xml
similarity index 100%
rename from library/eclair-mr1/res/drawable/suw_ic_expand.xml
rename to library/gingerbread/res/drawable/suw_ic_expand.xml
diff --git a/library/eclair-mr1/res/drawable/suw_ic_expand_more.xml b/library/gingerbread/res/drawable/suw_ic_expand_less.xml
similarity index 82%
copy from library/eclair-mr1/res/drawable/suw_ic_expand_more.xml
copy to library/gingerbread/res/drawable/suw_ic_expand_less.xml
index ed63e21..ef717c1 100644
--- a/library/eclair-mr1/res/drawable/suw_ic_expand_more.xml
+++ b/library/gingerbread/res/drawable/suw_ic_expand_less.xml
@@ -16,11 +16,13 @@
 -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="18dp"
-    android:height="18dp"
-    android:viewportWidth="24.0"
+    android:width="20dp"
+    android:height="24dp"
+    android:viewportWidth="20.0"
     android:viewportHeight="24.0">
+
     <path
         android:fillColor="#ff000000"
-        android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
+        android:pathData="M10,9.17l-5,5 1.18,1.18L10,11.53l3.83,3.82L15,14.17z" />
+
 </vector>
diff --git a/library/eclair-mr1/res/drawable/suw_ic_expand_more.xml b/library/gingerbread/res/drawable/suw_ic_expand_more.xml
similarity index 83%
rename from library/eclair-mr1/res/drawable/suw_ic_expand_more.xml
rename to library/gingerbread/res/drawable/suw_ic_expand_more.xml
index ed63e21..ff3a0c5 100644
--- a/library/eclair-mr1/res/drawable/suw_ic_expand_more.xml
+++ b/library/gingerbread/res/drawable/suw_ic_expand_more.xml
@@ -16,11 +16,13 @@
 -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="18dp"
-    android:height="18dp"
-    android:viewportWidth="24.0"
+    android:width="20dp"
+    android:height="24dp"
+    android:viewportWidth="20.0"
     android:viewportHeight="24.0">
+
     <path
         android:fillColor="#ff000000"
-        android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
+        android:pathData="M13.83,9.66L10,13.48 6.18,9.66 5,10.83l5,5 5,-5z"/>
+
 </vector>
diff --git a/library/eclair-mr1/res/drawable/suw_navbar_btn_bg_dark.xml b/library/gingerbread/res/drawable/suw_navbar_btn_bg_dark.xml
similarity index 100%
rename from library/eclair-mr1/res/drawable/suw_navbar_btn_bg_dark.xml
rename to library/gingerbread/res/drawable/suw_navbar_btn_bg_dark.xml
diff --git a/library/eclair-mr1/res/drawable/suw_navbar_btn_bg_light.xml b/library/gingerbread/res/drawable/suw_navbar_btn_bg_light.xml
similarity index 100%
rename from library/eclair-mr1/res/drawable/suw_navbar_btn_bg_light.xml
rename to library/gingerbread/res/drawable/suw_navbar_btn_bg_light.xml
diff --git a/library/eclair-mr1/res/drawable/suw_navbar_ic_back.xml b/library/gingerbread/res/drawable/suw_navbar_ic_back.xml
similarity index 100%
rename from library/eclair-mr1/res/drawable/suw_navbar_ic_back.xml
rename to library/gingerbread/res/drawable/suw_navbar_ic_back.xml
diff --git a/library/eclair-mr1/res/drawable/suw_navbar_ic_more.xml b/library/gingerbread/res/drawable/suw_navbar_ic_more.xml
similarity index 100%
rename from library/eclair-mr1/res/drawable/suw_navbar_ic_more.xml
rename to library/gingerbread/res/drawable/suw_navbar_ic_more.xml
diff --git a/library/eclair-mr1/res/drawable/suw_navbar_ic_next.xml b/library/gingerbread/res/drawable/suw_navbar_ic_next.xml
similarity index 100%
rename from library/eclair-mr1/res/drawable/suw_navbar_ic_next.xml
rename to library/gingerbread/res/drawable/suw_navbar_ic_next.xml
diff --git a/library/eclair-mr1/res/layout/suw_items_expandable_switch.xml b/library/gingerbread/res/layout/suw_items_expandable_switch.xml
similarity index 94%
rename from library/eclair-mr1/res/layout/suw_items_expandable_switch.xml
rename to library/gingerbread/res/layout/suw_items_expandable_switch.xml
index 91e482d..2b98a9f 100644
--- a/library/eclair-mr1/res/layout/suw_items_expandable_switch.xml
+++ b/library/gingerbread/res/layout/suw_items_expandable_switch.xml
@@ -94,7 +94,8 @@
         android:id="@+id/suw_items_switch_divider"
         android:layout_width="1dp"
         android:layout_height="@dimen/suw_switch_divider_height"
-        android:layout_gravity="center_vertical"
+        android:layout_gravity="top"
+        android:layout_marginTop="@dimen/suw_switch_divider_padding_top"
         android:background="?android:attr/listDivider" />
 
     <android.support.v7.widget.SwitchCompat
@@ -102,6 +103,8 @@
         style="@style/SuwSwitchStyle.Divided"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:layout_gravity="center_vertical" />
+        android:layout_gravity="top"
+        android:gravity="top"
+        android:paddingTop="@dimen/suw_switch_padding_top" />
 
 </LinearLayout>
diff --git a/library/eclair-mr1/res/layout/suw_items_switch.xml b/library/gingerbread/res/layout/suw_items_switch.xml
similarity index 100%
rename from library/eclair-mr1/res/layout/suw_items_switch.xml
rename to library/gingerbread/res/layout/suw_items_switch.xml
diff --git a/library/eclair-mr1/res/layout/suw_progress_bar.xml b/library/gingerbread/res/layout/suw_progress_bar.xml
similarity index 100%
rename from library/eclair-mr1/res/layout/suw_progress_bar.xml
rename to library/gingerbread/res/layout/suw_progress_bar.xml
diff --git a/library/eclair-mr1/res/values/attrs.xml b/library/gingerbread/res/values/attrs.xml
similarity index 100%
rename from library/eclair-mr1/res/values/attrs.xml
rename to library/gingerbread/res/values/attrs.xml
diff --git a/library/eclair-mr1/res/values/dimens.xml b/library/gingerbread/res/values/dimens.xml
similarity index 84%
rename from library/eclair-mr1/res/values/dimens.xml
rename to library/gingerbread/res/values/dimens.xml
index d40d7de..d06673f 100644
--- a/library/eclair-mr1/res/values/dimens.xml
+++ b/library/gingerbread/res/values/dimens.xml
@@ -20,10 +20,12 @@
     <!-- SwitchItem -->
     <dimen name="suw_switch_padding_start">16dp</dimen>
     <dimen name="suw_switch_padding_end">0dp</dimen>
+    <dimen name="suw_switch_padding_top">39dp</dimen>
     <dimen name="suw_switch_divider_height">32dp</dimen>
+    <dimen name="suw_switch_divider_padding_top">36dp</dimen>
     <dimen name="suw_switch_content_padding_end">16dp</dimen>
 
     <!-- ExpandableSwithItem -->
-    <dimen name="suw_expand_arrow_drawable_padding">6dp</dimen>
+    <dimen name="suw_expand_arrow_drawable_padding">4dp</dimen>
 
 </resources>
diff --git a/library/eclair-mr1/res/values/styles.xml b/library/gingerbread/res/values/styles.xml
similarity index 85%
rename from library/eclair-mr1/res/values/styles.xml
rename to library/gingerbread/res/values/styles.xml
index 1ea468d..6e525ef 100644
--- a/library/eclair-mr1/res/values/styles.xml
+++ b/library/gingerbread/res/values/styles.xml
@@ -24,6 +24,8 @@
         <!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
         <item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
         <item name="android:listPreferredItemHeight">@dimen/suw_items_preferred_height</item>
+        <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/suwMarginSides</item>
+        <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/suwMarginSides</item>
         <item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item>
         <item name="android:statusBarColor" tools:ignore="NewApi">@android:color/black</item>
         <item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item>
@@ -33,10 +35,15 @@
         <item name="android:windowSoftInputMode">adjustResize</item>
 
         <item name="colorAccent">@color/suw_color_accent_dark</item>
+        <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
         <item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item>
         <item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
         <item name="suwCardBackground">@drawable/suw_card_bg_dark</item>
+        <item name="suwDividerInsetEnd">0dp</item>
+        <item name="suwDividerInsetStart">@dimen/suw_items_icon_divider_inset</item>
+        <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_text_divider_inset</item>
         <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description</item>
+        <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle</item>
         <item name="suwListItemIconColor">@color/suw_list_item_icon_color_dark</item>
         <item name="suwMarginSides">@dimen/suw_layout_margin_sides</item>
         <item name="suwNavBarTheme">@style/SuwNavBarThemeDark</item>
@@ -48,6 +55,8 @@
         <!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
         <item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
         <item name="android:listPreferredItemHeight">@dimen/suw_items_preferred_height</item>
+        <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/suwMarginSides</item>
+        <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/suwMarginSides</item>
         <item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item>
         <item name="android:statusBarColor" tools:ignore="NewApi">@android:color/black</item>
         <item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item>
@@ -57,10 +66,15 @@
         <item name="android:windowSoftInputMode">adjustResize</item>
 
         <item name="colorAccent">@color/suw_color_accent_light</item>
+        <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
         <item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item>
         <item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
         <item name="suwCardBackground">@drawable/suw_card_bg_light</item>
+        <item name="suwDividerInsetEnd">0dp</item>
+        <item name="suwDividerInsetStart">@dimen/suw_items_icon_divider_inset</item>
+        <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_text_divider_inset</item>
         <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description</item>
+        <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle</item>
         <item name="suwListItemIconColor">@color/suw_list_item_icon_color_light</item>
         <item name="suwMarginSides">@dimen/suw_layout_margin_sides</item>
         <item name="suwNavBarTheme">@style/SuwNavBarThemeLight</item>
@@ -68,6 +82,7 @@
     </style>
 
     <style name="SuwThemeGlif" parent="Theme.AppCompat.NoActionBar">
+        <item name="android:colorBackground">@color/suw_glif_background_color_dark</item>
         <item name="android:indeterminateTint" tools:ignore="NewApi">?attr/colorControlActivated</item>
         <!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
         <item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
@@ -77,7 +92,7 @@
         <item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item>
         <item name="android:statusBarColor" tools:ignore="NewApi">@android:color/transparent</item>
         <item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item>
-        <item name="android:textColorLink">@color/suw_link_color_dark</item>
+        <item name="android:textColorLink">@color/suw_color_accent_glif_dark</item>
         <item name="android:windowAnimationStyle">@style/Animation.SuwWindowAnimation</item>
         <item name="android:windowDisablePreview">true</item>
         <item name="android:windowSoftInputMode">adjustResize</item>
@@ -87,9 +102,13 @@
         <item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item>
         <item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
         <item name="suwColorPrimary">?attr/colorPrimary</item>
-        <item name="suwDividerInset">@dimen/suw_items_glif_icon_divider_inset</item>
+        <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
+        <item name="suwDividerInsetEnd">0dp</item>
+        <item name="suwDividerInsetStart">@dimen/suw_items_glif_icon_divider_inset</item>
+        <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_glif_text_divider_inset</item>
         <item name="suwGlifHeaderGravity">start</item>
         <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description.Glif</item>
+        <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle.GlifDescription</item>
         <item name="suwListItemIconColor">@color/suw_list_item_icon_color_dark</item>
         <item name="suwMarginSides">@dimen/suw_glif_margin_sides</item>
         <item name="suwScrollIndicators">bottom</item>
@@ -98,6 +117,7 @@
     </style>
 
     <style name="SuwThemeGlif.Light" parent="Theme.AppCompat.Light.NoActionBar">
+        <item name="android:colorBackground">@color/suw_glif_background_color_light</item>
         <item name="android:indeterminateTint" tools:ignore="NewApi">?attr/colorControlActivated</item>
         <!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
         <item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
@@ -107,7 +127,7 @@
         <item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item>
         <item name="android:statusBarColor" tools:ignore="NewApi">@android:color/transparent</item>
         <item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item>
-        <item name="android:textColorLink">@color/suw_link_color_light</item>
+        <item name="android:textColorLink">@color/suw_color_accent_glif_light</item>
         <item name="android:windowAnimationStyle">@style/Animation.SuwWindowAnimation</item>
         <item name="android:windowDisablePreview">true</item>
         <item name="android:windowSoftInputMode">adjustResize</item>
@@ -117,9 +137,13 @@
         <item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item>
         <item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
         <item name="suwColorPrimary">?attr/colorPrimary</item>
-        <item name="suwDividerInset">@dimen/suw_items_glif_icon_divider_inset</item>
+        <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
+        <item name="suwDividerInsetEnd">0dp</item>
+        <item name="suwDividerInsetStart">@dimen/suw_items_glif_icon_divider_inset</item>
+        <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_glif_text_divider_inset</item>
         <item name="suwGlifHeaderGravity">start</item>
         <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description.Glif</item>
+        <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle.GlifDescription</item>
         <item name="suwListItemIconColor">@color/suw_list_item_icon_color_light</item>
         <item name="suwMarginSides">@dimen/suw_glif_margin_sides</item>
         <item name="suwScrollIndicators">bottom</item>
@@ -176,7 +200,6 @@
         <item name="buttonStyle">@style/SuwGlifButton.Primary</item>
 
         <!-- Values used in styles -->
-        <item name="android:minWidth">0dp</item>
         <item name="android:paddingLeft">@dimen/suw_glif_button_padding</item>
         <item name="android:paddingRight">@dimen/suw_glif_button_padding</item>
     </style>
diff --git a/library/eclair-mr1/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java b/library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java
similarity index 100%
rename from library/eclair-mr1/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java
rename to library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java
diff --git a/library/eclair-mr1/src/com/android/setupwizardlib/items/SwitchItem.java b/library/gingerbread/src/com/android/setupwizardlib/items/SwitchItem.java
similarity index 80%
rename from library/eclair-mr1/src/com/android/setupwizardlib/items/SwitchItem.java
rename to library/gingerbread/src/com/android/setupwizardlib/items/SwitchItem.java
index 604e47f..7459d77 100644
--- a/library/eclair-mr1/src/com/android/setupwizardlib/items/SwitchItem.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/items/SwitchItem.java
@@ -33,17 +33,35 @@
  */
 public class SwitchItem extends Item implements CompoundButton.OnCheckedChangeListener {
 
+    /**
+     * Listener for check state changes of this switch item.
+     */
     public interface OnCheckedChangeListener {
+
+        /**
+         * Callback when checked state of a {@link SwitchItem} is changed.
+         *
+         * @see #setOnCheckedChangeListener(OnCheckedChangeListener)
+         */
         void onCheckedChange(SwitchItem item, boolean isChecked);
     }
 
     private boolean mChecked = false;
     private OnCheckedChangeListener mListener;
 
+    /**
+     * Creates a default switch item.
+     */
     public SwitchItem() {
         super();
     }
 
+    /**
+     * Creates a switch item. This constructor is used for inflation from XML.
+     *
+     * @param context The context which this item is inflated in.
+     * @param attrs The XML attributes defined on the item.
+     */
     public SwitchItem(Context context, AttributeSet attrs) {
         super(context, attrs);
         final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwSwitchItem);
@@ -51,6 +69,9 @@
         a.recycle();
     }
 
+    /**
+     * Sets whether this item should be checked.
+     */
     public void setChecked(boolean checked) {
         if (mChecked != checked) {
             mChecked = checked;
@@ -61,6 +82,9 @@
         }
     }
 
+    /**
+     * @return True if this switch item is currently checked.
+     */
     public boolean isChecked() {
         return mChecked;
     }
@@ -91,6 +115,10 @@
         switchView.setEnabled(isEnabled());
     }
 
+    /**
+     * Sets a listener to listen for changes in checked state. This listener is invoked in both
+     * user toggling the switch and calls to {@link #setChecked(boolean)}.
+     */
     public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
         mListener = listener;
     }
diff --git a/library/eclair-mr1/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java b/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
similarity index 97%
rename from library/eclair-mr1/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
rename to library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
index d714eb8..1e663d6 100644
--- a/library/eclair-mr1/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
@@ -16,9 +16,8 @@
 
 package com.android.setupwizardlib.util;
 
-import static android.support.v4.os.BuildCompat.isAtLeastO;
-
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Bundle;
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
@@ -74,7 +73,7 @@
     private final ExploreByTouchHelper mExploreByTouchHelper;
 
     public LinkAccessibilityHelper(TextView view) {
-        if (!isAtLeastO()) {
+        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
             // Pre-O, we essentially extend ExploreByTouchHelper to expose a virtual view hierarchy
             mExploreByTouchHelper = new ExploreByTouchHelper(view) {
                 @Override
@@ -90,15 +89,15 @@
                 @Override
                 protected void onPopulateEventForVirtualView(int virtualViewId,
                         AccessibilityEvent event) {
-                    LinkAccessibilityHelper.this
-                            .onPopulateEventForVirtualView(virtualViewId, event);
+                    LinkAccessibilityHelper
+                            .this.onPopulateEventForVirtualView(virtualViewId, event);
                 }
 
                 @Override
                 protected void onPopulateNodeForVirtualView(int virtualViewId,
                         AccessibilityNodeInfoCompat infoCompat) {
-                    LinkAccessibilityHelper.this
-                            .onPopulateNodeForVirtualView(virtualViewId, infoCompat);
+                    LinkAccessibilityHelper
+                            .this.onPopulateNodeForVirtualView(virtualViewId, infoCompat);
 
                 }
 
diff --git a/library/eclair-mr1/src/com/android/setupwizardlib/view/NavigationBarButton.java b/library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java
similarity index 96%
rename from library/eclair-mr1/src/com/android/setupwizardlib/view/NavigationBarButton.java
rename to library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java
index 35e7050..5172c47 100644
--- a/library/eclair-mr1/src/com/android/setupwizardlib/view/NavigationBarButton.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java
@@ -26,6 +26,10 @@
 import android.util.AttributeSet;
 import android.widget.Button;
 
+/**
+ * Button for navigation bar, which includes tinting of its compound drawables to be used for dark
+ * and light themes.
+ */
 public class NavigationBarButton extends Button {
 
     public NavigationBarButton(Context context) {
@@ -120,7 +124,7 @@
 
         private ColorStateList mTintList = null;
 
-        public TintedDrawable(Drawable wrapped) {
+        TintedDrawable(Drawable wrapped) {
             super(new Drawable[] { wrapped });
         }
 
diff --git a/library/eclair-mr1/src/com/android/setupwizardlib/view/RichTextView.java b/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java
similarity index 90%
rename from library/eclair-mr1/src/com/android/setupwizardlib/view/RichTextView.java
rename to library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java
index 6ccedf0..e6bc9da 100644
--- a/library/eclair-mr1/src/com/android/setupwizardlib/view/RichTextView.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java
@@ -21,6 +21,7 @@
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.support.v4.view.ViewCompat;
+import android.support.v7.widget.AppCompatTextView;
 import android.text.Annotation;
 import android.text.SpannableString;
 import android.text.Spanned;
@@ -30,9 +31,9 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
-import android.widget.TextView;
 
 import com.android.setupwizardlib.span.LinkSpan;
+import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener;
 import com.android.setupwizardlib.span.SpanHelper;
 import com.android.setupwizardlib.util.LinkAccessibilityHelper;
 
@@ -40,7 +41,7 @@
  * An extension of TextView that automatically replaces the annotation tags as specified in
  * {@link SpanHelper#replaceSpan(android.text.Spannable, Object, Object)}
  */
-public class RichTextView extends TextView {
+public class RichTextView extends AppCompatTextView implements OnLinkClickListener {
 
     /* static section */
 
@@ -89,6 +90,7 @@
     /* non-static section */
 
     private LinkAccessibilityHelper mAccessibilityHelper;
+    private OnLinkClickListener mOnLinkClickListener;
 
     public RichTextView(Context context) {
         super(context);
@@ -164,4 +166,20 @@
             }
         }
     }
+
+    public void setOnLinkClickListener(OnLinkClickListener listener) {
+        mOnLinkClickListener = listener;
+    }
+
+    public OnLinkClickListener getOnLinkClickListener() {
+        return mOnLinkClickListener;
+    }
+
+    @Override
+    public boolean onLinkClick(LinkSpan span) {
+        if (mOnLinkClickListener != null) {
+            return mOnLinkClickListener.onLinkClick(span);
+        }
+        return false;
+    }
 }
diff --git a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java
similarity index 100%
rename from library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java
rename to library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java
diff --git a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java
similarity index 93%
rename from library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java
rename to library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java
index e2c492a..844e73e 100644
--- a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java
+++ b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java
@@ -16,12 +16,11 @@
 
 package com.android.setupwizardlib.test;
 
-import static android.support.v4.os.BuildCompat.isAtLeastO;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Bundle;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -59,7 +58,7 @@
 
     @Test
     public void testGetVirtualViewAt() {
-        if (isAtLeastO()) return;
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
         initTextView();
         final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(15), dp2Px(10));
         assertEquals("Virtual view ID should be 1", 1, virtualViewId);
@@ -67,7 +66,7 @@
 
     @Test
     public void testGetVirtualViewAtHost() {
-        if (isAtLeastO()) return;
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
         initTextView();
         final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(100), dp2Px(100));
         assertEquals("Virtual view ID should be INVALID_ID",
@@ -76,7 +75,7 @@
 
     @Test
     public void testGetVisibleVirtualViews() {
-        if (isAtLeastO()) return;
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
         initTextView();
         List<Integer> virtualViewIds = new ArrayList<>();
         mHelper.getVisibleVirtualViews(virtualViewIds);
@@ -87,7 +86,7 @@
 
     @Test
     public void testOnPopulateEventForVirtualView() {
-        if (isAtLeastO()) return;
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
         initTextView();
         AccessibilityEvent event = AccessibilityEvent.obtain();
         mHelper.onPopulateEventForVirtualView(1, event);
@@ -101,7 +100,7 @@
 
     @Test
     public void testOnPopulateEventForVirtualViewHost() {
-        if (isAtLeastO()) return;
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
         initTextView();
         AccessibilityEvent event = AccessibilityEvent.obtain();
         mHelper.onPopulateEventForVirtualView(ExploreByTouchHelper.INVALID_ID, event);
@@ -114,7 +113,7 @@
 
     @Test
     public void testOnPopulateNodeForVirtualView() {
-        if (isAtLeastO()) return;
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
         initTextView();
         AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
         mHelper.onPopulateNodeForVirtualView(1, info);
@@ -133,7 +132,7 @@
 
     @Test
     public void testNullLayout() {
-        if (isAtLeastO()) return;
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
         initTextView();
         // Setting the padding will cause the layout to be null-ed out.
         mTextView.setPadding(1, 1, 1, 1);
@@ -151,7 +150,7 @@
 
     @Test
     public void testRtlLayout() {
-        if (isAtLeastO()) return;
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
         SpannableStringBuilder ssb = new SpannableStringBuilder("מכונה בתרגום");
         ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */);
         initTextView(ssb);
@@ -171,7 +170,7 @@
 
     @Test
     public void testMultilineLink() {
-        if (isAtLeastO()) return;
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
         SpannableStringBuilder ssb = new SpannableStringBuilder(
                 "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
                 + "Praesent accumsan efficitur eros eu porttitor.");
@@ -193,7 +192,7 @@
 
     @Test
     public void testRtlMultilineLink() {
-        if (isAtLeastO()) return;
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
         String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
                 + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
                 + "דפים המחשב מיזמים ב.";
@@ -217,7 +216,7 @@
 
     @Test
     public void testBidiMultilineLink() {
-        if (isAtLeastO()) return;
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
         String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
                 + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
                 + "דפים המחשב מיזמים ב.";
diff --git a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java
similarity index 100%
rename from library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java
rename to library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java
diff --git a/library/eclair-mr1/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java
similarity index 100%
rename from library/eclair-mr1/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java
rename to library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java
diff --git a/library/eclair-mr1/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java
similarity index 100%
rename from library/eclair-mr1/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java
rename to library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java
diff --git a/library/gingerbread/test/robotest/src/com/android/setupwizardlib/util/DimensionConsistencyTest.java b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/util/DimensionConsistencyTest.java
new file mode 100644
index 0000000..43e7f03
--- /dev/null
+++ b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/util/DimensionConsistencyTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+
+import com.android.setupwizardlib.BuildConfig;
+import com.android.setupwizardlib.R;
+import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(SuwLibRobolectricTestRunner.class)
+@Config(constants = BuildConfig.class, sdk = Config.ALL_SDKS)
+public class DimensionConsistencyTest {
+
+    // Visual height of the framework switch widget
+    private static final int SWTICH_HEIGHT_DP = 26;
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light);
+    }
+
+    @Test
+    public void testSwitchPaddingTop() {
+        final Resources res = mContext.getResources();
+
+        assertEquals(
+                "Switch and divider should be aligned at center vertically: "
+                        + "suw_switch_padding_top + SWITCH_HEIGHT / 2 = "
+                        + "suw_switch_divider_padding_top + suw_switch_divider_height / 2",
+                res.getDimensionPixelSize(R.dimen.suw_switch_divider_padding_top)
+                        + (res.getDimensionPixelSize(R.dimen.suw_switch_divider_height) / 2),
+                res.getDimensionPixelSize(R.dimen.suw_switch_padding_top)
+                        + (dp2Px(SWTICH_HEIGHT_DP) / 2));
+    }
+
+    private int dp2Px(float dp) {
+        DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics);
+    }
+}
diff --git a/library/main/res/layout/suw_items_description.xml b/library/main/res/layout/suw_items_description.xml
index 26a604b..e27d7b8 100644
--- a/library/main/res/layout/suw_items_description.xml
+++ b/library/main/res/layout/suw_items_description.xml
@@ -48,7 +48,7 @@
 
         <com.android.setupwizardlib.view.RichTextView
             android:id="@+id/suw_items_title"
-            style="@style/SuwItemTitle.Description"
+            style="?attr/suwItemDescriptionTitleStyle"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:lineSpacingExtra="@dimen/suw_description_line_spacing_extra"
diff --git a/library/main/res/values-pt-rBR/strings.xml b/library/main/res/values-pt-rBR/strings.xml
index 41032f8..79d86e2 100644
--- a/library/main/res/values-pt-rBR/strings.xml
+++ b/library/main/res/values-pt-rBR/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="suw_next_button_label" msgid="7269625133873553978">"Próximo"</string>
+    <string name="suw_next_button_label" msgid="7269625133873553978">"Próxima"</string>
     <string name="suw_back_button_label" msgid="1460929053642711025">"Voltar"</string>
     <string name="suw_more_button_label" msgid="7769076059705546563">"Mais"</string>
 </resources>
diff --git a/library/main/res/values-pt/strings.xml b/library/main/res/values-pt/strings.xml
index 41032f8..79d86e2 100644
--- a/library/main/res/values-pt/strings.xml
+++ b/library/main/res/values-pt/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="suw_next_button_label" msgid="7269625133873553978">"Próximo"</string>
+    <string name="suw_next_button_label" msgid="7269625133873553978">"Próxima"</string>
     <string name="suw_back_button_label" msgid="1460929053642711025">"Voltar"</string>
     <string name="suw_more_button_label" msgid="7769076059705546563">"Mais"</string>
 </resources>
diff --git a/library/main/res/values-sw600dp/dimens.xml b/library/main/res/values-sw600dp/dimens.xml
index 2b5f3be..d1734ab 100644
--- a/library/main/res/values-sw600dp/dimens.xml
+++ b/library/main/res/values-sw600dp/dimens.xml
@@ -32,6 +32,8 @@
     <dimen name="suw_header_title_size">34sp</dimen>
 
     <!-- Items -->
+    <dimen name="suw_items_icon_divider_inset">88dp</dimen>
+    <dimen name="suw_items_text_divider_inset">40dp</dimen>
     <dimen name="suw_items_glif_icon_divider_inset">88dp</dimen>
     <dimen name="suw_items_glif_text_divider_inset">40dp</dimen>
 
diff --git a/library/main/res/values-v21/styles.xml b/library/main/res/values-v21/styles.xml
index fddfdc7..ab6f887 100644
--- a/library/main/res/values-v21/styles.xml
+++ b/library/main/res/values-v21/styles.xml
@@ -36,6 +36,12 @@
         <item name="android:background">?android:attr/colorPrimary</item>
     </style>
 
+    <!-- Button styles -->
+
+    <style name="SuwGlifButton.Tertiary" parent="SuwGlifButton.BaseTertiary">
+        <item name="android:fontFamily">sans-serif-medium</item>
+    </style>
+
     <!-- Items styles -->
 
     <style name="SuwItemContainer">
diff --git a/library/main/res/values/attrs.xml b/library/main/res/values/attrs.xml
index 14799df..36d5fb7 100644
--- a/library/main/res/values/attrs.xml
+++ b/library/main/res/values/attrs.xml
@@ -39,6 +39,7 @@
     </attr>
 
     <attr name="suwCardBackground" format="color|reference" />
+    <attr name="suwFillContentLayoutStyle" format="reference" />
     <attr name="suwDividerCondition">
         <enum name="either" value="0" />
         <enum name="both" value="1" />
@@ -72,8 +73,13 @@
     <!-- Custom view attributes -->
     <attr name="suwColorPrimary" format="color" />
     <attr name="suwHeader" format="reference" />
+    <!-- Deprecated. Use suwDividerInsetStart and suwDividerInsetEnd instead -->
     <attr name="suwDividerInset" format="dimension|reference" />
+    <attr name="suwDividerInsetEnd" format="dimension|reference" />
+    <attr name="suwDividerInsetStart" format="dimension|reference" />
+    <attr name="suwDividerInsetStartNoIcon" format="dimension|reference" />
     <attr name="suwItemDescriptionStyle" format="reference" />
+    <attr name="suwItemDescriptionTitleStyle" format="reference" />
 
     <declare-styleable name="SuwIllustration">
         <attr name="suwAspectRatio" format="float" />
@@ -87,6 +93,10 @@
         <attr name="suwHeader" />
     </declare-styleable>
 
+    <declare-styleable name="SuwIllustrationVideoView">
+        <attr name="suwVideo" format="reference" />
+    </declare-styleable>
+
     <declare-styleable name="SuwGlifLayout">
         <attr name="suwBackgroundPatterned" format="boolean" />
         <attr name="suwBackgroundBaseColor" format="color" />
@@ -98,11 +108,16 @@
         <attr name="suwStatusBarBackground" format="color|reference" />
     </declare-styleable>
 
-    <declare-styleable name="SuwMaxSizeFrameLayout">
+    <declare-styleable name="SuwIntrinsicSizeFrameLayout">
         <attr name="android:height" />
         <attr name="android:width" />
     </declare-styleable>
 
+    <declare-styleable name="SuwFillContentLayout">
+        <attr name="android:maxHeight" />
+        <attr name="android:maxWidth" />
+    </declare-styleable>
+
     <declare-styleable name="SuwSetupWizardLayout">
         <attr name="suwBackground" format="color|reference" />
         <attr name="suwBackgroundTile" format="color|reference" />
@@ -159,6 +174,8 @@
     <declare-styleable name="SuwListMixin">
         <attr name="android:entries" />
         <attr name="suwDividerInset" />
+        <attr name="suwDividerInsetEnd" />
+        <attr name="suwDividerInsetStart" />
     </declare-styleable>
 
 </resources>
diff --git a/library/main/res/values/colors.xml b/library/main/res/values/colors.xml
index f9c5da3..cd57a8a 100644
--- a/library/main/res/values/colors.xml
+++ b/library/main/res/values/colors.xml
@@ -40,5 +40,7 @@
     <!-- GLIF colors -->
     <color name="suw_color_accent_glif_dark">#ff4285f4</color>
     <color name="suw_color_accent_glif_light">#ff4285f4</color>
+    <color name="suw_glif_background_color_dark">#ff000000</color>
+    <color name="suw_glif_background_color_light">#ffffffff</color>
 
 </resources>
diff --git a/library/main/res/values/dimens.xml b/library/main/res/values/dimens.xml
index 96712f7..14d7429 100644
--- a/library/main/res/values/dimens.xml
+++ b/library/main/res/values/dimens.xml
@@ -25,7 +25,7 @@
     <!-- Calculated by (suw_glif_margin_sides - suw_glif_button_padding) -->
     <dimen name="suw_glif_button_margin_start">8dp</dimen>
     <dimen name="suw_glif_button_padding">16dp</dimen>
-    <dimen name="suw_glif_footer_bottom_padding">4dp</dimen>
+    <dimen name="suw_glif_footer_bottom_padding">0dp</dimen>
     <dimen name="suw_glif_footer_min_height">80dp</dimen>
     <dimen name="suw_glif_margin_sides">24dp</dimen>
     <dimen name="suw_glif_margin_top">48dp</dimen>
@@ -49,6 +49,12 @@
     <dimen name="suw_description_glif_margin_top">3dp</dimen>
     <dimen name="suw_description_glif_margin_bottom_lists">24dp</dimen>
 
+    <dimen name="suw_content_illustration_max_height">312dp</dimen>
+    <dimen name="suw_content_illustration_max_width">312dp</dimen>
+    <dimen name="suw_content_illustration_min_height">172dp</dimen>
+    <dimen name="suw_content_illustration_min_width">172dp</dimen>
+    <dimen name="suw_content_illustration_padding_vertical">24dp</dimen>
+
     <!-- Margin on the start to offset for margin in the drawable -->
     <dimen name="suw_radio_button_margin_start">-6dp</dimen>
     <dimen name="suw_radio_button_margin_top">0dp</dimen>
@@ -116,7 +122,7 @@
     <dimen name="suw_items_preferred_height">56dp</dimen>
 
     <!-- Navigation bar -->
-    <!-- TODO: Move this dimen to eclair-mr1, or see if it should be in platform style as well -->
+    <!-- TODO: Move this dimen to gingerbread, or see if it should be in platform style as well -->
     <dimen name="suw_navbar_button_drawable_padding" tools:ignore="UnusedResources">6dp</dimen>
     <dimen name="suw_navbar_button_padding_sides">10dp</dimen>
     <dimen name="suw_navbar_height">56dp</dimen>
diff --git a/library/main/res/values/styles.xml b/library/main/res/values/styles.xml
index 4bd99de..736bcc4 100644
--- a/library/main/res/values/styles.xml
+++ b/library/main/res/values/styles.xml
@@ -19,24 +19,38 @@
 
     <!-- General styles -->
 
-    <style name="SuwThemeGlifPixel" parent="SuwThemeGlif">
+    <style name="SuwThemeGlifV2" parent="SuwThemeGlif">
+        <item name="android:colorBackground">@color/suw_glif_background_color_dark</item>
         <item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
 
         <item name="suwBackgroundBaseColor">?android:attr/colorBackground</item>
         <item name="suwBackgroundPatterned">false</item>
+        <item name="suwDividerInsetEnd">?attr/suwMarginSides</item>
+        <item name="suwDividerInsetStart">?attr/suwMarginSides</item>
+        <item name="suwDividerInsetStartNoIcon">?attr/suwMarginSides</item>
         <item name="suwGlifHeaderGravity">center_horizontal</item>
         <item name="suwScrollIndicators">top|bottom</item>
     </style>
 
-    <style name="SuwThemeGlifPixel.Light" parent="SuwThemeGlif.Light">
+    <!-- Deprecated. Use SuwThemeGlifV2 instead -->
+    <style name="SuwThemeGlifPixel" parent="SuwThemeGlifV2" />
+
+    <style name="SuwThemeGlifV2.Light" parent="SuwThemeGlif.Light">
+        <item name="android:colorBackground">@color/suw_glif_background_color_light</item>
         <item name="android:windowLightStatusBar" tools:targetApi="m">true</item>
 
         <item name="suwBackgroundBaseColor">?android:attr/colorBackground</item>
         <item name="suwBackgroundPatterned">false</item>
+        <item name="suwDividerInsetEnd">?attr/suwMarginSides</item>
+        <item name="suwDividerInsetStart">?attr/suwMarginSides</item>
+        <item name="suwDividerInsetStartNoIcon">?attr/suwMarginSides</item>
         <item name="suwGlifHeaderGravity">center_horizontal</item>
         <item name="suwScrollIndicators">top|bottom</item>
     </style>
 
+    <!-- Deprecated. Use SuwThemeGlifV2.Light instead -->
+    <style name="SuwThemeGlifPixel.Light" parent="SuwThemeGlifV2.Light" />
+
     <style name="Animation.SuwWindowAnimation" parent="@android:style/Animation.Activity">
         <item name="android:activityOpenEnterAnimation">@anim/suw_slide_next_in</item>
         <item name="android:activityOpenExitAnimation">@anim/suw_slide_next_out</item>
@@ -109,6 +123,21 @@
         <item name="android:gravity">top</item>
     </style>
 
+    <style name="SuwFillContentLayout">
+        <item name="android:minWidth">@dimen/suw_content_illustration_min_width</item>
+        <item name="android:minHeight">@dimen/suw_content_illustration_min_height</item>
+        <item name="android:maxWidth">@dimen/suw_content_illustration_max_width</item>
+        <item name="android:maxHeight">@dimen/suw_content_illustration_max_height</item>
+        <item name="android:paddingTop">@dimen/suw_content_illustration_padding_vertical</item>
+        <item name="android:paddingBottom">@dimen/suw_content_illustration_padding_vertical</item>
+    </style>
+
+    <!-- Ignore UnusedResources: used by clients -->
+    <style name="SuwContentIllustration" tools:ignore="UnusedResources">
+        <item name="android:layout_gravity">center</item>
+        <item name="android:scaleType">fitCenter</item>
+    </style>
+
     <!-- Card layout (for tablets) -->
 
     <style name="SuwBaseCardTitle">
@@ -160,7 +189,7 @@
 
     <!-- Use this style with a Button not a TextView, so that TalkBack will announce it as a
          button. -->
-    <style name="SuwGlifButton.Tertiary" parent="SuwGlifButton.Secondary">
+    <style name="SuwGlifButton.BaseTertiary" parent="SuwGlifButton.Secondary">
         <!-- This style can be applied to a button either as a "style" in XML, or as a theme in
              ContextThemeWrapper. These self-referencing attributes make sure this is applied as
              both to the button. -->
@@ -169,13 +198,17 @@
 
         <item name="android:background">@null</item>
         <item name="android:fontFamily" tools:targetApi="jelly_bean">sans-serif</item>
+        <item name="android:layout_gravity">?attr/suwGlifHeaderGravity</item>
         <item name="android:padding">0dp</item>
         <item name="android:textAllCaps" tools:targetApi="ice_cream_sandwich">false</item>
     </style>
 
+    <style name="SuwGlifButton.Tertiary" parent="SuwGlifButton.BaseTertiary" />
+
     <!-- The start and end paddings are asymmetric because start buttons are borderless buttons
          which aligns the text label. -->
     <style name="SuwGlifButtonBar">
+        <item name="android:baselineAligned">false</item>
         <item name="android:clipChildren">false</item>
         <item name="android:clipToPadding">false</item>
         <item name="android:gravity">center_vertical</item>
@@ -206,7 +239,7 @@
         <item name="android:paddingTop">@dimen/suw_items_verbose_padding_vertical</item>
     </style>
 
-    <style name="SuwItemTitle.Description" parent="SuwItemTitle">
+    <style name="SuwItemTitle.GlifDescription" parent="SuwItemTitle">
         <item name="android:gravity">?attr/suwGlifHeaderGravity</item>
         <item name="android:textAlignment" tools:targetApi="jelly_bean_mr1">gravity</item>
     </style>
@@ -228,6 +261,7 @@
         <item name="android:layout_marginLeft">?attr/suwMarginSides</item>
         <item name="android:layout_marginRight">?attr/suwMarginSides</item>
         <item name="android:layout_marginTop">@dimen/suw_glif_header_title_margin_top</item>
+        <item name="android:fontFamily" tools:targetApi="jelly_bean">google-sans</item>
         <item name="android:textAlignment" tools:targetApi="jelly_bean_mr1">gravity</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
diff --git a/library/main/src/com/android/setupwizardlib/GlifListLayout.java b/library/main/src/com/android/setupwizardlib/GlifListLayout.java
index c6443f9..8266e5f 100644
--- a/library/main/src/com/android/setupwizardlib/GlifListLayout.java
+++ b/library/main/src/com/android/setupwizardlib/GlifListLayout.java
@@ -113,27 +113,51 @@
     }
 
     /**
-     * Sets the start inset of the divider. This will use the default divider drawable set in the
-     * theme and inset it {@code inset} pixels to the right (or left in RTL layouts).
-     *
-     * @param inset The number of pixels to inset on the "start" side of the list divider. Typically
-     *              this will be either {@code @dimen/suw_items_glif_icon_divider_inset} or
-     *              {@code @dimen/suw_items_glif_text_divider_inset}.
-     *
-     * @see ListMixin#setDividerInset(int)
+     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
      */
+    @Deprecated
     public void setDividerInset(int inset) {
         mListMixin.setDividerInset(inset);
     }
 
     /**
-     * @see ListMixin#getDividerInset()
+     * Sets the start inset of the divider. This will use the default divider drawable set in the
+     * theme and apply insets to it.
+     *
+     * @param start The number of pixels to inset on the "start" side of the list divider. Typically
+     *              this will be either {@code @dimen/suw_items_glif_icon_divider_inset} or
+     *              {@code @dimen/suw_items_glif_text_divider_inset}.
+     * @param end The number of pixels to inset on the "end" side of the list divider.
+     *
+     * @see ListMixin#setDividerInsets(int, int)
      */
+    public void setDividerInsets(int start, int end) {
+        mListMixin.setDividerInsets(start, end);
+    }
+
+    /**
+     * @deprecated Use {@link #getDividerInsetStart()} instead.
+     */
+    @Deprecated
     public int getDividerInset() {
         return mListMixin.getDividerInset();
     }
 
     /**
+     * @see ListMixin#getDividerInsetStart()
+     */
+    public int getDividerInsetStart() {
+        return mListMixin.getDividerInsetStart();
+    }
+
+    /**
+     * @see ListMixin#getDividerInsetEnd()
+     */
+    public int getDividerInsetEnd() {
+        return mListMixin.getDividerInsetEnd();
+    }
+
+    /**
      * @see ListMixin#getDivider()
      */
     public Drawable getDivider() {
diff --git a/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java b/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java
index e31edb3..51c1a49 100644
--- a/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java
+++ b/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java
@@ -228,7 +228,7 @@
             p.close();
 
             p = sPatternPaths[5] = new Path();
-            p.moveTo(178.44286f, 766.85714f);
+            p.moveTo(178.44286f, 766.8571f);
             p.lineTo(308.7f, 768f);
             p.cubicTo(381.7f, 604.6f, 481.6f, 344.3f, 562.2f, 0f);
             p.lineTo(0f, 0f);
diff --git a/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java b/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java
index 0457451..050d566 100644
--- a/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java
+++ b/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java
@@ -113,19 +113,51 @@
      *              {@code @dimen/suw_items_text_divider_inset}.
      *
      * @see ListMixin#setDividerInset(int)
+     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
      */
+    @Deprecated
     public void setDividerInset(int inset) {
         mListMixin.setDividerInset(inset);
     }
 
     /**
-     * @see ListMixin#getDividerInset()
+     * Sets the start inset of the divider. This will use the default divider drawable set in the
+     * theme and apply insets to it.
+     *
+     * @param start The number of pixels to inset on the "start" side of the list divider. Typically
+     *              this will be either {@code @dimen/suw_items_icon_divider_inset} or
+     *              {@code @dimen/suw_items_text_divider_inset}.
+     * @param end The number of pixels to inset on the "end" side of the list divider.
+     *
+     * @see ListMixin#setDividerInsets(int, int)
      */
+    public void setDividerInsets(int start, int end) {
+        mListMixin.setDividerInsets(start, end);
+    }
+
+    /**
+     * @deprecated Use {@link #getDividerInsetStart()} instead.
+     */
+    @Deprecated
     public int getDividerInset() {
         return mListMixin.getDividerInset();
     }
 
     /**
+     * @see ListMixin#getDividerInsetStart()
+     */
+    public int getDividerInsetStart() {
+        return mListMixin.getDividerInsetStart();
+    }
+
+    /**
+     * @see ListMixin#getDividerInsetEnd()
+     */
+    public int getDividerInsetEnd() {
+        return mListMixin.getDividerInsetEnd();
+    }
+
+    /**
      * @see ListMixin#getDivider()
      */
     public Drawable getDivider() {
diff --git a/library/main/src/com/android/setupwizardlib/items/Item.java b/library/main/src/com/android/setupwizardlib/items/Item.java
index 59ab1a1..fc8823e 100644
--- a/library/main/src/com/android/setupwizardlib/items/Item.java
+++ b/library/main/src/com/android/setupwizardlib/items/Item.java
@@ -155,8 +155,7 @@
             // Set the image drawable to null before setting the state and level to avoid affecting
             // any recycled drawable in the ImageView
             iconView.setImageDrawable(null);
-            iconView.setImageState(icon.getState(), false /* merge */);
-            iconView.setImageLevel(icon.getLevel());
+            onMergeIconStateAndLevels(iconView, icon);
             iconView.setImageDrawable(icon);
             iconContainer.setVisibility(View.VISIBLE);
         } else {
@@ -165,4 +164,14 @@
 
         view.setId(getViewId());
     }
+
+    /**
+     * Copies state and level information from {@link #getIcon()} to the currently bound view's
+     * ImageView. Subclasses can override this method to change whats being copied from the icon
+     * to the ImageView.
+     */
+    protected void onMergeIconStateAndLevels(ImageView iconView, Drawable icon) {
+        iconView.setImageState(icon.getState(), false /* merge */);
+        iconView.setImageLevel(icon.getLevel());
+    }
 }
diff --git a/library/main/src/com/android/setupwizardlib/span/LinkSpan.java b/library/main/src/com/android/setupwizardlib/span/LinkSpan.java
index e4f9854..a5f0424 100644
--- a/library/main/src/com/android/setupwizardlib/span/LinkSpan.java
+++ b/library/main/src/com/android/setupwizardlib/span/LinkSpan.java
@@ -17,8 +17,10 @@
 package com.android.setupwizardlib.span;
 
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.graphics.Typeface;
 import android.os.Build;
+import android.support.annotation.Nullable;
 import android.text.TextPaint;
 import android.text.style.ClickableSpan;
 import android.util.Log;
@@ -26,8 +28,8 @@
 
 /**
  * A clickable span that will listen for click events and send it back to the context. To use this
- * class, implement {@link com.android.setupwizardlib.span.LinkSpan.OnClickListener} in your
- * context (typically your Activity).
+ * class, implement {@link OnLinkClickListener} in your TextView, or use
+ * {@link com.android.setupwizardlib.view.RichTextView#setOnClickListener(View.OnClickListener)}.
  *
  * <p />Note on accessibility: For TalkBack to be able to traverse and interact with the links, you
  * should use {@code LinkAccessibilityHelper} in your {@code TextView} subclass. Optionally you can
@@ -49,10 +51,29 @@
     private static final Typeface TYPEFACE_MEDIUM =
             Typeface.create("sans-serif-medium", Typeface.NORMAL);
 
+    /**
+     * @deprecated Use {@link OnLinkClickListener}
+     */
+    @Deprecated
     public interface OnClickListener {
         void onClick(LinkSpan span);
     }
 
+    /**
+     * Listener that is invoked when a link span is clicked. If the containing view of this span
+     * implements this interface, this will be invoked when the link is clicked.
+     */
+    public interface OnLinkClickListener {
+
+        /**
+         * Called when a link has been clicked.
+         *
+         * @param span The span that was clicked.
+         * @return True if the click was handled, stopping further propagation of the click event.
+         */
+        boolean onLinkClick(LinkSpan span);
+    }
+
     /* non-static section */
 
     private final String mId;
@@ -63,9 +84,7 @@
 
     @Override
     public void onClick(View view) {
-        final Context context = view.getContext();
-        if (context instanceof OnClickListener) {
-            ((OnClickListener) context).onClick(this);
+        if (dispatchClick(view)) {
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                 view.cancelPendingInputEvents();
             }
@@ -74,6 +93,41 @@
         }
     }
 
+    private boolean dispatchClick(View view) {
+        boolean handled = false;
+        if (view instanceof OnLinkClickListener) {
+            handled = ((OnLinkClickListener) view).onLinkClick(this);
+        }
+        if (!handled) {
+            final OnClickListener listener = getLegacyListenerFromContext(view.getContext());
+            if (listener != null) {
+                listener.onClick(this);
+                handled = true;
+            }
+        }
+        return handled;
+    }
+
+    /**
+     * @deprecated Deprecated together with {@link OnClickListener}
+     */
+    @Nullable
+    @Deprecated
+    private OnClickListener getLegacyListenerFromContext(@Nullable Context context) {
+        while (true) {
+            if (context instanceof OnClickListener) {
+                return (OnClickListener) context;
+            } else if (context instanceof ContextWrapper) {
+                // Unwrap any context wrapper, in base the base context implements onClickListener.
+                // ContextWrappers cannot have circular base contexts, so at some point this will
+                // reach the one of the other cases and return.
+                context = ((ContextWrapper) context).getBaseContext();
+            } else {
+                return null;
+            }
+        }
+    }
+
     @Override
     public void updateDrawState(TextPaint drawState) {
         super.updateDrawState(drawState);
diff --git a/library/main/src/com/android/setupwizardlib/template/ListMixin.java b/library/main/src/com/android/setupwizardlib/template/ListMixin.java
index 497d46e..10e85a9 100644
--- a/library/main/src/com/android/setupwizardlib/template/ListMixin.java
+++ b/library/main/src/com/android/setupwizardlib/template/ListMixin.java
@@ -49,7 +49,9 @@
 
     private Drawable mDivider;
     private Drawable mDefaultDivider;
-    private int mDividerInset;
+
+    private int mDividerInsetStart;
+    private int mDividerInsetEnd;
 
     /**
      * @param layout The layout this mixin belongs to.
@@ -69,8 +71,16 @@
             setAdapter(new ItemAdapter(inflated));
         }
         int dividerInset =
-                a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInset, 0);
-        setDividerInset(dividerInset);
+                a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInset, -1);
+        if (dividerInset != -1) {
+            setDividerInset(dividerInset);
+        } else {
+            int dividerInsetStart =
+                    a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInsetStart, 0);
+            int dividerInsetEnd =
+                    a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInsetEnd, 0);
+            setDividerInsets(dividerInsetStart, dividerInsetEnd);
+        }
         a.recycle();
     }
 
@@ -136,23 +146,49 @@
     }
 
     /**
+     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
+     */
+    @Deprecated
+    public void setDividerInset(int inset) {
+        setDividerInsets(inset, 0);
+    }
+
+    /**
      * Sets the start inset of the divider. This will use the default divider drawable set in the
-     * theme and inset it {@code inset} pixels to the right (or left in RTL layouts).
+     * theme and apply insets to it.
      *
-     * @param inset The number of pixels to inset on the "start" side of the list divider. Typically
+     * @param start The number of pixels to inset on the "start" side of the list divider. Typically
      *              this will be either {@code @dimen/suw_items_glif_icon_divider_inset} or
      *              {@code @dimen/suw_items_glif_text_divider_inset}.
+     * @param end The number of pixels to inset on the "end" side of the list divider.
      */
-    public void setDividerInset(int inset) {
-        mDividerInset = inset;
+    public void setDividerInsets(int start, int end) {
+        mDividerInsetStart = start;
+        mDividerInsetEnd = end;
         updateDivider();
     }
 
     /**
      * @return The number of pixels inset on the start side of the divider.
+     * @deprecated This is the same as {@link #getDividerInsetStart()}. Use that instead.
      */
+    @Deprecated
     public int getDividerInset() {
-        return mDividerInset;
+        return getDividerInsetStart();
+    }
+
+    /**
+     * @return The number of pixels inset on the start side of the divider.
+     */
+    public int getDividerInsetStart() {
+        return mDividerInsetStart;
+    }
+
+    /**
+     * @return The number of pixels inset on the end side of the divider.
+     */
+    public int getDividerInsetEnd() {
+        return mDividerInsetEnd;
     }
 
     private void updateDivider() {
@@ -170,9 +206,9 @@
             }
             mDivider = DrawableLayoutDirectionHelper.createRelativeInsetDrawable(
                     mDefaultDivider,
-                    mDividerInset /* start */,
+                    mDividerInsetStart /* start */,
                     0 /* top */,
-                    0 /* end */,
+                    mDividerInsetEnd /* end */,
                     0 /* bottom */,
                     mTemplateLayout);
             listView.setDivider(mDivider);
diff --git a/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java b/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
index 5ccd081..a93694c 100644
--- a/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
+++ b/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
@@ -43,6 +43,8 @@
     private static final String EXTRA_RESULT_CODE = "com.android.setupwizard.ResultCode";
     @VisibleForTesting
     static final String EXTRA_IS_FIRST_RUN = "firstRun";
+    @VisibleForTesting
+    static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup";
 
     public static final String EXTRA_THEME = "theme";
     public static final String EXTRA_USE_IMMERSIVE_MODE = "useImmersiveMode";
@@ -71,13 +73,25 @@
      * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the dark variant of the
      * theme used in setup wizard for O DR.
      */
-    public static final String THEME_GLIF_PIXEL = "glif_pixel";
+    public static final String THEME_GLIF_V2 = "glif_v2";
+
+    /**
+     * @deprecated Use {@link #THEME_GLIF_V2} instead.
+     */
+    @Deprecated
+    public static final String THEME_GLIF_PIXEL = THEME_GLIF_V2;
 
     /**
      * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the default theme used in
      * setup wizard for O DR.
      */
-    public static final String THEME_GLIF_PIXEL_LIGHT = "glif_pixel_light";
+    public static final String THEME_GLIF_V2_LIGHT = "glif_v2_light";
+
+    /**
+     * @deprecated Use {@link #THEME_GLIF_V2_LIGHT} instead.
+     */
+    @Deprecated
+    public static final String THEME_GLIF_PIXEL_LIGHT = THEME_GLIF_V2_LIGHT;
 
     /**
      * Get an intent that will invoke the next step of setup wizard.
@@ -129,6 +143,8 @@
         dstIntent.putExtra(EXTRA_THEME, srcIntent.getStringExtra(EXTRA_THEME));
         dstIntent.putExtra(EXTRA_IS_FIRST_RUN,
                 srcIntent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false));
+        dstIntent.putExtra(EXTRA_IS_DEFERRED_SETUP,
+                srcIntent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false));
         dstIntent.putExtra(EXTRA_SCRIPT_URI, srcIntent.getStringExtra(EXTRA_SCRIPT_URI));
         dstIntent.putExtra(EXTRA_ACTION_ID, srcIntent.getStringExtra(EXTRA_ACTION_ID));
     }
@@ -185,6 +201,18 @@
     }
 
     /**
+     * Checks whether an intent is running in the deferred setup wizard flow.
+     *
+     * @param originalIntent The original intent that was used to start the step, usually via
+     *                       {@link android.app.Activity#getIntent()}.
+     * @return true if the intent passed in was running in deferred setup wizard.
+     */
+    public static boolean isDeferredSetupWizard(Intent originalIntent) {
+        return originalIntent != null
+                && originalIntent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false);
+    }
+
+    /**
      * Checks the intent whether the extra indicates that the light theme should be used or not. If
      * the theme is not specified in the intent, or the theme specified is unknown, the value def
      * will be returned.
@@ -208,10 +236,10 @@
      */
     public static boolean isLightTheme(String theme, boolean def) {
         if (THEME_HOLO_LIGHT.equals(theme) || THEME_MATERIAL_LIGHT.equals(theme)
-                || THEME_GLIF_LIGHT.equals(theme) || THEME_GLIF_PIXEL_LIGHT.equals(theme)) {
+                || THEME_GLIF_LIGHT.equals(theme) || THEME_GLIF_V2_LIGHT.equals(theme)) {
             return true;
         } else if (THEME_HOLO.equals(theme) || THEME_MATERIAL.equals(theme)
-                || THEME_GLIF.equals(theme) || THEME_GLIF_PIXEL.equals(theme)) {
+                || THEME_GLIF.equals(theme) || THEME_GLIF_V2.equals(theme)) {
             return false;
         } else {
             return def;
@@ -256,10 +284,10 @@
     public static @StyleRes int getThemeRes(String theme, @StyleRes int defaultTheme) {
         if (theme != null) {
             switch (theme) {
-                case THEME_GLIF_PIXEL_LIGHT:
-                    return R.style.SuwThemeGlifPixel_Light;
-                case THEME_GLIF_PIXEL:
-                    return R.style.SuwThemeGlifPixel;
+                case THEME_GLIF_V2_LIGHT:
+                    return R.style.SuwThemeGlifV2_Light;
+                case THEME_GLIF_V2:
+                    return R.style.SuwThemeGlifV2;
                 case THEME_GLIF_LIGHT:
                     return R.style.SuwThemeGlif_Light;
                 case THEME_GLIF:
@@ -268,6 +296,8 @@
                     return R.style.SuwThemeMaterial_Light;
                 case THEME_MATERIAL:
                     return R.style.SuwThemeMaterial;
+                default:
+                    // fall through
             }
         }
         return defaultTheme;
diff --git a/library/main/src/com/android/setupwizardlib/view/FillContentLayout.java b/library/main/src/com/android/setupwizardlib/view/FillContentLayout.java
new file mode 100644
index 0000000..2c28090
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/view/FillContentLayout.java
@@ -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 com.android.setupwizardlib.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.setupwizardlib.R;
+
+/**
+ * A layout that will measure its children size based on the space it is given, by using its
+ * {@code android:minWidth}, {@code android:minHeight}, {@code android:maxWidth}, and
+ * {@code android:maxHeight} values.
+ *
+ * <p>Typically this is used to show an illustration image or video on the screen. For optimal UX,
+ * those assets typically want to occupy the remaining space available on screen within a certain
+ * range, and then stop scaling beyond the min/max size attributes. Therefore this view is typically
+ * used inside a ScrollView with {@code fillViewport} set to true, together with a linear layout
+ * weight or relative layout to fill the remaining space visible on screen.
+ *
+ * <p>When measuring, this view ignores its children and simply layout according to the minWidth /
+ * minHeight given. Therefore it is common for children of this layout to have width / height set to
+ * {@code match_parent}. The maxWidth / maxHeight values will then be applied to the children to
+ * make sure they are not too big.
+ */
+public class FillContentLayout extends FrameLayout {
+
+    private int mMaxWidth;
+    private int mMaxHeight;
+
+    public FillContentLayout(Context context) {
+        this(context, null);
+    }
+
+    public FillContentLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.suwFillContentLayoutStyle);
+    }
+
+    public FillContentLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context, attrs, defStyleAttr);
+    }
+
+    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+        TypedArray a = context.obtainStyledAttributes(
+                attrs,
+                R.styleable.SuwFillContentLayout,
+                defStyleAttr,
+                0);
+
+        mMaxHeight = a.getDimensionPixelSize(
+                R.styleable.SuwFillContentLayout_android_maxHeight, -1);
+        mMaxWidth = a.getDimensionPixelSize(R.styleable.SuwFillContentLayout_android_maxWidth, -1);
+
+        a.recycle();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // Measure this view with the minWidth and minHeight, without asking the children.
+        // (Children size is the drawable's intrinsic size, and we don't want that to influence
+        // the size of the illustration).
+        setMeasuredDimension(
+                getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
+                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
+
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            measureIllustrationChild(getChildAt(i), getMeasuredWidth(), getMeasuredHeight());
+        }
+    }
+
+    private void measureIllustrationChild(View child, int parentWidth, int parentHeight) {
+        // Modified from ViewGroup#measureChildWithMargins
+        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+
+        // Create measure specs that are no bigger than min(parentSize, maxSize)
+
+        int childWidthMeasureSpec = getMaxSizeMeasureSpec(
+                Math.min(mMaxWidth, parentWidth),
+                getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin,
+                lp.width);
+        int childHeightMeasureSpec = getMaxSizeMeasureSpec(
+                Math.min(mMaxHeight, parentHeight),
+                getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin,
+                lp.height);
+
+        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+    }
+
+    private static int getMaxSizeMeasureSpec(int maxSize, int padding, int childDimension) {
+        // Modified from ViewGroup#getChildMeasureSpec
+        int size = Math.max(0, maxSize - padding);
+
+        if (childDimension >= 0) {
+            // Child wants a specific size... so be it
+            return MeasureSpec.makeMeasureSpec(childDimension, MeasureSpec.EXACTLY);
+        } else if (childDimension == LayoutParams.MATCH_PARENT) {
+            // Child wants to be our size. So be it.
+            return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
+            // Child wants to determine its own size. It can't be
+            // bigger than us.
+            return MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
+        }
+        return 0;
+    }
+}
diff --git a/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java b/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java
new file mode 100644
index 0000000..9f1d2e7
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.view;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.SurfaceTexture;
+import android.graphics.drawable.Animatable;
+import android.media.MediaPlayer;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.RawRes;
+import android.support.annotation.VisibleForTesting;
+import android.util.AttributeSet;
+import android.view.Surface;
+import android.view.TextureView;
+import android.view.View;
+
+import com.android.setupwizardlib.R;
+
+/**
+ * A view for displaying videos in a continuous loop (without audio). This is typically used for
+ * animated illustrations.
+ *
+ * <p>The video can be specified using {@code app:suwVideo}, specifying the raw resource to the mp4
+ * video. Optionally, {@code app:suwLoopStartMs} can be used to specify which part of the video it
+ * should loop back to
+ *
+ * <p>For optimal file size, use avconv or other video compression tool to remove the unused audio
+ * track and reduce the size of your video asset:
+ *     avconv -i [input file] -vcodec h264 -crf 20 -an [output_file]
+ */
+@TargetApi(VERSION_CODES.ICE_CREAM_SANDWICH)
+public class IllustrationVideoView extends TextureView implements Animatable,
+        TextureView.SurfaceTextureListener,
+        MediaPlayer.OnPreparedListener,
+        MediaPlayer.OnSeekCompleteListener,
+        MediaPlayer.OnInfoListener {
+
+    protected float mAspectRatio = 1.0f; // initial guess until we know
+
+    protected MediaPlayer mMediaPlayer;
+
+    private @RawRes int mVideoResId = 0;
+
+    @VisibleForTesting Surface mSurface;
+
+    public IllustrationVideoView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.SuwIllustrationVideoView);
+        mVideoResId = a.getResourceId(R.styleable.SuwIllustrationVideoView_suwVideo, 0);
+        a.recycle();
+
+        // By default the video scales without interpolation, resulting in jagged edges in the
+        // video. This works around it by making the view go through scaling, which will apply
+        // anti-aliasing effects.
+        setScaleX(0.9999999f);
+        setScaleX(0.9999999f);
+
+        setSurfaceTextureListener(this);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        int height = MeasureSpec.getSize(heightMeasureSpec);
+
+        if (height < width * mAspectRatio) {
+            // Height constraint is tighter. Need to scale down the width to fit aspect ratio.
+            width = (int) (height / mAspectRatio);
+        } else {
+            // Width constraint is tighter. Need to scale down the height to fit aspect ratio.
+            height = (int) (width * mAspectRatio);
+        }
+
+        super.onMeasure(
+                MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+    }
+
+    /**
+     * Set the video to be played by this view.
+     *
+     * @param resId Resource ID of the video, typically an MP4 under res/raw.
+     */
+    public void setVideoResource(@RawRes int resId) {
+        if (resId != mVideoResId) {
+            mVideoResId = resId;
+            createMediaPlayer();
+        }
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        super.onWindowFocusChanged(hasWindowFocus);
+        if (hasWindowFocus) {
+            start();
+        } else {
+            stop();
+        }
+    }
+
+    /**
+     * Creates a media player for the current URI. The media player will be started immediately if
+     * the view's window is visible. If there is an existing media player, it will be released.
+     */
+    private void createMediaPlayer() {
+        if (mMediaPlayer != null) {
+            mMediaPlayer.release();
+        }
+        if (mSurface == null || mVideoResId == 0) {
+            return;
+        }
+
+        mMediaPlayer = MediaPlayer.create(getContext(), mVideoResId);
+
+        mMediaPlayer.setSurface(mSurface);
+        mMediaPlayer.setOnPreparedListener(this);
+        mMediaPlayer.setOnSeekCompleteListener(this);
+        mMediaPlayer.setOnInfoListener(this);
+
+        float aspectRatio = (float) mMediaPlayer.getVideoHeight() / mMediaPlayer.getVideoWidth();
+        if (mAspectRatio != aspectRatio) {
+            mAspectRatio = aspectRatio;
+            requestLayout();
+        }
+        if (getWindowVisibility() == View.VISIBLE) {
+            start();
+        }
+    }
+
+    /**
+     * Whether the media player should play the video in a continuous loop. The default value is
+     * true.
+     */
+    protected boolean shouldLoop() {
+        return true;
+    }
+
+    /**
+     * Release any resources used by this view. This is automatically called in
+     * onSurfaceTextureDestroyed so in most cases you don't have to call this.
+     */
+    public void release() {
+        if (mMediaPlayer != null) {
+            mMediaPlayer.stop();
+            mMediaPlayer.release();
+            mMediaPlayer = null;
+        }
+        if (mSurface != null) {
+            mSurface.release();
+            mSurface = null;
+        }
+    }
+
+    /* SurfaceTextureListener methods */
+
+    @Override
+    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
+        // Keep the view hidden until video starts
+        setVisibility(View.INVISIBLE);
+        mSurface = new Surface(surfaceTexture);
+        createMediaPlayer();
+    }
+
+    @Override
+    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
+    }
+
+    @Override
+    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+        release();
+        return true;
+    }
+
+    @Override
+    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
+    }
+
+    /* Animatable methods */
+
+    @Override
+    public void start() {
+        if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
+            mMediaPlayer.start();
+        }
+    }
+
+    @Override
+    public void stop() {
+        if (mMediaPlayer != null) {
+            mMediaPlayer.pause();
+        }
+    }
+
+    @Override
+    public boolean isRunning() {
+        return mMediaPlayer != null && mMediaPlayer.isPlaying();
+    }
+
+    /* MediaPlayer callbacks */
+
+    @Override
+    public boolean onInfo(MediaPlayer mp, int what, int extra) {
+        if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
+            // Video available, show view now
+            setVisibility(View.VISIBLE);
+        }
+        return false;
+    }
+
+    @Override
+    public void onPrepared(MediaPlayer mp) {
+        mp.setLooping(shouldLoop());
+    }
+
+    @Override
+    public void onSeekComplete(MediaPlayer mp) {
+        mp.start();
+    }
+}
diff --git a/library/main/src/com/android/setupwizardlib/view/IntrinsicSizeFrameLayout.java b/library/main/src/com/android/setupwizardlib/view/IntrinsicSizeFrameLayout.java
index e9ab1a7..02fdcc7 100644
--- a/library/main/src/com/android/setupwizardlib/view/IntrinsicSizeFrameLayout.java
+++ b/library/main/src/com/android/setupwizardlib/view/IntrinsicSizeFrameLayout.java
@@ -56,11 +56,11 @@
 
     private void init(Context context, AttributeSet attrs, int defStyleAttr) {
         final TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.SuwMaxSizeFrameLayout, defStyleAttr, 0);
+                R.styleable.SuwIntrinsicSizeFrameLayout, defStyleAttr, 0);
         mIntrinsicHeight =
-                a.getDimensionPixelSize(R.styleable.SuwMaxSizeFrameLayout_android_height, 0);
+                a.getDimensionPixelSize(R.styleable.SuwIntrinsicSizeFrameLayout_android_height, 0);
         mIntrinsicWidth =
-                a.getDimensionPixelSize(R.styleable.SuwMaxSizeFrameLayout_android_width, 0);
+                a.getDimensionPixelSize(R.styleable.SuwIntrinsicSizeFrameLayout_android_width, 0);
         a.recycle();
     }
 
diff --git a/library/platform/res/values-v23/styles.xml b/library/platform/res/values-v23/styles.xml
index 59265d9..2eb5caf 100644
--- a/library/platform/res/values-v23/styles.xml
+++ b/library/platform/res/values-v23/styles.xml
@@ -41,7 +41,12 @@
         <item name="android:windowSoftInputMode">adjustResize</item>
 
         <item name="suwCardBackground">@drawable/suw_card_bg</item>
+        <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
+        <item name="suwDividerInsetEnd">0dp</item>
+        <item name="suwDividerInsetStart">@dimen/suw_items_icon_divider_inset</item>
+        <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_text_divider_inset</item>
         <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description</item>
+        <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle</item>
         <item name="suwListItemIconColor">@color/suw_list_item_icon_color_dark</item>
         <item name="suwMarginSides">@dimen/suw_layout_margin_sides</item>
         <item name="suwNavBarTheme">@style/SuwNavBarThemeDark</item>
@@ -64,7 +69,12 @@
         <item name="android:windowSoftInputMode">adjustResize</item>
 
         <item name="suwCardBackground">@drawable/suw_card_bg</item>
+        <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
+        <item name="suwDividerInsetEnd">0dp</item>
+        <item name="suwDividerInsetStart">@dimen/suw_items_icon_divider_inset</item>
+        <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_text_divider_inset</item>
         <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description</item>
+        <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle</item>
         <item name="suwListItemIconColor">@color/suw_list_item_icon_color_light</item>
         <item name="suwMarginSides">@dimen/suw_layout_margin_sides</item>
         <item name="suwNavBarTheme">@style/SuwNavBarThemeLight</item>
@@ -73,7 +83,8 @@
     <!-- Placeholder for GLIF dark theme, colors are not updated yet -->
     <style name="SuwThemeGlif" parent="android:Theme.Material.NoActionBar">
         <item name="android:colorAccent">@color/suw_color_accent_glif_dark</item>
-        <item name="android:colorPrimary">@color/suw_color_accent_glif_light</item>
+        <item name="android:colorBackground">@color/suw_glif_background_color_dark</item>
+        <item name="android:colorPrimary">@color/suw_color_accent_glif_dark</item>
         <item name="android:indeterminateTint">?android:attr/colorPrimary</item>
         <!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
         <item name="android:indeterminateTintMode">src_in</item>
@@ -84,22 +95,27 @@
         <item name="android:statusBarColor">?android:attr/colorPrimary</item>
         <item name="android:textAppearanceListItem">@style/TextAppearance.SuwGlifItemTitle</item>
         <item name="android:textAppearanceListItemSmall">@style/TextAppearance.SuwGlifItemSummary</item>
-        <item name="android:textColorLink">@color/suw_link_color_light</item>
+        <item name="android:textColorLink">@color/suw_color_accent_glif_dark</item>
         <item name="android:windowAnimationStyle">@style/Animation.SuwWindowAnimation</item>
         <item name="android:windowDisablePreview">true</item>
         <item name="android:windowSoftInputMode">adjustResize</item>
 
         <item name="suwColorPrimary">?android:attr/colorPrimary</item>
-        <item name="suwDividerInset">@dimen/suw_items_glif_icon_divider_inset</item>
+        <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
+        <item name="suwDividerInsetEnd">0dp</item>
+        <item name="suwDividerInsetStart">@dimen/suw_items_glif_icon_divider_inset</item>
+        <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_glif_text_divider_inset</item>
         <item name="suwGlifHeaderGravity">start</item>
         <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description.Glif</item>
+        <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle.GlifDescription</item>
         <item name="suwListItemIconColor">@color/suw_list_item_icon_color_dark</item>
         <item name="suwMarginSides">@dimen/suw_glif_margin_sides</item>
         <item name="suwScrollIndicators">bottom</item>
     </style>
 
     <style name="SuwThemeGlif.Light" parent="android:Theme.Material.Light.NoActionBar">
-        <item name="android:colorAccent">@color/suw_color_accent_glif_dark</item>
+        <item name="android:colorAccent">@color/suw_color_accent_glif_light</item>
+        <item name="android:colorBackground">@color/suw_glif_background_color_light</item>
         <item name="android:colorPrimary">@color/suw_color_accent_glif_light</item>
         <item name="android:indeterminateTint">?android:attr/colorPrimary</item>
         <!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
@@ -111,15 +127,19 @@
         <item name="android:statusBarColor">?android:attr/colorPrimary</item>
         <item name="android:textAppearanceListItem">@style/TextAppearance.SuwGlifItemTitle</item>
         <item name="android:textAppearanceListItemSmall">@style/TextAppearance.SuwGlifItemSummary</item>
-        <item name="android:textColorLink">@color/suw_link_color_light</item>
+        <item name="android:textColorLink">@color/suw_color_accent_glif_light</item>
         <item name="android:windowAnimationStyle">@style/Animation.SuwWindowAnimation</item>
         <item name="android:windowDisablePreview">true</item>
         <item name="android:windowSoftInputMode">adjustResize</item>
 
         <item name="suwColorPrimary">?android:attr/colorPrimary</item>
-        <item name="suwDividerInset">@dimen/suw_items_glif_icon_divider_inset</item>
+        <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
+        <item name="suwDividerInsetEnd">0dp</item>
+        <item name="suwDividerInsetStart">@dimen/suw_items_glif_icon_divider_inset</item>
+        <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_glif_text_divider_inset</item>
         <item name="suwGlifHeaderGravity">start</item>
         <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description.Glif</item>
+        <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle.GlifDescription</item>
         <item name="suwListItemIconColor">@color/suw_list_item_icon_color_light</item>
         <item name="suwMarginSides">@dimen/suw_glif_margin_sides</item>
         <item name="suwScrollIndicators">bottom</item>
@@ -134,7 +154,6 @@
         <item name="android:buttonStyle">@style/SuwGlifButton.Primary</item>
 
         <!-- Values used in styles -->
-        <item name="android:minWidth">0dp</item>
         <item name="android:paddingLeft">@dimen/suw_glif_button_padding</item>
         <item name="android:paddingRight">@dimen/suw_glif_button_padding</item>
     </style>
diff --git a/library/eclair-mr1/src/com/android/setupwizardlib/view/RichTextView.java b/library/platform/src/com/android/setupwizardlib/view/RichTextView.java
similarity index 78%
copy from library/eclair-mr1/src/com/android/setupwizardlib/view/RichTextView.java
copy to library/platform/src/com/android/setupwizardlib/view/RichTextView.java
index 6ccedf0..5a78561 100644
--- a/library/eclair-mr1/src/com/android/setupwizardlib/view/RichTextView.java
+++ b/library/platform/src/com/android/setupwizardlib/view/RichTextView.java
@@ -17,10 +17,6 @@
 package com.android.setupwizardlib.view;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
-import android.support.v4.view.ViewCompat;
 import android.text.Annotation;
 import android.text.SpannableString;
 import android.text.Spanned;
@@ -29,18 +25,22 @@
 import android.text.style.TextAppearanceSpan;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.MotionEvent;
 import android.widget.TextView;
 
 import com.android.setupwizardlib.span.LinkSpan;
+import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener;
 import com.android.setupwizardlib.span.SpanHelper;
-import com.android.setupwizardlib.util.LinkAccessibilityHelper;
 
 /**
  * An extension of TextView that automatically replaces the annotation tags as specified in
  * {@link SpanHelper#replaceSpan(android.text.Spannable, Object, Object)}
+ *
+ * <p>Note: The accessibility interaction for ClickableSpans (and therefore LinkSpans) are built
+ * into platform in O, although the interaction paradigm is different. (See b/17726921). In this
+ * platform version, the links are exposed in the Local Context Menu of TalkBack instead of
+ * accessible directly through swiping.
  */
-public class RichTextView extends TextView {
+public class RichTextView extends TextView implements OnLinkClickListener {
 
     /* static section */
 
@@ -88,21 +88,14 @@
 
     /* non-static section */
 
-    private LinkAccessibilityHelper mAccessibilityHelper;
+    private OnLinkClickListener mOnLinkClickListener;
 
     public RichTextView(Context context) {
         super(context);
-        init();
     }
 
     public RichTextView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        init();
-    }
-
-    private void init() {
-        mAccessibilityHelper = new LinkAccessibilityHelper(this);
-        ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
     }
 
     @Override
@@ -139,29 +132,19 @@
         return false;
     }
 
-    @Override
-    protected boolean dispatchHoverEvent(MotionEvent event) {
-        if (mAccessibilityHelper != null && mAccessibilityHelper.dispatchHoverEvent(event)) {
-            return true;
-        }
-        return super.dispatchHoverEvent(event);
+    public void setOnLinkClickListener(OnLinkClickListener listener) {
+        mOnLinkClickListener = listener;
+    }
+
+    public OnLinkClickListener getOnLinkClickListener() {
+        return mOnLinkClickListener;
     }
 
     @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-
-        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
-            // b/26765507 causes drawableStart and drawableEnd to not get the right state on M. As a
-            // workaround, set the state on those drawables directly.
-            final int[] state = getDrawableState();
-            for (Drawable drawable : getCompoundDrawablesRelative()) {
-                if (drawable != null) {
-                    if (drawable.setState(state)) {
-                        invalidateDrawable(drawable);
-                    }
-                }
-            }
+    public boolean onLinkClick(LinkSpan span) {
+        if (mOnLinkClickListener != null) {
+            return mOnLinkClickListener.onLinkClick(span);
         }
+        return false;
     }
 }
diff --git a/library/full-support/res/layout/suw_glif_preference_recycler_view.xml b/library/recyclerview/res/layout/suw_glif_preference_recycler_view.xml
similarity index 100%
rename from library/full-support/res/layout/suw_glif_preference_recycler_view.xml
rename to library/recyclerview/res/layout/suw_glif_preference_recycler_view.xml
diff --git a/library/full-support/res/layout/suw_glif_preference_template_header.xml b/library/recyclerview/res/layout/suw_glif_preference_template_header.xml
similarity index 100%
rename from library/full-support/res/layout/suw_glif_preference_template_header.xml
rename to library/recyclerview/res/layout/suw_glif_preference_template_header.xml
diff --git a/library/full-support/res/layout/suw_glif_recycler_template_card.xml b/library/recyclerview/res/layout/suw_glif_recycler_template_card.xml
similarity index 100%
rename from library/full-support/res/layout/suw_glif_recycler_template_card.xml
rename to library/recyclerview/res/layout/suw_glif_recycler_template_card.xml
diff --git a/library/full-support/res/layout/suw_glif_recycler_template_compact.xml b/library/recyclerview/res/layout/suw_glif_recycler_template_compact.xml
similarity index 100%
rename from library/full-support/res/layout/suw_glif_recycler_template_compact.xml
rename to library/recyclerview/res/layout/suw_glif_recycler_template_compact.xml
diff --git a/library/full-support/res/layout/suw_glif_recycler_template_content.xml b/library/recyclerview/res/layout/suw_glif_recycler_template_content.xml
similarity index 100%
rename from library/full-support/res/layout/suw_glif_recycler_template_content.xml
rename to library/recyclerview/res/layout/suw_glif_recycler_template_content.xml
diff --git a/library/full-support/res/layout/suw_preference_recycler_view_header.xml b/library/recyclerview/res/layout/suw_preference_recycler_view_header.xml
similarity index 100%
rename from library/full-support/res/layout/suw_preference_recycler_view_header.xml
rename to library/recyclerview/res/layout/suw_preference_recycler_view_header.xml
diff --git a/library/full-support/res/layout/suw_preference_recycler_view_normal.xml b/library/recyclerview/res/layout/suw_preference_recycler_view_normal.xml
similarity index 100%
rename from library/full-support/res/layout/suw_preference_recycler_view_normal.xml
rename to library/recyclerview/res/layout/suw_preference_recycler_view_normal.xml
diff --git a/library/full-support/res/layout/suw_preference_template_header.xml b/library/recyclerview/res/layout/suw_preference_template_header.xml
similarity index 100%
rename from library/full-support/res/layout/suw_preference_template_header.xml
rename to library/recyclerview/res/layout/suw_preference_template_header.xml
diff --git a/library/full-support/res/layout/suw_recycler_template_card.xml b/library/recyclerview/res/layout/suw_recycler_template_card.xml
similarity index 100%
rename from library/full-support/res/layout/suw_recycler_template_card.xml
rename to library/recyclerview/res/layout/suw_recycler_template_card.xml
diff --git a/library/full-support/res/layout/suw_recycler_template_card_wide.xml b/library/recyclerview/res/layout/suw_recycler_template_card_wide.xml
similarity index 100%
rename from library/full-support/res/layout/suw_recycler_template_card_wide.xml
rename to library/recyclerview/res/layout/suw_recycler_template_card_wide.xml
diff --git a/library/full-support/res/layout/suw_recycler_template_header.xml b/library/recyclerview/res/layout/suw_recycler_template_header.xml
similarity index 100%
rename from library/full-support/res/layout/suw_recycler_template_header.xml
rename to library/recyclerview/res/layout/suw_recycler_template_header.xml
diff --git a/library/full-support/res/layout/suw_recycler_template_header_collapsed.xml b/library/recyclerview/res/layout/suw_recycler_template_header_collapsed.xml
similarity index 100%
rename from library/full-support/res/layout/suw_recycler_template_header_collapsed.xml
rename to library/recyclerview/res/layout/suw_recycler_template_header_collapsed.xml
diff --git a/library/full-support/res/values-land/layouts.xml b/library/recyclerview/res/values-land/layouts.xml
similarity index 100%
rename from library/full-support/res/values-land/layouts.xml
rename to library/recyclerview/res/values-land/layouts.xml
diff --git a/library/full-support/res/values-sw600dp-land/layouts.xml b/library/recyclerview/res/values-sw600dp-land/layouts.xml
similarity index 100%
rename from library/full-support/res/values-sw600dp-land/layouts.xml
rename to library/recyclerview/res/values-sw600dp-land/layouts.xml
diff --git a/library/full-support/res/values-sw600dp/layouts.xml b/library/recyclerview/res/values-sw600dp/layouts.xml
similarity index 100%
rename from library/full-support/res/values-sw600dp/layouts.xml
rename to library/recyclerview/res/values-sw600dp/layouts.xml
diff --git a/library/full-support/res/values/attrs.xml b/library/recyclerview/res/values/attrs.xml
similarity index 92%
rename from library/full-support/res/values/attrs.xml
rename to library/recyclerview/res/values/attrs.xml
index e4fb41f..a098d5a 100644
--- a/library/full-support/res/values/attrs.xml
+++ b/library/recyclerview/res/values/attrs.xml
@@ -28,6 +28,8 @@
     <declare-styleable name="SuwRecyclerMixin">
         <attr name="android:entries" />
         <attr name="suwDividerInset" />
+        <attr name="suwDividerInsetEnd" />
+        <attr name="suwDividerInsetStart" />
         <attr name="suwHasStableIds" />
     </declare-styleable>
 
diff --git a/library/full-support/res/values/layouts.xml b/library/recyclerview/res/values/layouts.xml
similarity index 100%
rename from library/full-support/res/values/layouts.xml
rename to library/recyclerview/res/values/layouts.xml
diff --git a/library/full-support/src/com/android/setupwizardlib/DividerItemDecoration.java b/library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java
similarity index 97%
rename from library/full-support/src/com/android/setupwizardlib/DividerItemDecoration.java
rename to library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java
index bbd7b50..6c1a928 100644
--- a/library/full-support/src/com/android/setupwizardlib/DividerItemDecoration.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java
@@ -36,12 +36,16 @@
  * default, and the behavior of whether the divider is shown can be customized by subclassing
  * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}.
  *
- * <p>Modified from v14 PreferenceFragment.DividerDecoration, added with inset capabilities.
+ * <p>Modified from v14 PreferenceFragment.DividerDecoration.
  */
 public class DividerItemDecoration extends RecyclerView.ItemDecoration {
 
     /* static section */
 
+    /**
+     * An interface to be implemented by a {@link RecyclerView.ViewHolder} which controls whether
+     * dividers should be shown above and below that item.
+     */
     public interface DividedViewHolder {
 
         /**
diff --git a/library/full-support/src/com/android/setupwizardlib/GlifPreferenceLayout.java b/library/recyclerview/src/com/android/setupwizardlib/GlifPreferenceLayout.java
similarity index 100%
rename from library/full-support/src/com/android/setupwizardlib/GlifPreferenceLayout.java
rename to library/recyclerview/src/com/android/setupwizardlib/GlifPreferenceLayout.java
diff --git a/library/full-support/src/com/android/setupwizardlib/GlifRecyclerLayout.java b/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java
similarity index 89%
rename from library/full-support/src/com/android/setupwizardlib/GlifRecyclerLayout.java
rename to library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java
index d1a7947..75b1c7a 100644
--- a/library/full-support/src/com/android/setupwizardlib/GlifRecyclerLayout.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java
@@ -147,20 +147,43 @@
     }
 
     /**
-     * @see RecyclerMixin#setDividerInset(int)
+     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
      */
+    @Deprecated
     public void setDividerInset(int inset) {
         mRecyclerMixin.setDividerInset(inset);
     }
 
     /**
-     * @see RecyclerMixin#getDividerInset()
+     * @see RecyclerMixin#setDividerInset(int)
      */
+    public void setDividerInsets(int start, int end) {
+        mRecyclerMixin.setDividerInsets(start, end);
+    }
+
+    /**
+     * @deprecated Use {@link #getDividerInsetStart()} instead.
+     */
+    @Deprecated
     public int getDividerInset() {
         return mRecyclerMixin.getDividerInset();
     }
 
     /**
+     * @see RecyclerMixin#getDividerInsetStart()
+     */
+    public int getDividerInsetStart() {
+        return mRecyclerMixin.getDividerInsetStart();
+    }
+
+    /**
+     * @see RecyclerMixin#getDividerInsetEnd()
+     */
+    public int getDividerInsetEnd() {
+        return mRecyclerMixin.getDividerInsetEnd();
+    }
+
+    /**
      * @see RecyclerMixin#getDivider()
      */
     public Drawable getDivider() {
diff --git a/library/full-support/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java
similarity index 96%
rename from library/full-support/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java
rename to library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java
index f0df24c..6570694 100644
--- a/library/full-support/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java
@@ -59,8 +59,7 @@
  * }</pre>
  *
  * <p />Fragments using this layout <em>must</em> delegate {@code onCreateRecyclerView} to the
- * implementation in this class:
- * {@link #onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)}
+ * implementation in this class: {@link #onCreateRecyclerView}
  */
 public class SetupWizardPreferenceLayout extends SetupWizardRecyclerLayout {
 
diff --git a/library/full-support/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
similarity index 85%
rename from library/full-support/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
rename to library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
index 870a805..5ff825d 100644
--- a/library/full-support/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
@@ -139,27 +139,51 @@
     }
 
     /**
-     * Sets the start inset of the divider. This will use the default divider drawable set in the
-     * theme and inset it {@code inset} pixels to the right (or left in RTL layouts).
-     *
-     * @param inset The number of pixels to inset on the "start" side of the list divider. Typically
-     *              this will be either {@code @dimen/suw_items_icon_divider_inset} or
-     *              {@code @dimen/suw_items_text_divider_inset}.
-     *
-     * @see RecyclerMixin#setDividerInset(int)
+     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
      */
+    @Deprecated
     public void setDividerInset(int inset) {
         mRecyclerMixin.setDividerInset(inset);
     }
 
     /**
-     * @see RecyclerMixin#getDividerInset()
+     * Sets the start inset of the divider. This will use the default divider drawable set in the
+     * theme and apply insets to it.
+     *
+     * @param start The number of pixels to inset on the "start" side of the list divider. Typically
+     *              this will be either {@code @dimen/suw_items_icon_divider_inset} or
+     *              {@code @dimen/suw_items_text_divider_inset}.
+     * @param end The number of pixels to inset on the "end" side of the list divider.
+     *
+     * @see RecyclerMixin#setDividerInsets(int, int)
      */
+    public void setDividerInsets(int start, int end) {
+        mRecyclerMixin.setDividerInsets(start, end);
+    }
+
+    /**
+     * @deprecated Use {@link #getDividerInsetStart()} instead.
+     */
+    @Deprecated
     public int getDividerInset() {
         return mRecyclerMixin.getDividerInset();
     }
 
     /**
+     * @see RecyclerMixin#getDividerInsetStart()
+     */
+    public int getDividerInsetStart() {
+        return mRecyclerMixin.getDividerInsetStart();
+    }
+
+    /**
+     * @see RecyclerMixin#getDividerInsetEnd()
+     */
+    public int getDividerInsetEnd() {
+        return mRecyclerMixin.getDividerInsetEnd();
+    }
+
+    /**
      * @see RecyclerMixin#getDivider()
      */
     public Drawable getDivider() {
diff --git a/library/full-support/src/com/android/setupwizardlib/items/ItemViewHolder.java b/library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java
similarity index 96%
rename from library/full-support/src/com/android/setupwizardlib/items/ItemViewHolder.java
rename to library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java
index 8f89603..231f81d 100644
--- a/library/full-support/src/com/android/setupwizardlib/items/ItemViewHolder.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java
@@ -27,7 +27,7 @@
     private boolean mIsEnabled;
     private IItem mItem;
 
-    public ItemViewHolder(View itemView) {
+    ItemViewHolder(View itemView) {
         super(itemView);
     }
 
diff --git a/library/full-support/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java b/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java
similarity index 89%
rename from library/full-support/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java
rename to library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java
index 9b7f6c3..78280a6 100644
--- a/library/full-support/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java
@@ -46,7 +46,16 @@
      */
     public static final String TAG_NO_BACKGROUND = "noBackground";
 
+    /**
+     * Listener for item selection in this adapter.
+     */
     public interface OnItemSelectedListener {
+
+        /**
+         * Called when an item in this adapter is clicked.
+         *
+         * @param item The Item corresponding to the position being clicked.
+         */
         void onItemSelected(IItem item);
     }
 
@@ -58,6 +67,11 @@
         mItemHierarchy.registerObserver(this);
     }
 
+    /**
+     * Gets the item at the given position.
+     *
+     * @see ItemHierarchy#getItemAt(int)
+     */
     public IItem getItem(int position) {
         return mItemHierarchy.getItemAt(position);
     }
@@ -95,8 +109,11 @@
                         R.styleable.SuwRecyclerItemAdapter_selectableItemBackground);
             }
 
-            final Drawable background = typedArray.getDrawable(
-                    R.styleable.SuwRecyclerItemAdapter_android_colorBackground);
+            Drawable background = view.getBackground();
+            if (background == null) {
+                background = typedArray.getDrawable(
+                        R.styleable.SuwRecyclerItemAdapter_android_colorBackground);
+            }
 
             if (selectableItemBackground == null || background == null) {
                 Log.e(TAG, "Cannot resolve required attributes."
@@ -177,14 +194,27 @@
         notifyItemRangeRemoved(positionStart, itemCount);
     }
 
+    /**
+     * Find an item hierarchy within the root hierarchy.
+     *
+     * @see ItemHierarchy#findItemById(int)
+     */
     public ItemHierarchy findItemById(int id) {
         return mItemHierarchy.findItemById(id);
     }
 
+    /**
+     * Gets the root item hierarchy in this adapter.
+     */
     public ItemHierarchy getRootItemHierarchy() {
         return mItemHierarchy;
     }
 
+    /**
+     * Sets the listener to listen for when user clicks on a item.
+     *
+     * @see OnItemSelectedListener
+     */
     public void setOnItemSelectedListener(OnItemSelectedListener listener) {
         mListener = listener;
     }
diff --git a/library/full-support/src/com/android/setupwizardlib/template/RecyclerMixin.java b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java
similarity index 85%
rename from library/full-support/src/com/android/setupwizardlib/template/RecyclerMixin.java
rename to library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java
index 56751d4..fa5ac4a 100644
--- a/library/full-support/src/com/android/setupwizardlib/template/RecyclerMixin.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java
@@ -63,7 +63,9 @@
 
     private Drawable mDefaultDivider;
     private Drawable mDivider;
-    private int mDividerInset;
+
+    private int mDividerInsetStart;
+    private int mDividerInsetEnd;
 
     /**
      * Creates the RecyclerMixin. Unlike typical mixins which are created in the constructor, this
@@ -112,8 +114,17 @@
             setAdapter(adapter);
         }
         int dividerInset =
-                a.getDimensionPixelSize(R.styleable.SuwRecyclerMixin_suwDividerInset, 0);
-        setDividerInset(dividerInset);
+                a.getDimensionPixelSize(R.styleable.SuwRecyclerMixin_suwDividerInset, -1);
+        if (dividerInset != -1) {
+            setDividerInset(dividerInset);
+        } else {
+            int dividerInsetStart =
+                    a.getDimensionPixelSize(R.styleable.SuwRecyclerMixin_suwDividerInsetStart, 0);
+            int dividerInsetEnd =
+                    a.getDimensionPixelSize(R.styleable.SuwRecyclerMixin_suwDividerInsetEnd, 0);
+            setDividerInsets(dividerInsetStart, dividerInsetEnd);
+        }
+
         a.recycle();
     }
 
@@ -174,23 +185,49 @@
     }
 
     /**
+     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
+     */
+    @Deprecated
+    public void setDividerInset(int inset) {
+        setDividerInsets(inset, 0);
+    }
+
+    /**
      * Sets the start inset of the divider. This will use the default divider drawable set in the
-     * theme and inset it {@code inset} pixels to the right (or left in RTL layouts).
+     * theme and apply insets to it.
      *
-     * @param inset The number of pixels to inset on the "start" side of the list divider. Typically
+     * @param start The number of pixels to inset on the "start" side of the list divider. Typically
      *              this will be either {@code @dimen/suw_items_glif_icon_divider_inset} or
      *              {@code @dimen/suw_items_glif_text_divider_inset}.
+     * @param end The number of pixels to inset on the "end" side of the list divider.
      */
-    public void setDividerInset(int inset) {
-        mDividerInset = inset;
+    public void setDividerInsets(int start, int end) {
+        mDividerInsetStart = start;
+        mDividerInsetEnd = end;
         updateDivider();
     }
 
     /**
      * @return The number of pixels inset on the start side of the divider.
+     * @deprecated This is the same as {@link #getDividerInsetStart()}. Use that instead.
      */
+    @Deprecated
     public int getDividerInset() {
-        return mDividerInset;
+        return getDividerInsetStart();
+    }
+
+    /**
+     * @return The number of pixels inset on the start side of the divider.
+     */
+    public int getDividerInsetStart() {
+        return mDividerInsetStart;
+    }
+
+    /**
+     * @return The number of pixels inset on the end side of the divider.
+     */
+    public int getDividerInsetEnd() {
+        return mDividerInsetEnd;
     }
 
     private void updateDivider() {
@@ -204,9 +241,9 @@
             }
             mDivider = DrawableLayoutDirectionHelper.createRelativeInsetDrawable(
                     mDefaultDivider,
-                    mDividerInset /* start */,
+                    mDividerInsetStart /* start */,
                     0 /* top */,
-                    0 /* end */,
+                    mDividerInsetEnd /* end */,
                     0 /* bottom */,
                     mTemplateLayout);
             mDividerDecoration.setDivider(mDivider);
diff --git a/library/full-support/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java
similarity index 100%
rename from library/full-support/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java
rename to library/recyclerview/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java
diff --git a/library/full-support/src/com/android/setupwizardlib/view/HeaderRecyclerView.java b/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java
similarity index 91%
rename from library/full-support/src/com/android/setupwizardlib/view/HeaderRecyclerView.java
rename to library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java
index 09ddd33..cf13d01 100644
--- a/library/full-support/src/com/android/setupwizardlib/view/HeaderRecyclerView.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java
@@ -16,7 +16,6 @@
 
 package com.android.setupwizardlib.view;
 
-import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.Build;
@@ -41,7 +40,7 @@
     private static class HeaderViewHolder extends ViewHolder
             implements DividerItemDecoration.DividedViewHolder {
 
-        public HeaderViewHolder(View itemView) {
+        HeaderViewHolder(View itemView) {
             super(itemView);
         }
 
@@ -119,23 +118,18 @@
             setHasStableIds(mAdapter.hasStableIds());
         }
 
-        @SuppressLint("InlinedApi")  // MATCH_PARENT is the same constant as FILL_PARENT available
-                                     // on earlier versions.
         @Override
         public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-            /*
-             * Returning the same view (mHeader) results in crash ".. but view is not a real child."
-             * The framework creates more than one instance of header because of "disappear"
-             * animations applied on the header and this necessitates creation of another headerview
-             * to use after the animation. We work around this restriction by returning an empty
-             * framelayout to which the header is attached using #onBindViewHolder method.
-             */
+            // Returning the same view (mHeader) results in crash ".. but view is not a real child."
+            // The framework creates more than one instance of header because of "disappear"
+            // animations applied on the header and this necessitates creation of another header
+            // view to use after the animation. We work around this restriction by returning an
+            // empty FrameLayout to which the header is attached using #onBindViewHolder method.
             if (viewType == HEADER_VIEW_TYPE) {
                 FrameLayout frameLayout = new FrameLayout(parent.getContext());
                 FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
-                    FrameLayout.LayoutParams.MATCH_PARENT,
-                    FrameLayout.LayoutParams.WRAP_CONTENT
-                );
+                        FrameLayout.LayoutParams.MATCH_PARENT,
+                        FrameLayout.LayoutParams.WRAP_CONTENT);
                 frameLayout.setLayoutParams(params);
                 return new HeaderViewHolder(frameLayout);
             } else {
diff --git a/library/full-support/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java b/library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java
similarity index 97%
rename from library/full-support/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java
rename to library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java
index a733b6c..d51ea56 100644
--- a/library/full-support/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java
@@ -79,6 +79,10 @@
         }
     }
 
+    /**
+     * Call this method when the "sticky" view has changed, so this view can update its internal
+     * states as well.
+     */
     public void updateStickyView() {
         final View header = getHeader();
         if (header != null) {
diff --git a/library/recyclerview/test/instrumentation/res/drawable/item_bg.xml b/library/recyclerview/test/instrumentation/res/drawable/item_bg.xml
new file mode 100644
index 0000000..285ae28
--- /dev/null
+++ b/library/recyclerview/test/instrumentation/res/drawable/item_bg.xml
@@ -0,0 +1,19 @@
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="1dp"/>
+</shape>
\ No newline at end of file
diff --git a/library/recyclerview/test/instrumentation/res/layout/test_existing_background.xml b/library/recyclerview/test/instrumentation/res/layout/test_existing_background.xml
new file mode 100644
index 0000000..fa2b22a
--- /dev/null
+++ b/library/recyclerview/test/instrumentation/res/layout/test_existing_background.xml
@@ -0,0 +1,20 @@
+<!--
+    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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/item_bg" />
diff --git a/library/full-support/test/instrumentation/res/layout/test_glif_recycler_layout.xml b/library/recyclerview/test/instrumentation/res/layout/test_glif_recycler_layout.xml
similarity index 100%
rename from library/full-support/test/instrumentation/res/layout/test_glif_recycler_layout.xml
rename to library/recyclerview/test/instrumentation/res/layout/test_glif_recycler_layout.xml
diff --git a/library/full-support/test/instrumentation/res/layout/test_list_item.xml b/library/recyclerview/test/instrumentation/res/layout/test_list_item.xml
similarity index 100%
rename from library/full-support/test/instrumentation/res/layout/test_list_item.xml
rename to library/recyclerview/test/instrumentation/res/layout/test_list_item.xml
diff --git a/library/full-support/test/instrumentation/res/layout/test_list_item_no_background.xml b/library/recyclerview/test/instrumentation/res/layout/test_list_item_no_background.xml
similarity index 100%
rename from library/full-support/test/instrumentation/res/layout/test_list_item_no_background.xml
rename to library/recyclerview/test/instrumentation/res/layout/test_list_item_no_background.xml
diff --git a/library/full-support/test/instrumentation/res/layout/test_recycler_layout.xml b/library/recyclerview/test/instrumentation/res/layout/test_recycler_layout.xml
similarity index 100%
rename from library/full-support/test/instrumentation/res/layout/test_recycler_layout.xml
rename to library/recyclerview/test/instrumentation/res/layout/test_recycler_layout.xml
diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java
similarity index 87%
rename from library/full-support/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java
rename to library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java
index 3867bfe..1bfbb95 100644
--- a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java
+++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java
@@ -29,6 +29,7 @@
 
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.shapes.RectShape;
 import android.support.test.InstrumentationRegistry;
@@ -133,7 +134,7 @@
     }
 
     @Test
-    public void testCreateViewHolderNoBcakground() {
+    public void testCreateViewHolderNoBackground() {
         RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup);
         FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext());
 
@@ -141,4 +142,18 @@
                 adapter.onCreateViewHolder(parent, R.layout.test_list_item_no_background);
         assertNull("Background should be null", viewHolder.itemView.getBackground());
     }
+
+    @Test
+    public void testCreateViewHolderWithExistingBackground() {
+        RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup);
+        FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext());
+
+        final ItemViewHolder viewHolder =
+                adapter.onCreateViewHolder(parent, R.layout.test_existing_background);
+        Drawable background = viewHolder.itemView.getBackground();
+        assertTrue(background instanceof PatchedLayerDrawable);
+
+        PatchedLayerDrawable layerDrawable = (PatchedLayerDrawable) background;
+        assertTrue(layerDrawable.getDrawable(0) instanceof GradientDrawable);
+    }
 }
diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java
similarity index 74%
rename from library/full-support/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java
rename to library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java
index 79105d6..ce73791 100644
--- a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java
+++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java
@@ -96,7 +96,7 @@
     }
 
     @Test
-    public void testDividerInset() {
+    public void testDividerLegacyInset() {
         RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
         mixin.setDividerInset(123);
 
@@ -111,7 +111,23 @@
     }
 
     @Test
-    public void testDividerInsetRtl() {
+    public void testDividerInsets() {
+        RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
+        mixin.setDividerInsets(123, 456);
+
+        assertEquals(123, mixin.getDividerInsetStart());
+        assertEquals(456, mixin.getDividerInsetEnd());
+
+        final Drawable divider = mixin.getDivider();
+        InsetDrawable insetDrawable = (InsetDrawable) divider;
+        Rect rect = new Rect();
+        insetDrawable.getPadding(rect);
+
+        assertEquals(new Rect(123, 0, 456, 0), rect);
+    }
+
+    @Test
+    public void testDividerInsetLegacyRtl() {
         if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
             doReturn(View.LAYOUT_DIRECTION_RTL).when(mTemplateLayout).getLayoutDirection();
 
@@ -129,4 +145,25 @@
         }
         // else the test passes
     }
+
+    @Test
+    public void testDividerInsetsRtl() {
+        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
+            doReturn(View.LAYOUT_DIRECTION_RTL).when(mTemplateLayout).getLayoutDirection();
+
+            RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
+            mixin.setDividerInsets(123, 456);
+
+            assertEquals(123, mixin.getDividerInsetStart());
+            assertEquals(456, mixin.getDividerInsetEnd());
+
+            final Drawable divider = mixin.getDivider();
+            InsetDrawable insetDrawable = (InsetDrawable) divider;
+            Rect rect = new Rect();
+            insetDrawable.getPadding(rect);
+
+            assertEquals(new Rect(456, 0, 123, 0), rect);
+        }
+        // else the test passes
+    }
 }
diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java
similarity index 100%
rename from library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java
rename to library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java
diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java
similarity index 100%
rename from library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java
rename to library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java
diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java
similarity index 88%
rename from library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java
rename to library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java
index b27564d..4bd98ce 100644
--- a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java
+++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java
@@ -105,7 +105,7 @@
     }
 
     @Test
-    public void testDividerInset() {
+    public void testDividerInsetLegacy() {
         GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
             layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
@@ -120,6 +120,22 @@
     }
 
     @Test
+    public void testDividerInsets() {
+        GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+        }
+        assertRecyclerTemplateInflated(layout);
+
+        layout.setDividerInsets(10, 15);
+        assertEquals("Divider inset start should be 10", 10, layout.getDividerInsetStart());
+        assertEquals("Divider inset end should be 15", 15, layout.getDividerInsetEnd());
+
+        final Drawable divider = layout.getDivider();
+        assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable);
+    }
+
+    @Test
     public void testTemplateWithNoRecyclerView() {
         try {
             new GlifRecyclerLayout(mContext, R.layout.suw_glif_template);
diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java
similarity index 100%
rename from library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java
rename to library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java
diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java
similarity index 100%
rename from library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java
rename to library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java
diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java
similarity index 88%
rename from library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java
rename to library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java
index 4a72992..4b38945 100644
--- a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java
+++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java
@@ -106,7 +106,7 @@
     }
 
     @Test
-    public void testDividerInset() {
+    public void testDividerInsetLegacy() {
         SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
             layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
@@ -121,6 +121,22 @@
     }
 
     @Test
+    public void testDividerInsets() {
+        SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+        }
+        assertRecyclerTemplateInflated(layout);
+
+        layout.setDividerInsets(10, 15);
+        assertEquals("Divider inset start should be 10", 10, layout.getDividerInsetStart());
+        assertEquals("Divider inset end should be 15", 15, layout.getDividerInsetEnd());
+
+        final Drawable divider = layout.getDivider();
+        assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable);
+    }
+
+    @Test
     public void testTemplateWithNoRecyclerView() {
         try {
             new SetupWizardRecyclerLayout(
diff --git a/library/full-support/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java b/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java
similarity index 100%
rename from library/full-support/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java
rename to library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java
diff --git a/library/rules.gradle b/library/rules.gradle
index a3868cc..6b0acce 100644
--- a/library/rules.gradle
+++ b/library/rules.gradle
@@ -63,8 +63,8 @@
         }
 
         gingerbreadCompat {
-            java.srcDirs = ['eclair-mr1/src', 'full-support/src']
-            res.srcDirs = ['eclair-mr1/res', 'full-support/res']
+            java.srcDirs = ['gingerbread/src', 'recyclerview/src']
+            res.srcDirs = ['gingerbread/res', 'recyclerview/res']
         }
     }
 }
diff --git a/library/self.gradle b/library/self.gradle
index f6d14af..6a405e2 100644
--- a/library/self.gradle
+++ b/library/self.gradle
@@ -1,8 +1,10 @@
 /**
  * This self.gradle build file is only run when built in ub-setupwizard-* branches.
  */
-apply plugin: 'dist'
 apply from: 'standalone-rules.gradle'
+apply from: '../tools/gradle/dist-library-instrumentation-tests.gradle'
+apply from: '../tools/gradle/dist-unit-tests.gradle'
+
 // Add targets for tests
 android.sourceSets {
     androidTest {
@@ -25,8 +27,11 @@
     }
 
     androidTestGingerbreadCompat {
-        java.srcDirs = ['full-support/test/instrumentation/src', 'eclair-mr1/test/instrumentation/src']
-        res.srcDirs = ['full-support/test/instrumentation/res']
+        java.srcDirs = [
+            'gingerbread/test/instrumentation/src',
+            'recyclerview/test/instrumentation/src'
+        ]
+        res.srcDirs = ['recyclerview/test/instrumentation/res']
     }
 
     test {
@@ -43,7 +48,7 @@
     }
 
     testGingerbreadCompat {
-        java.srcDirs = ['eclair-mr1/test/robotest/src', 'full-support/test/robotest/src']
+        java.srcDirs = ['gingerbread/test/robotest/src', 'recyclerview/test/robotest/src']
     }
 }
 android.defaultConfig.testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -59,12 +64,14 @@
 android.libraryVariants.all { variant ->
     variant.assemble.dependsOn(tasks.findByName('lint'))
 }
-// Output all test APKs to the distribution folder
-def distTask = tasks.findByName('dist')
-if (distTask) {
-    android.testVariants.all { variant ->
-        // Make the dist task depend on the test variant, so the test APK will be built
-        distTask.dependsOn variant.assemble
-        // TODO: remap the different test variants to different file names
-    }
+
+// For compatibility with existing continuous test configurations, copy the file to
+// setup-wizard-libTest.apk
+// TODO: Remove this once continuous test configurations are updated to handle the new file name
+task createLegacyTestApk(type: Copy) {
+    from "${project.ext.distDir}/setup-wizard-lib-gingerbreadCompat-debug-androidTest.apk"
+    into "${project.ext.distDir}"
+    rename ('setup-wizard-lib-gingerbreadCompat-debug-androidTest.apk', 'setup-wizard-libTest.apk')
 }
+
+tasks.dist.finalizedBy createLegacyTestApk
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/template/ListMixinTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/template/ListMixinTest.java
index 93201ca..30d68f1 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/template/ListMixinTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/template/ListMixinTest.java
@@ -101,7 +101,7 @@
     }
 
     @Test
-    public void testDividerInset() {
+    public void testDividerInsetLegacy() {
         ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
         mixin.setDividerInset(123);
 
@@ -116,7 +116,23 @@
     }
 
     @Test
-    public void testDividerInsetRtl() {
+    public void testDividerInsets() {
+        ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
+        mixin.setDividerInsets(123, 456);
+
+        assertEquals(123, mixin.getDividerInsetStart());
+        assertEquals(456, mixin.getDividerInsetEnd());
+
+        final Drawable divider = mListView.getDivider();
+        InsetDrawable insetDrawable = (InsetDrawable) divider;
+        Rect rect = new Rect();
+        insetDrawable.getPadding(rect);
+
+        assertEquals(new Rect(123, 0, 456, 0), rect);
+    }
+
+    @Test
+    public void testDividerInsetLegacyRtl() {
         if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
             doReturn(View.LAYOUT_DIRECTION_RTL).when(mTemplateLayout).getLayoutDirection();
 
@@ -136,6 +152,27 @@
     }
 
     @Test
+    public void testDividerInsetsRtl() {
+        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
+            doReturn(View.LAYOUT_DIRECTION_RTL).when(mTemplateLayout).getLayoutDirection();
+
+            ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
+            mixin.setDividerInsets(123, 456);
+
+            assertEquals(123, mixin.getDividerInsetStart());
+            assertEquals(456, mixin.getDividerInsetEnd());
+
+            final Drawable divider = mListView.getDivider();
+            InsetDrawable insetDrawable = (InsetDrawable) divider;
+            Rect rect = new Rect();
+            insetDrawable.getPadding(rect);
+
+            assertEquals(new Rect(456, 0, 123, 0), rect);
+        }
+        // else the test passes
+    }
+
+    @Test
     public void testNoList() {
         doReturn(null).when(mTemplateLayout).findManagedViewById(eq(android.R.id.list));
 
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifLayoutTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifLayoutTest.java
index acb7fbb..e12b31d 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifLayoutTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifLayoutTest.java
@@ -95,29 +95,29 @@
     }
 
     @Test
-    public void testGlifPixelTheme() {
+    public void testGlifV2Theme() {
         mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                R.style.SuwThemeGlifPixel_Light);
+                R.style.SuwThemeGlifV2_Light);
         final GlifLayout glifLayout = new GlifLayout(mContext);
         final TextView titleView = (TextView) glifLayout.findManagedViewById(R.id.suw_layout_title);
         if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
             assertEquals(View.TEXT_ALIGNMENT_GRAVITY, titleView.getTextAlignment());
         }
-        assertEquals("Title text should be center aligned on GLIF Pixel theme",
+        assertEquals("Title text should be center aligned on GLIF v2 theme",
                 Gravity.CENTER_HORIZONTAL, titleView.getGravity() & Gravity.CENTER_HORIZONTAL);
 
         if (VERSION.SDK_INT >= VERSION_CODES.N) {
             // LinearLayout.getGravity is only available on versions >= N
             final View iconView = glifLayout.findManagedViewById(R.id.suw_layout_icon);
             final LinearLayout parent = (LinearLayout) iconView.getParent();
-            assertEquals("Icon should be center aligned on GLIF Pixel theme",
+            assertEquals("Icon should be center aligned on GLIF v2 theme",
                     Gravity.CENTER_HORIZONTAL, parent.getGravity() & Gravity.CENTER_HORIZONTAL);
         }
 
-        assertEquals("Status bar color should be white in GLIF Pixel theme",
-                "fffafafa",
+        assertEquals("Status bar color should be white in GLIF v2 theme",
+                "ffffffff",
                 Integer.toHexString(glifLayout.getBackgroundBaseColor().getDefaultColor()));
-        assertFalse("GLIF Pixel theme shuold not have patterned background",
+        assertFalse("GLIF v2 theme shuold not have patterned background",
                 glifLayout.isBackgroundPatterned());
 
         if (VERSION.SDK_INT >= VERSION_CODES.M) {
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifListLayoutTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifListLayoutTest.java
index 7b70cf7..c2e932c 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifListLayoutTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifListLayoutTest.java
@@ -105,7 +105,7 @@
     }
 
     @Test
-    public void testDividerInset() {
+    public void testDividerInsetLegacy() {
         GlifListLayout layout = new GlifListLayout(mContext);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
             layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
@@ -119,6 +119,22 @@
         assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable);
     }
 
+    @Test
+    public void testDividerInsets() {
+        GlifListLayout layout = new GlifListLayout(mContext);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+        }
+        assertListTemplateInflated(layout);
+
+        layout.setDividerInsets(10, 15);
+        assertEquals("Divider inset should be 10", 10, layout.getDividerInsetStart());
+        assertEquals("Divider inset should be 15", 15, layout.getDividerInsetEnd());
+
+        final Drawable divider = layout.getDivider();
+        assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable);
+    }
+
     private void assertListTemplateInflated(GlifListLayout layout) {
         View title = layout.findViewById(R.id.suw_layout_title);
         assertNotNull("@id/suw_layout_title should not be null", title);
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemLayoutTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemLayoutTest.java
new file mode 100644
index 0000000..85876b4
--- /dev/null
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemLayoutTest.java
@@ -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 com.android.setupwizardlib.test;
+
+import static android.support.test.InstrumentationRegistry.getTargetContext;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.support.test.filters.SmallTest;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.widget.FrameLayout;
+
+import com.android.setupwizardlib.R;
+import com.android.setupwizardlib.items.Item;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Sanity test for all the item layouts to make sure they won't crash when being inflated in
+ * different themes.
+ */
+@RunWith(Parameterized.class)
+@SmallTest
+public class ItemLayoutTest {
+
+    @Parameters
+    public static Iterable<Object[]> data() {
+        int[] themes = new int[] {
+                R.style.SuwThemeMaterial_Light,
+                R.style.SuwThemeMaterial,
+                R.style.SuwThemeGlif_Light,
+                R.style.SuwThemeGlif,
+                R.style.SuwThemeGlifV2_Light,
+                R.style.SuwThemeGlifV2
+        };
+        int[] layouts = new int[] {
+                R.layout.suw_items_default,
+                R.layout.suw_items_verbose,
+                R.layout.suw_items_description
+        };
+
+        // Test all the possible combinations of themes and layouts.
+        List<Object[]> params = new ArrayList<>();
+        for (int theme : themes) {
+            for (int layout : layouts) {
+                params.add(new Object[] { theme, layout });
+            }
+        }
+        return params;
+    }
+
+    private final Context mContext;
+    private final FrameLayout mParent;
+    private final Item mItem;
+
+    public ItemLayoutTest(int theme, int layout) {
+        mContext = new ContextThemeWrapper(getTargetContext(), theme);
+        mParent = new FrameLayout(mContext);
+        mItem = new Item();
+        mItem.setLayoutResource(layout);
+    }
+
+    @Test
+    public void testInflateLayoutHasBasicViews() {
+        LayoutInflater.from(mContext).inflate(mItem.getLayoutResource(), mParent, true);
+        mItem.onBindView(mParent);
+
+        assertNotNull("Title should exist", mParent.findViewById(R.id.suw_items_title));
+        assertNotNull("Summary should exist", mParent.findViewById(R.id.suw_items_summary));
+        assertNotNull("Icon should exist", mParent.findViewById(R.id.suw_items_icon));
+    }
+}
diff --git a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java
similarity index 68%
rename from library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java
rename to library/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java
index 2e2b01e..5f3eb9f 100644
--- a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java
@@ -18,9 +18,16 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
 import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.ContextWrapper;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -30,6 +37,7 @@
 import android.text.style.TextAppearanceSpan;
 
 import com.android.setupwizardlib.span.LinkSpan;
+import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener;
 import com.android.setupwizardlib.view.RichTextView;
 
 import org.junit.Test;
@@ -64,6 +72,45 @@
     }
 
     @Test
+    public void testOnLinkClickListener() {
+        Annotation link = new Annotation("link", "foobar");
+        SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
+        ssb.setSpan(link, 1, 2, 0 /* flags */);
+
+        RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
+        textView.setText(ssb);
+
+        OnLinkClickListener listener = mock(OnLinkClickListener.class);
+        textView.setOnLinkClickListener(listener);
+
+        assertSame(listener, textView.getOnLinkClickListener());
+
+        CharSequence text = textView.getText();
+        LinkSpan[] spans = ((Spanned) text).getSpans(0, text.length(), LinkSpan.class);
+        spans[0].onClick(textView);
+
+        verify(listener).onLinkClick(eq(spans[0]));
+    }
+
+    @Test
+    public void testLegacyContextOnClickListener() {
+        // Click listener implemented by context should still be invoked for compatibility.
+        Annotation link = new Annotation("link", "foobar");
+        SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
+        ssb.setSpan(link, 1, 2, 0 /* flags */);
+
+        TestContext context = spy(new TestContext(InstrumentationRegistry.getTargetContext()));
+        RichTextView textView = new RichTextView(context);
+        textView.setText(ssb);
+
+        CharSequence text = textView.getText();
+        LinkSpan[] spans = ((Spanned) text).getSpans(0, text.length(), LinkSpan.class);
+        spans[0].onClick(textView);
+
+        verify(context).onClick(eq(spans[0]));
+    }
+
+    @Test
     public void testTextStyle() {
         Annotation link = new Annotation("textAppearance", "foobar");
         SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
@@ -85,7 +132,7 @@
     }
 
     @Test
-    public void testTextContaininingLinksAreFocusable() {
+    public void testTextContainingLinksAreFocusable() {
         Annotation testLink = new Annotation("link", "value");
         SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("Linked");
         spannableStringBuilder.setSpan(testLink, 0, 3, 0);
@@ -112,7 +159,7 @@
     // should also be automatically changed.
     @SuppressLint("SetTextI18n")  // It's OK. This is just a test.
     @Test
-    public void testRichTxtViewFocusChangesWithTextChange() {
+    public void testRichTextViewFocusChangesWithTextChange() {
         RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
         textView.setText("Thou shall not be focusable!");
 
@@ -124,4 +171,16 @@
         textView.setText(spannableStringBuilder);
         assertTrue(textView.isFocusable());
     }
+
+    public static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener {
+
+        public TestContext(Context base) {
+            super(base);
+        }
+
+        @Override
+        public void onClick(LinkSpan span) {
+            // Ignore. Can be verified using Mockito
+        }
+    }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardListLayoutTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardListLayoutTest.java
index b33ef31..5c34fe0 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardListLayoutTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardListLayoutTest.java
@@ -16,11 +16,15 @@
 
 package com.android.setupwizardlib.test;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.os.Build;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -87,6 +91,37 @@
                 progressBar instanceof ProgressBar && progressBar.getVisibility() == View.VISIBLE);
     }
 
+    @Test
+    public void testDividerInsetLegacy() {
+        SetupWizardListLayout layout = new SetupWizardListLayout(mContext);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+        }
+        assertListTemplateInflated(layout);
+
+        layout.setDividerInset(10);
+        assertEquals("Divider inset should be 10", 10, layout.getDividerInset());
+
+        final Drawable divider = layout.getDivider();
+        assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable);
+    }
+
+    @Test
+    public void testDividerInsets() {
+        SetupWizardListLayout layout = new SetupWizardListLayout(mContext);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+        }
+        assertListTemplateInflated(layout);
+
+        layout.setDividerInsets(10, 15);
+        assertEquals("Divider inset start should be 10", 10, layout.getDividerInsetStart());
+        assertEquals("Divider inset end should be 15", 15, layout.getDividerInsetEnd());
+
+        final Drawable divider = layout.getDivider();
+        assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable);
+    }
+
     private void assertListTemplateInflated(SetupWizardLayout layout) {
         View decorView = layout.findViewById(R.id.suw_layout_decor);
         View navbar = layout.findViewById(R.id.suw_layout_navigation_bar);
diff --git a/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java b/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java
index 7dd0a6f..f86e057 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java
@@ -56,6 +56,18 @@
         // Just check that no uncaught exception here.
     }
 
+    @Test
+    public void testWrappedListener() {
+        final TestContext context = new TestContext(application);
+        final Context wrapperContext = new ContextWrapper(context);
+        final TextView textView = new TextView(wrapperContext);
+        final LinkSpan linkSpan = new LinkSpan("test_id");
+
+
+        linkSpan.onClick(textView);
+        assertSame("Clicked LinkSpan should be passed to setup", linkSpan, context.clickedSpan);
+    }
+
     private static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener {
 
         public LinkSpan clickedSpan = null;
diff --git a/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java b/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java
index d518e08..4c460c8 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java
@@ -80,6 +80,14 @@
     }
 
     @Test
+    public void testIsDeferredSetupTrue() {
+        final Intent intent = new Intent();
+        intent.putExtra("deferredSetup", true);
+        assertTrue("Is deferred setup wizard should be true",
+                WizardManagerHelper.isDeferredSetupWizard(intent));
+    }
+
+    @Test
     public void testIsSetupWizardFalse() {
         final Intent intent = new Intent();
         intent.putExtra("firstRun", false);
@@ -140,22 +148,22 @@
     }
 
     @Test
-    public void testGlifPixelIsDarkTheme() {
+    public void testGlifV2IsDarkTheme() {
         final Intent intent = new Intent();
-        intent.putExtra("theme", "glif_pixel");
-        assertFalse("Theme glif_pixel should be dark theme",
+        intent.putExtra("theme", "glif_v2");
+        assertFalse("Theme glif_v2 should be dark theme",
                 WizardManagerHelper.isLightTheme(intent, false));
-        assertFalse("Theme glif_pixel should be dark theme",
+        assertFalse("Theme glif_v2 should be dark theme",
                 WizardManagerHelper.isLightTheme(intent, true));
     }
 
     @Test
-    public void testGlifPixelLightIsLightTheme() {
+    public void testGlifV2LightIsLightTheme() {
         final Intent intent = new Intent();
-        intent.putExtra("theme", "glif_pixel_light");
-        assertTrue("Theme glif_pixel_light should be light theme",
+        intent.putExtra("theme", "glif_v2_light");
+        assertTrue("Theme glif_v2_light should be light theme",
                 WizardManagerHelper.isLightTheme(intent, false));
-        assertTrue("Theme glif_pixel_light should be light theme",
+        assertTrue("Theme glif_v2_light should be light theme",
                 WizardManagerHelper.isLightTheme(intent, true));
     }
 
@@ -195,15 +203,15 @@
     }
 
     @Test
-    public void testGetThemeResGlifPixelLight() {
-        assertEquals(R.style.SuwThemeGlifPixel_Light,
-                WizardManagerHelper.getThemeRes("glif_pixel_light", 0));
+    public void testGetThemeResGlifV2Light() {
+        assertEquals(R.style.SuwThemeGlifV2_Light,
+                WizardManagerHelper.getThemeRes("glif_v2_light", 0));
     }
 
     @Test
-    public void testGetThemeResGlifPixel() {
-        assertEquals(R.style.SuwThemeGlifPixel,
-                WizardManagerHelper.getThemeRes("glif_pixel", 0));
+    public void testGetThemeResGlifV2() {
+        assertEquals(R.style.SuwThemeGlifV2,
+                WizardManagerHelper.getThemeRes("glif_v2", 0));
     }
 
     @Test
@@ -257,6 +265,7 @@
                 .putExtra(WizardManagerHelper.EXTRA_THEME, "test_theme")
                 .putExtra(WizardManagerHelper.EXTRA_WIZARD_BUNDLE, wizardBundle)
                 .putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true)
+                .putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, true)
                 // Script URI and Action ID are kept for backwards compatibility
                 .putExtra(WizardManagerHelper.EXTRA_SCRIPT_URI, "test_script_uri")
                 .putExtra(WizardManagerHelper.EXTRA_ACTION_ID, "test_action_id");
@@ -273,6 +282,8 @@
 
         assertTrue("EXTRA_IS_FIRST_RUN should be copied",
                 intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, false));
+        assertTrue("EXTRA_IS_DEFERRED_SETUP should be copied",
+                intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, false));
 
         // Script URI and Action ID are replaced by Wizard Bundle in M, but are kept for backwards
         // compatibility
diff --git a/library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java b/library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java
new file mode 100644
index 0000000..f1332c0
--- /dev/null
+++ b/library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.view;
+
+import static org.junit.Assert.assertEquals;
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.view.View;
+import android.view.View.MeasureSpec;
+
+import com.android.setupwizardlib.BuildConfig;
+import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+
+@RunWith(SuwLibRobolectricTestRunner.class)
+@Config(constants = BuildConfig.class, sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
+public class FillContentLayoutTest {
+
+    @Test
+    public void testMeasureMinSize() {
+        FillContentLayout layout = new FillContentLayout(
+                application,
+                Robolectric.buildAttributeSet()
+                        .addAttribute(android.R.attr.minWidth, "123dp")
+                        .addAttribute(android.R.attr.minHeight, "123dp")
+                        .build());
+        layout.measure(
+                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+
+        assertEquals(123, layout.getMeasuredWidth());
+        assertEquals(123, layout.getMeasuredHeight());
+    }
+
+    @Test
+    public void testMeasureChildIsSmallerThanMaxSize() {
+        View child = new View(application);
+        FillContentLayout layout = new FillContentLayout(
+                application,
+                Robolectric.buildAttributeSet()
+                        .addAttribute(android.R.attr.maxWidth, "123dp")
+                        .addAttribute(android.R.attr.maxHeight, "123dp")
+                        .build());
+        layout.addView(child);
+        layout.measure(
+                MeasureSpec.makeMeasureSpec(300, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(300, MeasureSpec.EXACTLY));
+
+        assertEquals(123, child.getMeasuredWidth());
+        assertEquals(123, child.getMeasuredHeight());
+    }
+
+    @Test
+    public void testMeasureChildIsSmallerThanParent() {
+        View child = new View(application);
+        FillContentLayout layout = new FillContentLayout(
+                application,
+                Robolectric.buildAttributeSet()
+                        .addAttribute(android.R.attr.maxWidth, "123dp")
+                        .addAttribute(android.R.attr.maxHeight, "123dp")
+                        .build());
+        layout.addView(child);
+        layout.measure(
+                MeasureSpec.makeMeasureSpec(88, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(88, MeasureSpec.EXACTLY));
+
+        assertEquals(88, child.getMeasuredWidth());
+        assertEquals(88, child.getMeasuredHeight());
+    }
+}
diff --git a/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java b/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
new file mode 100644
index 0000000..ffa228d
--- /dev/null
+++ b/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.view;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.media.MediaPlayer;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.RawRes;
+import android.view.Surface;
+
+import com.android.setupwizardlib.BuildConfig;
+import com.android.setupwizardlib.R;
+import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
+import com.android.setupwizardlib.view.IllustrationVideoViewTest.ShadowMockMediaPlayer;
+import com.android.setupwizardlib.view.IllustrationVideoViewTest.ShadowSurface;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.internal.Shadow;
+import org.robolectric.shadows.ShadowMediaPlayer;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SuwLibRobolectricTestRunner.class)
+@Config(
+        constants = BuildConfig.class,
+        sdk = Config.NEWEST_SDK,
+        shadows = {
+                ShadowMockMediaPlayer.class,
+                ShadowSurface.class
+        })
+public class IllustrationVideoViewTest {
+
+    @Mock
+    private SurfaceTexture mSurfaceTexture;
+
+    private IllustrationVideoView mView;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowMockMediaPlayer.reset();
+    }
+
+    @Test
+    public void testPausedWhenWindowFocusLost() {
+        createDefaultView();
+        mView.start();
+
+        assertNotNull(mView.mMediaPlayer);
+        assertNotNull(mView.mSurface);
+
+        mView.onWindowFocusChanged(false);
+        verify(ShadowMockMediaPlayer.getMock()).pause();
+    }
+
+    @Test
+    public void testStartedWhenWindowFocusRegained() {
+        testPausedWhenWindowFocusLost();
+
+        // Clear verifications for calls in the other test
+        reset(ShadowMockMediaPlayer.getMock());
+
+        mView.onWindowFocusChanged(true);
+        verify(ShadowMockMediaPlayer.getMock()).start();
+    }
+
+    @Test
+    public void testSurfaceReleasedWhenTextureDestroyed() {
+        createDefaultView();
+        mView.start();
+
+        assertNotNull(mView.mMediaPlayer);
+        assertNotNull(mView.mSurface);
+
+        mView.onSurfaceTextureDestroyed(mSurfaceTexture);
+        verify(ShadowMockMediaPlayer.getMock()).release();
+    }
+
+    @Test
+    public void testXmlSetVideoResId() {
+        createDefaultView();
+        assertEquals(android.R.color.white, ShadowMockMediaPlayer.sResId);
+    }
+
+    @Test
+    public void testSetVideoResId() {
+        createDefaultView();
+
+        @RawRes int black = android.R.color.black;
+        mView.setVideoResource(black);
+
+        assertEquals(android.R.color.black, ShadowMockMediaPlayer.sResId);
+    }
+
+    private void createDefaultView() {
+        mView = new IllustrationVideoView(
+                application,
+                Robolectric.buildAttributeSet()
+                        // Any resource attribute should work, since the media player is mocked
+                        .addAttribute(R.attr.suwVideo, "@android:color/white")
+                        .build());
+        mView.onSurfaceTextureAvailable(mSurfaceTexture, 500, 500);
+    }
+
+    @Implements(MediaPlayer.class)
+    public static class ShadowMockMediaPlayer extends ShadowMediaPlayer {
+
+        private static MediaPlayer sMediaPlayer = mock(MediaPlayer.class);
+        private static int sResId;
+
+        public static void reset() {
+            sMediaPlayer = mock(MediaPlayer.class);
+            sResId = 0;
+        }
+
+        @Implementation
+        public static MediaPlayer create(Context context, int resId) {
+            sResId = resId;
+            return sMediaPlayer;
+        }
+
+        public static MediaPlayer getMock() {
+            return sMediaPlayer;
+        }
+    }
+
+    @Implements(Surface.class)
+    @TargetApi(VERSION_CODES.HONEYCOMB)
+    public static class ShadowSurface extends org.robolectric.shadows.ShadowSurface {
+
+        @RealObject
+        private Surface mRealSurface;
+
+        public void __constructor__(SurfaceTexture surfaceTexture) {
+            // Call the constructor on the real object, so that critical fields such as mLock is
+            // initialized properly.
+            Shadow.invokeConstructor(Surface.class, mRealSurface,
+                    ReflectionHelpers.ClassParameter.from(SurfaceTexture.class, surfaceTexture));
+            super.__constructor__(surfaceTexture);
+        }
+    }
+}
diff --git a/navigationbar/res/values-pt-rBR/strings.xml b/navigationbar/res/values-pt-rBR/strings.xml
index c8104d7..181fafe 100644
--- a/navigationbar/res/values-pt-rBR/strings.xml
+++ b/navigationbar/res/values-pt-rBR/strings.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="setup_wizard_next_button_label" msgid="6681282266022780599">"Próximo"</string>
+    <string name="setup_wizard_next_button_label" msgid="6681282266022780599">"Próxima"</string>
     <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Voltar"</string>
 </resources>
diff --git a/navigationbar/res/values-pt/strings.xml b/navigationbar/res/values-pt/strings.xml
index c8104d7..181fafe 100644
--- a/navigationbar/res/values-pt/strings.xml
+++ b/navigationbar/res/values-pt/strings.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="setup_wizard_next_button_label" msgid="6681282266022780599">"Próximo"</string>
+    <string name="setup_wizard_next_button_label" msgid="6681282266022780599">"Próxima"</string>
     <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Voltar"</string>
 </resources>
diff --git a/tools/build_for_build_server.sh b/tools/build_for_build_server.sh
index 14b5a3b..a90ae67 100755
--- a/tools/build_for_build_server.sh
+++ b/tools/build_for_build_server.sh
@@ -6,4 +6,4 @@
 export TARGET_BUILD_TYPE="release"
 export TARGET_BUILD_APPS="setup-wizard-lib"
 
-./gradlew
+./gradlew buildProjectFull test
diff --git a/tools/checkstyle/checkstyle.xml b/tools/checkstyle/checkstyle.xml
new file mode 100644
index 0000000..0dbccae
--- /dev/null
+++ b/tools/checkstyle/checkstyle.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd" [
+  <!ENTITY defaultCopyrightCheck SYSTEM "../../../../../prebuilts/checkstyle/default-copyright-check.xml">
+  <!ENTITY defaultJavadocChecks SYSTEM "../../../../../prebuilts/checkstyle/default-javadoc-checks.xml">
+  <!ENTITY defaultTreewalkerChecks SYSTEM "../../../../../prebuilts/checkstyle/default-treewalker-checks.xml">
+  <!ENTITY defaultModuleChecks SYSTEM "../../../../../prebuilts/checkstyle/default-module-checks.xml">
+]>
+
+<module name="Checker">
+  &defaultModuleChecks;
+  &defaultCopyrightCheck;
+  <module name="TreeWalker">
+    &defaultJavadocChecks;
+    &defaultTreewalkerChecks;
+  </module>
+
+  <module name="SuppressionFilter">
+    <property name="file" value="tools/checkstyle/checkstyle_suppression.xml" />
+  </module>
+</module>
diff --git a/tools/checkstyle/checkstyle_suppression.xml b/tools/checkstyle/checkstyle_suppression.xml
new file mode 100644
index 0000000..6bf7b21
--- /dev/null
+++ b/tools/checkstyle/checkstyle_suppression.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN" "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
+<suppressions>
+
+    <!-- Note: Checkstyle puts the absolute path of files through the suppress filter, so the
+         patterns below will match sub-directories. Notably, for overlays where the path is
+         something like overlay/frameworks/opt/setupwizard will match the regex filter
+         "frameworks/opt/setupwizard". This is probably OK for most cases since they are overlay
+         of the original app and should have the same coding style. -->
+
+    <!-- Robolectric uses magic method names like `__constructor__` -->
+    <suppress files="/robotest/" checks="MethodName" />
+
+</suppressions>
diff --git a/tools/gradle/dist-library-instrumentation-tests.gradle b/tools/gradle/dist-library-instrumentation-tests.gradle
new file mode 100644
index 0000000..2be6349
--- /dev/null
+++ b/tools/gradle/dist-library-instrumentation-tests.gradle
@@ -0,0 +1,33 @@
+/**
+ * This script plugin is used to build and dist the test APK outputs of a library with multiple
+ * build flavors.
+ *
+ * Compared to the defaults of the 'dist' plugin, it does two additional things:
+ * 1. It builds the "debug" test APKs when the 'dist' task is run.
+ * 2. It dist the test APKs using the original output file name instead of hard coding
+ *    "${project.archivesBaseName}Tests.apk". This allows multiple flavors of test APKs to be built
+ *    without conflicting file names.
+ */
+
+apply plugin: 'dist'
+
+// Set the dist files to empty map, and to tell DistExtension to not include the default files,
+// because the default output only supports one test APK output for libraries.
+dist.files = [:]
+android.testVariants.all { variant ->
+    // "Debug" tests are not built by BuildSrc by default. Depend on the task so it will be built.
+    tasks.dist.dependsOn variant.assemble
+
+    // Output all test APKs to the distribution folder.
+    // For a project named "setup-wizard-lib" with build flavor "platform" and build type "debug",
+    // the output file will be named "setup-wizard-lib-platform-debug-androidTest.apk"
+    variant.outputs.each { output ->
+        dist.file output.outputFile.canonicalPath, output.outputFile.name
+    }
+}
+android.libraryVariants.all { variant ->
+    // Output all library AARs to the distribution folder
+    variant.outputs.each { output ->
+        dist.file output.outputFile.canonicalPath, output.outputFile.name
+    }
+}
diff --git a/tools/gradle/dist-unit-tests.gradle b/tools/gradle/dist-unit-tests.gradle
new file mode 100644
index 0000000..faae260
--- /dev/null
+++ b/tools/gradle/dist-unit-tests.gradle
@@ -0,0 +1,38 @@
+/**
+ * This script plugin is used to bundle the host test (e.g. Robolectric) results and dist it in
+ * a location where TradeFed knows how to parse.
+ *
+ * - If a non-dist build is run with test, it will run the normal unit tests, failing the build if
+ *   there are test failures.
+ * - If a dist build is run with test (e.g. ./gradlew dist test), the build will ignore any test
+ *   failures, and will create a zip of the XML test reports for each test run, and copy them to
+ *   dist/host-test-reports for consumption by TradeFed.
+ */
+
+apply plugin: 'dist'
+
+// If unit tests are run as part of the build, dist the test XML reports to host-test-reports/*.zip
+android.unitTestVariants.all { variant ->
+    def task = tasks.findByName('test' + variant.name.capitalize())
+    gradle.taskGraph.whenReady { taskGraph ->
+        // Ignore the failures, so the build continues even on test errors when the build is
+        // running with 'dist'. (Usually as part of a build server build)
+        task.ignoreFailures = taskGraph.hasTask(tasks.dist)
+    }
+
+    def junitReport = task.reports.junitXml
+    if (junitReport.enabled) {
+        // Create a zip file of the XML test reports
+        def zipTask = tasks.create("zipResultsOf${task.name.capitalize()}", Zip) {
+            from junitReport.destination
+            archiveName = task.name + 'Result.zip'
+            destinationDir = junitReport.destination.parentFile
+        }
+        zipTask.mustRunAfter task
+
+        // Copy the test reports to dist/host-test-reports
+        // The file path and format should match GradleHostBasedTest class in TradeFed.
+        tasks.dist.dependsOn zipTask
+        dist.file zipTask.archivePath.path, "host-test-reports/${zipTask.archiveName}"
+    }
+}